pull/6016/merge
Angel Fernando Quiroz Campos 7 months ago committed by GitHub
commit 152a049868
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 280
      public/main/admin/sub_language_add.php
  2. 45
      public/main/inc/lib/sub_language.class.php
  3. 13
      src/CoreBundle/Entity/Language.php
  4. 20
      src/CoreBundle/Entity/Listener/LanguageListener.php
  5. 5
      src/CoreBundle/Migrations/Schema/V200/Version20.php
  6. 25
      src/CoreBundle/Migrations/Schema/V200/Version20240122221400.php
  7. 34
      src/CoreBundle/Migrations/Schema/V200/Version20250106152600.php
  8. 78
      src/CoreBundle/Migrations/Schema/V200/Version20250106152601.php
  9. 27
      src/CoreBundle/Migrations/Schema/V200/Version20250106152602.php
  10. 4
      src/CoreBundle/Resources/config/listeners.yml

@ -17,6 +17,7 @@ $this_section = SECTION_PLATFORM_ADMIN;
api_protect_admin_script();
$request = Container::getRequest();
$em = Database::getManager();
$requestAction = $request->query->get('action');
@ -32,181 +33,172 @@ $content = '';
$interbreadcrumb[] = ['url' => 'index.php', 'name' => get_lang('Administration')];
$interbreadcrumb[] = ['url' => 'languages.php', 'name' => get_lang('Chamilo Portal Languages')];
$sub_language_id_exist = SubLanguageManager::languageExistsById($request->query->getInt('sub_language_id'));
$language_id_exist = SubLanguageManager::languageExistsById($request->query->getInt('id'));
$language_name = '';
$language_details = [];
$parent_id = $request->query->getInt('id');
//add data
if ($sub_language_id_exist) {
$language_name = SubLanguageManager::get_name_of_language_by_id($_GET['sub_language_id']);
$sub_language_id = $request->query->getInt('sub_language_id');
if (!SubLanguageManager::languageExistsById($parent_id)) {
Display::addFlash(
Display::return_message(get_lang('The parent language does not exist.'))
);
api_location(api_get_path(WEB_CODE_PATH).'admin/languages.php');
}
if ($language_id_exist) {
$language_details = SubLanguageManager::get_all_information_of_language($_GET['id']);
$language_name = $language_details['original_name'];
$parent_id = $request->query->getInt('id');
}
$language_details = SubLanguageManager::get_all_information_of_language($parent_id);
$language_name = $language_details['original_name'];
//removed and register
if ($language_id_exist && $sub_language_id_exist) {
$get_all_information = SubLanguageManager::getAllInformationOfSubLanguage($parent_id, $sub_language_id);
$original_name = $get_all_information->getOriginalName();
$english_name = $get_all_information->getEnglishName();
$isocode = $get_all_information->getIsocode();
}
if ('definenewsublanguage' === $requestAction) {
$form = new FormValidator(
'addsublanguage',
'post',
'sub_language_add.php?id='.Security::remove_XSS($_GET['id']).'&action=definenewsublanguage'
);
$form->addHeader(
get_lang('Create sub-languageForLanguage').' ( '.strtolower($language_name).' )'
);
$form->addText('original_name', get_lang('Original name'), true, ['class' => 'input_titles']);
$form->addText('english_name', get_lang('English name'), true, ['class' => 'input_titles']);
$form->addText('isocode', get_lang('ISO code'), true, ['class' => 'input_titles']);
$form->addElement('static', null, '&nbsp;', '<i>en, es, fr</i>');
$form->addCheckBox('sub_language_is_visible', '', get_lang('Visibility'));
$form->addButtonCreate(get_lang('Create sub-language'), 'SubmitAddNewLanguage');
$form->protect();
$language_name = get_lang('Create sub-languageForLanguage').' ( '.strtolower($language_name).' )';
if ($form->validate()) {
$values = $form->exportValues();
$values['english_name'] = str_replace(' ', '_', $values['english_name']);
$values['isocode'] = str_replace(' ', '_', $values['isocode']);
if ($request->request->has('SubmitAddNewLanguage')) {
$original_name = $request->request->get('original_name');
$english_name = str_replace(' ', '_', $request->request->get('english_name'));
$isocode = str_replace(' ', '_', $request->request->get('isocode'));
$check_information = SubLanguageManager::checkIfLanguageExists(
$values['original_name'],
$values['english_name'],
$values['isocode']
);
$allow_insert_info = $check_information['execute_add'] ?? false;
$sublanguage_available = $request->request->getInt('sub_language_is_visible');
$check_information = SubLanguageManager::checkIfLanguageExists($original_name, $english_name, $isocode);
$allow_insert_info = $check_information['execute_add'] ?? false;
if ($check_information['original_name'] ) {
Display::addFlash(
Display::return_message(
sprintf(
'%s "%s (%s)',
get_lang('Already exists'),
get_lang('Original name'),
$values['original_name']
),
'error'
)
);
}
if ($check_information['english_name'] ) {
Display::addFlash(
Display::return_message(
get_lang('Already exists').' "'.get_lang('English name').'" '.'('.$values['english_name'].')',
'error'
)
);
}
if ($check_information['isocode'] ) {
Display::addFlash(
Display::return_message(get_lang('This code does not exist').': '.$values['isocode'], 'error')
);
}
if ($check_information['original_name'] ) {
Display::addFlash(
Display::return_message(
get_lang('Already exists').' "'.get_lang('Original name').'" '.'('.$original_name.')',
'error'
)
);
}
if ($check_information['english_name'] ) {
Display::addFlash(
Display::return_message(
get_lang('Already exists').' "'.get_lang('English name').'" '.'('.$english_name.')',
'error'
)
);
}
if ($check_information['isocode'] ) {
Display::addFlash(
Display::return_message(get_lang('This code does not exist').': '.$isocode.'', 'error')
);
}
if (strlen($values['original_name']) > 0 && strlen($values['english_name']) > 0 && strlen($values['isocode']) > 0) {
if ($allow_insert_info) {
//$values['english_name'] = str_replace(' ', '_', $values['english_name']);
//Fixes BT#1636
$values['english_name'] = api_strtolower($values['english_name']);
try {
$newSubLanguage = SubLanguageManager::addSubLanguage(
$values['original_name'],
$values['english_name'],
$values['sub_language_is_visible'] ?? false,
$parent_id,
$language_details['isocode']
);
if (strlen($original_name) > 0 && strlen($english_name) > 0 && strlen($isocode) > 0) {
if ($allow_insert_info && $language_id_exist) {
$english_name = str_replace(' ', '_', $english_name);
//Fixes BT#1636
$english_name = api_strtolower($english_name);
$firstIso = substr($language_details['isocode'], 0, 2);
//$english_name = str_starts_with($english_name, $firstIso.'_') ? $english_name : $firstIso.'_'.$english_name;
$isocode = SubLanguageManager::generateSublanguageCode($firstIso, $request->request->get('english_name'));
$str_info = '<br/>'.get_lang('Original name').' : '
.$original_name.'<br/>'.get_lang('English name').' : '
.$english_name.'<br/>'.get_lang('Character set').' : '.$isocode;
$mkdir_result = SubLanguageManager::addPoFileForSubLanguage($isocode);
if ($mkdir_result) {
$sl_id = SubLanguageManager::addSubLanguage(
$original_name,
$english_name,
$sublanguage_available,
$parent_id,
$isocode
);
if (false === $sl_id) {
SubLanguageManager::removePoFileForSubLanguage($isocode);
if (SubLanguageManager::addPoFileForSubLanguage($newSubLanguage->getIsocode())) {
$str_info = '<br/>'.get_lang('Original name').' : '
.$values['original_name'].'<br/>'.get_lang('English name').' : '
.$values['english_name'].'<br/>'.get_lang('Character set').' : '.$newSubLanguage->getIsocode();
Display::addFlash(
Display::return_message(get_lang('The new sub-language has been added').$str_info, 'normal', false)
);
api_location(api_get_path(WEB_CODE_PATH).'admin/languages.php?sub_language_id='.$newSubLanguage->getId());
} else {
$em->remove($newSubLanguage);
$em->flush();
throw new Exception();
}
} catch (Exception $e) {
Display::addFlash(
Display::return_message(
get_lang('The /main/lang directory, used on this portal to store the languages, is not writable. Please contact your platform administrator and report this message.'),
'error'
)
);
} else {
Display::addFlash(
Display::return_message(get_lang('The new sub-language has been added').$str_info, null, false)
);
api_location(api_get_path(WEB_CODE_PATH).'admin/languages.php?sub_language_id='.$sl_id);
}
} else {
Display::addFlash(
Display::return_message(
get_lang('The /main/lang directory, used on this portal to store the languages, is not writable. Please contact your platform administrator and report this message.'),
'error'
)
);
}
} elseif (false === $language_id_exist) {
} else {
Display::addFlash(
Display::return_message(get_lang('The parent language does not exist.'), 'error')
Display::return_message(
get_lang('The form contains incorrect or incomplete data. Please check your input.'),
'error'
)
);
}
} else {
Display::addFlash(
Display::return_message(
get_lang('The form contains incorrect or incomplete data. Please check your input.'),
'error'
)
);
}
}
if (isset($_POST['SubmitAddDeleteLanguage'])) {
$removed = SubLanguageManager::removeSubLanguage($_GET['id'], $_GET['sub_language_id']);
if ($removed) {
Display::addFlash(
Display::return_message(get_lang('The sub language has been removed.'))
);
api_location(api_get_path(WEB_CODE_PATH).'admin/languages.php');
}
}
if ('definenewsublanguage' === $requestAction) {
$form = new FormValidator(
'addsublanguage',
'post',
'sub_language_add.php?id='.Security::remove_XSS($_GET['id']).'&action=definenewsublanguage'
);
$class = 'add';
$form->addHeader($language_name);
$form->addText('original_name', get_lang('Original name'), true, ['class' => 'input_titles']);
$form->addText('english_name', get_lang('English name'), true, ['class' => 'input_titles']);
$form->addText('isocode', get_lang('ISO code'), true, ['class' => 'input_titles']);
$form->addElement('static', null, '&nbsp;', '<i>en, es, fr</i>');
$form->addCheckBox('sub_language_is_visible', '', get_lang('Visibility'));
$form->addButtonCreate(get_lang('Create sub-language'), 'SubmitAddNewLanguage');
$form->setDefaults([
//'original_name' => $language_details['original_name'].'...'; -> cannot be used because of quickform filtering (freeze),
'english_name' => $language_details['english_name'].'2',
'isocode' => $language_details['isocode'],
]);
$content .= $form->returnForm();
} else {
if (true === SubLanguageManager::isParentOfSubLanguage($parent_id)
&& 'deletesublanguage' === $requestAction
) {
$language_name = get_lang('Delete sub-language');
$form = new FormValidator(
'deletesublanguage',
'post',
'sub_language_add.php?id='.http_build_query([
'id' => $request->query->getInt('id'),
'sub_language_id' => $request->query->getInt('sub_language_id'),
])
);
$class = 'minus';
$form->addHeader($language_name);
$form->addElement('static', '', get_lang('Original name'), $original_name);
$form->addElement('static', '', get_lang('English name'), $english_name);
$form->addElement('static', '', get_lang('Character set'), $isocode);
$form->addButtonCreate(get_lang('Delete sub-language'), 'SubmitAddDeleteLanguage');
$content .= $form->returnForm();
}
if ('definenewsublanguage' == $requestAction) {
} elseif (true === SubLanguageManager::isParentOfSubLanguage($parent_id)
&& 'deletesublanguage' === $requestAction
) {
$sub_language_id = $request->query->getInt('sub_language_id');
if (!SubLanguageManager::languageExistsById($sub_language_id)) {
Display::addFlash(
Display::return_message(get_lang('The sub-language of this language has been added'))
Display::return_message(get_lang('The sub-language does not exist.'))
);
api_location(api_get_path(WEB_CODE_PATH).'admin/languages.php');
}
$language_name = SubLanguageManager::get_name_of_language_by_id($sub_language_id);
$get_all_information = SubLanguageManager::getAllInformationOfSubLanguage($parent_id, $sub_language_id);
$form = new FormValidator(
'deletesublanguage',
'post',
'sub_language_add.php?id='.http_build_query([
'id' => $parent_id,
'sub_language_id' => $sub_language_id,
])
);
$form->addHeader(get_lang('Delete sub-language'));
$form->addElement('static', '', get_lang('Original name'), $get_all_information->getOriginalName());
$form->addElement('static', '', get_lang('English name'), $get_all_information->getEnglishName());
$form->addElement('static', '', get_lang('Character set'), $get_all_information->getIsocode());
$form->addButtonCreate(get_lang('Delete sub-language'), 'SubmitAddDeleteLanguage');
$form->protect();
if ($form->validate()) {
$removed = SubLanguageManager::removeSubLanguage($parent_id, $sub_language_id);
if ($removed) {
Display::addFlash(
Display::return_message(get_lang('The sub language has been removed.'))
);
api_location(api_get_path(WEB_CODE_PATH).'admin/languages.php');
}
}
$content .= $form->returnForm();
}
/**
* Footer.

@ -691,13 +691,15 @@ class SubLanguageManager
/**
* Add a sub-language.
*
* @throws Exception
*/
public static function addSubLanguage(string $originalName, string $englishName, bool $isAvailable, int $parentId, string $isoCode): bool|int
public static function addSubLanguage(string $originalName, string $englishName, bool $isAvailable, int $parentId, string $isoCode): Language
{
$entityManager = Database::getManager();
$parentLanguage = $entityManager->getRepository(Language::class)->find($parentId);
if (!$parentLanguage) {
return false;
throw new Exception();
}
$subLanguage = new Language();
@ -707,15 +709,10 @@ class SubLanguageManager
->setAvailable($isAvailable)
->setParent($parentLanguage);
try {
$entityManager->persist($subLanguage);
$entityManager->flush();
} catch (\Exception $e) {
// Handle exception if needed
return false;
}
$entityManager->persist($subLanguage);
$entityManager->flush();
return $subLanguage->getId();
return $subLanguage;
}
/**
@ -744,15 +741,17 @@ class SubLanguageManager
/**
* Check if a language exists by its ID.
*
* @throws NotSupported
*/
public static function languageExistsById(int $languageId): bool
{
$entityManager = Database::getManager();
$language = $entityManager->getRepository(Language::class)->find($languageId);
try {
$language = $entityManager->getRepository(Language::class)->find($languageId);
return $language !== null;
return $language !== null;
} catch (NotSupported) {
return false;
}
}
/**
@ -770,18 +769,20 @@ class SubLanguageManager
/**
* Get all information of a sub-language.
*
* @throws NotSupported
*/
public static function getAllInformationOfSubLanguage(int $parentId, int $subLanguageId): ?Language
{
$entityManager = Database::getManager();
$languageRepository = $entityManager->getRepository(Language::class);
try {
$languageRepository = $entityManager->getRepository(Language::class);
return $languageRepository->findOneBy([
'parent' => $parentId,
'id' => $subLanguageId
]);
return $languageRepository->findOneBy([
'parent' => $parentId,
'id' => $subLanguageId
]);
} catch (NotSupported) {
return null;
}
}
/**
@ -857,7 +858,7 @@ class SubLanguageManager
return null; // No parent language
}
public static function generateSublanguageCode(string $parentCode, string $variant, int $maxLength = 10): string
public static function generateSublanguageCode(string $parentCode, string $variant, int $maxLength = Language::ISO_MAX_LENGTH): string
{
$parentCode = strtolower(trim($parentCode));
$variant = strtolower(trim($variant));

@ -10,6 +10,7 @@ use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\OrderFilter;
use ApiPlatform\Doctrine\Orm\Filter\BooleanFilter;
use ApiPlatform\Metadata\ApiFilter;
use ApiPlatform\Metadata\ApiResource;
use Chamilo\CoreBundle\Entity\Listener\LanguageListener;
use Chamilo\CoreBundle\Repository\LanguageRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
@ -25,8 +26,11 @@ use Symfony\Component\Validator\Constraints as Assert;
#[ApiFilter(OrderFilter::class, properties: ['english_name' => 'DESC'])]
#[ORM\Table(name: 'language', options: ['row_format' => 'DYNAMIC'])]
#[ORM\Entity(repositoryClass: LanguageRepository::class)]
#[ORM\EntityListeners([LanguageListener::class])]
class Language
{
public const ISO_MAX_LENGTH = 8;
#[Groups(['language:read'])]
#[ORM\Column(name: 'id', type: 'integer')]
#[ORM\Id]
@ -45,7 +49,7 @@ class Language
#[Groups(['language:read', 'language:write'])]
#[Assert\NotBlank]
#[ORM\Column(name: 'isocode', type: 'string', length: 10)]
#[ORM\Column(name: 'isocode', type: 'string', length: self::ISO_MAX_LENGTH)]
protected string $isocode;
#[Groups(['language:read', 'language:write'])]
@ -167,4 +171,11 @@ class Language
return $this;
}
public function generateIsoCodeForChild(): string
{
$isoCode = explode('_', $this->getParent()->getIsocode());
return $isoCode[0].'_'.$this->getId();
}
}

@ -0,0 +1,20 @@
<?php
namespace Chamilo\CoreBundle\Entity\Listener;
use Chamilo\CoreBundle\Entity\Language;
use Doctrine\ORM\Event\PostPersistEventArgs;
class LanguageListener
{
public function postPersist(Language $language, PostPersistEventArgs $args): void
{
if ($language->getParent()) {
$newIsoCode = $language->generateIsoCodeForChild();
$language->setIsocode($newIsoCode);
$args->getObjectManager()->flush();
}
}
}

@ -7,6 +7,7 @@ declare(strict_types=1);
namespace Chamilo\CoreBundle\Migrations\Schema\V200;
use Chamilo\CoreBundle\DataFixtures\LanguageFixtures;
use Chamilo\CoreBundle\Entity\Language;
use Chamilo\CoreBundle\Migrations\AbstractMigrationChamilo;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Schema\Schema;
@ -162,7 +163,7 @@ class Version20 extends AbstractMigrationChamilo
// Update language to ISO.
$this->addSql('UPDATE language SET isocode = "en" WHERE isocode IS NULL');
$this->addSql('ALTER TABLE language CHANGE isocode isocode VARCHAR(10) NOT NULL');
$this->addSql('ALTER TABLE language CHANGE isocode isocode VARCHAR('.Language::ISO_MAX_LENGTH.') NOT NULL');
$this->addSql('UPDATE language SET english_name = "english" WHERE english_name IS NULL');
$this->addSql('ALTER TABLE language CHANGE english_name english_name VARCHAR(255) NOT NULL');
@ -182,7 +183,7 @@ class Version20 extends AbstractMigrationChamilo
$englishName = $item['english_name'];
if (isset($languages[$englishName])) {
$newIso = $languages[$englishName];
$this->addSql("UPDATE language SET isocode = '$newIso' WHERE id = $id");
$this->addSql("UPDATE language SET isocode = '$newIso', parent_id = NULL WHERE id = $id");
}
}

@ -51,8 +51,8 @@ final class Version20240122221400 extends AbstractMigrationChamilo
$parentIsoCode = $this->connection->executeQuery($parentIsoQuery, [$parentId])->fetchOne();
// Get the prefix of the parent language's isocode
$firstIso = substr($parentIsoCode, 0, 2);
$newIsoCode = $this->generateSublanguageCode($firstIso, $sublanguage['english_name']);
$firstIso = explode('_', $parentIsoCode)[0];
$newIsoCode = $firstIso.'_'.$sublanguage['id'];
// Update the isocode in the language table
$updateLanguageQuery = 'UPDATE language SET isocode = ? WHERE id = ?';
@ -171,27 +171,6 @@ final class Version20240122221400 extends AbstractMigrationChamilo
error_log($content);
}
private function generateSublanguageCode(string $parentCode, string $variant, int $maxLength = 10): string
{
$parentCode = strtolower(trim($parentCode));
$variant = strtolower(trim($variant));
// Generate a variant code by truncating the variant name
$variantCode = substr($variant, 0, $maxLength - \strlen($parentCode) - 1);
// Build the complete code
return substr($parentCode.'_'.$variantCode, 0, $maxLength);
}
private function deleteImportFolder(): void
{
$kernel = $this->container->get('kernel');
$rootPath = $kernel->getProjectDir();
$importPath = $rootPath.'/var/translations/import/';
$this->recursiveRemoveDirectory($importPath);
}
private function recursiveRemoveDirectory($directory): void
{
foreach (glob("{$directory}/*") as $file) {

@ -0,0 +1,34 @@
<?php
/* For licensing terms, see /license.txt */
declare(strict_types=1);
namespace Chamilo\CoreBundle\Migrations\Schema\V200;
use Chamilo\CoreBundle\DataFixtures\LanguageFixtures;
use Chamilo\CoreBundle\Migrations\AbstractMigrationChamilo;
use Doctrine\DBAL\Schema\Schema;
final class Version20250106152600 extends AbstractMigrationChamilo
{
public function getDescription(): string
{
return 'Set parent as null for initial lenguages.';
}
/**
* @inheritDoc
*/
public function up(Schema $schema): void
{
$languageNameList = array_column(
LanguageFixtures::getLanguages(),
'english_name',
);
$this->addSql(
'UPDATE language SET parent_id = NULL WHERE english_name IN ("'.implode('", "', $languageNameList).'")'
);
}
}

@ -0,0 +1,78 @@
<?php
/* For licensing terms, see /license.txt */
declare(strict_types=1);
namespace Chamilo\CoreBundle\Migrations\Schema\V200;
use Chamilo\CoreBundle\Migrations\AbstractMigrationChamilo;
use Doctrine\DBAL\Schema\Schema;
use Symfony\Component\Filesystem\Filesystem;
final class Version20250106152601 extends AbstractMigrationChamilo
{
public function getDescription(): string
{
return 'Set new ISO code for sub-languages.';
}
/**
* @inheritDoc
*/
public function up(Schema $schema): void
{
$kernel = $this->container->get('kernel');
$baseTranslationPath = $kernel->getProjectDir().'/var/translations/messages.';
$fs = new Filesystem();
$subLanguages = $this->connection
->executeQuery("SELECT id, isocode, parent_id FROM language WHERE parent_id IS NOT NULL")
->fetchAllAssociative()
;
/** @var array $subLanguage */
foreach ($subLanguages as $subLanguage) {
$parentIsoCode = $this->connection
->executeQuery('SELECT isocode FROM language WHERE id = ?', [$subLanguage['parent_id']])
->fetchOne()
;
$newIsoCode = sprintf(
'%s_%d',
explode('_', $parentIsoCode)[0],
$subLanguage['id']
);
$params = [
'new_iso' => $newIsoCode,
'old_iso' => $subLanguage['isocode'],
];
if ($params['new_iso'] === $params['old_iso']) {
continue;
}
$this->addSql(
'UPDATE language SET isocode = :new_iso WHERE id = :id',
[
'new_iso' => $newIsoCode,
'id' => $subLanguage['id'],
]
);
$this->addSql('UPDATE user SET locale = :new_iso WHERE locale = :old_iso', $params);
$this->addSql('UPDATE course SET course_language = :new_iso WHERE course_language = :old_iso', $params);
$this->addSql("UPDATE settings SET selected_value = :new_iso WHERE variable = 'platform_language' AND selected_value = :old_iso", $params);
$oldPoFile = $baseTranslationPath.$params['old_iso'].'.po';
$newPoFile = $baseTranslationPath.$params['new_iso'].'.po';
if ($fs->exists($oldPoFile)) {
$fs->rename($oldPoFile, $newPoFile);
}
}
}
}

@ -0,0 +1,27 @@
<?php
/* For licensing terms, see /license.txt */
declare(strict_types=1);
namespace Chamilo\CoreBundle\Migrations\Schema\V200;
use Chamilo\CoreBundle\Entity\Language;
use Chamilo\CoreBundle\Migrations\AbstractMigrationChamilo;
use Doctrine\DBAL\Schema\Schema;
final class Version20250106152602 extends AbstractMigrationChamilo
{
public function getDescription(): string
{
return 'Change iso code length to 8 characters.';
}
/**
* @inheritDoc
*/
public function up(Schema $schema): void
{
$this->addSql('ALTER TABLE language CHANGE isocode isocode VARCHAR('.Language::ISO_MAX_LENGTH.') NOT NULL');
}
}

@ -102,6 +102,10 @@ services:
tags:
- {name: doctrine.orm.entity_listener, entity_manager: default, lazy: true}
Chamilo\CoreBundle\Entity\Listener\LanguageListener:
tags:
- { name: doctrine.orm.entity_listener, entity_manager: default, lazy: true }
Chamilo\CoreBundle\EventListener\MessageStatusListener: ~
Chamilo\CoreBundle\EventListener\ResourceLinkListener: ~

Loading…
Cancel
Save