diff --git a/src/CoreBundle/Controller/Admin/SettingsController.php b/src/CoreBundle/Controller/Admin/SettingsController.php
index 2300967121b..219bd7cffa9 100644
--- a/src/CoreBundle/Controller/Admin/SettingsController.php
+++ b/src/CoreBundle/Controller/Admin/SettingsController.php
@@ -41,7 +41,73 @@ 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();
+ $currentUrlId = $currentUrl->getId();
+
+ // Only main URL (ID = 1) can toggle the flag.
+ 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 = (string) $payload['variable'];
+ $status = (int) $payload['status'];
+
+ $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')]
@@ -69,6 +135,35 @@ 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);
+ $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();
+ }
+ }
+
+ $currentUrlId = $url->getId();
+ // 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,
@@ -80,17 +175,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) {
@@ -110,7 +200,7 @@ public function searchSetting(Request $request, AccessUrlHelper $accessUrlHelper
// Convert category to schema alias and validate it BEFORE loading/creating the form
$schemaAlias = $manager->convertNameSpaceToService($category);
- // skip unknown/legacy categories (e.g., "tools")
+ // Skip unknown/legacy categories (e.g., "tools")
if (!isset($schemas[$schemaAlias])) {
continue;
}
@@ -139,11 +229,14 @@ 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,
]);
}
/**
- * Edit configuration with given namespace.
+ * Edit configuration with given namespace (main settings page).
*/
#[IsGranted('ROLE_ADMIN')]
#[Route('/settings/{namespace}', name: 'chamilo_platform_settings')]
@@ -217,6 +310,7 @@ public function updateSetting(Request $request, AccessUrlHelper $accessUrlHelper
$templateMap = [];
$settingsRepo = $this->entityManager->getRepository(SettingsCurrent::class);
+ // Template map for current URL (existing behavior – JSON helper)
$settingsWithTemplate = $settingsRepo->findBy(['url' => $url]);
foreach ($settingsWithTemplate as $s) {
@@ -224,10 +318,30 @@ public function updateSetting(Request $request, AccessUrlHelper $accessUrlHelper
$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),
];
+ $currentUrlId = $url->getId();
+ // 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,
@@ -238,6 +352,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,
]);
}
diff --git a/src/CoreBundle/Resources/views/Admin/Settings/default.html.twig b/src/CoreBundle/Resources/views/Admin/Settings/default.html.twig
index cf699afce2b..cd960cc604c 100644
--- a/src/CoreBundle/Resources/views/Admin/Settings/default.html.twig
+++ b/src/CoreBundle/Resources/views/Admin/Settings/default.html.twig
@@ -62,6 +62,13 @@
{% for field in form %}
{% set fieldName = field.vars.name %}
{% set isHidden = 'hidden' in field.vars.block_prefixes %}
+ {% set isDisabledOnSubUrl =
+ current_url_id is defined
+ and current_url_id != 1
+ and changeable_map is defined
+ and changeable_map[fieldName] is defined
+ and not changeable_map[fieldName]
+ %}
{% if isHidden %}
{{ form_widget(field) }}
@@ -73,6 +80,7 @@
@@ -110,19 +153,25 @@
{% for child in field %}
+ {% set childAttr = isDisabledOnSubUrl ? { disabled: 'disabled' } : {} %}
- {{ form_widget(child) }}
+ {{ form_widget(child, { attr: childAttr }) }}
{{ child.vars.label }}
{% endfor %}
{% else %}
- {{ form_widget(field, {
- attr: {
- class: 'w-full rounded border border-gray-25 focus:border-primary focus:ring focus:ring-primary/30 transition'
- }
- }) }}
+ {# Build base attributes and add disabled only when needed #}
+ {% set baseAttr = {
+ class: 'w-full rounded border border-gray-25 focus:border-primary focus:ring focus:ring-primary/30 transition'
+ } %}
+ {% set widgetAttr = isDisabledOnSubUrl
+ ? baseAttr|merge({ disabled: 'disabled' })
+ : baseAttr
+ %}
+
+ {{ form_widget(field, { attr: widgetAttr }) }}
{% endif %}
@@ -203,6 +252,43 @@
document.getElementById('jsonTemplateModal').classList.add('hidden');
});
});
+
+ // MultiURL eye toggle: send AJAX to toggle access_url_changeable
+ document.addEventListener('click', function (e) {
+ const btn = e.target.closest('.toggle-changeable');
+ if (!btn) {
+ return;
+ }
+
+ const variable = btn.dataset.variable;
+ const current = btn.dataset.status === '1' ? 1 : 0;
+ const next = current === 1 ? 0 : 1;
+
+ fetch('/admin/settings/toggle_changeable', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({ variable: variable, status: next })
+ })
+ .then(r => r.json())
+ .then(data => {
+ if (data.result === 1) {
+ btn.dataset.status = String(next);
+ btn.innerHTML = next
+ ? ' '
+ : ' ';
+ } else if (data.error) {
+ console.error('Failed to update changeable state:', data.error);
+ alert(data.error);
+ } else {
+ console.error('Failed to update changeable state:', data);
+ }
+ })
+ .catch(err => {
+ console.error('AJAX error:', err);
+ });
+ });
{% endblock %}
{% block javascripts %}
diff --git a/src/CoreBundle/Resources/views/Admin/Settings/search.html.twig b/src/CoreBundle/Resources/views/Admin/Settings/search.html.twig
index 6d2d82ca6e3..65127070ae6 100644
--- a/src/CoreBundle/Resources/views/Admin/Settings/search.html.twig
+++ b/src/CoreBundle/Resources/views/Admin/Settings/search.html.twig
@@ -52,6 +52,13 @@
{% for field in form %}
{% set fieldName = field.vars.name %}
{% set isHidden = 'hidden' in field.vars.block_prefixes %}
+ {% set isDisabledOnSubUrl =
+ current_url_id is defined
+ and current_url_id != 1
+ and changeable_map is defined
+ and changeable_map[fieldName] is defined
+ and changeable_map[fieldName] == 0
+ %}
{% if isHidden %}
{{ form_widget(field) }}
{% else %}
@@ -59,23 +66,61 @@
{{ field.vars.label|trans }}
- {# Optional JSON template helper link #}
- {% set templateId = null %}
- {% if template_map is defined and template_map[fieldName] is defined %}
- {% set templateId = template_map[fieldName] %}
- {% elseif template_map_by_category is defined
- and template_map_by_category[category] is defined
- and template_map_by_category[category][fieldName] is defined %}
- {% set templateId = template_map_by_category[category][fieldName] %}
- {% endif %}
+
+ {# Optional JSON template helper link #}
+ {% set templateId = null %}
+ {% if template_map is defined and template_map[fieldName] is defined %}
+ {% set templateId = template_map[fieldName] %}
+ {% elseif template_map_by_category is defined
+ and template_map_by_category[category] is defined
+ and template_map_by_category[category][fieldName] is defined %}
+ {% set templateId = template_map_by_category[category][fieldName] %}
+ {% endif %}
- {% if templateId %}
-
-
- {{ 'Show JSON Template'|trans }}
-
- {% endif %}
+ {% if templateId %}
+
+
+ {{ 'Show JSON Template'|trans }}
+
+ {% endif %}
+
+ {# MultiURL changeable eye on search results #}
+ {% if changeable_map is defined %}
+ {% set changeable = changeable_map[fieldName] is defined ? changeable_map[fieldName] : 1 %}
+
+ {% if can_toggle_multiurl_setting %}
+ {# Main URL + admin: clickable toggle #}
+
+ {% if changeable %}
+
+ {% else %}
+
+ {% endif %}
+
+ {% else %}
+ {# Other URLs or non-privileged admins: read-only indicator #}
+
+ {% if changeable %}
+
+ {% else %}
+
+ {% endif %}
+
+ {% endif %}
+ {% endif %}
+
@@ -89,16 +134,24 @@
{% if (field.vars.name in verticalChoiceFields) and isExpanded and isMultiple %}
{% for child in field %}
+ {% set childAttr = isDisabledOnSubUrl ? { disabled: 'disabled' } : {} %}
- {{ form_widget(child) }}
+ {{ form_widget(child, { attr: childAttr }) }}
{{ child.vars.label }}
{% endfor %}
{% else %}
- {{ form_widget(field, { attr: {
+ {# Build base attributes and add disabled only when needed #}
+ {% set baseAttr = {
class: 'w-full max-w-none rounded border border-gray-25 focus:border-primary focus:ring focus:ring-primary/30 transition'
- } }) }}
+ } %}
+ {% set widgetAttr = isDisabledOnSubUrl
+ ? baseAttr|merge({ disabled: 'disabled' })
+ : baseAttr
+ %}
+
+ {{ form_widget(field, { attr: widgetAttr }) }}
{% endif %}
@@ -198,5 +251,38 @@
if (e.key === 'Escape') closeModal();
});
})();
+
+ // MultiURL eye toggle on search results
+ document.addEventListener('click', function (e) {
+ const btn = e.target.closest('.toggle-changeable');
+ if (!btn) {
+ return;
+ }
+
+ const variable = btn.dataset.variable;
+ const current = btn.dataset.status === '1' ? 1 : 0;
+ const next = current ? 0 : 1;
+
+ fetch('/admin/settings/toggle_changeable', {
+ method: 'POST',
+ headers: {'Content-Type': 'application/json'},
+ body: JSON.stringify({ variable: variable, status: next })
+ })
+ .then(r => r.json())
+ .then(data => {
+ if (data.result === 1) {
+ btn.dataset.status = String(next);
+ btn.innerHTML = next
+ ? ' '
+ : ' ';
+ } else if (data.error) {
+ console.error('Failed to update changeable state:', data.error);
+ alert(data.error);
+ } else {
+ console.error('Failed to update changeable state:', data);
+ }
+ })
+ .catch(err => console.error('AJAX error:', err));
+ });
{% endblock %}
diff --git a/src/CoreBundle/Settings/SettingsManager.php b/src/CoreBundle/Settings/SettingsManager.php
index 7bcacac551e..ffccf353090 100644
--- a/src/CoreBundle/Settings/SettingsManager.php
+++ b/src/CoreBundle/Settings/SettingsManager.php
@@ -288,20 +288,24 @@ public function update(SettingsInterface $settings): void
$raw = $settings->getParameters();
$raw = $this->normalizeNullsBeforeResolve($raw, $settingsBuilder);
$parameters = $settingsBuilder->resolve($raw);
- // Transform value. Example array to string using transformer. Example:
- // 1. Setting "tool_visible_by_default_at_creation" it's a multiple select
- // 2. Is defined as an array in class DocumentSettingsSchema
- // 3. Add transformer for that variable "ArrayToIdentifierTransformer"
- // 4. Here we recover the transformer and convert the array to string
+
foreach ($parameters as $parameter => $value) {
$parameters[$parameter] = $this->transformToString($value);
}
$settings->setParameters($parameters);
$category = $this->convertServiceToNameSpace($settings->getSchemaAlias());
- $persistedParameters = $this->repository->findBy([
+
+ // Restrict lookup to current URL so we do not override settings from other URLs.
+ $criteria = [
'category' => $category,
- ]);
+ ];
+
+ if (null !== $this->url) {
+ $criteria['url'] = $this->url;
+ }
+
+ $persistedParameters = $this->repository->findBy($criteria);
$persistedParametersMap = [];
@@ -314,22 +318,18 @@ public function update(SettingsInterface $settings): void
$simpleCategoryName = str_replace('chamilo_core.settings.', '', $namespace);
foreach ($parameters as $name => $value) {
+ // MultiURL: respect access_url_changeable defined on main URL.
+ if (!$this->isSettingChangeableForCurrentUrl($simpleCategoryName, $name)) {
+ continue;
+ }
+
if (isset($persistedParametersMap[$name])) {
$parameter = $persistedParametersMap[$name];
$parameter->setSelectedValue($value);
$parameter->setCategory($simpleCategoryName);
$this->manager->persist($parameter);
} else {
- $parameter = (new SettingsCurrent())
- ->setVariable($name)
- ->setCategory($simpleCategoryName)
- ->setTitle($name)
- ->setSelectedValue($value)
- ->setUrl($url)
- ->setAccessUrlChangeable(1)
- ->setAccessUrlLocked(1)
- ;
-
+ $parameter = $this->createSettingForCurrentUrl($simpleCategoryName, $name, $value);
$this->manager->persist($parameter);
}
}
@@ -352,18 +352,21 @@ public function save(SettingsInterface $settings): void
$raw = $settings->getParameters();
$raw = $this->normalizeNullsBeforeResolve($raw, $settingsBuilder);
$parameters = $settingsBuilder->resolve($raw);
- // Transform value. Example array to string using transformer. Example:
- // 1. Setting "tool_visible_by_default_at_creation" it's a multiple select
- // 2. Is defined as an array in class DocumentSettingsSchema
- // 3. Add transformer for that variable "ArrayToIdentifierTransformer"
- // 4. Here we recover the transformer and convert the array to string
+
foreach ($parameters as $parameter => $value) {
$parameters[$parameter] = $this->transformToString($value);
}
$settings->setParameters($parameters);
- $persistedParameters = $this->repository->findBy([
+ $criteria = [
'category' => $this->convertServiceToNameSpace($settings->getSchemaAlias()),
- ]);
+ ];
+
+ // Limit to current URL so we do not write across all URLs.
+ if (null !== $this->url) {
+ $criteria['url'] = $this->url;
+ }
+
+ $persistedParameters = $this->repository->findBy($criteria);
$persistedParametersMap = [];
foreach ($persistedParameters as $parameter) {
$persistedParametersMap[$parameter->getVariable()] = $parameter;
@@ -373,20 +376,16 @@ public function save(SettingsInterface $settings): void
$simpleCategoryName = str_replace('chamilo_core.settings.', '', $namespace);
foreach ($parameters as $name => $value) {
+ // MultiURL: respect access_url_changeable defined on main URL.
+ if (!$this->isSettingChangeableForCurrentUrl($simpleCategoryName, $name)) {
+ continue;
+ }
+
if (isset($persistedParametersMap[$name])) {
$parameter = $persistedParametersMap[$name];
$parameter->setSelectedValue($value);
} else {
- $parameter = (new SettingsCurrent())
- ->setVariable($name)
- ->setCategory($simpleCategoryName)
- ->setTitle($name)
- ->setSelectedValue($value)
- ->setUrl($url)
- ->setAccessUrlChangeable(1)
- ->setAccessUrlLocked(1)
- ;
-
+ $parameter = $this->createSettingForCurrentUrl($simpleCategoryName, $name, $value);
$this->manager->persist($parameter);
}
}
@@ -399,11 +398,18 @@ public function save(SettingsInterface $settings): void
*/
public function getParametersFromKeywordOrderedByCategory($keyword): array
{
- $query = $this->repository->createQueryBuilder('s')
+ $qb = $this->repository->createQueryBuilder('s')
->where('s.variable LIKE :keyword OR s.title LIKE :keyword')
- ->setParameter('keyword', "%{$keyword}%")
- ;
- $parametersFromDb = $query->getQuery()->getResult();
+ ->setParameter('keyword', "%{$keyword}%");
+
+ // Restrict search to current URL when available.
+ if (null !== $this->url) {
+ $qb
+ ->andWhere('s.url = :url')
+ ->setParameter('url', $this->url);
+ }
+
+ $parametersFromDb = $qb->getQuery()->getResult();
$parameters = [];
foreach ($parametersFromDb as $parameter) {
@@ -443,13 +449,24 @@ public function getParametersFromKeyword($namespace, $keyword = '', $returnObjec
$criteria = [
'category' => $namespace,
];
+
+ if (null !== $this->url) {
+ $criteria['url'] = $this->url;
+ }
+
$parametersFromDb = $this->repository->findBy($criteria);
} else {
- $query = $this->repository->createQueryBuilder('s')
+ $qb = $this->repository->createQueryBuilder('s')
->where('s.variable LIKE :keyword')
- ->setParameter('keyword', "%{$keyword}%")
- ;
- $parametersFromDb = $query->getQuery()->getResult();
+ ->setParameter('keyword', "%{$keyword}%");
+
+ if (null !== $this->url) {
+ $qb
+ ->andWhere('s.url = :url')
+ ->setParameter('url', $this->url);
+ }
+
+ $parametersFromDb = $qb->getQuery()->getResult();
}
if ($returnObjects) {
@@ -503,7 +520,15 @@ private function validateSetting(string $name): string
private function getParameters($namespace)
{
$parameters = [];
- $category = $this->repository->findBy(['category' => $namespace]);
+
+ $criteria = ['category' => $namespace];
+
+ // MultiURL: only parameters for current URL (if set).
+ if (null !== $this->url) {
+ $criteria['url'] = $this->url;
+ }
+
+ $category = $this->repository->findBy($criteria);
/** @var SettingsCurrent $parameter */
foreach ($category as $parameter) {
@@ -516,7 +541,13 @@ private function getParameters($namespace)
private function getAllParametersByCategory()
{
$parameters = [];
- $all = $this->repository->findAll();
+
+ // MultiURL: either all parameters (single URL mode) or only for current URL.
+ if (null !== $this->url) {
+ $all = $this->repository->findBy(['url' => $this->url]);
+ } else {
+ $all = $this->repository->findAll();
+ }
/** @var SettingsCurrent $parameter */
foreach ($all as $parameter) {
@@ -526,6 +557,123 @@ private function getAllParametersByCategory()
return $parameters;
}
+ /**
+ * Check if a setting is changeable for the current URL, using the
+ * access_url_changeable flag from the main URL (ID = 1).
+ */
+ private function isSettingChangeableForCurrentUrl(string $category, string $variable): bool
+ {
+ // No URL bound: behave as legacy single-URL platform.
+ if (null === $this->url) {
+ return true;
+ }
+
+ // Main URL can always edit settings. UI already restricts who can see/edit fields.
+ if (1 === $this->url->getId()) {
+ return true;
+ }
+
+ // Try to load main (canonical) URL.
+ $mainUrl = $this->manager->getRepository(AccessUrl::class)->find(1);
+ if (null === $mainUrl) {
+ // If main URL is missing, fallback to permissive behaviour.
+ return true;
+ }
+
+ /** @var SettingsCurrent|null $mainSetting */
+ $mainSetting = $this->repository->findOneBy([
+ 'category' => $category,
+ 'variable' => $variable,
+ 'url' => $mainUrl,
+ ]);
+
+ if (null === $mainSetting) {
+ // If there is no canonical row, do not block changes.
+ return true;
+ }
+
+ // When access_url_changeable is false/0 on main URL,
+ // secondary URLs must not override the value.
+ return (bool) $mainSetting->getAccessUrlChangeable();
+ }
+
+ private function createSettingForCurrentUrl(string $category, string $variable, string $value): SettingsCurrent
+ {
+ $url = $this->getUrl();
+
+ // Try to reuse metadata from main URL (ID = 1) as canonical definition.
+ $mainUrl = $this->manager->getRepository(AccessUrl::class)->find(1);
+
+ $reference = null;
+
+ if (null !== $mainUrl) {
+ $reference = $this->repository->findOneBy([
+ 'category' => $category,
+ 'variable' => $variable,
+ 'url' => $mainUrl,
+ ]);
+ }
+
+ if (!$reference instanceof SettingsCurrent) {
+ // Fallback: any existing row for this category + variable (legacy / no-URL case).
+ $reference = $this->repository->findOneBy([
+ 'category' => $category,
+ 'variable' => $variable,
+ ]);
+ }
+
+ $setting = (new SettingsCurrent())
+ ->setVariable($variable)
+ ->setCategory($category)
+ ->setSelectedValue($value)
+ ->setUrl($url)
+ ;
+
+ if ($reference instanceof SettingsCurrent) {
+ // Copy descriptive metadata so the new URL row behaves like the canonical one.
+ $setting->setTitle($reference->getTitle());
+
+ // These fields may or may not exist in the entity in Chamilo 2,
+ // so we check for method existence to stay safe.
+ if (method_exists($setting, 'setType') && method_exists($reference, 'getType')) {
+ $setting->setType($reference->getType());
+ }
+
+ if (method_exists($setting, 'setComment') && method_exists($reference, 'getComment')) {
+ $setting->setComment($reference->getComment());
+ }
+
+ if (method_exists($setting, 'setScope') && method_exists($reference, 'getScope')) {
+ $setting->setScope($reference->getScope());
+ }
+
+ if (method_exists($setting, 'setSubkey') && method_exists($reference, 'getSubkey')) {
+ $setting->setSubkey($reference->getSubkey());
+ }
+
+ if (method_exists($setting, 'setSubkeytext') && method_exists($reference, 'getSubkeytext')) {
+ $setting->setSubkeytext($reference->getSubkeytext());
+ }
+
+ // Copy flags and template; the "changeable" flag is still interpreted from main URL.
+ $setting->setAccessUrlChangeable($reference->getAccessUrlChangeable());
+ $setting->setAccessUrlLocked($reference->getAccessUrlLocked());
+
+ if (method_exists($setting, 'setValueTemplate') && method_exists($reference, 'getValueTemplate')) {
+ $setting->setValueTemplate($reference->getValueTemplate());
+ }
+ } else {
+ // Fallback: minimal metadata if no canonical definition was found.
+ $setting
+ ->setTitle($variable)
+ ->setAccessUrlChangeable(1)
+ ->setAccessUrlLocked(1)
+ ;
+ }
+
+ return $setting;
+ }
+
/*private function transformParameters(SettingsBuilder $settingsBuilder, array $parameters)
* {
* $transformedParameters = $parameters;