diff --git a/core/database/migrations/2025_12_25_000000_initial_schema.php b/core/database/migrations/2025_12_25_000000_initial_schema.php index cde5ac1e09..24c85988e0 100644 --- a/core/database/migrations/2025_12_25_000000_initial_schema.php +++ b/core/database/migrations/2025_12_25_000000_initial_schema.php @@ -297,7 +297,7 @@ public function up(): void $indexPrefix = \DB::getTablePrefix() . $table->getTable(); $table->comment('Site content (documents) - main table storing all site pages, documents, and content resources'); $table->increments('id'); - $table->string('type', 20)->default('document')->index('typeidx'); + $table->string('type', 20)->default('document')->index("{$indexPrefix}_typeidx"); $table->string('contentType', 50)->default('text/html'); $table->string('pagetitle')->default(''); $table->string('longtitle')->default(''); @@ -354,9 +354,9 @@ public function up(): void $table->unsignedInteger('descendant'); $table->unsignedInteger('depth'); - $table->index('ancestor', 'closure_ancestor_idx'); - $table->index('descendant', 'closure_descendant_idx'); - $table->index('depth', 'closure_depth_idx'); + $table->index('ancestor', "{$indexPrefix}_closure_ancestor_idx"); + $table->index('descendant', "{$indexPrefix}_closure_descendant_idx"); + $table->index('depth', "{$indexPrefix}_closure_depth_idx"); $table->unique(['ancestor', 'descendant'], "{$indexPrefix}_ix_unique_path"); }); diff --git a/install/src/controllers/connection.php b/install/src/controllers/connection.php index 18861156a8..6268de6b1a 100644 --- a/install/src/controllers/connection.php +++ b/install/src/controllers/connection.php @@ -1,6 +1,6 @@ 'MySQL', 'pgsql' => 'PostgreSQL']; +$dbTypes = ['mysql' => 'MySQL', 'pgsql' => 'PostgreSQL', 'sqlite' => 'SQLite']; // Determine upgradeability $upgradeable = 0; @@ -50,6 +50,14 @@ $conn = false; $result = false; } + } elseif ($database_type === 'sqlite') { + try { + $conn = new PDO('sqlite:' . $database_name); + $result = true; + } catch (PDOException $e) { + $conn = false; + $result = false; + } } if (!$conn || !$result) { $upgradeable = (isset($_POST['installmode']) && $_POST['installmode'] === 'new') ? 0 : 2; @@ -92,12 +100,18 @@ $pos = strpos($database_collation, '.'); $database_charset = ($pos !== false) ? substr($database_collation, $pos + 1) : 'utf8'; $database_connection_charset = $database_charset; + } elseif ($database_type === 'sqlite') { + $database_collation = 'utf8'; + $database_charset = 'utf8'; + $database_connection_charset = 'utf8'; } } else { if ($database_type === 'mysql') { $database_collation = 'utf8mb4_general_ci'; } elseif ($database_type === 'pgsql') { $database_collation = 'en_US.utf8'; + } elseif ($database_type === 'sqlite') { + $database_collation = 'utf8'; } } @@ -107,6 +121,8 @@ $database_connection_method = 'SET CHARACTER SET'; } elseif ($database_type === 'pgsql') { $database_connection_method = 'SET client_encoding'; + } elseif ($database_type === 'sqlite') { + $database_connection_method = ''; } } $ph['databaseTypeOptions'] = ''; diff --git a/install/src/controllers/connection/collation.php b/install/src/controllers/connection/collation.php index 464cb32fba..5b651c9ead 100644 --- a/install/src/controllers/connection/collation.php +++ b/install/src/controllers/connection/collation.php @@ -89,6 +89,10 @@ } $output .= ''; break; + case 'sqlite': + $output .= ''; + $output .= ''; + break; } echo $output; } catch (Exception $e) { diff --git a/install/src/controllers/connection/databasetest.php b/install/src/controllers/connection/databasetest.php index 42b298cbc1..278f8e46ff 100644 --- a/install/src/controllers/connection/databasetest.php +++ b/install/src/controllers/connection/databasetest.php @@ -14,7 +14,11 @@ $database_charset = getDatabaseCharset($database_collation, $driver); try { - $dbh = new PDO($driver . ':host=' . $host . ';dbname=' . $database_name, $uid, $pwd); + if ($driver === 'sqlite') { + $dbh = new PDO('sqlite:' . EVO_CORE_PATH . "database/$database_name.sqlite"); + } else { + $dbh = new PDO($driver . ':host=' . $host . ';dbname=' . $database_name, $uid, $pwd); + } switch ($driver) { case 'pgsql': $result = $dbh->query("SELECT * FROM pg_settings WHERE name='client_encoding'"); @@ -79,6 +83,18 @@ exit(); } break; + case 'sqlite': + try { + $result = $dbh->query("SELECT COUNT(*) FROM {$tableprefix}site_content"); + } catch (PDOException $e) { + // no table is expected + } + + if ($dbh->errorCode() == 0) { + echo $output . '' . $_lang['status_failed_table_prefix_already_in_use'] . ''; + exit(); + } + break; } } catch (PDOException $e) { @@ -89,7 +105,11 @@ } try { - $dbh = new PDO($driver . ':host=' . $host . ($driver === 'pgsql' ? ';dbname=postgres' : ''), $uid, $pwd); + if ($driver === 'sqlite') { + $dbh = new PDO('sqlite:' . EVO_CORE_PATH . "database/$database_name.sqlite"); + } else { + $dbh = new PDO($driver . ':host=' . $host . ($driver === 'pgsql' ? ';dbname=postgres' : ''), $uid, $pwd); + } switch ($driver) { case 'pgsql': try { @@ -118,6 +138,8 @@ exit(); } break; + case 'sqlite': + break; } echo $output . ' ' . $_lang['status_passed'] . ''; diff --git a/install/src/controllers/connection/servertest.php b/install/src/controllers/connection/servertest.php index 046dcd2763..6eb5a805ed 100644 --- a/install/src/controllers/connection/servertest.php +++ b/install/src/controllers/connection/servertest.php @@ -6,7 +6,11 @@ $output = $_lang['status_connecting']; try { - $dbh = new PDO($method . ':host=' . $host . ($method === 'pgsql' ? ';dbname=postgres' : ''), $uid, $pwd); + if ($method === 'sqlite') { + $dbh = new PDO('sqlite::memory:'); + } else { + $dbh = new PDO($method . ':host=' . $host . ($method === 'pgsql' ? ';dbname=postgres' : ''), $uid, $pwd); + } $output .= ' ' . $_lang['status_passed_server'] . ''; } catch (PDOException $e) { $output .= ' ' . $_lang['status_failed'] . ' ' . $e->getMessage() . ''; diff --git a/install/src/controllers/install.php b/install/src/controllers/install.php index 2dda0034ed..38fe1f3f35 100644 --- a/install/src/controllers/install.php +++ b/install/src/controllers/install.php @@ -71,7 +71,11 @@ global $conn; try { - $dbh = new PDO($_POST['database_type'] . ':host=' . $_POST['databasehost'] . ';dbname=' . $_POST['database_name'], $database_user, $database_password); + if ($_POST['database_type'] === 'sqlite') { + $dbh = new PDO('sqlite:' . $_POST['database_name']); + } else { + $dbh = new PDO($_POST['database_type'] . ':host=' . $_POST['databasehost'] . ';dbname=' . $_POST['database_name'], $database_user, $database_password); + } include dirname(__DIR__) . '/processor/result.php'; @@ -109,6 +113,9 @@ $confph['database_engine'] = ", 'innodb'"; } break; + case 'sqlite': + $confph['database_port'] = ''; + break; } $configString = file_get_contents(dirname(__DIR__, 2) . '/stubs/files/config/database/connections/default.tpl'); $configString = parse($configString, $confph); diff --git a/install/src/controllers/options.php b/install/src/controllers/options.php index 122c47d660..57d16d6578 100644 --- a/install/src/controllers/options.php +++ b/install/src/controllers/options.php @@ -76,6 +76,16 @@ if (!isset($database_connection_method) || empty($database_connection_method)) { $database_connection_method = 'SET client_encoding'; } + } elseif ($db_config['driver'] === 'sqlite') { + try { + $conn = new PDO('sqlite:' . $db_config['database']); + $database_collation = 'utf8'; + $database_charset = 'utf8'; + $database_connection_charset = 'utf8'; + $database_connection_method = ''; + } catch (PDOException $e) { + // + } } $_POST['database_name'] = $db_config['database']; diff --git a/install/src/controllers/summary.php b/install/src/controllers/summary.php index f7a8fddf2a..8950380c19 100644 --- a/install/src/controllers/summary.php +++ b/install/src/controllers/summary.php @@ -211,13 +211,22 @@ function f_owc($path, $data, $mode = null) $database_charset = substr($database_collation, 0, strpos($database_collation, '_') - 1); $database_connection_charset = $_POST['database_connection_charset']; $database_connection_method = $_POST['database_connection_method']; - $dbase = '`' . strip_tags($_POST['database_name']) . '`'; + if ($database_type === 'sqlite') { + $database_name = strip_tags($_POST['database_name']); // TODO: replace strip_tags with validation everywhere + $dbase = EVO_CORE_PATH . "database/$database_name.sqlite"; + } else { + $dbase = '`' . strip_tags($_POST['database_name']) . '`'; + } $table_prefix = strip_tags($_POST['tableprefix']); } echo '

