diff --git a/public/main/admin/extra_field_options.php b/public/main/admin/extra_field_options.php index 9c34063f33..873fa2a0e3 100644 --- a/public/main/admin/extra_field_options.php +++ b/public/main/admin/extra_field_options.php @@ -1,4 +1,5 @@ get($field_id); $check = Security::check_token('request'); $token = Security::get_token(); -if ('add' == $action) { +if ('add' === $action) { $interbreadcrumb[] = ['url' => 'extra_fields.php?type='.$extra_field->type, 'name' => $extra_field->pageName]; $interbreadcrumb[] = [ 'url' => 'extra_fields.php?type='.$extra_field->type.'&action=edit&id='.$extra_field_info['id'], @@ -44,7 +45,7 @@ if ('add' == $action) { 'name' => get_lang('Edit extra field options'), ]; $interbreadcrumb[] = ['url' => '#', 'name' => get_lang('Add')]; -} elseif ('edit' == $action) { +} elseif ('edit' === $action) { $interbreadcrumb[] = ['url' => 'extra_fields.php?type='.$extra_field->type, 'name' => $extra_field->pageName]; $interbreadcrumb[] = [ 'url' => 'extra_fields.php?type='.$extra_field->type.'&action=edit&id='.$extra_field_info['id'], diff --git a/public/main/extrafield/translate.php b/public/main/extrafield/translate.php index 391d810e99..5ae1d6911e 100644 --- a/public/main/extrafield/translate.php +++ b/public/main/extrafield/translate.php @@ -15,9 +15,6 @@ api_protect_admin_script(); $em = Database::getManager(); -$extraField = null; -$originalName = null; - $extraFieldRepo = Container::getExtraFieldRepository(); $languageRepo = Container::getLanguageRepository(); diff --git a/public/main/extrafield/translate_option.php b/public/main/extrafield/translate_option.php new file mode 100644 index 0000000000..d813634d73 --- /dev/null +++ b/public/main/extrafield/translate_option.php @@ -0,0 +1,110 @@ +find($fieldId); + +if (null === $extraFieldOption) { + api_not_allowed(true); +} + +$extraField = $extraFieldOption->getField(); + +$currentUrl = api_get_self().'?id='.$fieldId; +$qb = $languageRepo->getAllAvailable(); +$languages = $qb->getQuery()->getResult(); + +$form = new FormValidator('translate', 'POST', $currentUrl); +$form->addHidden('id', $fieldId); +$form->addHeader($extraFieldOption->getDisplayText()); + +$repository = $em->getRepository(Translation::class); +$translations = $repository->findTranslations($extraFieldOption); + +$defaults = []; +/** @var Language $language */ +foreach ($languages as $language) { + $iso = $language->getIsocode(); + $variable = 'variable['.$iso.']'; + $form->addText($variable, $language->getOriginalName().' ('.$iso.')', false); + if (isset($translations[$iso]) && $translations[$iso]['displayText']) { + $defaults['variable['.$iso.']'] = $translations[$iso]['displayText']; + } +} + +$form->setDefaults($defaults); +$form->addButtonSave(get_lang('Save')); + +$interbreadcrumb[] = ['url' => api_get_path(WEB_CODE_PATH).'admin', 'name' => get_lang('Administration')]; + +switch ($extraField->getExtraFieldType()) { + case ExtraField::USER_FIELD_TYPE: + $interbreadcrumb[] = [ + 'url' => api_get_path(WEB_CODE_PATH).'admin/extra_fields.php?type=user', + 'name' => get_lang('Profile attributes'), + ]; + break; + case ExtraField::COURSE_FIELD_TYPE: + $interbreadcrumb[] = [ + 'url' => api_get_path(WEB_CODE_PATH).'admin/extra_fields.php?type=course', + 'name' => get_lang('Course fields'), + ]; + break; + case ExtraField::SESSION_FIELD_TYPE: + $interbreadcrumb[] = [ + 'url' => api_get_path(WEB_CODE_PATH).'admin/extra_fields.php?type=session', + 'name' => get_lang('Session fields'), + ]; + break; +} + +if ($form->validate()) { + $values = $form->getSubmitValues(); + foreach ($languages as $language) { + if (!isset($values['variable'][$language->getIsocode()])) { + continue; + } + $translation = $values['variable'][$language->getIsocode()]; + if (empty($translation)) { + continue; + } + + $extraFieldOption = $extraFieldOptionsRepo->find($fieldId); + $extraFieldOption + ->setTranslatableLocale($language->getIsocode()) + ->setDisplayText($translation) + ; + $em->persist($extraFieldOption); + $em->flush(); + } + + Display::addFlash(Display::return_message(get_lang('Updated'))); + api_location($currentUrl); +} + +$tpl = new Template(get_lang('Translations')); +$tpl->assign('form', $form->returnForm()); +$template = $tpl->get_template('extrafield/translate.html.twig'); +$content = $tpl->fetch($template); +$tpl->assign('content', $content); +$tpl->display_one_col_template(); diff --git a/public/main/inc/ajax/model.ajax.php b/public/main/inc/ajax/model.ajax.php index 603f3cb958..66020ecf0d 100644 --- a/public/main/inc/ajax/model.ajax.php +++ b/public/main/inc/ajax/model.ajax.php @@ -2365,7 +2365,7 @@ switch ($action) { get_lang('No') ); foreach ($result as $item) { - $item['display_text'] = ExtraField::translateDisplayName($item['variable'], $item['displayText']); + $item['display_text'] = $item['displayText']; $item['field_type'] = $obj->get_field_type_by_id($item['fieldType']); $item['changeable'] = $item['changeable'] ? $checkIcon : $timesIcon; $item['visible_to_self'] = $item['visibleToSelf'] ? $checkIcon : $timesIcon; @@ -2482,9 +2482,9 @@ switch ($action) { $columns = ['display_text', 'option_value', 'option_order']; $sidx = in_array($sidx, $columns) ? $sidx : 'display_text'; $result = $obj->get_all([ - 'where' => ['field_id = ? ' => $field_id], - 'order' => "$sidx $sord", - 'LIMIT' => "$start , $limit", + 'where' => ['field_id = ? ' => $field_id], + 'order' => "$sidx $sord", + 'LIMIT' => "$start , $limit", ]); break; case 'get_usergroups_teacher': diff --git a/public/main/inc/lib/extra_field.lib.php b/public/main/inc/lib/extra_field.lib.php index 930c58a908..bc52f09fde 100644 --- a/public/main/inc/lib/extra_field.lib.php +++ b/public/main/inc/lib/extra_field.lib.php @@ -640,16 +640,17 @@ class ExtraField extends Model variable = '$variable' AND extra_field_type = $this->extraFieldType"; $result = Database::query($sql); + $extraFieldRepo = Container::getExtraFieldRepository(); if (Database::num_rows($result)) { $row = Database::fetch_array($result, 'ASSOC'); - $row['display_text'] = $this->translateDisplayName( - $row['variable'], - $row['display_text'] - ); + $extraFieldId = $row['id']; + /** @var \Chamilo\CoreBundle\Entity\ExtraField $extraField */ + $extraField = $extraFieldRepo->find($extraFieldId); + $row['display_text'] = $extraField->getDisplayText(); // All the tags of the field $sql = "SELECT * FROM $this->table_field_tag - WHERE field_id='".intval($row['id'])."' + WHERE field_id='".$extraFieldId."' ORDER BY id ASC"; $result = Database::query($sql); while ($option = Database::fetch_array($result, 'ASSOC')) { @@ -657,34 +658,9 @@ class ExtraField extends Model } return $row; - } else { - return false; } - } - /** - * Translate the display text for a extra field. - * - * @param string $variable - * @param string $defaultDisplayText - * - * @return string - */ - public static function translateDisplayName($variable, $defaultDisplayText): string - { - // 1st priority variable. - $translatedVariable = get_lang($variable); - if ($variable !== $translatedVariable) { - return $translatedVariable; - } - - // 2nd priority display text. - $translatedDisplayText = get_lang($defaultDisplayText); - if ($defaultDisplayText !== $translatedDisplayText) { - return $translatedVariable; - } - - return $defaultDisplayText; + return false; } /** @@ -947,10 +923,14 @@ class ExtraField extends Model $result = Database::query($sql); $extraFields = Database::store_result($result, 'ASSOC'); + $extraFieldRepo = Container::getExtraFieldRepository(); $option = new ExtraFieldOption($this->type); if (!empty($extraFields)) { foreach ($extraFields as &$extraField) { - $extraField['display_text'] = self::translateDisplayName($extraField['variable'], $extraField['display_text']); + $extraFieldId = $extraField['id']; + /** @var \Chamilo\CoreBundle\Entity\ExtraField $extraField */ + $field = $extraFieldRepo->find($extraFieldId); + $extraField['display_text'] = $field->getDisplayText(); $extraField['options'] = $option->get_field_options_by_field( $extraField['id'], false, @@ -2013,13 +1993,17 @@ class ExtraField extends Model extra_field_type = $this->extraFieldType"; $result = Database::query($sql); if (Database::num_rows($result)) { + $extraFieldRepo = Container::getExtraFieldRepository(); $row = Database::fetch_array($result, 'ASSOC'); if ($row) { - $row['display_text'] = $this->translateDisplayName($row['variable'], $row['display_text']); + $extraFieldId = $row['id']; + /** @var \Chamilo\CoreBundle\Entity\ExtraField $extraField */ + $field = $extraFieldRepo->find($extraFieldId); + $row['display_text'] = $field->getDisplayText(); // All the options of the field $sql = "SELECT * FROM $this->table_field_options - WHERE field_id='".intval($row['id'])."' + WHERE field_id='".$extraFieldId."' ORDER BY option_order ASC"; $result = Database::query($sql); while ($option = Database::fetch_array($result)) { @@ -2279,7 +2263,12 @@ class ExtraField extends Model if ('edit' === $action) { $translateUrl = api_get_path(WEB_CODE_PATH).'extrafield/translate.php?'.http_build_query(['id' => $id]); - $translateButton = Display::toolbarButton(get_lang('Translate this term'), $translateUrl, 'language', 'link'); + $translateButton = Display::toolbarButton( + get_lang('Translate this term'), + $translateUrl, + 'language', + 'link' + ); $form->addText( 'display_text', @@ -2319,18 +2308,20 @@ class ExtraField extends Model self::FIELD_TYPE_TRIPLE_SELECT, ]; - if ('edit' == $action) { + if ('edit' === $action) { if (in_array($defaults['field_type'], $fieldWithOptions)) { $url = Display::url( get_lang('Edit extra field options'), - 'extra_field_options.php?type='.$this->type.'&field_id='.$id + 'extra_field_options.php?type='.$this->type.'&field_id='.$id, + ['class' => 'btn'] ); $form->addElement('label', null, $url); if (self::FIELD_TYPE_SELECT == $defaults['field_type']) { $urlWorkFlow = Display::url( get_lang('Edit this field\'s workflow'), - 'extra_field_workflow.php?type='.$this->type.'&field_id='.$id + 'extra_field_workflow.php?type='.$this->type.'&field_id='.$id, + ['class' => 'btn'] ); $form->addElement('label', null, $urlWorkFlow); } @@ -2414,7 +2405,10 @@ class ExtraField extends Model $info = parent::get($id); if ($translateDisplayText) { - $info['display_text'] = self::translateDisplayName($info['variable'], $info['display_text']); + $extraFieldRepo = Container::getExtraFieldRepository(); + /** @var \Chamilo\CoreBundle\Entity\ExtraField $extraField */ + $field = $extraFieldRepo->find($id); + $info['display_text'] = $field->getDisplayText(); } return $info; diff --git a/public/main/inc/lib/extra_field_option.lib.php b/public/main/inc/lib/extra_field_option.lib.php index bc34cc7e4a..6a155d5168 100644 --- a/public/main/inc/lib/extra_field_option.lib.php +++ b/public/main/inc/lib/extra_field_option.lib.php @@ -3,6 +3,7 @@ /* For licensing terms, see /license.txt */ use Chamilo\CoreBundle\Entity\ExtraFieldOptions; +use Chamilo\CoreBundle\Framework\Container; /** * Handles the extra fields for various objects (users, sessions, courses). @@ -533,9 +534,8 @@ class ExtraFieldOption extends Model break; } - $result = Database::getManager() - ->getRepository(ExtraFieldOptions::class) - ->findBy(['field' => $field_id], $orderBy); + $extraFieldOptionsRepo = Container::getExtraFieldOptionsRepository(); + $result = $extraFieldOptionsRepo->findBy(['field' => $field_id], $orderBy); if (!$result) { return false; @@ -548,7 +548,7 @@ class ExtraFieldOption extends Model 'id' => $row->getId(), 'field_id' => $row->getField()->getId(), 'option_value' => $row->getValue(), - 'display_text' => \ExtraField::translateDisplayName($row->getValue(), $row->getDisplayText()), + 'display_text' => $row->getDisplayText(), 'priority' => $row->getPriority(), 'priority_message' => $row->getPriorityMessage(), 'option_order' => $row->getOptionOrder(), @@ -574,16 +574,16 @@ class ExtraFieldOption extends Model */ public function get_second_select_field_options_by_field($option_value_id, $to_json = false) { - $em = Database::getManager(); - $option = $em->find(ExtraFieldOptions::class, $option_value_id); + $extraFieldOptionsRepo = Container::getExtraFieldOptionsRepository(); + $option = $extraFieldOptionsRepo->find($option_value_id); if (!$option) { return !$to_json ? [] : '{}'; } - $subOptions = $em->getRepository(ExtraFieldOptions::class)->findSecondaryOptions($option); - + $subOptions = $extraFieldOptionsRepo->findSecondaryOptions($option); $optionsInfo = []; + /** @var ExtraFieldOptions $subOption */ foreach ($subOptions as $subOption) { $optionsInfo[] = [ @@ -748,10 +748,10 @@ class ExtraFieldOption extends Model $form->addElement('hidden', 'type', $this->type); $form->addElement('hidden', 'field_id', $this->fieldId); - if ('edit' == $action) { - $translateUrl = api_get_path(WEB_CODE_PATH).'extrafield/translate.php?'.http_build_query([ - 'extra_field_option' => $id, - ]); + if ('edit' === $action) { + $translateUrl = api_get_path(WEB_CODE_PATH).'extrafield/translate_option.php?'.http_build_query( + ['id' => $id] + ); $translateButton = Display::toolbarButton( get_lang('Translate this term'), $translateUrl, @@ -774,7 +774,7 @@ class ExtraFieldOption extends Model $defaults = []; - if ('edit' == $action) { + if ('edit' === $action) { // Setting the defaults $defaults = $this->get($id, false); $form->freeze('option_value'); @@ -785,7 +785,6 @@ class ExtraFieldOption extends Model $form->setDefaults($defaults); - // Setting the rules $form->addRule('display_text', get_lang('Required field'), 'required'); $form->addRule('option_value', get_lang('Required field'), 'required'); @@ -860,7 +859,9 @@ class ExtraFieldOption extends Model $info = parent::get($id); if ($info && $translateDisplayText) { - $info['display_text'] = ExtraField::translateDisplayName($info['option_value'], $info['display_text']); + $extraFieldOptionsRepo = Container::getExtraFieldOptionsRepository(); + $option = $extraFieldOptionsRepo->find($id); + $info['display_text'] = $option->getDisplayText(); } return $info; @@ -876,7 +877,9 @@ class ExtraFieldOption extends Model $result = parent::get_all($options); foreach ($result as &$row) { - $row['display_text'] = ExtraField::translateDisplayName($row['option_value'], $row['display_text']); + $extraFieldOptionsRepo = Container::getExtraFieldOptionsRepository(); + $option = $extraFieldOptionsRepo->find($row['id']); + $row['display_text'] = $option->getDisplayText(); } return $result; diff --git a/src/CoreBundle/Entity/Course.php b/src/CoreBundle/Entity/Course.php index cf45802ae2..f900c8dd8b 100644 --- a/src/CoreBundle/Entity/Course.php +++ b/src/CoreBundle/Entity/Course.php @@ -263,6 +263,7 @@ class Course extends AbstractResource implements ResourceInterface, ResourceWith * @ORM\Column(name="course_language", type="string", length=20, nullable=false, unique=false) */ #[Groups(['course:read'])] + #[Assert\NotBlank] protected string $courseLanguage; /** @@ -368,11 +369,13 @@ class Course extends AbstractResource implements ResourceInterface, ResourceWith /** * @ORM\Column(name="subscribe", type="boolean", nullable=false, unique=false) */ + #[Assert\NotNull] protected bool $subscribe; /** * @ORM\Column(name="unsubscribe", type="boolean", nullable=false, unique=false) */ + #[Assert\NotNull] protected bool $unsubscribe; /** diff --git a/src/CoreBundle/Entity/ExtraFieldOptions.php b/src/CoreBundle/Entity/ExtraFieldOptions.php index a50261b636..83253f4a62 100644 --- a/src/CoreBundle/Entity/ExtraFieldOptions.php +++ b/src/CoreBundle/Entity/ExtraFieldOptions.php @@ -7,6 +7,8 @@ declare(strict_types=1); namespace Chamilo\CoreBundle\Entity; use Doctrine\ORM\Mapping as ORM; +use Gedmo\Mapping\Annotation as Gedmo; +use Symfony\Component\Validator\Constraints as Assert; /** * @ORM\Entity(repositoryClass="Chamilo\CoreBundle\Repository\ExtraFieldOptionsRepository") @@ -27,6 +29,7 @@ class ExtraFieldOptions * @ORM\ManyToOne(targetEntity="Chamilo\CoreBundle\Entity\ExtraField", inversedBy="options") * @ORM\JoinColumn(name="field_id", referencedColumnName="id") */ + #[Assert\NotNull] protected ExtraField $field; /** @@ -35,10 +38,16 @@ class ExtraFieldOptions protected ?string $value = null; /** + * @Gedmo\Translatable * @ORM\Column(name="display_text", type="string", length=255, nullable=true) */ protected ?string $displayText = null; + /** + * @Gedmo\Locale + */ + protected ?string $locale = null; + /** * @ORM\Column(name="priority", type="string", length=255, nullable=true) */ @@ -119,10 +128,7 @@ class ExtraFieldOptions return $this; } - /** - * @return string - */ - public function getPriority() + public function getPriority(): ?string { return $this->priority; } @@ -134,10 +140,7 @@ class ExtraFieldOptions return $this; } - /** - * @return string - */ - public function getPriorityMessage() + public function getPriorityMessage(): ?string { return $this->priorityMessage; } @@ -148,4 +151,16 @@ class ExtraFieldOptions return $this; } + + public function setTranslatableLocale($locale) + { + $this->locale = $locale; + + return $this; + } + + public function getTranslatableLocale() + { + return $this->locale; + } } diff --git a/src/CoreBundle/Framework/Container.php b/src/CoreBundle/Framework/Container.php index 5fee064bb5..cc32e7e0c2 100644 --- a/src/CoreBundle/Framework/Container.php +++ b/src/CoreBundle/Framework/Container.php @@ -11,6 +11,7 @@ use Chamilo\CoreBundle\Component\Editor\Editor; use Chamilo\CoreBundle\Repository\AssetRepository; use Chamilo\CoreBundle\Repository\CareerRepository; use Chamilo\CoreBundle\Repository\CourseCategoryRepository; +use Chamilo\CoreBundle\Repository\ExtraFieldOptionsRepository; use Chamilo\CoreBundle\Repository\ExtraFieldRepository; use Chamilo\CoreBundle\Repository\GradeBookCategoryRepository; use Chamilo\CoreBundle\Repository\LanguageRepository; @@ -338,6 +339,11 @@ class Container return self::$container->get(ExtraFieldRepository::class); } + public static function getExtraFieldOptionsRepository(): ExtraFieldOptionsRepository + { + return self::$container->get(ExtraFieldOptionsRepository::class); + } + public static function getGlossaryRepository(): CGlossaryRepository { return self::$container->get(CGlossaryRepository::class); diff --git a/tests/CoreBundle/Repository/ExtraFieldOptionsRepositoryTest.php b/tests/CoreBundle/Repository/ExtraFieldOptionsRepositoryTest.php new file mode 100644 index 0000000000..7d6bd10db2 --- /dev/null +++ b/tests/CoreBundle/Repository/ExtraFieldOptionsRepositoryTest.php @@ -0,0 +1,115 @@ +getEntityManager(); + $extraFieldRepo = static::getContainer()->get(ExtraFieldRepository::class); + $extraFieldOptionsRepo = static::getContainer()->get(ExtraFieldOptionsRepository::class); + + $defaultCount = $extraFieldRepo->count([]); + $defaultCountOptions = $extraFieldOptionsRepo->count([]); + + $extraField = (new ExtraField()) + ->setDisplayText('test') + ->setVariable('test') + ->setDescription('desc') + ->setHelperText('help') + ->setExtraFieldType(ExtraField::USER_FIELD_TYPE) + ->setFieldType(\ExtraField::FIELD_TYPE_TEXT) + ; + $em->persist($extraField); + + $extraFieldOptions = (new ExtraFieldOptions()) + ->setDisplayText('test in ENGLISH') + ->setValue('value') + ->setField($extraField) + ->setOptionOrder(0) + ->setPriority('urgent') + ->setPriorityMessage('is urgent!') + ; + $this->assertHasNoEntityViolations($extraFieldOptions); + $em->persist($extraFieldOptions); + $em->flush(); + + $this->assertSame('test in ENGLISH', $extraFieldOptions->getDisplayText()); + $this->assertSame('value', $extraFieldOptions->getValue()); + $this->assertSame(0, $extraFieldOptions->getOptionOrder()); + $this->assertSame('urgent', $extraFieldOptions->getPriority()); + $this->assertSame('is urgent!', $extraFieldOptions->getPriorityMessage()); + + $this->assertSame($defaultCount + 1, $extraFieldRepo->count([])); + $this->assertSame($defaultCountOptions + 1, $extraFieldOptionsRepo->count([])); + } + + public function testCreateWithTranslation(): void + { + $this->testCreate(); + $em = $this->getEntityManager(); + + $extraFieldRepo = static::getContainer()->get(ExtraFieldRepository::class); + $extraFieldOptionsRepo = static::getContainer()->get(ExtraFieldOptionsRepository::class); + + /** @var ExtraFieldOptions $extraFieldOption */ + $extraFieldOption = $extraFieldOptionsRepo->findOneBy(['value' => 'value']); + $this->assertNotNull($extraFieldOption); + $this->assertInstanceOf(ExtraFieldOptions::class, $extraFieldOption); + + $extraFieldOption + ->setTranslatableLocale('fr_FR') + ->setDisplayText('test in FRENCH') + ; + $em->persist($extraFieldOption); + $em->flush(); + + /** @var ExtraFieldOptions $extraFieldOption */ + $extraFieldOption = $extraFieldOptionsRepo->find($extraFieldOption->getId()); + + $extraFieldOption + ->setTranslatableLocale('pl') + ->setDisplayText('test in POLISH') + ; + $em->persist($extraFieldOption); + $em->flush(); + $em->clear(); + + /** @var ExtraFieldOptions $extraFieldOption */ + $extraFieldOption = $extraFieldOptionsRepo->find($extraFieldOption->getId()); + $repository = $em->getRepository(Translation::class); + + $translations = $repository->findTranslations($extraFieldOption); + + $this->assertCount(2, $translations); + $expected = [ + 'fr_FR' => [ + 'displayText' => 'test in FRENCH', + ], + 'pl' => [ + 'displayText' => 'test in POLISH', + ], + ]; + $this->assertSame($expected, $translations); + + /** @var ExtraFieldOptions $extraFieldOption */ + $extraFieldOption = $extraFieldOptionsRepo->find($extraFieldOption->getId()); + $this->assertSame('test in ENGLISH', $extraFieldOption->getDisplayText()); + } +}