Internal: Improve migration process from install page

pull/5771/head
christianbeeznst 1 year ago
parent 2cfb8ac018
commit 1a0bef78d0
  1. 3
      assets/vue/AppInstaller.vue
  2. 2
      assets/vue/components/installer/Step1.vue
  3. 10
      assets/vue/components/installer/Step2.vue
  4. 166
      assets/vue/components/installer/Step6.vue
  5. 22
      public/main/install/get_migration_status.php
  6. 110
      public/main/install/index.php
  7. 262
      public/main/install/install.lib.php
  8. 33
      public/main/install/migrate.php
  9. 16
      src/CoreBundle/Migrations/AbstractMigrationChamilo.php
  10. 1
      src/CoreBundle/Migrations/Schema/V200/Version20.php
  11. 6
      src/CoreBundle/Migrations/Schema/V200/Version20170524110000.php
  12. 2
      src/CoreBundle/Migrations/Schema/V200/Version20191101132000.php
  13. 2
      src/CoreBundle/Migrations/Schema/V200/Version20191206150030.php
  14. 6
      src/CoreBundle/Migrations/Schema/V200/Version20201212203625.php
  15. 4
      src/CoreBundle/Migrations/Schema/V200/Version20201215072917.php
  16. 2
      src/CoreBundle/Migrations/Schema/V200/Version20201215072920.php
  17. 2
      src/CoreBundle/Migrations/Schema/V200/Version20201215153517.php
  18. 4
      src/CoreBundle/Migrations/Schema/V200/Version20201215160445.php
  19. 6
      src/CoreBundle/Migrations/Schema/V200/Version20201217124011.php
  20. 4
      src/CoreBundle/Migrations/Schema/V200/Version20210205082253.php
  21. 2
      src/CoreBundle/Migrations/Schema/V200/Version20210221082033.php
  22. 2
      src/CoreBundle/Migrations/Schema/V200/Version20210813150011.php
  23. 2
      src/CoreBundle/Migrations/Schema/V200/Version20210923090920.php
  24. 2
      src/CoreBundle/Migrations/Schema/V200/Version20211005154000.php
  25. 2
      src/CoreBundle/Migrations/Schema/V200/Version20230204150030.php
  26. 3
      src/CoreBundle/Migrations/Schema/V200/Version20230216122900.php
  27. 2
      src/CoreBundle/Migrations/Schema/V200/Version20230315115019.php
  28. 2
      src/CoreBundle/Migrations/Schema/V200/Version20230720143000.php
  29. 2
      src/CoreBundle/Migrations/Schema/V200/Version20230720222140.php
  30. 2
      src/CoreBundle/Migrations/Schema/V200/Version20230913162700.php
  31. 2
      src/CoreBundle/Migrations/Schema/V200/Version20231026231100.php
  32. 2
      src/CoreBundle/Migrations/Schema/V200/Version20231110194300.php
  33. 14
      src/CoreBundle/Migrations/Schema/V200/Version20240122221400.php
  34. 2
      src/CoreBundle/Migrations/Schema/V200/Version20240128205500.php
  35. 2
      src/CoreBundle/Migrations/Schema/V200/Version20240130161800.php
  36. 4
      src/CoreBundle/Migrations/Schema/V200/Version20240425192900.php
  37. 2
      src/CoreBundle/Migrations/Schema/V200/Version20240507160300.php
  38. 4
      src/CoreBundle/Migrations/Schema/V200/Version20240519210000.php
  39. 4
      src/CoreBundle/Migrations/Schema/V200/Version20240715183456.php
  40. 4
      src/CoreBundle/Migrations/Schema/V200/Version20240731120000.php
  41. 39
      src/CoreBundle/Migrations/Schema/V200/Version20240806120000.php

@ -44,8 +44,7 @@
<h1 <h1
v-else-if="'update' === installerData.installType" v-else-if="'update' === installerData.installType"
v-t="{ v-t="{
path: 'Update from Chamilo {versions}', path: 'Update from Chamilo ' + installerData.upgradeFromVersion.join(' | ')
args: { versions: installerData.upgradeFromVersion.join(' | ') },
}" }"
class="mb-4" class="mb-4"
/> />

@ -67,7 +67,7 @@
:label="t('Next')" :label="t('Next')"
:class="[installerData.isUpdateAvailable ? 'p-button-secondary' : 'p-button-success']" :class="[installerData.isUpdateAvailable ? 'p-button-secondary' : 'p-button-success']"
icon="mdi mdi-page-next" icon="mdi mdi-page-next"
:name="installerData.isUpdateAvailable ? 'step5' : 'step1'" :name="'step1'"
type="submit" type="submit"
/> />
<input <input

@ -328,7 +328,7 @@
icon="mdi mdi-page-next" icon="mdi mdi-page-next"
name="step2_install" name="step2_install"
type="submit" type="submit"
:disabled="installerData.stepData.installError" :disabled="installerData.stepData.installError || installerData.isUpdateAvailable"
/> />
</div> </div>
<Button <Button
@ -336,8 +336,8 @@
class="p-button-secondary" class="p-button-secondary"
icon="mdi mdi-page-next" icon="mdi mdi-page-next"
name="step2_update_8" name="step2_update_8"
type="submit" type="button"
:disabled="!installerData.stepData.installError" @click.prevent="goToUpgrade"
/> />
<input <input
id="is_executable" id="is_executable"
@ -363,6 +363,10 @@ const { t } = useI18n()
const installerData = inject("installerData") const installerData = inject("installerData")
function goToUpgrade() {
window.location = `/main/install/index.php?running=1&installType=${installerData.installType || "update"}&step=step2_update_8`
}
function goToIndex() { function goToIndex() {
window.location = "index.php" window.location = "index.php"
} }

