mc-cms-namelessmc/core/init.php
tadhgboyle e569c92cde
Update Template config to use Settings system rather than Cache (#3669)
* Update Template config to use Settings system rather than Cache

* Remove is_default from templates, use only the Settings value

* Convert template_settings

* lint

* fix

* fix
2025-06-04 21:45:50 -07:00

549 lines
19 KiB
PHP

<?php
/*
* Made by Samerton
* https://github.com/NamelessMC/Nameless/
* NamelessMC version 2.1.1
*
* License: MIT
*
* Initialisation file
*/
// Nameless error handling
set_exception_handler([ErrorHandler::class, 'catchException']);
// catchError() used for throw_error or any exceptions which may be missed by catchException()
set_error_handler([ErrorHandler::class, 'catchError']);
register_shutdown_function([ErrorHandler::class, 'catchShutdownError']);
session_start();
// Page variable must be set
if (!isset($page)) {
die('$page variable is unset. Cannot continue.');
}
Debugging::setCanViewDetailedError(defined('DEBUGGING') && DEBUGGING);
Debugging::setCanGenerateDebugLink(defined('DEBUGGING') && DEBUGGING);
// All paths should be writable, but recursively checking everything would take too much time.
// Only check the most important paths.
$writable_check_paths = [
ROOT_PATH,
ROOT_PATH . '/cache',
ROOT_PATH . '/cache/logs',
ROOT_PATH . '/cache/sitemaps',
ROOT_PATH . '/cache/templates_c',
ROOT_PATH . '/uploads',
ROOT_PATH . '/core/config.php',
];
foreach ($writable_check_paths as $path) {
if (is_dir($path) && !is_writable($path)) {
$message = '<p>Your website directory or a subdirectory is not writable. Please ensure all files and directories are owned by
the correct user.</p>';
if (function_exists('posix_geteuid')) {
$uid = posix_geteuid();
$gid = posix_getegid();
$chown_command = 'sudo chown -R ' . $uid . ':' . $gid . ' ' . Output::getClean(ROOT_PATH);
$message .= '<p>The command to fix this for your system was determined to be: <code>' . $chown_command . '</code>. Please check if it makes sense before running it.</p>';
} else {
$message .= '<p><strong>Example</strong> command to change owner recursively: <code>sudo chown -R www-data: ' . Output::getClean(ROOT_PATH) . '</code></p>';
}
die($message);
}
}
if (!file_exists(ROOT_PATH . '/cache/templates_c')) {
try {
mkdir(ROOT_PATH . '/cache/templates_c', 0777, true);
} catch (Exception $e) {
die('Unable to create <strong>/cache</strong> directories, please check your file permissions.');
}
}
if (!Config::exists()) {
$page = 'install';
}
// If we're accessing the upgrade script don't initialise further
if (isset($_GET['route']) && rtrim($_GET['route'], '/') == '/panel/upgrade') {
$pages = new Pages();
$pages->add('Core', '/panel/upgrade', 'pages/panel/upgrade.php');
return;
}
if ($page != 'install') {
/*
* Initialise
*/
$container = new \DI\Container();
$container->set(Cache::class, function () {
return new Cache([
'name' => 'nameless',
'extension' => '.cache',
'path' => ROOT_PATH . '/cache/',
]);
});
$cache = $container->get(Cache::class);
// Friendly URLs?
define('FRIENDLY_URLS', Config::get('core.friendly') == 'true');
// Force https/www?
if (Config::get('core.force_https')) {
define('FORCE_SSL', true);
}
if (Config::get('core.force_www')) {
define('FORCE_WWW', true);
}
$host = HttpUtils::getHeader('Host');
// Only check force HTTPS and force www. when Host header is set
// These options don't make sense when making requests to IP addresses anyway
if ($host !== null) {
if (defined('FORCE_SSL') && HttpUtils::getProtocol() === 'http') {
if (defined('FORCE_WWW') && !str_contains($host, 'www.')) {
Redirect::to('https://www.' . $host . $_SERVER['REQUEST_URI']);
} else {
Redirect::to('https://' . $host . $_SERVER['REQUEST_URI']);
}
} elseif (defined('FORCE_WWW') && !str_contains($host, 'www.')) {
Redirect::to(HttpUtils::getProtocol() . '://www.' . $host . $_SERVER['REQUEST_URI']);
}
}
// Ensure database is up-to-date
PhinxAdapter::ensureUpToDate('Core');
// Error reporting
if (!defined('DEBUGGING')) {
if (Settings::get('error_reporting') === '1') {
ini_set('display_startup_errors', 1);
ini_set('display_errors', 1);
error_reporting(-1);
define('DEBUGGING', 1);
} else {
// Disable by default
error_reporting(0);
ini_set('display_errors', 0);
}
}
if ((defined('DEBUGGING') && DEBUGGING) && class_exists('DebugBar\DebugBar')) {
define('PHPDEBUGBAR', true);
DebugBarHelper::getInstance()->enable();
}
// Get the Nameless version
define('NAMELESS_VERSION', Settings::get('nameless_version'));
// Set the date format
define('DATE_FORMAT', Config::get('core.date_format') ?: 'd M Y, H:i');
// User initialisation
$user = new User();
// Do they need logging in (checked remember me)?
if (Cookie::exists(Config::get('remember.cookie_name')) && !Session::exists(Config::get('session.session_name'))) {
$hash = Cookie::get(Config::get('remember.cookie_name'));
$hashCheck = DB::getInstance()->get('users_session', [['hash', $hash], ['active', true]]);
if ($hashCheck->count()) {
$user = new User($hashCheck->first()->user_id);
$user->login(null, $hash, true, 'hash');
}
}
// Check if we're in a subdirectory
if (isset($directories)) {
if (empty($directories[0])) {
unset($directories[0]);
}
$directories = array_values($directories);
$config_path = Config::get('core.path');
if (!empty($config_path)) {
$config_path = explode('/', Config::get('core.path'));
for ($i = 0, $iMax = count($config_path); $i < $iMax; $i++) {
unset($directories[$i]);
}
define('CONFIG_PATH', '/' . Config::get('core.path'));
$directories = array_values($directories);
}
$directory = implode('/', $directories);
$directory = '/' . $directory;
// Remove the trailing /
if (strlen($directory) > 1) {
$directory = rtrim($directory, '/');
}
}
// Set timezone
define('TIMEZONE', $user->isLoggedIn() ? $user->data()->timezone : Settings::get('timezone', 'Europe/London'));
date_default_timezone_set(TIMEZONE);
// Language
$cache->setCache('languagecache');
$default_language = $cache->fetch('language', function () {
$default_language = DB::getInstance()->get('languages', ['is_default', true])->results();
if (count($default_language)) {
return $default_language[0]->short_code;
}
return 'en_UK';
});
define('DEFAULT_LANGUAGE', $default_language);
if (!$user->isLoggedIn() || !$user->data()->language_id) {
if (Settings::get('auto_language_detection') && (!Cookie::exists('auto_language') || Cookie::get('auto_language') === 'true')) {
// Attempt to get the requested language from the browser if it exists
$automatic_locale = Language::acceptFromHttp(HttpUtils::getHeader('Accept-Language') ?? '');
if ($automatic_locale !== false) {
define('AUTO_LANGUAGE_VALUE', $automatic_locale[1]);
$default_language = $automatic_locale[0];
}
}
// Default language for guests
define('LANGUAGE', $default_language);
} else {
// User selected language
$language = DB::getInstance()->get('languages', ['id', $user->data()->language_id])->results();
if (!count($language)) {
// Get default language
define('LANGUAGE', $default_language);
} else {
define('LANGUAGE', $language[0]->short_code);
}
}
$container->set(Language::class, function () {
return new Language('core', LANGUAGE);
});
$language = $container->get(Language::class);
// Site name
$sitename = Settings::get('sitename');
if ($sitename === null) {
die('No sitename in settings table');
}
define('SITE_NAME', $sitename);
// Template
if (!$user->isLoggedIn() || !$user->data()->theme_id) {
// Default template for guests
define('TEMPLATE', Settings::get('default_template', 'DefaultRevamp'));
} else {
// User selected template
$template = DB::getInstance()->get('templates', ['id', $user->data()->theme_id])->results();
if (!count($template)) {
// Get default template
define('TEMPLATE', Settings::get('default_template', 'DefaultRevamp'));
} else {
// Check permissions
$template = $template[0];
$hasPermission = false;
if ($template->enabled) {
$user_templates = $user->getUserTemplates();
foreach ($user_templates as $user_template) {
if ($user_template->id === $template->id) {
$hasPermission = true;
define('TEMPLATE', $template->name);
break;
}
}
}
if (!$hasPermission) {
// Get default template
define('TEMPLATE', Settings::get('default_template', 'DefaultRevamp'));
}
}
}
// Panel template
define('PANEL_TEMPLATE', Settings::get('default_panel_template', 'Default'));
// Navbar links
$navigation = new Navigation();
$cc_nav = new Navigation();
$staffcp_nav = new Navigation(true); // $staffcp_nav = panel nav
// Add links to cc_nav
$cc_nav->add('cc_overview', $language->get('user', 'overview'), URL::build('/user'));
$cc_nav->add('cc_alerts', $language->get('user', 'alerts'), URL::build('/user/alerts'));
$cc_nav->add('cc_messaging', $language->get('user', 'messaging'), URL::build('/user/messaging'));
$cc_nav->add('cc_connections', $language->get('user', 'connections'), URL::build('/user/connections'));
$cc_nav->add('cc_notification_settings', $language->get('user', 'notification_settings'), URL::build('/user/notification_settings'));
$cc_nav->add('cc_settings', $language->get('user', 'profile_settings'), URL::build('/user/settings'));
$cc_nav->add('cc_sessions', $language->get('general', 'sessions'), URL::build('/user/sessions'));
// Placeholders enabled?
if (Settings::get('placeholders') === '1') {
$cc_nav->add('cc_placeholders', $language->get('user', 'placeholders'), URL::build('/user/placeholders'));
}
// Add homepage to navbar
// Check navbar order + icon in cache
$cache->setCache('navbar_order');
$home_order = $cache->fetch('index_order', 1);
$cache->setCache('navbar_icons');
$home_icon = $cache->fetch('index_icon', '');
$navigation->add('index', $language->get('general', 'home'), URL::build('/'), 'top', null, $home_order, $home_icon);
$endpoints = $container->get(Endpoints::class);
$announcements = $container->get(Announcements::class);
// Modules
$cache->setCache('modulescache');
if (!$cache->isCached('enabled_modules')) {
$cache->store('enabled_modules', [
['name' => 'Core', 'priority' => 1],
]);
$cache->store('module_core', true);
}
$enabled_modules = $cache->retrieve('enabled_modules');
foreach ($enabled_modules as $module) {
if ($module['name'] == 'Core') {
$core_exists = true;
break;
}
}
if (!isset($core_exists)) {
$enabled_modules[] = [
'name' => 'Core',
'priority' => 1,
];
}
$pages = $container->get(Pages::class);
// Sort by priority
usort($enabled_modules, static function ($a, $b) {
return $a['priority'] - $b['priority'];
});
// Load module dependencies
foreach ($enabled_modules as $module) {
if (file_exists(ROOT_PATH . '/modules/' . $module['name'] . '/autoload.php')) {
require_once ROOT_PATH . '/modules/' . $module['name'] . '/autoload.php';
}
}
// Load modules
foreach ($enabled_modules as $module) {
if (file_exists(ROOT_PATH . '/modules/' . $module['name'] . '/init.php')) {
require_once ROOT_PATH . '/modules/' . $module['name'] . '/init.php';
}
}
// Maintenance mode?
if (Settings::get('maintenance') === '1') {
// Enabled
// Admins only beyond this point
if (!$user->isLoggedIn() || !$user->canViewStaffCP()) {
// Maintenance mode
if (isset($_GET['route']) && (
rtrim($_GET['route'], '/') === '/login'
|| rtrim($_GET['route'], '/') === '/forgot_password'
|| str_contains($_GET['route'], '/api/')
|| str_contains($_GET['route'], 'queries')
|| str_contains($_GET['route'], 'oauth/')
|| str_contains($_GET['route'], 'store/listener')
)) {
// Can continue as normal
} else {
require(ROOT_PATH . '/core/includes/maintenance.php');
die;
}
} else {
// Display notice to admin stating maintenance mode is enabled
define('BYPASS_MAINTENANCE', true);
}
}
// Webhooks
$hook_array = [];
if (Util::isModuleEnabled('Discord Integration')) {
$cache->setCache('hooks');
$hook_array = $cache->fetch('hooks', function () {
$hooks = DB::getInstance()->get('hooks', ['id', '<>', 0])->results();
$hook_array = [];
if (count($hooks)) {
foreach ($hooks as $hook) {
if ($hook->action != 1 && $hook->action != 2) {
continue;
}
// TODO: more extendable webhook system, #2676
if ($hook->action == 2 && !class_exists(DiscordHook::class)) {
continue;
}
$hook_array[] = [
'id' => $hook->id,
'url' => Output::getClean($hook->url),
'action' => $hook->action == 1
? [WebHook::class, 'execute']
: [DiscordHook::class, 'execute'],
'events' => json_decode($hook->events, true),
];
}
}
return $hook_array;
});
}
EventHandler::registerWebhooks($hook_array);
// Get IP
$ip = HttpUtils::getRemoteAddress();
// 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)) {
$user->logout();
Session::flash('home_error', $language->get('user', 'you_have_been_banned'));
Redirect::to(URL::build('/'));
}
// Update user last IP and last online
if (filter_var($ip, FILTER_VALIDATE_IP)) {
$user->update([
'last_online' => date('U'),
'lastip' => $ip,
]);
} else {
$user->update([
'last_online' => date('U'),
]);
}
// Insert it into the logs
$user_ip_logged = DB::getInstance()->get('users_ips', ['ip', $ip])->results();
if (!count($user_ip_logged)) {
// Create the entry now
DB::getInstance()->insert('users_ips', [
'user_id' => $user->data()->id,
'ip' => $ip,
]);
} else {
if (count($user_ip_logged) > 1) {
foreach ($user_ip_logged as $user_ip) {
// Check to see if it's been logged by the current user
if ($user_ip->user_id == $user->data()->id) {
// Already logged for this user
$already_logged = true;
break;
}
}
if (!isset($already_logged)) {
// Not yet logged, do so now
DB::getInstance()->insert('users_ips', [
'user_id' => $user->data()->id,
'ip' => $ip,
]);
}
} else {
// Does the entry already belong to the current user?
if ($user_ip_logged[0]->user_id != $user->data()->id) {
DB::getInstance()->insert('users_ips', [
'user_id' => $user->data()->id,
'ip' => $ip,
]);
}
}
}
// Does their group have TFA forced?
foreach ($user->getGroups() as $group) {
if ($group->force_tfa) {
$forced = true;
break;
}
}
if (isset($forced) && $forced) {
// Do they have TFA configured?
if (!$user->data()->tfa_enabled && rtrim($_GET['route'], '/') != '/logout') {
if (!str_contains($_SERVER['REQUEST_URI'], 'do=enable_tfa') && !isset($_SERVER['HTTP_X_REQUESTED_WITH'])) {
Session::put('force_tfa_alert', $language->get('admin', 'force_tfa_alert'));
Redirect::to(URL::build('/user/settings', 'do=enable_tfa'));
}
}
}
$user_integrations = [];
foreach ($user->getIntegrations() as $integrationUser) {
$user_integrations[$integrationUser->getIntegration()->getName()] = [
'username' => Output::getClean($integrationUser->data()->username),
'identifier' => Output::getClean($integrationUser->data()->identifier),
];
}
} else {
// Perform tasks for guests
if (!$_SESSION['checked'] || (isset($_SESSION['checked']) && $_SESSION['checked'] <= strtotime('-5 minutes'))) {
$already_online = DB::getInstance()->get('online_guests', ['ip', $ip])->results();
$date = date('U');
if (count($already_online)) {
DB::getInstance()->update('online_guests', $already_online[0]->id, ['last_seen' => $date]);
} else {
DB::getInstance()->insert('online_guests', ['ip' => $ip, 'last_seen' => $date]);
}
$_SESSION['checked'] = $date;
}
// Auto language enabled?
if (Settings::get('auto_language_detection')) {
define('AUTO_LANGUAGE', true);
}
}
// Dark mode
$darkMode = Settings::get('dark_mode', '0');
if ($user->isLoggedIn()) {
$darkMode = $user->data()->night_mode !== null ? $user->data()->night_mode : $darkMode;
} else {
if (Cookie::exists('night_mode')) {
$darkMode = Cookie::get('night_mode');
}
}
define('DARK_MODE', $darkMode);
}