fix: Handle empty database on fresh install (#4467)

* fix: Handle empty database on fresh install
* feat: Add migration progress bar with jQuery AJAX

- Session.php: Switch to file-based sessions when migrations table doesn't exist
- OSPOS.php: Catch DatabaseException when config table missing, set defaults
- MY_Migration.php: Handle database connection failures gracefully
- Load_config.php: Set default language settings when config empty
---------

Co-authored-by: Ollama <ollama@steganos.dev>
This commit is contained in:
jekkos
2026-04-08 20:19:25 +00:00
committed by GitHub
parent 71056d9b03
commit 7f9321eca0
59 changed files with 800 additions and 176 deletions

View File

@@ -1,3 +0,0 @@
FROM php:8.4-cli
RUN apt-get update && apt-get install -y libicu-dev && docker-php-ext-install intl
WORKDIR /app

View File

@@ -70,7 +70,7 @@ class Filters extends BaseFilters
public array $globals = [
'before' => [
'honeypot',
'csrf' => ['except' => 'login'],
'csrf' => ['except' => 'login|migrate'],
'invalidchars',
],
'after' => [

View File

@@ -5,6 +5,7 @@ namespace Config;
use App\Models\Appconfig;
use CodeIgniter\Cache\CacheInterface;
use CodeIgniter\Config\BaseConfig;
use CodeIgniter\Database\Exceptions\DatabaseException;
/**
* This class holds the configuration options stored from the database so that on launch those settings can be cached
@@ -34,11 +35,21 @@ class OSPOS extends BaseConfig
if ($cache) {
$this->settings = decode_array($cache);
} else {
$appconfig = model(Appconfig::class);
foreach ($appconfig->get_all()->getResult() as $app_config) {
$this->settings[$app_config->key] = $app_config->value;
try {
$appconfig = model(Appconfig::class);
foreach ($appconfig->get_all()->getResult() as $app_config) {
$this->settings[$app_config->key] = $app_config->value;
}
$this->cache->save('settings', encode_array($this->settings));
} catch (DatabaseException $e) {
// Database table doesn't exist yet (migrations haven't run)
// Return empty settings to allow migration page to display
$this->settings = [
'language' => 'english',
'language_code' => 'en',
'company' => 'Home'
];
}
$this->cache->save('settings', encode_array($this->settings));
}
}
@@ -50,4 +61,4 @@ class OSPOS extends BaseConfig
$this->cache->delete('settings');
$this->set_settings();
}
}
}

View File

@@ -10,6 +10,7 @@ $routes->setDefaultController('Login');
$routes->get('/', 'Login::index');
$routes->get('login', 'Login::index');
$routes->post('login', 'Login::index');
$routes->post('migrate', 'Login::migrate');
$routes->add('no_access/index/(:segment)', 'No_access::index/$1');
$routes->add('no_access/index/(:segment)/(:segment)', 'No_access::index/$1/$2');

View File

@@ -5,6 +5,8 @@ namespace Config;
use CodeIgniter\Config\BaseConfig;
use CodeIgniter\Session\Handlers\BaseHandler;
use CodeIgniter\Session\Handlers\DatabaseHandler;
use CodeIgniter\Session\Handlers\FileHandler;
use Config\Database;
class Session extends BaseConfig
{
@@ -124,4 +126,23 @@ class Session extends BaseConfig
* seconds.
*/
public int $lockMaxRetries = 300;
public function __construct()
{
parent::__construct();
if ($this->driver === DatabaseHandler::class) {
try {
$db = Database::connect();
if (!$db->tableExists($this->savePath)) {
$this->driver = FileHandler::class;
$this->savePath = WRITEPATH . 'session';
}
} catch (\CodeIgniter\Database\Exceptions\DatabaseException $e) {
$this->driver = FileHandler::class;
$this->savePath = WRITEPATH . 'session';
}
}
}
}

View File

@@ -2,6 +2,7 @@
namespace App\Controllers;
use App\Libraries\MY_Migration;
use CodeIgniter\HTTP\RedirectResponse;
use CodeIgniter\HTTP\ResponseInterface;
@@ -81,7 +82,7 @@ class Home extends Secure_Controller
if ($this->employee->check_password($this->request->getPost('username', FILTER_SANITIZE_FULL_SPECIAL_CHARS), $this->request->getPost('current_password'))) {
// Validate password length BEFORE hashing
$new_password = $this->request->getPost('password');
if (strlen($new_password) < 8) {
return $this->response->setJSON([
'success' => false,
@@ -89,7 +90,7 @@ class Home extends Secure_Controller
'id' => NEW_ENTRY
]);
}
$employee_data = [
'username' => $this->request->getPost('username', FILTER_SANITIZE_FULL_SPECIAL_CHARS),
'password' => password_hash($new_password, PASSWORD_DEFAULT),
@@ -124,4 +125,4 @@ class Home extends Secure_Controller
]);
}
}
}
}

View File

@@ -5,6 +5,7 @@ namespace App\Controllers;
use App\Libraries\MY_Migration;
use App\Models\Employee;
use CodeIgniter\HTTP\RedirectResponse;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\Model;
use Config\OSPOS;
use Config\Services;
@@ -36,6 +37,7 @@ class Login extends BaseController
$data = [
'has_errors' => false,
'is_new_install' => !(MY_Migration::get_current_version()),
'is_latest' => $migration->is_latest(),
'latest_version' => $migration->get_latest_migration(),
'gcaptcha_enabled' => $gcaptcha_enabled,
@@ -71,4 +73,28 @@ class Login extends BaseController
return redirect()->to('home');
}
public function migrate(): ResponseInterface
{
try {
$migration = new MY_Migration(config('Migrations'));
$migration->migrate_to_ci4();
set_time_limit(3600);
$migration->setNamespace('App')->latest();
return $this->response->setJSON([
'success' => true,
'message' => 'Migration completed successfully'
]);
} catch (\Exception $e) {
log_message('error', 'Migration failed: ' . $e->getMessage());
return $this->response->setJSON([
'success' => false,
'message' => 'Migration failed: ' . $e->getMessage()
])->setStatusCode(500);
}
}
}

View File