@ -265,7 +265,6 @@
id="button_step6" id="button_step6"
:label="installerData.isUpdateAvailable ? t('Update Chamilo') : t('Install Chamilo')" :label="installerData.isUpdateAvailable ? t('Update Chamilo') : t('Install Chamilo')"
:loading="loading" :loading="loading"
:disabled="isButtonDisabled"
class="p-button-success" class="p-button-success"
icon="mdi mdi-progress-download" icon="mdi mdi-progress-download"
name="button_step6" name="button_step6"
@ -275,49 +274,57 @@
</div> </div>
</div> </div>
<div <div v-show="loading" class="install-step">
v-show="loading" <h2 v-if="'update' !== installerData.installType" v-t="'Step 7 - Installation process execution'" class="RequirementHeading mb-8" />
class="install-step" <h2 v-else v-t="'Step 7 - Update process execution'" class="RequirementHeading mb-8" />
>
<h2
v-if="'update' !== installerData.installType"
v-t="'Step 7 - Installation process execution'"
class="RequirementHeading mb-8"
/>
<h2
v-else
v-t="'Step 7 - Update process execution'"
class="RequirementHeading mb-8"
/>
<p> <Message id="pleasewait" :closable="false" severity="success">
<strong <p v-t="'Please wait. This could take a while...'" class="mb-3" />
v-if="installerData.installationProfile"
v-text="installerData.installationProfile"
/>
</p>
<Message
id="pleasewait"
:closable="false"
severity="success"
>
<p
v-t="'Please wait. This could take a while...'"
class="mb-3"
/>
<ProgressBar mode="indeterminate" />
</Message> </Message>
<ProgressBar
:value="progressPercentage" <div v-if="'update' === installerData.installType">
style="height: 22px" <ProgressBar :value="progressPercentage" style="height: 22px">{{ progressPercentage }}%</ProgressBar>
> <p class="current-migration">
{{ progressPercentage }}/100</ProgressBar <strong>{{ t('Verifying migration:') }}</strong> {{ currentMigration }}
> </p>
<div class="log-container">
<div v-html="logTerminalContent"></div>
</div> </div>
</div> </div>
<Dialog v-model:visible="successDialogVisible" :closable="false" :modal="true" :show-header="false">
<div class="p-d-flex p-ai-center p-jc-center">
<h3 v-t="'Migration completed successfully!'" class="mb-4" />
</div>
<div class="formgroup-inline">
<div class="field">
<Button
:label="t('Go to your newly created portal.')"
class="p-button-success"
type="button"
@click="btnFinishOnClick"
/>
</div>
</div>
</Dialog>
<Dialog v-model:visible="errorDialogVisible" :closable="false" :modal="true" :show-header="false">
<div class="p-d-flex p-ai-center p-jc-center">
<h3 v-t="'Migration failed!'" class="mb-4 text-error" />
</div>
<div class="p-d-flex p-ai-center p-jc-center">
<p class="text-error">{{ errorMessage }}</p>
</div>
<div class="formgroup-inline">
<div class="field">
<Button
:label="t('Contact Support')"
class="p-button-danger"
type="button"
@click="btnSupportOnClick"
/>
</div>
</div>
</Dialog>
</template> </template>
<script setup> <script setup>
@ -327,6 +334,7 @@ import { useI18n } from "vue-i18n"
import Message from "primevue/message" import Message from "primevue/message"
import Button from "primevue/button" import Button from "primevue/button"
import ProgressBar from "primevue/progressbar" import ProgressBar from "primevue/progressbar"
import Dialog from "primevue/dialog"
const { t } = useI18n() const { t } = useI18n()
@ -336,35 +344,77 @@ const loading = ref(false)
const isButtonDisabled = ref(installerData.value.isUpdateAvailable) const isButtonDisabled = ref(installerData.value.isUpdateAvailable)
const isExecutable = ref("") const isExecutable = ref("")
const logTerminalContent = ref("")
const progressPercentage = ref(0) const progressPercentage = ref(0)
const currentMigration = ref("")
const successDialogVisible = ref(false)
const errorDialogVisible = ref(false)
const errorMessage = ref('')
function btnStep6OnClick() {
loading.value = true
isButtonDisabled.value = true
function updateLog() { const updatePath = installerData.value.updatePath || '';
if (installerData.value.installType === 'update') {
startMigration(updatePath)
setTimeout(pollMigrationStatus, 7000)
} else {
isExecutable.value = "step6"
document.getElementById("install_form").submit()
}
}
function startMigration(updatePath) {
var xhr = new XMLHttpRequest() var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () { xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) { if (xhr.readyState === 4 && xhr.status !== 200) {
const response = JSON.parse(xhr.responseText) loading.value = false
logTerminalContent.value = response.log_terminal
progressPercentage.value = response.progress_percentage
scrollToBottom()
isButtonDisabled.value = false isButtonDisabled.value = false
errorDialogVisible.value = true
errorMessage.value = `${t('Migration request failed with status:')} ${xhr.status}`
} }
} };
xhr.open("GET", installerData.value.logUrl, true)
const url = `/main/install/migrate.php?updatePath=${encodeURIComponent(updatePath)}`
xhr.open("POST", url, true)
xhr.send() xhr.send()
} }
function btnStep6OnClick() { function pollMigrationStatus() {
loading.value = true setTimeout(() => {
isExecutable.value = "step6" var xhr = new XMLHttpRequest();
document.getElementById("install_form").submit() xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
const response = JSON.parse(xhr.responseText)
progressPercentage.value = response.progress_percentage
currentMigration.value = response.current_migration
if (response.progress_percentage < 100) {
pollMigrationStatus()
} else {
loading.value = false
isButtonDisabled.value = false
successDialogVisible.value = true
}
} else if (xhr.readyState === 4 && xhr.status !== 200) {
loading.value = false
isButtonDisabled.value = false
errorDialogVisible.value = true
errorMessage.value = `${t('Migration request failed with status:')} ${xhr.status}`
}
};
xhr.open("GET", "/main/install/get_migration_status.php", true)
xhr.send()
}, 2000)
} }
function scrollToBottom() { function btnFinishOnClick() {
const logContainer = document.querySelector(".log-container") window.location = "../../"
logContainer.scrollTop = logContainer.scrollHeight
} }
setInterval(updateLog, 2000) function btnSupportOnClick() {
alert(t('Please contact support with the error details.'))
}
</script> </script>

@ -1,35 +1,15 @@
<?php <?php
/* For licensing terms, see /license.txt */ /* For licensing terms, see /license.txt */
ini_set('memory_limit', '1024M'); ini_set('memory_limit', '1024M');
require_once __DIR__.'/../../../vendor/autoload.php'; require_once __DIR__.'/../../../vendor/autoload.php';
require_once api_get_path(SYS_CODE_PATH).'install/install.lib.php'; require_once api_get_path(SYS_CODE_PATH).'install/install.lib.php';
$result = checkMigrationStatus(); $result = checkMigrationStatus();
$logFile = api_get_path(SYS_PATH) . '/../var/log/migration-to-2.0.log';
if (!file_exists($logFile)) {
error_log('Initiating migration at '.date('d/m/Y H:i') . PHP_EOL, 3, $logFile);
chmod($logFile, 0644);
}
if (!empty($result['current_migration'])) {
$migrationPath = "Current Migration path: " . $result['current_migration'];
error_log($migrationPath . PHP_EOL, 3, $logFile);
}
$logContent = '';
if (file_exists($logFile)) {
// The migration log file was found
chmod($logFile, 0644);
$logContent = file_get_contents($logFile);
}
// Prepare the response array // Prepare the response array
$response = [ $response = [
'log_terminal' => '<pre class="terminal">' . $logContent . '</pre>',
'progress_percentage' => $result['progress_percentage'], 'progress_percentage' => $result['progress_percentage'],
'current_migration' => $result['current_migration'],
]; ];
// Send the response as JSON // Send the response as JSON

