Skip to content
8 changes: 5 additions & 3 deletions public/main/inc/lib/urlmanager.lib.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Chamilo\CoreBundle\Entity\AccessUrlRelSession;
use Chamilo\CoreBundle\Entity\AccessUrlRelUser;
use Chamilo\CoreBundle\Entity\AccessUrlRelUserGroup;
use Chamilo\CoreBundle\Entity\SettingsCurrent;
use Chamilo\CoreBundle\Entity\UserAuthSource;
use Chamilo\CoreBundle\Framework\Container;
use Doctrine\ORM\NonUniqueResultException;
Expand Down Expand Up @@ -98,12 +99,13 @@ public static function delete($id)

/*
* $tableCourseCategory = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE_CATEGORY);
$sql = "DELETE FROM $tableCourseCategory WHERE access_url_id = ".$id;
Database::query($sql);
*/
* $sql = "DELETE FROM $tableCourseCategory WHERE access_url_id = ".$id;
* Database::query($sql);
*/
$em = Container::getEntityManager();

$relEntities = [
SettingsCurrent::class,
AccessUrlRelCourse::class,
AccessUrlRelSession::class,
AccessUrlRelUserGroup::class,
Expand Down
178 changes: 162 additions & 16 deletions src/CoreBundle/Controller/Admin/SettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
namespace Chamilo\CoreBundle\Controller\Admin;

use Chamilo\CoreBundle\Controller\BaseController;
use Chamilo\CoreBundle\Entity\AccessUrl;
use Chamilo\CoreBundle\Entity\SearchEngineField;
use Chamilo\CoreBundle\Entity\SettingsCurrent;
use Chamilo\CoreBundle\Entity\SettingsValueTemplate;
Expand Down Expand Up @@ -47,7 +48,85 @@ public function index(): Response
}

/**
* Edit configuration with given namespace.
* Toggle access_url_changeable for a given setting variable.
* Only platform admins on the main URL (ID = 1) are allowed to change it,
*/
#[IsGranted('ROLE_ADMIN')]
#[Route('/settings/toggle_changeable', name: 'settings_toggle_changeable', methods: ['POST'])]
public function toggleChangeable(Request $request, AccessUrlHelper $accessUrlHelper): JsonResponse
{
// Security: only admins.
if (!$this->isGranted('ROLE_ADMIN')) {
return $this->json([
'error' => 'Only platform admins can modify this flag.',
], 403);
}

$currentUrl = $accessUrlHelper->getCurrent();
if (!$currentUrl) {
return $this->json([
'error' => 'Access URL not resolved.',
], 500);
}

$currentUrlId = (int) $currentUrl->getId();

if (1 !== $currentUrlId) {
return $this->json([
'error' => 'Only the main URL (ID 1) can toggle this setting.',
], 403);
}

$payload = json_decode($request->getContent(), true);

if (!\is_array($payload) || !isset($payload['variable'], $payload['status'])) {
return $this->json([
'error' => 'Invalid payload.',
], 400);
}

$variable = trim((string) $payload['variable']);
$status = (int) $payload['status'];
$status = $status === 1 ? 1 : 0;

if ('' === $variable) {
return $this->json([
'error' => 'Invalid variable.',
], 400);
}

$repo = $this->entityManager->getRepository(SettingsCurrent::class);

// We search by variable + current main AccessUrl entity.
$setting = $repo->findOneBy([
'variable' => $variable,
'url' => $currentUrl,
]);

if (!$setting) {
return $this->json([
'error' => 'Setting not found.',
], 404);
}

try {
$setting->setAccessUrlChangeable($status);
$this->entityManager->flush();

return $this->json([
'result' => 1,
'status' => $status,
]);
} catch (\Throwable $e) {
return $this->json([
'error' => 'Unable to update setting.',
'details' => $e->getMessage(),
], 500);
}
}