' . $_lang['creating_database_connection']; $host = explode(':', $database_server, 2); try { - $dbh = new PDO($database_type . ':host=' . $database_server . ';dbname=' . $_POST['database_name'], $database_user, $database_password); + if ($database_type === 'sqlite') { + $dbh = new PDO('sqlite:' . $dbase); + } else { + $dbh = new PDO($database_type . ':host=' . $database_server . ';dbname=' . $_POST['database_name'], $database_user, $database_password); + } echo '' . $_lang['ok'] . '

'; } catch (PDOException $e) { $errors++; @@ -226,7 +235,7 @@ function f_owc($path, $data, $mode = null) } // check the database collation if not specified in the configuration -if (!isset ($database_connection_charset) || empty ($database_connection_charset)) { +if ($database_type === 'mysql' && empty ($database_connection_charset)) { if (!$rs = mysqli_query($conn, "show session variables like 'collation_database'")) { $rs = mysqli_query($conn, "show session variables like 'collation_server'"); } diff --git a/install/src/functions.php b/install/src/functions.php index 9ca7d68f51..1390a3c91e 100644 --- a/install/src/functions.php +++ b/install/src/functions.php @@ -44,6 +44,8 @@ function getDatabaseCharset($database_collation, $driver): string { $database_charset = 'UTF8'; } $database_charset = str_ireplace(['utf-8', 'utf8'], 'UTF8', $database_charset); + } elseif ($driver === 'sqlite') { + $database_charset = 'utf8'; } else { // MySQL 5.7 & 8.0: "utf8mb4_general_ci", "utf8_unicode_ci", "latin1_swedish_ci" // MySQL 8.0+: "utf8mb4_0900_ai_ci" (with version number) @@ -70,20 +72,20 @@ function install_sessionCheck() echo ' - Install Problem - + Install Problem + -
-