@ -301,6 +301,32 @@ if (isset($_POST['step2'])) {
$current_step = 5; $current_step = 5;
// STEP 5 : CONFIGURATION SETTINGS // STEP 5 : CONFIGURATION SETTINGS
if ('update' === $installType) { if ('update' === $installType) {
// Create .env file
$envFile = api_get_path(SYMFONY_SYS_PATH) . '.env';
$distFile = api_get_path(SYMFONY_SYS_PATH) . '.env.dist';
$params = [
'{{DATABASE_HOST}}' => $dbHostForm,
'{{DATABASE_PORT}}' => $dbPortForm,
'{{DATABASE_NAME}}' => $dbNameForm,
'{{DATABASE_USER}}' => $dbUsernameForm,
'{{DATABASE_PASSWORD}}' => $dbPassForm,
'{{APP_INSTALLED}}' => 1,
'{{APP_ENCRYPT_METHOD}}' => $encryptPassForm,
'{{APP_SECRET}}' => generateRandomToken(),
'{{DB_MANAGER_ENABLED}}' => '0',
'{{SOFTWARE_NAME}}' => 'Chamilo',
'{{SOFTWARE_URL}}' => $institutionUrlForm,
'{{DENY_DELETE_USERS}}' => '0',
'{{HOSTING_TOTAL_SIZE_LIMIT}}' => '0',
'{{THEME_FALLBACK}}' => 'chamilo',
'{{PACKAGER}}' => 'chamilo',
'{{DEFAULT_TEMPLATE}}' => 'default',
'{{ADMIN_CHAMILO_ANNOUNCEMENTS_DISABLE}}' => '0',
];
error_log('Update env file');
updateEnvFile($distFile, $envFile, $params);
(new Dotenv())->load($envFile);
$db_name = $dbNameForm; $db_name = $dbNameForm;
connectToDatabase( connectToDatabase(
$dbHostForm, $dbHostForm,
@ -421,15 +447,7 @@ if (isset($_POST['step2'])) {
$stepData['institutionUrlForm'] = $institutionUrlForm; $stepData['institutionUrlForm'] = $institutionUrlForm;
$stepData['encryptPassForm'] = $encryptPassForm; $stepData['encryptPassForm'] = $encryptPassForm;
$isPendingMigration = false;
if ($isUpdateAvailable) { if ($isUpdateAvailable) {
$checkMigrationStatus = checkMigrationStatus();
$isPendingMigration = false === $checkMigrationStatus['status'];
}
if ($isPendingMigration) {
$envFile = api_get_path(SYMFONY_SYS_PATH) . '.env'; $envFile = api_get_path(SYMFONY_SYS_PATH) . '.env';
$dotenv = new Dotenv(); $dotenv = new Dotenv();
$envFile = api_get_path(SYMFONY_SYS_PATH) . '.env'; $envFile = api_get_path(SYMFONY_SYS_PATH) . '.env';
@ -451,79 +469,11 @@ if (isset($_POST['step2'])) {
$current_step = 7; $current_step = 7;
if ('update' === $installType) { if ('update' === $installType) {
connectToDatabase( // The migration process for updates has been moved to migrate.php and is now
$dbHostForm, // handled via AJAX requests from Vue.js. This section of the code is no longer
$dbUsernameForm, // necessary and has been removed to streamline the update process.
$dbPassForm,
$dbNameForm,
$dbPortForm
);
$manager = Database::getManager();
//$perm = api_get_permissions_for_new_directories();
//$perm_file = api_get_permissions_for_new_files();
// @todo fix permissions.
$perm = octdec('0777');
$perm_file = octdec('0777');
if (!$isUpdateAvailable) {
$installType = 'update';
// Create .env file
$envFile = api_get_path(SYMFONY_SYS_PATH) . '.env';
$distFile = api_get_path(SYMFONY_SYS_PATH) . '.env.dist';
$params = [
'{{DATABASE_HOST}}' => $dbHostForm,
'{{DATABASE_PORT}}' => $dbPortForm,
'{{DATABASE_NAME}}' => $dbNameForm,
'{{DATABASE_USER}}' => $dbUsernameForm,
'{{DATABASE_PASSWORD}}' => $dbPassForm,
'{{APP_INSTALLED}}' => 1,
'{{APP_ENCRYPT_METHOD}}' => $encryptPassForm,
'{{APP_SECRET}}' => generateRandomToken(),
'{{DB_MANAGER_ENABLED}}' => '0',
'{{SOFTWARE_NAME}}' => 'Chamilo',
'{{SOFTWARE_URL}}' => $institutionUrlForm,
'{{DENY_DELETE_USERS}}' => '0',
'{{HOSTING_TOTAL_SIZE_LIMIT}}' => '0',
'{{THEME_FALLBACK}}' => 'chamilo',
'{{PACKAGER}}' => 'chamilo',
'{{DEFAULT_TEMPLATE}}' => 'default',
'{{ADMIN_CHAMILO_ANNOUNCEMENTS_DISABLE}}' => '0',
];
error_log('Update env file');
updateEnvFile($distFile, $envFile, $params);
(new Dotenv())->load($envFile);
} else {
$dotenv = new Dotenv();
$envFile = api_get_path(SYMFONY_SYS_PATH) . '.env';
$dotenv->loadEnv($envFile);
}
// Load Symfony Kernel
$kernel = new Kernel('dev', true);
$application = new Application($kernel);
error_log('Set Kernel');
session_unset(); error_log('Migration process moved to migrate.php');
$_SESSION = [];
session_destroy();
// No errors
//if ($result == 0) {
// Boot kernel and get the doctrine from Symfony container
$kernel->boot();
error_log('Boot');
$container = $kernel->getContainer();
Container::setContainer($container);
Container::setLegacyServices($container);
$manager = $container->get('doctrine')->getManager();
migrateSwitch($my_old_version, $manager);
upgradeWithContainer($container);
error_log('Set upgradeWithContainer');
error_log('------------------------------');
error_log('Upgrade 2.0.0 process concluded! ('.date('Y-m-d H:i:s').')'); error_log('Upgrade 2.0.0 process concluded! ('.date('Y-m-d H:i:s').')');
} else { } else {
error_log('------------------------------'); error_log('------------------------------');

@ -9,6 +9,7 @@ use Chamilo\CoreBundle\Framework\Container;
use Chamilo\CoreBundle\Repository\GroupRepository; use Chamilo\CoreBundle\Repository\GroupRepository;
use Chamilo\CoreBundle\Repository\Node\AccessUrlRepository; use Chamilo\CoreBundle\Repository\Node\AccessUrlRepository;
use Chamilo\CoreBundle\Tool\ToolChain; use Chamilo\CoreBundle\Tool\ToolChain;
use Doctrine\DBAL\Connection;
use Doctrine\Migrations\Configuration\Connection\ExistingConnection; use Doctrine\Migrations\Configuration\Connection\ExistingConnection;
use Doctrine\Migrations\Configuration\Migration\PhpFile; use Doctrine\Migrations\Configuration\Migration\PhpFile;
use Doctrine\Migrations\DependencyFactory; use Doctrine\Migrations\DependencyFactory;
@ -16,6 +17,9 @@ use Doctrine\Migrations\Query\Query;
use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityManager;
use Symfony\Component\DependencyInjection\Container as SymfonyContainer; use Symfony\Component\DependencyInjection\Container as SymfonyContainer;
use Symfony\Component\Dotenv\Dotenv; use Symfony\Component\Dotenv\Dotenv;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Bundle\FrameworkBundle\Console\Application;
/* /*
* Chamilo LMS * Chamilo LMS
@ -306,10 +310,19 @@ function get_config_param_from_db($param = '')
{ {
$param = Database::escape_string($param); $param = Database::escape_string($param);
if (false !== ($res = Database::query("SELECT * FROM settings WHERE variable = '$param'"))) { $schemaManager = Database::getConnection()->createSchemaManager();
if ($schemaManager->tablesExist('settings_current')) {
$query = "SELECT * FROM settings_current WHERE variable = '$param'";
} elseif ($schemaManager->tablesExist('settings')) {
$query = "SELECT * FROM settings WHERE variable = '$param'";
} else {
return null;
}
if (false !== ($res = Database::query($query))) {
if (Database::num_rows($res) > 0) { if (Database::num_rows($res) > 0) {
$row = Database::fetch_array($res); $row = Database::fetch_array($res);
return $row['selected_value']; return $row['selected_value'];
} }
} }
@ -689,6 +702,10 @@ function display_requirements(
'item' => $basePath.'var/', 'item' => $basePath.'var/',
'status' => is_writable($basePath.'var'), 'status' => is_writable($basePath.'var'),
]; ];
$pathPermissions[] = [
'item' => $basePath.'config/',
'status' => is_writable($basePath.'config'),
];
$pathPermissions[] = [ $pathPermissions[] = [
'item' => $basePath.'.env', 'item' => $basePath.'.env',
'status' => checkCanCreateFile($basePath.'.env'), 'status' => checkCanCreateFile($basePath.'.env'),
@ -1703,6 +1720,10 @@ function checkCanCreateFile(string $file): bool
*/ */
function isUpdateAvailable(): bool function isUpdateAvailable(): bool
{ {
$dotenv = new Dotenv();
$envFile = api_get_path(SYMFONY_SYS_PATH) . '.env';
$dotenv->loadEnv($envFile);
// Check if APP_INSTALLED is set and equals '1' // Check if APP_INSTALLED is set and equals '1'
if (isset($_ENV['APP_INSTALLED']) && $_ENV['APP_INSTALLED'] === '1') { if (isset($_ENV['APP_INSTALLED']) && $_ENV['APP_INSTALLED'] === '1') {
return true; return true;
@ -1712,91 +1733,194 @@ function isUpdateAvailable(): bool
return false; return false;
} }
/**
* Check the current migration status.
*
* This function calculates the progress of the database migration by comparing the number of executed migrations
* with the total number of migration files available in the system. It also retrieves the latest executed migration version.
*
* @return array {
* An array containing the following keys:
*
* @type int $progress_percentage The percentage of migrations that have been executed.
* @type string $current_migration The version of the last executed migration, or null if no migrations have been executed.
* }
*/
function checkMigrationStatus(): array function checkMigrationStatus(): array
{ {
$resultStatus = [ Database::setManager(initializeEntityManager());
'status' => false, $manager = Database::getManager();
'message' => 'Error executing migrations status command.', $connection = $manager->getConnection();
'current_migration' => null,
'progress_percentage' => 0 $migrationFiles = glob(__DIR__ . '/../../../src/CoreBundle/Migrations/Schema/V200/Version*.php');
$totalMigrations = count($migrationFiles);
$executedMigrations = $connection->createQueryBuilder()
->select('COUNT(*) as count')
->from('version')
->execute()
->fetchOne();
$progress_percentage = 0;
if ($totalMigrations > 0) {
$progress_percentage = ($executedMigrations / $totalMigrations) * 100;
}
$current_migration = $connection->createQueryBuilder()
->select('version')
->from('version')
->orderBy('executed_at', 'DESC')
->setMaxResults(1)
->execute()
->fetchOne();
return [
'progress_percentage' => ceil($progress_percentage),
'current_migration' => $current_migration,
]; ];
}
/**
* Initializes the EntityManager by loading environment variables and connecting to the database.
*
* @return EntityManager The initialized EntityManager
*/
function initializeEntityManager(): EntityManager
{
$dotenv = new Dotenv(); $dotenv = new Dotenv();
$envFile = api_get_path(SYMFONY_SYS_PATH) . '.env'; $envFile = api_get_path(SYMFONY_SYS_PATH) . '.env';
$dotenv->loadEnv($envFile); $dotenv->loadEnv($envFile);
try { connectToDatabase(
connectToDatabase( $_ENV['DATABASE_HOST'],
$_ENV['DATABASE_HOST'], $_ENV['DATABASE_USER'],
$_ENV['DATABASE_USER'], $_ENV['DATABASE_PASSWORD'],
$_ENV['DATABASE_PASSWORD'], $_ENV['DATABASE_NAME'],
$_ENV['DATABASE_NAME'], $_ENV['DATABASE_PORT']
$_ENV['DATABASE_PORT'] );
);
$manager = Database::getManager(); $manager = Database::getManager();
$connection = $manager->getConnection(); return $manager;
}
// Loading migration configuration. /**
$config = new PhpFile('./migrations.php'); * Checks if the version table in the database is valid.
$dependency = DependencyFactory::fromConnection($config, new ExistingConnection($connection)); *
* @param Connection $connection The database connection
*
* @return bool True if the version table is valid, false otherwise
*/
function isVersionTableValid($connection): bool
{
$schema = $connection->createSchemaManager();
if ($schema->tablesExist('version')) {
$columns = $schema->listTableColumns('version');
// Check if old "version" table exists from 1.11.x, use new version. $requiredColumns = ['version', 'executed_at', 'execution_time'];
$schema = $manager->getConnection()->createSchemaManager(); foreach ($requiredColumns as $column) {
$hasOldVersionTable = false; if (!isset($columns[$column])) {
$anyVersionYet = !$schema->tablesExist('version'); return false;
$isVersionEmpty = false;
$executedMigrations = 0;
$currentMigration = '';
if ($schema->tablesExist('version')) {
$columns = $schema->listTableColumns('version');
if (in_array('id', array_keys($columns), true)) {
$hasOldVersionTable = true;
}
$query = $connection->createQueryBuilder()
->select('*')
->from('version');
$result = $query->execute();
$executedMigrations = $result->rowCount();
$isVersionEmpty = ($executedMigrations == 0);
if (!$isVersionEmpty) {
$query = $connection->createQueryBuilder()
->select('*')
->from('version')
->orderBy('executed_at', 'DESC')
->setMaxResults(1);
$result = $query->execute()->fetch();
$currentMigration = $result['version'];
} }
} }
if (!$hasOldVersionTable) { $query = $connection->createQueryBuilder()
// Loading migrations. ->select('*')
$migratorConfigurationFactory = $dependency->getConsoleInputMigratorConfigurationFactory(); ->from('version')
$result = ''; ->orderBy('executed_at', 'DESC')
$input = new Symfony\Component\Console\Input\StringInput($result); ->setMaxResults(1);
$planCalculator = $dependency->getMigrationPlanCalculator(); $result = $query->execute()->fetchAll();
$migrations = $planCalculator->getMigrations();
$totalMigrations = $migrations->count(); if (!empty($result)) {
$lastVersion = $migrations->getLast(); $latestMigrationDate = new DateTime($result[0]['executed_at']);
$now = new DateTime();
if ($anyVersionYet || $isVersionEmpty) {
$currentMigration = ''; if ($latestMigrationDate->diff($now)->days < 1) {
$executedMigrations = 0; return true;
} }
}
}
// Calculate progress percentage return false;
$progressPercentage = ceil(($executedMigrations / $totalMigrations) * 100); }
$resultStatus = [
'status' => ($progressPercentage >= 100), /**
'message' => ($progressPercentage >= 100) ? 'All migrations have been executed.' : 'Migrations are pending.', * Retrieves the last executed migration version from the database.
'current_migration' => ($progressPercentage >= 100) ? null : $currentMigration, *
'progress_percentage' => $progressPercentage * @param Connection $connection The database connection
]; *
* @return string The last executed migration version
*/
function getLastExecutedMigration(Connection $connection): string
{
$query = $connection->createQueryBuilder()
->select('version')
->from('version')
->orderBy('executed_at', 'DESC')
->setMaxResults(1);
$result = $query->execute()->fetchAssociative();
return $result['version'] ?? '';
}
/**
* Executes the database migration and returns the status.
*
* @return array The result status of the migration
*/
function executeMigration(): array
{
$resultStatus = [
'status' => false,
'message' => 'Error executing migration.',
'progress_percentage' => 0,
'current_migration' => '',
];
try {
Database::setManager(initializeEntityManager());
$manager = Database::getManager();
$connection = $manager->getConnection();
$config = new PhpFile(api_get_path(SYS_CODE_PATH) . 'install/migrations.php');
$dependency = DependencyFactory::fromConnection($config, new ExistingConnection($connection));
if (!isVersionTableValid($connection)) {
$schema = $connection->createSchemaManager();
$schema->dropTable('version');
}
$dependency->getMetadataStorage()->ensureInitialized();
$env = $_SERVER['APP_ENV'] ?? 'dev';
$kernel = new Chamilo\Kernel($env, false);
$kernel->boot();
$application = new Application($kernel);
$application->setAutoExit(false);
$input = new ArrayInput([
'command' => 'doctrine:migrations:migrate',
'--no-interaction' => true,
]);
$output = new BufferedOutput();
$application->run($input, $output);
$result = $output->fetch();
if (strpos($result, '[OK] Successfully migrated to version') !== false) {
$resultStatus['status'] = true;
$resultStatus['message'] = 'Migration completed successfully.';
$resultStatus['progress_percentage'] = 100;
} else {
$resultStatus['message'] = 'Migration completed with errors.';
$resultStatus['progress_percentage'] = 0;
} }
} catch (Exception) {
return $resultStatus; $resultStatus['current_migration'] = getLastExecutedMigration($connection);
} catch (Exception $e) {
$resultStatus['message'] = 'Migration failed: ' . $e->getMessage();
} }
return $resultStatus; return $resultStatus;

@ -0,0 +1,33 @@
<?php
/* For licensing terms, see /license.txt */
ini_set('memory_limit', '4G');
ini_set('max_execution_time', 0);
require_once __DIR__.'/../../../vendor/autoload.php';
require_once api_get_path(SYS_CODE_PATH).'install/install.lib.php';
$logFile = api_get_path(SYS_PATH) . '/../var/log/migration-to-2.0.log';
if (isset($_GET['updatePath'])) {
$updatePath = strip_tags($_GET['updatePath']);
putenv('UPDATE_PATH=' . $updatePath);
}
$response = executeMigration();
$logContent = '';
if (file_exists($logFile)) {
chmod($logFile, 0644);
$logContent = file_get_contents($logFile);
}
$response = [
'log_terminal' => '<pre class="terminal">' . $logContent . '</pre>',
'progress_percentage' => $response['progress_percentage'],
'message' => $response['message'],
'current_migration' => $response['current_migration'],
'redirect_to_step7' => $response['status'] === true,
];
header('Content-Type: application/json');
echo json_encode($response);

@ -369,7 +369,7 @@ abstract class AbstractMigrationChamilo extends AbstractMigration
global $platform_email; global $platform_email;
$rootPath = $this->container->get('kernel')->getProjectDir(); $rootPath = $this->container->get('kernel')->getProjectDir();
$oldConfigPath = $rootPath.'/app/config/mail.conf.php'; $oldConfigPath = $this->getUpdateRootPath().'/app/config/mail.conf.php';
$configFileLoaded = \in_array($oldConfigPath, get_included_files(), true); $configFileLoaded = \in_array($oldConfigPath, get_included_files(), true);
@ -417,4 +417,18 @@ abstract class AbstractMigrationChamilo extends AbstractMigration
$fs = new Filesystem(); $fs = new Filesystem();
$fs->remove($fullFilename); $fs->remove($fullFilename);
} }
protected function getUpdateRootPath(): string
{
$updateRootPath = getenv('UPDATE_PATH');
if (!empty($updateRootPath)) {
error_log('getUpdateRootPath ::: '.$updateRootPath);
return rtrim($updateRootPath, '/');
}
return $this->container->getParameter('kernel.project_dir');
}
} }

@ -232,7 +232,6 @@ class Version20 extends AbstractMigrationChamilo
); );
$this->addSql('CREATE UNIQUE INDEX UNIQ_4B019DDB5E237E06 ON fos_group (name);'); $this->addSql('CREATE UNIQUE INDEX UNIQ_4B019DDB5E237E06 ON fos_group (name);');
} }
$this->addSql('ALTER TABLE fos_group CHANGE name title VARCHAR(255) NOT NULL');
$table = $schema->getTable('fos_user_user_group'); $table = $schema->getTable('fos_user_user_group');
if ($table->hasForeignKey('FK_B3C77447A76ED395')) { if ($table->hasForeignKey('FK_B3C77447A76ED395')) {

@ -39,6 +39,12 @@ final class Version20170524110000 extends AbstractMigrationChamilo
'ALTER TABLE track_e_hotpotatoes CHANGE exe_name title VARCHAR(255) NOT NULL' 'ALTER TABLE track_e_hotpotatoes CHANGE exe_name title VARCHAR(255) NOT NULL'
); );
} }
if ($schema->hasTable('fos_group')) {
$this->addSql(
'ALTER TABLE fos_group CHANGE name title VARCHAR(255) NOT NULL'
);
}
} }
public function down(Schema $schema): void public function down(Schema $schema): void

@ -149,7 +149,7 @@ class Version20191101132000 extends AbstractMigrationChamilo
continue; continue;
} }
$filePath = $rootPath.'/app/upload/course_category/'.$category['image']; $filePath = $this->getUpdateRootPath().'/app/upload/course_category/'.$category['image'];
error_log('MIGRATIONS :: $filePath -- '.$filePath.' ...'); error_log('MIGRATIONS :: $filePath -- '.$filePath.' ...');
if ($this->fileExists($filePath)) { if ($this->fileExists($filePath)) {
$fileName = basename($filePath); $fileName = basename($filePath);

@ -40,7 +40,7 @@ class Version20191206150030 extends AbstractMigrationChamilo
if (empty($path)) { if (empty($path)) {
continue; continue;
} }
$filePath = $rootPath.'/app/upload/'.$path; $filePath = $this->getUpdateRootPath().'/app/upload/'.$path;
error_log('MIGRATIONS :: $filePath -- '.$filePath.' ...'); error_log('MIGRATIONS :: $filePath -- '.$filePath.' ...');
if ($this->fileExists($filePath)) { if ($this->fileExists($filePath)) {
$fileName = basename($path); $fileName = basename($path);

@ -58,7 +58,7 @@ final class Version20201212203625 extends AbstractMigrationChamilo
$path = str_replace('//', '/', $path); $path = str_replace('//', '/', $path);
$path = str_replace('/../exercises/teacher_audio/', '', $path); $path = str_replace('/../exercises/teacher_audio/', '', $path);
$filePath = $rootPath.'/app/courses/'.$course->getDirectory().'/exercises/teacher_audio/'.$path; $filePath = $this->getUpdateRootPath().'/app/courses/'.$course->getDirectory().'/exercises/teacher_audio/'.$path;
error_log('MIGRATIONS :: $filePath -- '.$filePath.' ...'); error_log('MIGRATIONS :: $filePath -- '.$filePath.' ...');
if ($this->fileExists($filePath)) { if ($this->fileExists($filePath)) {
preg_match('#/(.*)/#', '/'.$path, $matches); preg_match('#/(.*)/#', '/'.$path, $matches);
@ -130,7 +130,7 @@ final class Version20201212203625 extends AbstractMigrationChamilo
$path = str_replace('//', '/', $path); $path = str_replace('//', '/', $path);
$path = str_replace('/../exercises/', '', $path); $path = str_replace('/../exercises/', '', $path);
$filePath = $rootPath.'/app/courses/'.$course->getDirectory().'/exercises/'.$path; $filePath = $this->getUpdateRootPath().'/app/courses/'.$course->getDirectory().'/exercises/'.$path;
error_log('MIGRATIONS :: $filePath -- '.$filePath.' ...'); error_log('MIGRATIONS :: $filePath -- '.$filePath.' ...');
if ($this->fileExists($filePath)) { if ($this->fileExists($filePath)) {
$fileName = basename($filePath); $fileName = basename($filePath);
@ -238,7 +238,7 @@ final class Version20201212203625 extends AbstractMigrationChamilo
continue; continue;
} }
$documentPath = ltrim($documentPath, '/'); $documentPath = ltrim($documentPath, '/');
$filePath = $rootPath.'/app/courses/'.$course->getDirectory().'/document/'.$documentPath; $filePath = $this->getUpdateRootPath().'/app/courses/'.$course->getDirectory().'/document/'.$documentPath;
error_log('MIGRATIONS :: $filePath -- '.$filePath.' ...'); error_log('MIGRATIONS :: $filePath -- '.$filePath.' ...');
$this->addLegacyFileToResource($filePath, $documentRepo, $document, $documentId); $this->addLegacyFileToResource($filePath, $documentRepo, $document, $documentId);
$this->entityManager->persist($document); $this->entityManager->persist($document);

@ -49,8 +49,8 @@ class Version20201215072917 extends AbstractMigrationChamilo
private function getConfigurationSelectedValue(): string private function getConfigurationSelectedValue(): string
{ {
global $_configuration; global $_configuration;
$rootPath = $this->container->getParameter('kernel.project_dir'); $updateRootPath = $this->getUpdateRootPath();
$oldConfigPath = $rootPath.'/app/config/configuration.php'; $oldConfigPath = $updateRootPath.'/app/config/configuration.php';
if (!\in_array($oldConfigPath, get_included_files(), true)) { if (!\in_array($oldConfigPath, get_included_files(), true)) {
include_once $oldConfigPath; include_once $oldConfigPath;
} }

@ -124,7 +124,7 @@ final class Version20201215072920 extends AbstractMigrationChamilo
continue; continue;
} }
$filePath = $rootPath.'/app/courses/'.$course->getDirectory().'/upload/calendar/'.$attachmentPath; $filePath = $this->getUpdateRootPath().'/app/courses/'.$course->getDirectory().'/upload/calendar/'.$attachmentPath;
error_log('MIGRATIONS :: $filePath -- '.$filePath.' ...'); error_log('MIGRATIONS :: $filePath -- '.$filePath.' ...');
$this->addLegacyFileToResource($filePath, $eventAttachmentRepo, $attachment, $id, $fileName); $this->addLegacyFileToResource($filePath, $eventAttachmentRepo, $attachment, $id, $fileName);
$this->entityManager->persist($attachment); $this->entityManager->persist($attachment);

@ -99,7 +99,7 @@ final class Version20201215153517 extends AbstractMigrationChamilo
$this->entityManager->persist($resource); $this->entityManager->persist($resource);
$this->entityManager->flush(); $this->entityManager->flush();
$filePath = $rootPath.'/app/courses/'.$course->getDirectory().'/upload/announcements/'.$path; $filePath = $this->getUpdateRootPath().'/app/courses/'.$course->getDirectory().'/upload/announcements/'.$path;
error_log('MIGRATIONS :: $filePath -- '.$filePath.' ...'); error_log('MIGRATIONS :: $filePath -- '.$filePath.' ...');
$this->addLegacyFileToResource($filePath, $announcementAttachmentRepo, $resource, $id, $fileName); $this->addLegacyFileToResource($filePath, $announcementAttachmentRepo, $resource, $id, $fileName);
$this->entityManager->persist($resource); $this->entityManager->persist($resource);

@ -125,7 +125,7 @@ final class Version20201215160445 extends AbstractMigrationChamilo
$forumImage = $itemData['forum_image']; $forumImage = $itemData['forum_image'];
if (!empty($forumImage)) { if (!empty($forumImage)) {
$filePath = $rootPath.'/app/courses/'.$course->getDirectory().'/upload/forum/images/'.$forumImage; $filePath = $this->getUpdateRootPath().'/app/courses/'.$course->getDirectory().'/upload/forum/images/'.$forumImage;
error_log('MIGRATIONS :: $filePath -- '.$filePath.' ...'); error_log('MIGRATIONS :: $filePath -- '.$filePath.' ...');
if ($this->fileExists($filePath)) { if ($this->fileExists($filePath)) {
$this->addLegacyFileToResource($filePath, $forumRepo, $resource, $id, $forumImage); $this->addLegacyFileToResource($filePath, $forumRepo, $resource, $id, $forumImage);
@ -272,7 +272,7 @@ final class Version20201215160445 extends AbstractMigrationChamilo
} }
if (!empty($fileName) && !empty($path)) { if (!empty($fileName) && !empty($path)) {
$filePath = $rootPath.'/app/courses/'.$course->getDirectory().'/upload/forum/'.$path; $filePath = $this->getUpdateRootPath().'/app/courses/'.$course->getDirectory().'/upload/forum/'.$path;
error_log('MIGRATIONS :: $filePath -- '.$filePath.' ...'); error_log('MIGRATIONS :: $filePath -- '.$filePath.' ...');
if ($this->fileExists($filePath)) { if ($this->fileExists($filePath)) {
$this->addLegacyFileToResource($filePath, $forumPostRepo, $post, $id, $fileName); $this->addLegacyFileToResource($filePath, $forumPostRepo, $post, $id, $fileName);

@ -122,7 +122,7 @@ final class Version20201217124011 extends AbstractMigrationChamilo
continue; continue;
} }
$filePath = $rootPath.'/app/courses/'.$course->getDirectory().'/'.$path; $filePath = $this->getUpdateRootPath().'/app/courses/'.$course->getDirectory().'/'.$path;
error_log('MIGRATIONS :: $filePath -- '.$filePath.' ...'); error_log('MIGRATIONS :: $filePath -- '.$filePath.' ...');
$this->addLegacyFileToResource($filePath, $studentPublicationRepo, $resource, $id, $title); $this->addLegacyFileToResource($filePath, $studentPublicationRepo, $resource, $id, $title);
$this->entityManager->persist($resource); $this->entityManager->persist($resource);
@ -162,7 +162,7 @@ final class Version20201217124011 extends AbstractMigrationChamilo
$studentPublicationCorrectionRepo->addResourceNode($correction, $admin, $studentPublication); $studentPublicationCorrectionRepo->addResourceNode($correction, $admin, $studentPublication);
$this->entityManager->persist($correction); $this->entityManager->persist($correction);
$filePath = $rootPath.'/app/courses/'.$course->getDirectory().'/'.$path; $filePath = $this->getUpdateRootPath().'/app/courses/'.$course->getDirectory().'/'.$path;
error_log('MIGRATIONS :: $filePath -- '.$filePath.' ...'); error_log('MIGRATIONS :: $filePath -- '.$filePath.' ...');
$this->addLegacyFileToResource($filePath, $studentPublicationCorrectionRepo, $correction, null, $title); $this->addLegacyFileToResource($filePath, $studentPublicationCorrectionRepo, $correction, null, $title);
$this->entityManager->persist($correction); $this->entityManager->persist($correction);
@ -202,7 +202,7 @@ final class Version20201217124011 extends AbstractMigrationChamilo
$resource->setParent($parent); $resource->setParent($parent);
$resource->addCourseLink($course, $session, $group, ResourceLink::VISIBILITY_PUBLISHED); $resource->addCourseLink($course, $session, $group, ResourceLink::VISIBILITY_PUBLISHED);
$filePath = $rootPath.'/app/courses/'.$course->getDirectory().'/'.$file; $filePath = $this->getUpdateRootPath().'/app/courses/'.$course->getDirectory().'/'.$file;
error_log('MIGRATIONS :: $filePath -- '.$filePath.' ...'); error_log('MIGRATIONS :: $filePath -- '.$filePath.' ...');
$this->addLegacyFileToResource($filePath, $studentPublicationRepo, $resource, $id, $title); $this->addLegacyFileToResource($filePath, $studentPublicationRepo, $resource, $id, $title);
$this->entityManager->persist($resource); $this->entityManager->persist($resource);

@ -52,7 +52,7 @@ final class Version20210205082253 extends AbstractMigrationChamilo
if (!empty($setting) && 'true' === $setting['selected_value']) { if (!empty($setting) && 'true' === $setting['selected_value']) {
$path = 'users/'.substr((string) $id, 0, 1).'/'.$id.'/'; $path = 'users/'.substr((string) $id, 0, 1).'/'.$id.'/';
} }
$picturePath = $rootPath.'/app/upload/'.$path.'/'.$picture; $picturePath = $this->getUpdateRootPath().'/app/upload/'.$path.'/'.$picture;
error_log('MIGRATIONS :: $filePath -- '.$picturePath.' ...'); error_log('MIGRATIONS :: $filePath -- '.$picturePath.' ...');
if ($this->fileExists($picturePath)) { if ($this->fileExists($picturePath)) {
$mimeType = mime_content_type($picturePath); $mimeType = mime_content_type($picturePath);
@ -122,7 +122,7 @@ final class Version20210205082253 extends AbstractMigrationChamilo
if (!empty($setting) && 'true' === $setting['selected_value']) { if (!empty($setting) && 'true' === $setting['selected_value']) {
$path = 'groups/'.substr((string) $id, 0, 1).'/'.$id.'/'; $path = 'groups/'.substr((string) $id, 0, 1).'/'.$id.'/';
} }
$picturePath = $rootPath.'/app/upload/'.$path.'/'.$picture; $picturePath = $this->getUpdateRootPath().'/app/upload/'.$path.'/'.$picture;
error_log('MIGRATIONS :: $filePath -- '.$picturePath.' ...'); error_log('MIGRATIONS :: $filePath -- '.$picturePath.' ...');
if ($this->fileExists($picturePath)) { if ($this->fileExists($picturePath)) {
$mimeType = mime_content_type($picturePath); $mimeType = mime_content_type($picturePath);

@ -41,7 +41,7 @@ class Version20210221082033 extends AbstractMigrationChamilo
$path = $itemData['preview_image']; $path = $itemData['preview_image'];
$lp = $lpRepo->find($id); $lp = $lpRepo->find($id);
if ($lp && !empty($path)) { if ($lp && !empty($path)) {
$filePath = $rootPath.'/app/courses/'.$course->getDirectory().'/upload/learning_path/images/'.$path; $filePath = $this->getUpdateRootPath().'/app/courses/'.$course->getDirectory().'/upload/learning_path/images/'.$path;
error_log('MIGRATIONS :: $filePath -- '.$filePath.' ...'); error_log('MIGRATIONS :: $filePath -- '.$filePath.' ...');
if ($this->fileExists($filePath)) { if ($this->fileExists($filePath)) {
$this->addLegacyFileToResource($filePath, $lpRepo, $lp, $lp->getIid(), $path); $this->addLegacyFileToResource($filePath, $lpRepo, $lp, $lp->getIid(), $path);

@ -43,7 +43,7 @@ class Version20210813150011 extends AbstractMigrationChamilo
continue; continue;
} }
$filePath = $rootPath.'/app/upload/badges/'.$icon; $filePath = $this->getUpdateRootPath().'/app/upload/badges/'.$icon;
error_log('MIGRATIONS :: $filePath -- '.$filePath.' ...'); error_log('MIGRATIONS :: $filePath -- '.$filePath.' ...');
if ($this->fileExists($filePath)) { if ($this->fileExists($filePath)) {
$mimeType = mime_content_type($filePath); $mimeType = mime_content_type($filePath);

@ -39,7 +39,7 @@ final class Version20210923090920 extends AbstractMigrationChamilo
continue; continue;
} }
$picturePath = $rootPath.'/app/courses/'.$directory.'/course-pic.png'; $picturePath = $this->getUpdateRootPath().'/app/courses/'.$directory.'/course-pic.png';
error_log('MIGRATIONS :: $filePath -- '.$picturePath.' ...'); error_log('MIGRATIONS :: $filePath -- '.$picturePath.' ...');
if ($this->fileExists($picturePath)) { if ($this->fileExists($picturePath)) {
$admin = $this->getAdmin(); $admin = $this->getAdmin();

@ -55,7 +55,7 @@ class Version20211005154000 extends AbstractMigrationChamilo
$attachmentRepo->create($messageAttachment); $attachmentRepo->create($messageAttachment);
$filePath = $rootPath.'/app/upload/ticket_attachment/'.$item['path']; $filePath = $this->getUpdateRootPath().'/app/upload/ticket_attachment/'.$item['path'];
error_log('MIGRATIONS :: $filePath -- '.$filePath.' ...'); error_log('MIGRATIONS :: $filePath -- '.$filePath.' ...');
$this->addLegacyFileToResource($filePath, $attachmentRepo, $messageAttachment, $item['id']); $this->addLegacyFileToResource($filePath, $attachmentRepo, $messageAttachment, $item['id']);

@ -46,7 +46,7 @@ class Version20230204150030 extends AbstractMigrationChamilo
if (empty($path)) { if (empty($path)) {
continue; continue;
} }
$filePath = $rootPath.'/app/upload/'.$path; $filePath = $this->getUpdateRootPath().'/app/upload/'.$path;
error_log('MIGRATIONS :: $filePath -- '.$filePath.' ...'); error_log('MIGRATIONS :: $filePath -- '.$filePath.' ...');
if ($this->fileExists($filePath)) { if ($this->fileExists($filePath)) {
$fileName = basename($path); $fileName = basename($path);

@ -374,8 +374,7 @@ class Version20230216122900 extends AbstractMigrationChamilo
{ {
global $_configuration; global $_configuration;
$kernel = $this->container->get('kernel'); $kernel = $this->container->get('kernel');
$rootPath = $kernel->getProjectDir(); $oldConfigPath = $this->getUpdateRootPath().'/app/config/configuration.php';
$oldConfigPath = $rootPath.'/app/config/configuration.php';
$configFileLoaded = \in_array($oldConfigPath, get_included_files(), true); $configFileLoaded = \in_array($oldConfigPath, get_included_files(), true);
if (!$configFileLoaded) { if (!$configFileLoaded) {
include_once $oldConfigPath; include_once $oldConfigPath;

@ -39,7 +39,7 @@ final class Version20230315115019 extends AbstractMigrationChamilo
continue; continue;
} }
$filePath = $rootPath.'/app/home/default_platform_document/template_thumb/'.$systemTemplate['image']; $filePath = $this->getUpdateRootPath().'/app/home/default_platform_document/template_thumb/'.$systemTemplate['image'];
if ($this->fileExists($filePath)) { if ($this->fileExists($filePath)) {
$fileName = basename($filePath); $fileName = basename($filePath);
$mimeType = mime_content_type($filePath); $mimeType = mime_content_type($filePath);

@ -53,7 +53,7 @@ final class Version20230720143000 extends AbstractMigrationChamilo
$path = 'users/'.substr((string) $id, 0, 1).'/'.$id.'/'; $path = 'users/'.substr((string) $id, 0, 1).'/'.$id.'/';
} }
$baseDir = $rootPath.'/app/upload/'.$path; $baseDir = $this->getUpdateRootPath().'/app/upload/'.$path;
// Check if the base directory exists, if not, continue to the next user // Check if the base directory exists, if not, continue to the next user
if (!is_dir($baseDir)) { if (!is_dir($baseDir)) {

@ -53,7 +53,7 @@ final class Version20230720222140 extends AbstractMigrationChamilo
$filename = $matches[3]; $filename = $matches[3];
// Get the full file path // Get the full file path
$filePath = $rootPath."/app/upload/users/{$folderId}/{$userId}/my_files/{$filename}"; $filePath = $this->getUpdateRootPath()."/app/upload/users/{$folderId}/{$userId}/my_files/{$filename}";
// Check if the path corresponds to a file // Check if the path corresponds to a file
if (is_file($filePath)) { if (is_file($filePath)) {

@ -230,7 +230,7 @@ final class Version20230913162700 extends AbstractMigrationChamilo
return $document; return $document;
} }
$generalCoursesPath = $rootPath.'/app/courses/'; $generalCoursesPath = $this->getUpdateRootPath().'/app/courses/';
$foundPath = $this->recursiveFileSearch($generalCoursesPath, $title); $foundPath = $this->recursiveFileSearch($generalCoursesPath, $title);
if ($foundPath) { if ($foundPath) {
$document = new CDocument(); $document = new CDocument();

@ -50,7 +50,7 @@ final class Version20231026231100 extends AbstractMigrationChamilo
if ($message) { if ($message) {
$messageId = $message->getId(); $messageId = $message->getId();
$filename = $attachment->getFilename(); $filename = $attachment->getFilename();
$rootDir = $rootPath.'/app/upload/users'; $rootDir = $this->getUpdateRootPath().'/app/upload/users';
$targetFile = $attachment->getPath(); $targetFile = $attachment->getPath();
$foundFilePath = $this->findFileRecursively($rootDir, $targetFile); $foundFilePath = $this->findFileRecursively($rootDir, $targetFile);
$sender = $message->getSender(); $sender = $message->getSender();

@ -60,7 +60,7 @@ final class Version20231110194300 extends AbstractMigrationChamilo
$defaulThemesFolders = $this->getDefaultThemeNames(); $defaulThemesFolders = $this->getDefaultThemeNames();
$sourceDir = $rootPath.'/app/Resources/public/css/themes'; $sourceDir = $this->getUpdateRootPath().'/app/Resources/public/css/themes';
if (!is_dir($sourceDir)) { if (!is_dir($sourceDir)) {
return; return;

@ -37,7 +37,7 @@ final class Version20240122221400 extends AbstractMigrationChamilo
$this->executeVueTranslationsUpdate(); $this->executeVueTranslationsUpdate();
// Delete the 'import' folder at the end of the process. // Delete the 'import' folder at the end of the process.
$this->deleteImportFolder(); //$this->deleteImportFolder();
} }
private function updateAndGenerateSubLanguage(array $sublanguage): string private function updateAndGenerateSubLanguage(array $sublanguage): string
@ -80,11 +80,11 @@ final class Version20240122221400 extends AbstractMigrationChamilo
private function generatePoFileFromTrad4All(string $englishName, string $isocode): void private function generatePoFileFromTrad4All(string $englishName, string $isocode): void
{ {
$kernel = $this->container->get('kernel'); $kernel = $this->container->get('kernel');
$rootPath = $kernel->getProjectDir(); $updateRootPath = $this->getUpdateRootPath();
$langPath = $rootPath.'/var/translations/import/'.$englishName.'/trad4all.inc.php'; $langPath = $updateRootPath.'/main/lang/'.$englishName.'/trad4all.inc.php';
$destinationFilePath = $rootPath.'/var/translations/messages.'.$isocode.'.po'; $destinationFilePath = $kernel->getProjectDir().'/var/translations/messages.'.$isocode.'.po';
$originalFile = $rootPath.'/var/translations/import/english/trad4all.inc.php'; $originalFile = $updateRootPath.'/main/lang/english/trad4all.inc.php';
if (!file_exists($langPath)) { if (!file_exists($langPath)) {
error_log("Original file not found: $langPath"); error_log("Original file not found: $langPath");
@ -159,10 +159,10 @@ final class Version20240122221400 extends AbstractMigrationChamilo
$process->run(); $process->run();
if (!$process->isSuccessful()) { if (!$process->isSuccessful()) {
throw new RuntimeException($process->getErrorOutput()); //throw new RuntimeException($process->getErrorOutput());
} }
echo $process->getOutput(); error_log($process->getOutput());
} }
private function generateSublanguageCode(string $parentCode, string $variant, int $maxLength = 10): string private function generateSublanguageCode(string $parentCode, string $variant, int $maxLength = 10): string

@ -32,7 +32,7 @@ final class Version20240128205500 extends AbstractMigrationChamilo
$id = $userEntity->getId(); $id = $userEntity->getId();
$path = 'users/'.substr((string) $id, 0, 1).'/'.$id.'/'; $path = 'users/'.substr((string) $id, 0, 1).'/'.$id.'/';
$certificateDir = $rootPath.'/app/upload/'.$path.'certificate/'; $certificateDir = $this->getUpdateRootPath().'/app/upload/'.$path.'certificate/';
if (!is_dir($certificateDir)) { if (!is_dir($certificateDir)) {
continue; continue;

@ -43,7 +43,7 @@ class Version20240130161800 extends AbstractMigrationChamilo
if ($courseRow) { if ($courseRow) {
$directory = $courseRow['directory']; $directory = $courseRow['directory'];
$thumbPath = $rootPath.'/app/courses/'.$directory.'/upload/template_thumbnails/'.$imagePath; $thumbPath = $this->getUpdateRootPath().'/app/courses/'.$directory.'/upload/template_thumbnails/'.$imagePath;
if (file_exists($thumbPath)) { if (file_exists($thumbPath)) {
$mimeType = mime_content_type($thumbPath); $mimeType = mime_content_type($thumbPath);

@ -6,10 +6,10 @@ declare(strict_types=1);
namespace Chamilo\CoreBundle\Migrations\Schema\V200; namespace Chamilo\CoreBundle\Migrations\Schema\V200;
use Chamilo\CoreBundle\Migrations\AbstractMigrationChamilo;
use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
class Version20240425192900 extends AbstractMigration class Version20240425192900 extends AbstractMigrationChamilo
{ {
public function getDescription(): string public function getDescription(): string
{ {

@ -37,7 +37,7 @@ final class Version20240507160300 extends AbstractMigrationChamilo
} }
$path = $this->determinePath($userEntity->getId(), $picture); $path = $this->determinePath($userEntity->getId(), $picture);
$picturePath = $rootPath.'/app/upload/'.$path.$picture; $picturePath = $this->getUpdateRootPath().'/app/upload/'.$path.$picture;
if (file_exists($picturePath)) { if (file_exists($picturePath)) {
$mimeType = mime_content_type($picturePath); $mimeType = mime_content_type($picturePath);

@ -45,7 +45,7 @@ final class Version20240519210000 extends AbstractMigrationChamilo
$courseDir = $this->findCourseDirectory($pathPattern); $courseDir = $this->findCourseDirectory($pathPattern);
if ($courseDir) { if ($courseDir) {
$filePath = "app/courses/{$courseDir}/exercises/{$pathPattern}"; $filePath = $this->getUpdateRootPath()."app/courses/{$courseDir}/exercises/{$pathPattern}";
$this->processFile($filePath, $attemptRepo, $attemptData); $this->processFile($filePath, $attemptRepo, $attemptData);
} else { } else {
error_log('MIGRATIONS :: File not found for pattern: '.$pathPattern); error_log('MIGRATIONS :: File not found for pattern: '.$pathPattern);
@ -58,7 +58,7 @@ final class Version20240519210000 extends AbstractMigrationChamilo
private function findCourseDirectory(string $pathPattern): ?string private function findCourseDirectory(string $pathPattern): ?string
{ {
$kernel = $this->container->get('kernel'); $kernel = $this->container->get('kernel');
$rootPath = $kernel->getProjectDir().'/app/courses/'; $rootPath = $this->getUpdateRootPath().'/app/courses/';
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($rootPath)); $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($rootPath));
foreach ($iterator as $file) { foreach ($iterator as $file) {

@ -7,10 +7,10 @@ declare(strict_types=1);
namespace Chamilo\CoreBundle\Migrations\Schema\V200; namespace Chamilo\CoreBundle\Migrations\Schema\V200;
use Chamilo\CoreBundle\Entity\ExtraField; use Chamilo\CoreBundle\Entity\ExtraField;
use Chamilo\CoreBundle\Migrations\AbstractMigrationChamilo;
use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20240715183456 extends AbstractMigration final class Version20240715183456 extends AbstractMigrationChamilo
{ {
public function getDescription(): string public function getDescription(): string
{ {

@ -7,10 +7,10 @@ declare(strict_types=1);
namespace Chamilo\CoreBundle\Migrations\Schema\V200; namespace Chamilo\CoreBundle\Migrations\Schema\V200;
use Chamilo\CoreBundle\Entity\MessageRelUser; use Chamilo\CoreBundle\Entity\MessageRelUser;
use Chamilo\CoreBundle\Migrations\AbstractMigrationChamilo;
use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20240731120000 extends AbstractMigration final class Version20240731120000 extends AbstractMigrationChamilo
{ {
public function getDescription(): string public function getDescription(): string
{ {

@ -24,17 +24,18 @@ final class Version20240806120000 extends AbstractMigrationChamilo
global $_configuration; global $_configuration;
$rootPath = $this->getRootPath(); $rootPath = $this->getRootPath();
$oldConfigPath = $rootPath.'/app/config/configuration.php'; $updateRootPath = $this->getUpdateRootPath();
$oldConfigPath = $updateRootPath.'/app/config/configuration.php';
if (!\in_array($oldConfigPath, get_included_files(), true)) { if (!\in_array($oldConfigPath, get_included_files(), true)) {
include_once $oldConfigPath; include_once $oldConfigPath;
} }
// Update .env and .env.local files // Update .env and .env.local files
$this->updateEnvFiles($rootPath, [ $this->updateEnvFiles($rootPath, [
'DB_MANAGER_ENABLED' => $_configuration['db_manager_enabled'] ? '1' : '0', 'DB_MANAGER_ENABLED' => !empty($_configuration['db_manager_enabled']) ? '1' : '0',
'SOFTWARE_NAME' => $_configuration['software_name'], 'SOFTWARE_NAME' => $_configuration['software_name'] ?? '',
'SOFTWARE_URL' => $_configuration['software_url'], 'SOFTWARE_URL' => $_configuration['software_url'] ?? '',
'DENY_DELETE_USERS' => $_configuration['deny_delete_users'] ? '1' : '0', 'DENY_DELETE_USERS' => !empty($_configuration['deny_delete_users']) ? '1' : '0',
'HOSTING_TOTAL_SIZE_LIMIT' => $_configuration['hosting_total_size_limit'] ?? 0, 'HOSTING_TOTAL_SIZE_LIMIT' => $_configuration['hosting_total_size_limit'] ?? 0,
]); ]);
@ -43,18 +44,20 @@ final class Version20240806120000 extends AbstractMigrationChamilo
$hostingLimits = ['hosting_limits' => ['urls' => []]]; $hostingLimits = ['hosting_limits' => ['urls' => []]];
// Prepare hosting limits // Prepare hosting limits
foreach ($_configuration as $key => $config) { if (is_array($_configuration)) {
if (is_numeric($key) && \is_array($config)) { foreach ($_configuration as $key => $config) {
// Handle configurations specific to URL IDs if (is_numeric($key) && \is_array($config)) {
$hostingLimits['hosting_limits']['urls'][$key] = [ // Handle configurations specific to URL IDs
['hosting_limit_users' => $config['hosting_limit_users'] ?? 0], $hostingLimits['hosting_limits']['urls'][$key] = [
['hosting_limit_teachers' => $config['hosting_limit_teachers'] ?? 0], ['hosting_limit_users' => $config['hosting_limit_users'] ?? 0],
['hosting_limit_courses' => $config['hosting_limit_courses'] ?? 0], ['hosting_limit_teachers' => $config['hosting_limit_teachers'] ?? 0],
['hosting_limit_sessions' => $config['hosting_limit_sessions'] ?? 0], ['hosting_limit_courses' => $config['hosting_limit_courses'] ?? 0],
['hosting_limit_disk_space' => $config['hosting_limit_disk_space'] ?? 0], ['hosting_limit_sessions' => $config['hosting_limit_sessions'] ?? 0],
['hosting_limit_active_courses' => $config['hosting_limit_active_courses'] ?? 0], ['hosting_limit_disk_space' => $config['hosting_limit_disk_space'] ?? 0],
['hosting_total_size_limit' => $_configuration['hosting_total_size_limit'] ?? 0], ['hosting_limit_active_courses' => $config['hosting_limit_active_courses'] ?? 0],
]; ['hosting_total_size_limit' => $_configuration['hosting_total_size_limit'] ?? 0],
];
}
} }
} }
@ -82,7 +85,7 @@ final class Version20240806120000 extends AbstractMigrationChamilo
private function updateEnvFiles(string $rootPath, array $envSettings): void private function updateEnvFiles(string $rootPath, array $envSettings): void
{ {
$envFiles = [$rootPath.'/.env', $rootPath.'/.env.local']; $envFiles = [$rootPath.'/.env'];
foreach ($envFiles as $envFile) { foreach ($envFiles as $envFile) {
if (file_exists($envFile)) { if (file_exists($envFile)) {

Loading…
Cancel
Save