@@ -21,63 +21,47 @@ class Load_config
{
public Session $session;
/**
* Loads configuration from database into App CI config and then applies those settings
*/
public function load_config(): void
{
// Migrations
$migration_config = config('Migrations');
$migration = new MY_Migration($migration_config);
// Use file-based session until database is migrated
$this->session = $this->createSession($migration->is_latest());
$this->session = session();
// Database Configuration
$config = config(OSPOS::class);
if (!$migration->is_latest()) {
$this->session->destroy();
}
// Language
$language_exists = file_exists('../app/Language/' . current_language_code());
if (current_language_code() == null || current_language() == null || !$language_exists) { // TODO: current_language() is undefined
$config->settings['language'] = 'english';
$config->settings['language_code'] = 'en';
}
$this->setDefaultLanguage($config);
$language = Services::language();
$language->setLocale($config->settings['language_code']);
$language->setLocale(current_language_code());
// Time Zone
date_default_timezone_set($config->settings['timezone'] ?? ini_get('date.timezone'));
bcscale(max(2, totals_decimals() + tax_decimals()));
}
/**
* Creates session with appropriate handler.
* Uses file-based session until database is migrated, then switches to database session.
*
* This prevents a circular dependency where login requires session, but the sessions
* database table doesn't exist yet because migrations run after login.
*/
private function createSession(bool $isDbMigrated): Session
private function setDefaultLanguage(OSPOS $config): void
{
$sessionConfig = config('Session');
// If database is not migrated and we're configured to use database sessions,
// temporarily fall back to file-based sessions to allow migrations to complete.
// Once migrations run, the user must re-authenticate (session is destroyed in
// load_config() when migrations are pending).
if (!$isDbMigrated && $sessionConfig->driver === DatabaseHandler::class) {
$sessionConfig = clone $sessionConfig;
$sessionConfig->driver = FileHandler::class;
$sessionConfig->savePath = WRITEPATH . 'session';
$languageCode = $config->settings['language_code'] ?? null;
if (empty($config->settings) || $languageCode === null) {
$config->settings['language'] = 'english';
$config->settings['language_code'] = 'en';
return;
}
return Services::session($sessionConfig);
if (!$this->languageExists($languageCode)) {
$config->settings['language'] = 'english';
$config->settings['language_code'] = 'en';
}
}
private function languageExists(string $languageCode): bool
{
return file_exists(APPPATH . 'Language/' . $languageCode);
}
}

View File

@@ -22,9 +22,7 @@ function current_language_code(bool $load_system_language = false): string
}
}
$language_code = $config['language_code'];
return empty($language_code) ? DEFAULT_LANGUAGE_CODE : $language_code;
return $config->language_code ?? DEFAULT_LANGUAGE_CODE;
}
/**
@@ -45,9 +43,7 @@ function current_language(bool $load_system_language = false): string
}
}
$language = $config['language'];
return empty($language) ? DEFAULT_LANGUAGE : $language;
return $config->language ?? DEFAULT_LANGUAGE_CODE;
}
/**

View File

@@ -11,56 +11,54 @@ function check_encryption(): bool
$old_key = config('Encryption')->key;
if ((empty($old_key)) || (strlen($old_key) < 64)) {
// Create Key
$encryption = new Encryption();
$key = bin2hex($encryption->createKey());
config('Encryption')->key = $key;
// Write to .env
$config_path = ROOTPATH . '.env';
$new_config_path = WRITEPATH . '/backup/.env';
$backup_path = WRITEPATH . '/backup/.env.bak';
$backup_folder = WRITEPATH . '/backup';
if (!file_exists($backup_folder) && !mkdir($backup_folder)) {
log_message('error', 'Could not create backup folder');
return false;
if (!file_exists($backup_folder)) {
@mkdir($backup_folder, 0750, true);
}
if (!copy($config_path, $backup_path)) {
log_message('error', "Unable to copy $config_path to $backup_path");
if (!file_exists($config_path)) {
$example_path = ROOTPATH . '.env.example';
if (file_exists($example_path)) {
@copy($example_path, $config_path);
} else {
@file_put_contents($config_path, "# OSPOS Configuration\n\n");
}
@chmod($config_path, 0640);
}
// Copy to backup
@chmod($config_path, 0660);
@chmod($backup_path, 0660);
if (file_exists($config_path)) {
@copy($config_path, $backup_path);
@chmod($backup_path, 0640);
@chmod($config_path, 0640);
$config_file = file_get_contents($config_path);
$config_file = preg_replace("/(encryption\.key.*=.*)('.*')/", "$1'$key'", $config_file);
$config_file = file_get_contents($config_path);
if (!empty($old_key)) {
$old_line = "# encryption.key = '$old_key' REMOVE IF UNNEEDED\r\n";
$insertion_point = stripos($config_file, 'encryption.key');
$config_file = substr_replace($config_file, $old_line, $insertion_point, 0);
if (strpos($config_file, 'encryption.key') !== false) {
$config_file = preg_replace("/(encryption\.key.*=.*)('.*')/", "$1'$key'", $config_file);
} else {
$config_file .= "\nencryption.key = '$key'\n";
}
if (!empty($old_key)) {
$old_line = "# encryption.key = '$old_key' REMOVE IF UNNEEDED\r\n";
$insertion_point = stripos($config_file, 'encryption.key');
if ($insertion_point !== false) {
$config_file = substr_replace($config_file, $old_line, $insertion_point, 0);
}
}
@file_put_contents($config_path, $config_file);
@chmod($config_path, 0640);
log_message('info', "Updated encryption key in $config_path");
}
$handle = @fopen($config_path, 'w+');
if (empty($handle)) {
log_message('error', "Unable to open $config_path for updating");
return false;
}
@chmod($config_path, 0660);
$write_failed = !fwrite($handle, $config_file);
fclose($handle);
if ($write_failed) {
log_message('error', "Unable to write to $config_path for updating.");
return false;
}
log_message('info', "File $config_path has been updated.");
}
return true;
@@ -74,23 +72,14 @@ function abort_encryption_conversion(): void
$config_path = ROOTPATH . '.env';
$backup_path = WRITEPATH . '/backup/.env.bak';
$config_file = file_get_contents($backup_path);
$handle = @fopen($config_path, 'w+');
if (empty($handle)) {
log_message('error', "Unable to open $config_path to undo encryption conversion");
} else {
@chmod($config_path, 0660);
$write_failed = !fwrite($handle, $config_file);
fclose($handle);
if ($write_failed) {
log_message('error', "Unable to write to $config_path to undo encryption conversion.");
return;
}
log_message('info', "File $config_path has been updated to undo encryption conversion");
if (!file_exists($backup_path)) {
return;
}
@chmod($config_path, 0640);
$config_file = file_get_contents($backup_path);
@file_put_contents($config_path, $config_file);
log_message('info', "Restored $config_path from backup");
}
/**
@@ -99,13 +88,10 @@ function abort_encryption_conversion(): void
function remove_backup(): void
{
$backup_path = WRITEPATH . '/backup/.env.bak';
if (! file_exists($backup_path)) {
if (!file_exists($backup_path)) {
return;
}
if (!unlink($backup_path)) {
log_message('error', "Unable to remove $backup_path.");
return;
}
log_message('info', "File $backup_path has been removed");
@unlink($backup_path);
log_message('info', "Removed $backup_path");
}

View File

@@ -9,6 +9,15 @@ return [
"login" => "دخول",
"logout" => "تسجيل خروج",
"migration_needed" => "سيبدأ ترحيل قاعدة البيانات إلى{0} بعد تسجيل الدخول.",
"migration_required" => "",
"migration_auth_message" => "",
"migration_initializing" => "",
"migration_running" => "",
"migration_complete" => "",
"migration_complete_login" => "",
"migration_failed" => "",
"migration_error_connection" => "",
"migration_complete_redirect" => "",
"password" => "كلمة السر",
"required_username" => "",
"username" => "اسم المستخدم",

View File

@@ -9,6 +9,15 @@ return [
"login" => "دخول",
"logout" => "تسجيل خروج",
"migration_needed" => "سيبدأ ترحيل قاعدة البيانات إلى{0} بعد تسجيل الدخول.",
"migration_required" => "",
"migration_auth_message" => "",
"migration_initializing" => "",
"migration_running" => "",
"migration_complete" => "",
"migration_complete_login" => "",
"migration_failed" => "",
"migration_error_connection" => "",
"migration_complete_redirect" => "",
"password" => "كلمة السر",
"required_username" => "خانة أسم المستخدم مطلوبة.",
"username" => "اسم المستخدم",

View File

@@ -9,6 +9,15 @@ return [
"login" => "Giriş",
"logout" => "Çıxış",
"migration_needed" => "{0} -ə daxil olandan sonra verilənlər bazası miqrasiyası başlayacaq.",
"migration_required" => "",
"migration_auth_message" => "",
"migration_initializing" => "",
"migration_running" => "",
"migration_complete" => "",
"migration_complete_login" => "",
"migration_failed" => "",
"migration_error_connection" => "",
"migration_complete_redirect" => "",
"password" => "Şifrə",
"required_username" => "",
"username" => "İstifadəçi",

View File

@@ -9,6 +9,15 @@ return [
"login" => "Login",
"logout" => "",
"migration_needed" => "",
"migration_required" => "",
"migration_auth_message" => "",
"migration_initializing" => "",
"migration_running" => "",
"migration_complete" => "",
"migration_complete_login" => "",
"migration_failed" => "",
"migration_error_connection" => "",
"migration_complete_redirect" => "",
"password" => "Password",
"required_username" => "",
"username" => "Username",

View File

@@ -9,6 +9,15 @@ return [
"login" => "Prijava",
"logout" => "Odjava",
"migration_needed" => "Migracija baze podataka na {0} će početi nakon prijavljivanja.",
"migration_required" => "",
"migration_auth_message" => "",
"migration_initializing" => "",
"migration_running" => "",
"migration_complete" => "",
"migration_complete_login" => "",
"migration_failed" => "",
"migration_error_connection" => "",
"migration_complete_redirect" => "",
"password" => "Lozinka",
"required_username" => "",
"username" => "Korisničko ime",

View File

@@ -9,6 +9,15 @@ return [
'login' => "چوونەژوورەوە",
'logout' => "چوونەدەرەوە",
'migration_needed' => "گواستنەوەی داتابەیس بۆ {0} دوای چوونەژوورەوە دەست پێدەکات.",
'migration_required' => "",
'migration_auth_message' => "",
'migration_initializing' => "",
'migration_running' => "",
'migration_complete' => "",
'migration_complete_login' => "",
'migration_failed' => "",
'migration_error_connection' => "",
'migration_complete_redirect' => "",
'password' => "وشەی نهێنی",
'required_username' => "خانەی ناوی بەکارهێنەر پێویستە.",
'username' => "ناوی بەکارهێنەر",

View File

@@ -9,6 +9,15 @@ return [
"login" => "Login",
"logout" => "",
"migration_needed" => "",
"migration_required" => "",
"migration_auth_message" => "",
"migration_initializing" => "",
"migration_running" => "",
"migration_complete" => "",
"migration_complete_login" => "",
"migration_failed" => "",
"migration_error_connection" => "",
"migration_complete_redirect" => "",
"password" => "Heslo",
"required_username" => "",
"username" => "Uživatelské jméno",

View File

@@ -9,6 +9,15 @@ return [
"login" => "",
"logout" => "",
"migration_needed" => "",
"migration_required" => "",
"migration_auth_message" => "",
"migration_initializing" => "",
"migration_running" => "",
"migration_complete" => "",
"migration_complete_login" => "",
"migration_failed" => "",
"migration_error_connection" => "",
"migration_complete_redirect" => "",
"password" => "",
"required_username" => "",
"username" => "",

View File

@@ -7,10 +7,19 @@ return [
"invalid_installation" => "",
"invalid_username_and_password" => "Ungültiger Benutzername/Passwort",
"login" => "Login",
"logout" => "",
"migration_needed" => "",
"logout" => "Abmelden",
"migration_needed" => "Eine Datenbank-Migration auf {0} wird nach der Anmeldung gestartet.",
"migration_required" => "Datenbank-Migration erforderlich",
"migration_auth_message" => "Administrator-Anmeldedaten sind erforderlich, um die Datenbank-Migration auf Version {0} zu autorisieren. Bitte melden Sie sich an, um fortzufahren.",
"migration_initializing" => "Datenbank wird initialisiert",
"migration_running" => "Datenbank-Migrationen werden ausgeführt...",
"migration_complete" => "Datenbank erfolgreich initialisiert!",
"migration_complete_login" => "Sie können sich jetzt anmelden.",
"migration_failed" => "Migration fehlgeschlagen",
"migration_error_connection" => "Verbindungsfehler. Bitte versuchen Sie es erneut.",
"migration_complete_redirect" => "Migration abgeschlossen. Weiterleitung zur Anmeldung...",
"password" => "Passwort",
"required_username" => "",
"required_username" => "Das Feld Benutzername ist erforderlich.",
"username" => "Benutzername",
"welcome" => "",
"welcome" => "Willkommen bei {0}!",
];

View File

@@ -7,10 +7,19 @@ return [
"invalid_installation" => "Die Installation ist nicht korrekt, überprüfen Sie Ihre php.ini-Datei.",
"invalid_username_and_password" => "Ungültiger Benutzername oder Passwort.",
"login" => "Login",
"logout" => "",
"migration_needed" => "",
"logout" => "Abmelden",
"migration_needed" => "Eine Datenbank-Migration auf {0} wird nach der Anmeldung gestartet.",
"migration_required" => "Datenbank-Migration erforderlich",
"migration_auth_message" => "Administrator-Anmeldedaten sind erforderlich, um die Datenbank-Migration auf Version {0} zu autorisieren. Bitte melden Sie sich an, um fortzufahren.",
"migration_initializing" => "Datenbank wird initialisiert",
"migration_running" => "Datenbank-Migrationen werden ausgeführt...",
"migration_complete" => "Datenbank erfolgreich initialisiert!",
"migration_complete_login" => "Sie können sich jetzt anmelden.",
"migration_failed" => "Migration fehlgeschlagen",
"migration_error_connection" => "Verbindungsfehler. Bitte versuchen Sie es erneut.",
"migration_complete_redirect" => "Migration abgeschlossen. Weiterleitung zur Anmeldung...",
"password" => "Passwort",
"required_username" => "",
"required_username" => "Das Feld Benutzername ist erforderlich.",
"username" => "Benutzername",
"welcome" => "",
"welcome" => "Willkommen bei {0}!",
];

View File

@@ -9,6 +9,15 @@ return [
"login" => "",
"logout" => "",
"migration_needed" => "",
"migration_required" => "",
"migration_auth_message" => "",
"migration_initializing" => "",
"migration_running" => "",
"migration_complete" => "",
"migration_complete_login" => "",
"migration_failed" => "",
"migration_error_connection" => "",
"migration_complete_redirect" => "",
"password" => "",
"required_username" => "",
"username" => "",

View File

@@ -9,6 +9,15 @@ return [
"login" => "Login",
"logout" => "Logout",
"migration_needed" => "A database migration to {0} will start after login.",
"migration_required" => "Database Migration Required",
"migration_auth_message" => "Administrator credentials are required to authorize the database migration to version {0}. Please login to proceed.",
"migration_initializing" => "Initializing Database",
"migration_running" => "Running database migrations...",
"migration_complete" => "Database initialized successfully!",
"migration_complete_login" => "You can now log in.",
"migration_failed" => "Migration failed",
"migration_error_connection" => "Connection error. Please try again.",
"migration_complete_redirect" => "Migration complete. Redirecting to login...",
"password" => "Password",
"required_username" => "The username field is required.",
"username" => "Username",

View File

@@ -11,6 +11,12 @@ return [
"migration_needed" => "A database migration to {0} will start after login.",
"migration_required" => "Database Migration Required",
"migration_auth_message" => "Administrator credentials are required to authorize the database migration to version {0}. Please login to proceed.",
"migration_initializing" => "Initializing Database",
"migration_running" => "Running database migrations...",
"migration_complete" => "Database initialized successfully!",
"migration_complete_login" => "You can now log in.",
"migration_failed" => "Migration failed",
"migration_error_connection" => "Connection error. Please try again.",
"migration_complete_redirect" => "Migration complete. Redirecting to login...",
"password" => "Password",
"required_username" => "The username field is required.",

View File

@@ -9,6 +9,15 @@ return [
"login" => "Iniciar Sesión",
"logout" => "Cerrar sesión",
"migration_needed" => "La migración de la base de datos a {0} se iniciará después del inicio de sesión.",
"migration_required" => "Migración de base de datos requerida",
"migration_auth_message" => "Se requieren credenciales de administrador para autorizar la migración de la base de datos a la versión {0}. Inicie sesión para continuar.",
"migration_initializing" => "Inicializando base de datos",
"migration_running" => "Ejecutando migraciones de base de datos...",
"migration_complete" => "¡Base de datos inicializada correctamente!",
"migration_complete_login" => "Ahora puede iniciar sesión.",
"migration_failed" => "Migración fallida",
"migration_error_connection" => "Error de conexión. Por favor, inténtelo de nuevo.",
"migration_complete_redirect" => "Migración completada. Redirigiendo al inicio de sesión...",
"password" => "Contraseña",
"required_username" => "El campo de nombre de usuario es obligatorio.",
"username" => "Usuario",

View File

@@ -9,6 +9,15 @@ return [
"login" => "Login",
"logout" => "Salir",
"migration_needed" => "Una migración de base de datos a {0} empezara después de entrar.",
"migration_required" => "Migración de base de datos requerida",
"migration_auth_message" => "Se requieren credenciales de administrador para autorizar la migración de la base de datos a la versión {0}. Inicie sesión para continuar.",
"migration_initializing" => "Inicializando base de datos",
"migration_running" => "Ejecutando migraciones de base de datos...",
"migration_complete" => "¡Base de datos inicializada correctamente!",
"migration_complete_login" => "Ahora puede iniciar sesión.",
"migration_failed" => "Migración fallida",
"migration_error_connection" => "Error de conexión. Por favor, inténtelo de nuevo.",
"migration_complete_redirect" => "Migración completada. Redirigiendo al inicio de sesión...",
"password" => "Contraseña",
"required_username" => "El nombre de usuario es obligatorio.",
"username" => "Usuario",

View File

@@ -9,6 +9,15 @@ return [
"login" => "وارد شدن",
"logout" => "",
"migration_needed" => "",
"migration_required" => "",
"migration_auth_message" => "",
"migration_initializing" => "",
"migration_running" => "",
"migration_complete" => "",
"migration_complete_login" => "",
"migration_failed" => "",
"migration_error_connection" => "",
"migration_complete_redirect" => "",
"password" => "کلمه عبور",
"required_username" => "",
"username" => "نام کاربری",

View File

@@ -9,6 +9,15 @@ return [
"login" => "Login",
"logout" => "Déconnexion",
"migration_needed" => "Une migration de base de données vers {0} débutera après l'ouverture de session.",
"migration_required" => "Migration de base de données requise",
"migration_auth_message" => "Les identifiants administrateur sont requis pour autoriser la migration de la base de données vers la version {0}. Veuillez vous connecter pour continuer.",
"migration_initializing" => "Initialisation de la base de données",
"migration_running" => "Exécution des migrations de la base de données...",
"migration_complete" => "Base de données initialisée avec succès !",
"migration_complete_login" => "Vous pouvez maintenant vous connecter.",
"migration_failed" => "Échec de la migration",
"migration_error_connection" => "Erreur de connexion. Veuillez réessayer.",
"migration_complete_redirect" => "Migration terminée. Redirection vers la connexion...",
"password" => "Mot de passe",
"required_username" => "Le champ nom utilisateur est obligatoire.",
"username" => "Nom d'utilisateur",

View File

@@ -9,6 +9,15 @@ return [
"login" => "כניסה",
"logout" => "",
"migration_needed" => "",
"migration_required" => "",
"migration_auth_message" => "",
"migration_initializing" => "",
"migration_running" => "",
"migration_complete" => "",
"migration_complete_login" => "",
"migration_failed" => "",
"migration_error_connection" => "",
"migration_complete_redirect" => "",
"password" => "סיסמה",
"required_username" => "",
"username" => "שם משתמש",

View File

@@ -9,6 +9,15 @@ return [
"login" => "Prijava",
"logout" => "",
"migration_needed" => "",
"migration_required" => "",
"migration_auth_message" => "",
"migration_initializing" => "",
"migration_running" => "",
"migration_complete" => "",
"migration_complete_login" => "",
"migration_failed" => "",
"migration_error_connection" => "",
"migration_complete_redirect" => "",
"password" => "Lozinka",
"required_username" => "",
"username" => "Korisničko ime",

View File

@@ -9,6 +9,15 @@ return [
"login" => "Belépés",
"logout" => "",
"migration_needed" => "",
"migration_required" => "",
"migration_auth_message" => "",
"migration_initializing" => "",
"migration_running" => "",
"migration_complete" => "",
"migration_complete_login" => "",
"migration_failed" => "",
"migration_error_connection" => "",
"migration_complete_redirect" => "",
"password" => "Jelszó",
"required_username" => "",
"username" => "Felhasználó név",

View File

@@ -9,6 +9,15 @@ return [
"login" => "",
"logout" => "",
"migration_needed" => "",
"migration_required" => "",
"migration_auth_message" => "",
"migration_initializing" => "",
"migration_running" => "",
"migration_complete" => "",
"migration_complete_login" => "",
"migration_failed" => "",
"migration_error_connection" => "",
"migration_complete_redirect" => "",
"password" => "",
"required_username" => "",
"username" => "",

View File

@@ -9,6 +9,15 @@ return [
"login" => "Masuk",
"logout" => "Keluar",
"migration_needed" => "Migrasi basis data untuk {0} akan mulai setelah masuk.",
"migration_required" => "",
"migration_auth_message" => "",
"migration_initializing" => "",
"migration_running" => "",
"migration_complete" => "",
"migration_complete_login" => "",
"migration_failed" => "",
"migration_error_connection" => "",
"migration_complete_redirect" => "",
"password" => "Kata kunci",
"required_username" => "Kolom nama pengguna wajib diisi.",
"username" => "Nama Anda",

View File

@@ -9,8 +9,17 @@ return [
"login" => "Login",
"logout" => "Uscita",
"migration_needed" => "Dopo l'accesso verrà avviata una migrazione del database a {0}.",
"migration_required" => "Migratione del database richiesta",
"migration_auth_message" => "Le credenziali di amministratore sono richieste per autorizzare la migrazione del database alla versione {0}. Effettua il login per continuare.",
"migration_initializing" => "Inizializzazione database",
"migration_running" => "Esecuzione migrazioni database...",
"migration_complete" => "Database inizializzato con successo!",
"migration_complete_login" => "È ora possibile effettuare il login.",
"migration_failed" => "Migrazione fallita",
"migration_error_connection" => "Errore di connessione. Riprova.",
"migration_complete_redirect" => "Migrazione completata. Reindirizzamento al login...",
"password" => "Password",
"required_username" => "",
"required_username" => "Il campo nome utente è obbligatorio.",
"username" => "Username",
"welcome" => "Benvenuto in {0}!",
];

View File

@@ -9,6 +9,15 @@ return [
"login" => "ចូល",
"logout" => "",
"migration_needed" => "",
"migration_required" => "",
"migration_auth_message" => "",
"migration_initializing" => "",
"migration_running" => "",
"migration_complete" => "",
"migration_complete_login" => "",
"migration_failed" => "",
"migration_error_connection" => "",
"migration_complete_redirect" => "",
"password" => "ពាក្យសំងាត់",
"required_username" => "",
"username" => "ឈ្មោះ",

View File

@@ -9,6 +9,15 @@ return [
"login" => "ເຂົ້າລະບົບ",
"logout" => "",
"migration_needed" => "",
"migration_required" => "",
"migration_auth_message" => "",
"migration_initializing" => "",
"migration_running" => "",
"migration_complete" => "",
"migration_complete_login" => "",
"migration_failed" => "",
"migration_error_connection" => "",
"migration_complete_redirect" => "",
"password" => "Password",
"required_username" => "",
"username" => "Username",

View File

@@ -9,6 +9,15 @@ return [
"login" => "",
"logout" => "",
"migration_needed" => "",
"migration_required" => "",
"migration_auth_message" => "",
"migration_initializing" => "",
"migration_running" => "",
"migration_complete" => "",
"migration_complete_login" => "",
"migration_failed" => "",
"migration_error_connection" => "",
"migration_complete_redirect" => "",
"password" => "",
"required_username" => "",
"username" => "",

View File

@@ -9,6 +9,15 @@ return [
"login" => "",
"logout" => "",
"migration_needed" => "",
"migration_required" => "",
"migration_auth_message" => "",
"migration_initializing" => "",
"migration_running" => "",
"migration_complete" => "",
"migration_complete_login" => "",
"migration_failed" => "",
"migration_error_connection" => "",
"migration_complete_redirect" => "",
"password" => "",
"required_username" => "",
"username" => "",

View File

@@ -9,6 +9,15 @@ return [
"login" => "Login",
"logout" => "Log-uit",
"migration_needed" => "Een database migratie naar {0} zal starten na het inloggen.",
"migration_required" => "Database migratie vereist",
"migration_auth_message" => "Beheerdersreferenties zijn vereist om de databasemigratie naar versie {0} te autoriseren. Log in om verder te gaan.",
"migration_initializing" => "Database initialiseren",
"migration_running" => "Databasemigraties uitvoeren...",
"migration_complete" => "Database succesvol geïnitialiseerd!",
"migration_complete_login" => "U kunt nu inloggen.",
"migration_failed" => "Migratie mislukt",
"migration_error_connection" => "Verbindingsfout. Probeer het opnieuw.",
"migration_complete_redirect" => "Migratie voltooid. Doorsturen naar login...",
"password" => "Paswoord",
"required_username" => "",
"username" => "Gebruiker",

View File

@@ -9,6 +9,15 @@ return [
"login" => "Aanmelden",
"logout" => "Afmelden",
"migration_needed" => "Een databasemigratie naar {0} zal starten na aanmelding.",
"migration_required" => "Database migratie vereist",
"migration_auth_message" => "Beheerdersreferenties zijn vereist om de databasemigratie naar versie {0} te autoriseren. Log in om verder te gaan.",
"migration_initializing" => "Database initialiseren",
"migration_running" => "Databasemigraties uitvoeren...",
"migration_complete" => "Database succesvol geïnitialiseerd!",
"migration_complete_login" => "U kunt nu inloggen.",
"migration_failed" => "Migratie mislukt",
"migration_error_connection" => "Verbindingsfout. Probeer het opnieuw.",
"migration_complete_redirect" => "Migratie voltooid. Doorsturen naar login...",
"password" => "Wachtwoord",
"required_username" => "Het gebruikersnaam veld is verplicht.",
"username" => "Gebruikersnaam",

View File

@@ -9,6 +9,15 @@ return [
"login" => "Zaloguj",
"logout" => "Wyloguj",
"migration_needed" => "Migracja bazy danych do {0} zacznie się po zalogowaniu.",
"migration_required" => "",
"migration_auth_message" => "",
"migration_initializing" => "",
"migration_running" => "",
"migration_complete" => "",
"migration_complete_login" => "",
"migration_failed" => "",
"migration_error_connection" => "",
"migration_complete_redirect" => "",
"password" => "Hasło",
"required_username" => "",
"username" => "Nazwa użytkownika",

View File

@@ -9,8 +9,17 @@ return [
'login' => "Autenticação",
'logout' => "Sair",
'migration_needed' => "Uma migração do banco de dados para {0} será iniciada após o login.",
'migration_required' => "Migração de banco de dados necessária",
'migration_auth_message' => "Credenciais de administrador são necessárias para autorizar a migração do banco de dados para a versão {0}. Faça login para continuar.",
'migration_initializing' => "Inicializando banco de dados",
'migration_running' => "Executando migrações do banco de dados...",
'migration_complete' => "Banco de dados inicializado com sucesso!",
'migration_complete_login' => "Agora você pode fazer login.",
'migration_failed' => "Migração falhou",
'migration_error_connection' => "Erro de conexão. Por favor, tente novamente.",
'migration_complete_redirect' => "Migração concluída. Redirecionando para login...",
'password' => "Senha",
'required_username' => "O campo nome de usuário é obrigatório.",
'username' => "Usuário",
'welcome' => "Bem-vindo",
'welcome' => "Bem-vindo ao {0}!",
];

View File

@@ -9,6 +9,15 @@ return [
"login" => "",
"logout" => "",
"migration_needed" => "",
"migration_required" => "",
"migration_auth_message" => "",
"migration_initializing" => "",
"migration_running" => "",
"migration_complete" => "",
"migration_complete_login" => "",
"migration_failed" => "",
"migration_error_connection" => "",
"migration_complete_redirect" => "",
"password" => "",
"required_username" => "",
"username" => "",

View File

@@ -9,6 +9,15 @@ return [
"login" => "Вход",
"logout" => "Выход",
"migration_needed" => "Миграция базы данных в {0} начнется после входа в систему.",
"migration_required" => "",
"migration_auth_message" => "",
"migration_initializing" => "",
"migration_running" => "",
"migration_complete" => "",
"migration_complete_login" => "",
"migration_failed" => "",
"migration_error_connection" => "",
"migration_complete_redirect" => "",
"password" => "Пароль",
"required_username" => "",
"username" => "Имя пользователя",

View File

@@ -9,6 +9,15 @@ return [
"login" => "Login",
"logout" => "Logga ut",
"migration_needed" => "En migration av databasen till {0} kommer att påbörjas efter inloggningen.",
"migration_required" => "Databasmigration krävs",
"migration_auth_message" => "Admin-uppgifter krävs för att godkänna databasmigrationen till version {0}. Logga in för att fortsätta.",
"migration_initializing" => "Initierar databas",
"migration_running" => "Kör databasmigrationer...",
"migration_complete" => "Databas initierad framgångsrikt!",
"migration_complete_login" => "Du kan nu logga in.",
"migration_failed" => "Migration misslyckades",
"migration_error_connection" => "Anslutningsfel. Försök igen.",
"migration_complete_redirect" => "Migration klar. Omdirigerar till login...",
"password" => "Lösenord",
"required_username" => "Fältet för användarnamn krävs.",
"username" => "Användarnamn",

View File

@@ -9,6 +9,15 @@ return [
"login" => "Ingia",
"logout" => "Toka",
"migration_needed" => "Uhamishaji wa kanzidata hadi {0} utaanza baada ya kuingia.",
"migration_required" => "",
"migration_auth_message" => "",
"migration_initializing" => "",
"migration_running" => "",
"migration_complete" => "",
"migration_complete_login" => "",
"migration_failed" => "",
"migration_error_connection" => "",
"migration_complete_redirect" => "",
"password" => "Nenosiri",
"required_username" => "Jina la mtumiaji ni lazima.",
"username" => "Jina la Mtumiaji",

View File

@@ -9,6 +9,15 @@ return [
"login" => "Ingia",
"logout" => "Toka",
"migration_needed" => "Uhamishaji wa kanzidata hadi {0} utaanza baada ya kuingia.",
"migration_required" => "",
"migration_auth_message" => "",
"migration_initializing" => "",
"migration_running" => "",
"migration_complete" => "",
"migration_complete_login" => "",
"migration_failed" => "",
"migration_error_connection" => "",
"migration_complete_redirect" => "",
"password" => "Nenosiri",
"required_username" => "Jina la mtumiaji ni lazima.",
"username" => "Jina la Mtumiaji",

View File

@@ -9,6 +9,15 @@ return [
"login" => "உள்நுழைய",
"logout" => "விடுபதிகை",
"migration_needed" => "{0} க்கு தரவுத்தள இடம்பெயர்வு உள்நுழைந்த பிறகு தொடங்கும்.",
"migration_required" => "",
"migration_auth_message" => "",
"migration_initializing" => "",
"migration_running" => "",
"migration_complete" => "",
"migration_complete_login" => "",
"migration_failed" => "",
"migration_error_connection" => "",
"migration_complete_redirect" => "",
"password" => "கடவுச்சொல்",
"required_username" => "",
"username" => "பயனர்பெயர்",

View File

@@ -9,6 +9,15 @@ return [
"login" => "ลงชื่อเข้าใช้",
"logout" => "ออกจากระบบ",
"migration_needed" => "การย้ายฐานข้อมูลไปยัง {0} จะเริ่มต้นหลังจากเข้าสู่ระบบ",
"migration_required" => "",
"migration_auth_message" => "",
"migration_initializing" => "",
"migration_running" => "",
"migration_complete" => "",
"migration_complete_login" => "",
"migration_failed" => "",
"migration_error_connection" => "",
"migration_complete_redirect" => "",
"password" => "รหัสผ่าน",
"required_username" => "จำเป็นต้องระบุชื่อผู้ใช้งาน",
"username" => "ชื่อผู้ใช้",

View File

@@ -9,6 +9,15 @@ return [
"login" => "Login",
"logout" => "",
"migration_needed" => "",
"migration_required" => "",
"migration_auth_message" => "",
"migration_initializing" => "",
"migration_running" => "",
"migration_complete" => "",
"migration_complete_login" => "",
"migration_failed" => "",
"migration_error_connection" => "",
"migration_complete_redirect" => "",
"password" => "Password",
"required_username" => "",
"username" => "Username",

View File

@@ -9,8 +9,17 @@ return [
"login" => "Giriş",
"logout" => "Çıkış",
"migration_needed" => "Girişten sonra {0} veri tabanına göç başlayacak.",
"migration_required" => "Veritabanı Göçü Gerekli",
"migration_auth_message" => "Veritabanı göçünün {0} versiyonuna yetkilendirilmesi için yönetici kimlik bilgileri gereklidir. Devam etmek için giriş yapın.",
"migration_initializing" => "Veritabanı başlatılıyor",
"migration_running" => "Veritabanı göçleri çalışıyor...",
"migration_complete" => "Veritabanı başarıyla başlatıldı!",
"migration_complete_login" => "Artık giriş yapabilirsiniz.",
"migration_failed" => "Göç başarısız oldu",
"migration_error_connection" => "Bağlantı hatası. Lütfen tekrar deneyin.",
"migration_complete_redirect" => "Göç tamamlandı. Girişe yönlendiriliyor...",
"password" => "Parola",
"required_username" => "",
"required_username" => "Kullanıcı adı alanı gereklidir.",
"username" => "Kullanıcı Adı",
"welcome" => "{0}'e Hoş Geldiniz!",
];

View File

@@ -9,6 +9,15 @@ return [
"login" => "Логін",
"logout" => "",
"migration_needed" => "",
"migration_required" => "",
"migration_auth_message" => "",
"migration_initializing" => "",
"migration_running" => "",
"migration_complete" => "",
"migration_complete_login" => "",
"migration_failed" => "",
"migration_error_connection" => "",
"migration_complete_redirect" => "",
"password" => "Пароль",
"required_username" => "",
"username" => "Ім'я користувача",

View File

@@ -9,6 +9,15 @@ return [
"login" => "",
"logout" => "",
"migration_needed" => "",
"migration_required" => "",
"migration_auth_message" => "",
"migration_initializing" => "",
"migration_running" => "",
"migration_complete" => "",
"migration_complete_login" => "",
"migration_failed" => "",
"migration_error_connection" => "",
"migration_complete_redirect" => "",
"password" => "",
"required_username" => "",
"username" => "",

View File

@@ -9,6 +9,15 @@ return [
"login" => "Đăng nhập",
"logout" => "Đăng xuất",
"migration_needed" => "Di chuyển cơ sở dữ liệu sang {0} sẽ được bắt đầu sau khi đăng nhập.",
"migration_required" => "",
"migration_auth_message" => "",
"migration_initializing" => "",
"migration_running" => "",
"migration_complete" => "",
"migration_complete_login" => "",
"migration_failed" => "",
"migration_error_connection" => "",
"migration_complete_redirect" => "",
"password" => "Mật khẩu",
"required_username" => "",
"username" => "Tài khoản",

View File

@@ -9,6 +9,15 @@ return [
"login" => "登入",
"logout" => "退出",
"migration_needed" => "",
"migration_required" => "",
"migration_auth_message" => "",
"migration_initializing" => "",
"migration_running" => "",
"migration_complete" => "",
"migration_complete_login" => "",
"migration_failed" => "",
"migration_error_connection" => "",
"migration_complete_redirect" => "",
"password" => "密码",
"username" => "用户名",
"welcome" => "欢迎来到 {0}!",

View File

@@ -9,6 +9,15 @@ return [
"login" => "登入",
"logout" => "登出",
"migration_needed" => "登錄後將開始向 {0} 的數據庫遷移。",
"migration_required" => "",
"migration_auth_message" => "",
"migration_initializing" => "",
"migration_running" => "",
"migration_complete" => "",
"migration_complete_login" => "",
"migration_failed" => "",
"migration_error_connection" => "",
"migration_complete_redirect" => "",
"password" => "密碼",
"required_username" => "",
"username" => "帳號",

View File

@@ -2,6 +2,7 @@
namespace App\Libraries;
use CodeIgniter\Database\Exceptions\DatabaseException;
use CodeIgniter\Database\MigrationRunner;
use Config\Database;
use stdClass;
@@ -35,11 +36,16 @@ class MY_Migration extends MigrationRunner
*/
public static function get_current_version(): int
{
$db = Database::connect();
if ($db->tableExists('migrations')) {
$builder = $db->table('migrations');
$builder->select('version')->orderBy('version', 'DESC')->limit(1);
return $builder->get()->getRow()->version;
try {
$db = Database::connect();
if ($db->tableExists('migrations')) {
$builder = $db->table('migrations');
$builder->select('version')->orderBy('version', 'DESC')->limit(1);
$result = $builder->get()->getRow();
return $result ? $result->version : 0;
}
} catch (DatabaseException $e) {
return 0;
}
return 0;
@@ -63,10 +69,15 @@ class MY_Migration extends MigrationRunner
*/
private function ci3_migrations_exists(): bool|string
{
if ($this->db->tableExists('migrations') && !$this->db->fieldExists('id', 'migrations')) {
$builder = $this->db->table('migrations');
$builder->select('version');
return $builder->get()->getRow()->version;
try {
if ($this->db->tableExists('migrations') && !$this->db->fieldExists('id', 'migrations')) {
$builder = $this->db->table('migrations');
$builder->select('version');
$result = $builder->get()->getRow();
return $result ? $result->version : false;
}
} catch (DatabaseException $e) {
// Database doesn't exist yet or connection failed
}
return false;
@@ -143,4 +154,5 @@ class MY_Migration extends MigrationRunner
$this->ensureTable();
}
}

View File

@@ -2,6 +2,7 @@
/**
* @var bool $has_errors
* @var bool $is_latest
* @var bool $is_new_install
* @var string $latest_version
* @var bool $gcaptcha_enabled
* @var array $config
@@ -46,15 +47,20 @@
<?php endif; ?>
</div>
<section class="box-login d-flex flex-column justify-content-center align-items-center p-md-4">
<?= form_open('login') ?>
<?php if (!$is_latest): ?>
<h3 class="text-center m-0"><?= lang('Login.migration_required') ?></h3>
<div class="alert alert-warning mt-3">
<strong><?= lang('Login.migration_auth_message', [$latest_version]) ?></strong>
</div>
<?php else: ?>
<h3 class="text-center m-0"><?= lang('Login.welcome', [lang('Common.software_short')]) ?></h3>
<?php endif; ?>
<?= form_open('login', ['id' => 'login-form']) ?>
<h3 id="form-heading" class="text-center m-0">
<?php if (!$is_latest || $is_new_install): ?>
<?= lang('Login.migration_required') ?>
<?php else: ?>
<?= lang('Login.welcome', [lang('Common.software_short')]) ?>
<?php endif; ?>
</h3>
<div id="migration-warning" class="alert alert-warning mt-3<?= $is_new_install ? '' : ' d-none' ?>">
<strong><?= lang('Login.migration_auth_message', [$latest_version]) ?></strong>
</div>
<?php if ($has_errors): ?>
<?php foreach ($validation->getErrors() as $error): ?>
<div class="alert alert-danger mt-3">
@@ -62,44 +68,72 @@
</div>
<?php endforeach; ?>
<?php endif; ?>
<?php if (empty($config['login_form']) || 'floating_labels' == ($config['login_form'])): ?>
<div class="form-floating mt-3">
<input class="form-control" id="input-username" name="username" type="text" placeholder="<?= lang('Login.username') ?>" <?php if (ENVIRONMENT == "testing") echo 'value="admin"'; ?>>
<label for="input-username"><?= lang('Login.username') ?></label>
<div id="migration-success" class="alert alert-success d-none mt-3">
<strong><?= lang('Login.migration_complete') ?></strong> <?= lang('Login.migration_complete_login') ?>
</div>
<div id="migration-progress" class="d-none mt-4">
<h3 class="text-center mb-4"><?= lang('Login.migration_initializing') ?></h3>
<div class="progress mb-3" style="height: 30px;">
<div class="progress-bar progress-bar-striped progress-bar-animated bg-primary"
role="progressbar"
style="width: 100%">
</div>
</div>
<div class="form-floating mb-3">
<input class="form-control" id="input-password" name="password" type="password" placeholder="<?= lang('Login.password') ?>" <?php if (ENVIRONMENT == "testing") echo 'value="pointofsale"'; ?>>
<label for="input-password"><?= lang('Login.password') ?></label>
</div>
<?php elseif ('input_groups' == ($config['login_form'])): ?>
<div class="input-group mt-3">
<span class="input-group-text" id="input-username">
<svg class="bi bi-person-fill" fill="currentColor" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<title><?= lang('Common.icon') . '&nbsp;' . lang('Login.username') ?></title>
<path d="M3 14s-1 0-1-1 1-4 6-4 6 3 6 4-1 1-1 1zm5-6a3 3 0 1 0 0-6 3 3 0 0 0 0 6" />
</svg>
</span>
<input class="form-control" name="username" type="text" placeholder="<?= lang('Login.username'); ?>" aria-label="<?= lang('Login.username') ?>" aria-describedby="input-username" <?php if (ENVIRONMENT == "testing") echo 'value="admin"'; ?>>
</div>
<div class="input-group mb-3">
<span class="input-group-text" id="input-password">
<svg class="bi bi-key-fill" fill="currentColor" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<title><?= lang('Common.icon') . '&nbsp;' . lang('Login.password') ?></title>
<path d="M3.5 11.5a3.5 3.5 0 1 1 3.163-5H14L15.5 8 14 9.5l-1-1-1 1-1-1-1 1-1-1-1 1H6.663a3.5 3.5 0 0 1-3.163 2M2.5 9a1 1 0 1 0 0-2 1 1 0 0 0 0 2" />
</svg>
</span>
<input class="form-control" name="password" type="password" placeholder="<?= lang('Login.password') ?>" aria-label="<?= lang('Login.password') ?>" aria-describedby="input-password" <?php if (ENVIRONMENT == "testing") echo 'value="pointofsale"'; ?>>
</div>
<?php endif; ?>
<?php
if ($gcaptcha_enabled) {
echo '<script src="https://www.google.com/recaptcha/api.js"></script>';
echo '<div class="g-recaptcha mb-3" style="text-align: center;" data-sitekey="' . esc($config['gcaptcha_site_key']) . '"></div>';
}
?>
<p class="text-center text-muted" id="migration-status">
<?= lang('Login.migration_running') ?>
</p>
</div>
<div id="migration-error" class="alert alert-danger d-none mt-3" role="alert">
<strong>Error:</strong> <span id="migration-error-message"></span>
</div>
<div id="login-fields" class="w-100<?= $is_new_install ? ' d-none' : '' ?>">
<?php if (empty($config['login_form']) || 'floating_labels' == ($config['login_form'])): ?>
<div class="form-floating mt-3">
<input class="form-control" id="input-username" name="username" type="text" placeholder="<?= lang('Login.username') ?>" <?php if (ENVIRONMENT == "testing") echo 'value="admin"'; ?>>
<label for="input-username"><?= lang('Login.username') ?></label>
</div>
<div class="form-floating mb-3">
<input class="form-control" id="input-password" name="password" type="password" placeholder="<?= lang('Login.password') ?>" <?php if (ENVIRONMENT == "testing") echo 'value="pointofsale"'; ?>>
<label for="input-password"><?= lang('Login.password') ?></label>
</div>
<?php elseif ('input_groups' == ($config['login_form'])): ?>
<div class="input-group mt-3">
<span class="input-group-text" id="input-username">
<svg class="bi bi-person-fill" fill="currentColor" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<title><?= lang('Common.icon') . '&nbsp;' . lang('Login.username') ?></title>
<path d="M3 14s-1 0-1-1 1-4 6-4 6 3 6 4-1 1-1 1zm5-6a3 3 0 1 0 0-6 3 3 0 0 0 0 6" />
</svg>
</span>
<input class="form-control" name="username" type="text" placeholder="<?= lang('Login.username'); ?>" aria-label="<?= lang('Login.username') ?>" aria-describedby="input-username" <?php if (ENVIRONMENT == "testing") echo 'value="admin"'; ?>>
</div>
<div class="input-group mb-3">
<span class="input-group-text" id="input-password">
<svg class="bi bi-key-fill" fill="currentColor" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<title><?= lang('Common.icon') . '&nbsp;' . lang('Login.password') ?></title>
<path d="M3.5 11.5a3.5 3.5 0 1 1 3.163-5H14L15.5 8 14 9.5l-1-1-1 1-1-1-1 1-1-1-1 1H6.663a3.5 3.5 0 0 1-3.163 2M2.5 9a1 1 0 1 0 0-2 1 1 0 0 0 0 2" />
</svg>
</span>
<input class="form-control" name="password" type="password" placeholder="<?= lang('Login.password') ?>" aria-label="<?= lang('Login.password') ?>" aria-describedby="input-password" <?php if (ENVIRONMENT == "testing") echo 'value="pointofsale"'; ?>>
</div>
<?php endif; ?>
<?php if ($gcaptcha_enabled): ?>
<script src="https://www.google.com/recaptcha/api.js"></script>
<div class="g-recaptcha mb-3" style="text-align: center;" data-sitekey="<?= esc($config['gcaptcha_site_key']) ?>"></div>
<?php endif; ?>
</div>
<div class="d-grid">
<button class="btn btn-lg btn-primary" name="login-button" type="submit">
<?= $is_latest ? lang('Login.go') : lang('Module.migrate') ?>
<button id="submit-button" class="btn btn-lg btn-primary" name="login-button" type="submit">
<?php if ($is_new_install): ?>
<?= lang('Module.migrate') ?>
<?php else: ?>
<?= lang('Login.go') ?>
<?php endif; ?>
</button>
</div>
<?= form_close() ?>
@@ -119,6 +153,141 @@
<span><?= lang('Common.software_title') ?></span>
</div>
</footer>
<?php
use Config\Services;
$request = Services::request();
?>
<?php if (ENVIRONMENT == 'development' || get_cookie('debug') == 'true' || $request->getGet('debug') == 'true') : ?>
<!-- inject:login:debug:js -->
<!-- endinject -->
<?php else : ?>
<!-- inject:login:prod:js -->
<!-- endinject -->
<?php endif; ?>
<script>
const APP_STATE = {
isNewInstall: <?= $is_new_install ? 'true' : 'false' ?>,
isLatest: <?= $is_latest ? 'true' : 'false' ?>,
csrfToken: '<?= csrf_token() ?>',
csrfHash: '<?= csrf_hash() ?>',
migrateUrl: '<?= site_url('migrate') ?>',
loginUrl: '<?= site_url('login') ?>',
i18n: {
welcome: <?= json_encode(lang('Login.welcome', [lang('Common.software_short')])) ?>,
migrate: <?= json_encode(lang('Module.migrate')) ?>,
go: <?= json_encode(lang('Login.go')) ?>,
migrationRequired: <?= json_encode(lang('Login.migration_required')) ?>,
migrationInitializing: <?= json_encode(lang('Login.migration_initializing')) ?>,
migrationRunning: <?= json_encode(lang('Login.migration_running')) ?>,
migrationComplete: <?= json_encode(lang('Login.migration_complete')) ?>,
migrationCompleteLogin: <?= json_encode(lang('Login.migration_complete_login')) ?>,
migrationFailed: <?= json_encode(lang('Login.migration_failed')) ?>,
migrationErrorConnection: <?= json_encode(lang('Login.migration_error_connection')) ?>
}
};
$(document).ready(function() {
const $form = $('#login-form');
const $heading = $('#form-heading');
const $warning = $('#migration-warning');
const $success = $('#migration-success');
const $progress = $('#migration-progress');
const $error = $('#migration-error');
const $errorMessage = $('#migration-error-message');
const $loginFields = $('#login-fields');
const $submitButton = $('#submit-button');
function showMigrationRequired() {
$heading.text(APP_STATE.i18n.migrationRequired);
$warning.removeClass('d-none');
$success.addClass('d-none');
$progress.addClass('d-none');
$error.addClass('d-none');
$loginFields.addClass('d-none');
$submitButton.text(APP_STATE.i18n.migrate);
}
function showMigrationProgress() {
$warning.addClass('d-none');
$success.addClass('d-none');
$error.addClass('d-none');
$loginFields.addClass('d-none');
$progress.removeClass('d-none');
$submitButton.prop('disabled', true);
}
function showMigrationSuccess() {
$progress.addClass('d-none');
$error.addClass('d-none');
$warning.addClass('d-none');
$success.removeClass('d-none');
$heading.text(APP_STATE.i18n.welcome);
$loginFields.removeClass('d-none');
$submitButton.text(APP_STATE.i18n.go);
$submitButton.prop('disabled', false);
}
function showMigrationError(message) {
$progress.addClass('d-none');
$success.addClass('d-none');
$loginFields.addClass('d-none');
$errorMessage.text(message);
$error.removeClass('d-none');
$warning.addClass('d-none');
$submitButton.text(APP_STATE.i18n.migrate);
$submitButton.prop('disabled', false);
}
function showLoginForm() {
$heading.text(APP_STATE.i18n.welcome);
$warning.addClass('d-none');
$progress.addClass('d-none');
$error.addClass('d-none');
$success.addClass('d-none');
$loginFields.removeClass('d-none');
$submitButton.text(APP_STATE.i18n.go);
}
if (!APP_STATE.isNewInstall) {
showLoginForm();
}
$form.on('submit', function(e) {
if (APP_STATE.isNewInstall) {
e.preventDefault();
showMigrationProgress();
$.ajax({
url: APP_STATE.migrateUrl,
type: 'POST',
dataType: 'json',
timeout: 3600000,
data: {
[APP_STATE.csrfToken]: APP_STATE.csrfHash
},
success: function(response) {
if (response.success) {
APP_STATE.isNewInstall = false;
showMigrationSuccess();
} else {
showMigrationError(response.message || APP_STATE.i18n.migrationFailed);
}
},
error: function(xhr, status, error) {
let message = APP_STATE.i18n.migrationErrorConnection;
if (xhr.responseJSON && xhr.responseJSON.message) {
message = xhr.responseJSON.message;
}
showMigrationError(message);
}
});
}
});
});
</script>
</body>
</html>

View File

@@ -190,6 +190,22 @@ gulp.task('prod-js', function() {
});
// Inject jQuery into login.php (debug mode) - reuse the jQuery file already created by debug-js
gulp.task('debug-login-js', function() {
// Match only core jQuery (jquery-HASH.js), exclude jquery plugins (jquery-HASH.form.js, etc) and jquery-ui (jquery-ui-HASH.js)
// Pattern: jquery-[hash].js where hash is alphanumeric - core jQuery only
var loginDebugJs = gulp.src(['./public/resources/js/jquery-*.js', '!./public/resources/js/jquery-*.form.js', '!./public/resources/js/jquery-*.validate.js', '!./public/resources/js/jquery-ui-*.js']);
return gulp.src('./app/Views/login.php').pipe(inject(loginDebugJs, {addRootSlash: false, ignorePath: '/public/', starttag: '<!-- inject:login:debug:js -->'})).pipe(gulp.dest('./app/Views'));
});
// Inject jQuery into login.php (production mode) - reuse the jQuery file already created by prod-js
gulp.task('prod-login-js', function() {
// jQuery prod file is already in resources/jquery-*.min.js from prod-js task
var loginProdJs = gulp.src('./public/resources/jquery-*.min.js');
return gulp.src('./app/Views/login.php').pipe(inject(loginProdJs, {addRootSlash: false, ignorePath: '/public/', starttag: '<!-- inject:login:prod:js -->'})).pipe(gulp.dest('./app/Views'));
});
gulp.task('debug-css', function() {
var debugcss = gulp.src(['./node_modules/jquery-ui-dist/jquery-ui.css',
@@ -289,6 +305,8 @@ gulp.task('default',
'copy-bootstrap',
'debug-js',
'prod-js',
'debug-login-js',
'prod-login-js',
'debug-css',
'prod-css',
'copy-fonts',

View File