/**
* Edit configuration with given namespace (search page).
*/
#[IsGranted('ROLE_ADMIN')]
#[Route('/settings/search_settings', name: 'chamilo_platform_settings_search')]
Expand Down Expand Up @@ -75,6 +154,47 @@ public function searchSetting(Request $request, AccessUrlHelper $accessUrlHelper
$schemas = $manager->getSchemas();
[$ordered, $labelMap] = $this->computeOrderedNamespacesByTranslatedLabel($schemas, $request);

// Template map for current URL (existing behavior – JSON helper)
$settingsRepo = $this->entityManager->getRepository(SettingsCurrent::class);

// Build template map: current URL overrides main URL when missing.
$currentUrlId = (int) $url->getId();
$mainUrl = $this->entityManager->getRepository(AccessUrl::class)->find(1);

if ($mainUrl instanceof AccessUrl && 1 !== $currentUrlId) {
$mainRows = $settingsRepo->findBy(['url' => $mainUrl]);
foreach ($mainRows as $s) {
if ($s->getValueTemplate()) {
$templateMap[$s->getVariable()] = $s->getValueTemplate()->getId();
}
}
}

$currentRows = $settingsRepo->findBy(['url' => $url]);
foreach ($currentRows as $s) {
if ($s->getValueTemplate()) {
$templateMap[$s->getVariable()] = $s->getValueTemplate()->getId();
}
}

// MultiURL changeable flags: read from main URL (ID = 1) only
$changeableMap = [];
$mainUrlRows = $settingsRepo->createQueryBuilder('sc')
->join('sc.url', 'u')
->andWhere('u.id = :mainId')
->setParameter('mainId', 1)
->getQuery()
->getResult();

foreach ($mainUrlRows as $row) {
if ($row instanceof SettingsCurrent) {
$changeableMap[$row->getVariable()] = $row->getAccessUrlChangeable();
}
}

// Only platform admins on the main URL can toggle the MultiURL flag.
$canToggleMultiUrlSetting = $this->isGranted('ROLE_ADMIN') && 1 === $currentUrlId;

if ('' === $keyword) {
return $this->render('@ChamiloCore/Admin/Settings/search.html.twig', [
'keyword' => $keyword,
Expand All @@ -86,17 +206,12 @@ public function searchSetting(Request $request, AccessUrlHelper $accessUrlHelper
'template_map_by_category' => $templateMapByCategory,
'ordered_namespaces' => $ordered,
'namespace_labels' => $labelMap,
'changeable_map' => $changeableMap,
'current_url_id' => $currentUrlId,
'can_toggle_multiurl_setting' => $canToggleMultiUrlSetting,
]);
}

$settingsRepo = $this->entityManager->getRepository(SettingsCurrent::class);
$settingsWithTemplate = $settingsRepo->findBy(['url' => $url]);
foreach ($settingsWithTemplate as $s) {
if ($s->getValueTemplate()) {
$templateMap[$s->getVariable()] = $s->getValueTemplate()->getId();
}
}

$settingsFromKeyword = $manager->getParametersFromKeywordOrderedByCategory($keyword);
if (!empty($settingsFromKeyword)) {
foreach ($settingsFromKeyword as $category => $parameterList) {
Expand Down Expand Up @@ -145,6 +260,9 @@ public function searchSetting(Request $request, AccessUrlHelper $accessUrlHelper
'template_map_by_category' => $templateMapByCategory,
'ordered_namespaces' => $ordered,
'namespace_labels' => $labelMap,
'changeable_map' => $changeableMap,
'current_url_id' => $currentUrlId,
'can_toggle_multiurl_setting' => $canToggleMultiUrlSetting,
]);
}

Expand Down Expand Up @@ -230,17 +348,48 @@ public function updateSetting(Request $request, AccessUrlHelper $accessUrlHelper
$templateMap = [];
$settingsRepo = $this->entityManager->getRepository(SettingsCurrent::class);

$settingsWithTemplate = $settingsRepo->findBy(['url' => $url]);
$currentUrlId = (int) $url->getId();
$mainUrl = $this->entityManager->getRepository(AccessUrl::class)->find(1);

// Build template map: fallback to main URL templates when sub-URL has no row for a locked setting.
if ($mainUrl instanceof AccessUrl && 1 !== $currentUrlId) {
$mainRows = $settingsRepo->findBy(['url' => $mainUrl]);
foreach ($mainRows as $s) {
if ($s->getValueTemplate()) {
$templateMap[$s->getVariable()] = $s->getValueTemplate()->getId();
}
}
}

$settingsWithTemplate = $settingsRepo->findBy(['url' => $url]);
foreach ($settingsWithTemplate as $s) {
if ($s->getValueTemplate()) {
$templateMap[$s->getVariable()] = $s->getValueTemplate()->getId();
}
}

// MultiURL changeable flags: read from main URL (ID = 1) only
$changeableMap = [];
$mainUrlRows = $settingsRepo->createQueryBuilder('sc')
->join('sc.url', 'u')
->andWhere('u.id = :mainId')
->setParameter('mainId', 1)
->getQuery()
->getResult();

foreach ($mainUrlRows as $row) {
if ($row instanceof SettingsCurrent) {
$changeableMap[$row->getVariable()] = $row->getAccessUrlChangeable();
}
}

$platform = [
'server_type' => (string) $manager->getSetting('platform.server_type', true),
];

// Only platform admins on the main URL can toggle the MultiURL flag.
$canToggleMultiUrlSetting = $this->isGranted('ROLE_ADMIN') && 1 === $currentUrlId;

return $this->render('@ChamiloCore/Admin/Settings/default.html.twig', [
'schemas' => $schemas,
'settings' => $settings,
Expand All @@ -251,6 +400,9 @@ public function updateSetting(Request $request, AccessUrlHelper $accessUrlHelper
'ordered_namespaces' => $ordered,
'namespace_labels' => $labelMap,
'platform' => $platform,
'changeable_map' => $changeableMap,
'current_url_id' => $currentUrlId,
'can_toggle_multiurl_setting' => $canToggleMultiUrlSetting,
'search_diagnostics' => $searchDiagnostics,
]);
}
Expand Down Expand Up @@ -369,12 +521,6 @@ private function computeOrderedNamespacesByTranslatedLabel(array $schemas, Reque

/**
* Build environment diagnostics for the "search" settings page.
*
* This replicates the legacy Chamilo 1 behaviour:
* - Check Xapian PHP extension
* - Check the index directory and permissions
* - Check custom search fields
* - Check external converters (pdftotext, ps2pdf, ...)
*/
private function buildSearchDiagnostics(SettingsManager $manager): array
{
Expand Down
29 changes: 29 additions & 0 deletions src/CoreBundle/Migrations/Schema/V200/Version20251213154100.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

/* For licensing terms, see /license.txt */

namespace Chamilo\CoreBundle\Migrations\Schema\V200;

use Chamilo\CoreBundle\Migrations\AbstractMigrationChamilo;
use Doctrine\DBAL\Schema\Schema;

final class Version20251213154100 extends AbstractMigrationChamilo
{
public function getDescription(): string
{
return 'Set settings.access_url_locked to 0 for all rows (MultiURL default unlock).';
}

public function up(Schema $schema): void
{
$this->addSql('UPDATE settings SET access_url_locked = 0 WHERE access_url_locked IS NULL OR access_url_locked = 1');
}

public function down(Schema $schema): void
{
// Revert to previous "locked everywhere" behavior (not recommended, but reversible).
$this->addSql('UPDATE settings SET access_url_locked = 1 WHERE access_url_locked = 0');
}
}
77 changes: 77 additions & 0 deletions src/CoreBundle/Migrations/Schema/V200/Version20251215074200.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php

declare(strict_types=1);

/* For licensing terms, see /license.txt */

namespace Chamilo\CoreBundle\Migrations\Schema\V200;

use Chamilo\CoreBundle\Migrations\AbstractMigrationChamilo;
use Doctrine\DBAL\Schema\Schema;

final class Version20251215074200 extends AbstractMigrationChamilo
{
public function getDescription(): string
{
return 'MultiURL: lock selected global settings (access_url_locked = 1).';
}

public function up(Schema $schema): void
{
$this->addSql("
UPDATE settings
SET access_url_locked = 1
WHERE variable IN (
'permissions_for_new_directories',
'permissions_for_new_files',
'course_creation_form_set_extra_fields_mandatory',
'access_url_specific_files',
'cron_remind_course_finished_activate',
'cron_remind_course_expiration_frequency',
'cron_remind_course_expiration_activate',
'donotlistcampus',
'server_type',
'chamilo_database_version',
'unoconv_binaries',
'session_admin_access_to_all_users_on_all_urls',
'split_users_upload_directory',
'multiple_url_hide_disabled_settings',
'login_is_email',
'proxy_settings',
'login_max_attempt_before_blocking_account',
'permanently_remove_deleted_files',
'allow_use_sub_language'
)
");
}

public function down(Schema $schema): void
{
// Unlock back (sub-URLs editable) for the same list.
$this->addSql("
UPDATE settings
SET access_url_locked = 0
WHERE variable IN (
'permissions_for_new_directories',
'permissions_for_new_files',
'course_creation_form_set_extra_fields_mandatory',
'access_url_specific_files',
'cron_remind_course_finished_activate',
'cron_remind_course_expiration_frequency',
'cron_remind_course_expiration_activate',
'donotlistcampus',
'server_type',
'chamilo_database_version',
'unoconv_binaries',
'session_admin_access_to_all_users_on_all_urls',
'split_users_upload_directory',
'multiple_url_hide_disabled_settings',
'login_is_email',
'proxy_settings',
'login_max_attempt_before_blocking_account',
'permanently_remove_deleted_files',
'allow_use_sub_language'
)
");
}
}
Loading
Loading