' . $_lang["session_problem"] . '

-

' . $_lang["session_problem_try_again"] . '

-
+
+

' . $_lang["session_problem"] . '

+

' . $_lang["session_problem_try_again"] . '

+
'; exit; diff --git a/install/src/template/actions/connection.tpl b/install/src/template/actions/connection.tpl index 61858530c6..1af019dd70 100644 --- a/install/src/template/actions/connection.tpl +++ b/install/src/template/actions/connection.tpl @@ -140,20 +140,43 @@ }; }); + // Toggle server credential fields based on database type + function toggleServerCredentials() { + var type = document.getElementById('database_type').value; + var isSqlite = (type === 'sqlite'); + var serverFields = ['databasehost', 'databaseloginname', 'databaseloginpassword']; + serverFields.forEach(function(id) { + var el = document.getElementById(id); + var p = el.parentElement; + p.style.display = isSqlite ? 'none' : 'block'; + if (isSqlite) { + p.classList.remove('has-error'); + el.value = ''; + } + }); + } + + document.getElementById('database_type').addEventListener('change', toggleServerCredentials); + // Initial toggle on load + toggleServerCredentials(); + // get collation from the database server document.getElementById('servertest').addEventListener('click', function(e) { e.preventDefault(); - if (form.databasehost.value === '') { - form.databasehost.parentElement.classList.add('has-error'); - form.databasehost.focus(); - return false; - } + var type = form.database_type.value; + if (type !== 'sqlite') { + if (form.databasehost.value === '') { + form.databasehost.parentElement.classList.add('has-error'); + form.databasehost.focus(); + return false; + } - if (form.databaseloginname.value === '') { - form.databaseloginname.parentElement.classList.add('has-error'); - form.databaseloginname.focus(); - return false; + if (form.databaseloginname.value === '') { + form.databaseloginname.parentElement.classList.add('has-error'); + form.databaseloginname.focus(); + return false; + } } var url = 'index.php?s=1&action=connection/collation'; diff --git a/install/stubs/migrations/2018_06_29_182342_create_manager_users_table.php b/install/stubs/migrations/2018_06_29_182342_create_manager_users_table.php index fd7eb8c606..221934938d 100644 --- a/install/stubs/migrations/2018_06_29_182342_create_manager_users_table.php +++ b/install/stubs/migrations/2018_06_29_182342_create_manager_users_table.php @@ -15,8 +15,9 @@ public function up() { Schema::create('manager_users', function(Blueprint $table) { + $indexPrefix = \DB::getTablePrefix() . $table->getTable(); $table->integer('id', true); - $table->string('username', 100)->default('')->unique('username'); + $table->string('username', 100)->default('')->unique("{$indexPrefix}_username"); $table->string('password', 100)->default(''); }); } diff --git a/install/stubs/migrations/2018_06_29_182342_create_site_content_table.php b/install/stubs/migrations/2018_06_29_182342_create_site_content_table.php index 49d90270ae..7f9b4803c7 100644 --- a/install/stubs/migrations/2018_06_29_182342_create_site_content_table.php +++ b/install/stubs/migrations/2018_06_29_182342_create_site_content_table.php @@ -57,8 +57,12 @@ public function up() $prefix = DB::getTablePrefix(); $site_content_table_name = (new \EvolutionCMS\Models\SiteContent())->getTable(); $indexPrefix = $prefix . $site_content_table_name; - if(isset($_POST['database_type']) && $_POST['database_type'] != 'pgsql') - DB::statement('ALTER TABLE '.$prefix.$site_content_table_name." ADD FULLTEXT {$indexPrefix}_content_ft_idx(pagetitle, description, content)"); + if (isset($_POST['database_type']) && $_POST['database_type'] === 'mysql') { + DB::statement( + 'ALTER TABLE ' . $prefix . $site_content_table_name + . " ADD FULLTEXT {$indexPrefix}_content_ft_idx(pagetitle, description, content)" + ); + } } diff --git a/install/stubs/migrations/2018_06_29_182342_create_site_tmplvar_contentvalues_table.php b/install/stubs/migrations/2018_06_29_182342_create_site_tmplvar_contentvalues_table.php index 15f8f67101..6dd62fb4a9 100644 --- a/install/stubs/migrations/2018_06_29_182342_create_site_tmplvar_contentvalues_table.php +++ b/install/stubs/migrations/2018_06_29_182342_create_site_tmplvar_contentvalues_table.php @@ -25,8 +25,11 @@ public function up() $prefix = DB::getTablePrefix(); $site_content_tmplvar = (new \EvolutionCMS\Models\SiteTmplvarContentvalue())->getTable(); $indexPrefix = $prefix . $site_content_tmplvar; - if(isset($_POST['database_type']) && $_POST['database_type'] != 'pgsql') - DB::statement('ALTER TABLE '.$prefix.$site_content_tmplvar." ADD FULLTEXT {$indexPrefix}_content_ft_idx(value)"); + if (isset($_POST['database_type']) && $_POST['database_type'] === 'mysql') { + DB::statement( + 'ALTER TABLE ' . $prefix . $site_content_tmplvar . " ADD FULLTEXT {$indexPrefix}_content_ft_idx(value)" + ); + } } diff --git a/install/stubs/migrations/2018_06_29_182342_create_user_attributes_table.php b/install/stubs/migrations/2018_06_29_182342_create_user_attributes_table.php index cb6c77589a..46816eeaa7 100644 --- a/install/stubs/migrations/2018_06_29_182342_create_user_attributes_table.php +++ b/install/stubs/migrations/2018_06_29_182342_create_user_attributes_table.php @@ -15,8 +15,9 @@ public function up() { Schema::create('user_attributes', function(Blueprint $table) { + $indexPrefix = \DB::getTablePrefix() . $table->getTable(); $table->integer('id', true); - $table->integer('internalKey')->default(0)->index("{}_userid"); + $table->integer('internalKey')->default(0)->index("{$indexPrefix}_userid"); $table->string('fullname', 100)->default(''); $table->integer('role')->default(0); $table->string('email', 100)->default(''); diff --git a/install/stubs/migrations/2018_06_29_182342_create_web_user_settings_table.php b/install/stubs/migrations/2018_06_29_182342_create_web_user_settings_table.php index 78e148e206..28c6f92484 100644 --- a/install/stubs/migrations/2018_06_29_182342_create_web_user_settings_table.php +++ b/install/stubs/migrations/2018_06_29_182342_create_web_user_settings_table.php @@ -15,8 +15,9 @@ public function up() { Schema::create('web_user_settings', function(Blueprint $table) { - $table->integer('webuser')->index('webuserid'); - $table->string('setting_name', 50)->default('')->index(); + $indexPrefix = \DB::getTablePrefix() . $table->getTable(); + $table->integer('webuser')->index("{$indexPrefix}_webuserid"); + $table->string('setting_name', 50)->default('')->index("{$indexPrefix}_setting_name"); $table->text('setting_value', 65535)->nullable(); $table->primary(['webuser','setting_name']); });