diff --git a/webapp/migrations/Version20231120225210.php b/webapp/migrations/Version20231120225210.php new file mode 100644 index 0000000000..4f2bedd3cc --- /dev/null +++ b/webapp/migrations/Version20231120225210.php @@ -0,0 +1,54 @@ +connection->fetchAllAssociative('SELECT langid, extensions FROM language'); + + foreach ($languages as $language) { + $extensions = json_decode($language['extensions'], true); + + $updated = false; + foreach ($extensions as &$extension) { + if (strpos($extension, '.') === 0) { + $extension = ltrim($extension, '.'); + $updated = true; + } + } + + if ($updated) { + $newExtensionsJson = json_encode($extensions); + $this->connection->executeQuery('UPDATE language SET extensions = :extensions WHERE langid = :langid', [ + 'extensions' => $newExtensionsJson, + 'langid' => $language['langid'], + ]); + } + } + } + + public function down(Schema $schema): void + { + // This migration is not reversible + } + + public function isTransactional(): bool + { + return false; + } +} diff --git a/webapp/src/Entity/Language.php b/webapp/src/Entity/Language.php index 2ff25e0e0d..4d2e76627e 100644 --- a/webapp/src/Entity/Language.php +++ b/webapp/src/Entity/Language.php @@ -53,6 +53,12 @@ class Language extends BaseApiEntity options: ['comment' => 'List of recognized extensions (JSON encoded)'] )] #[Assert\NotBlank] + #[Assert\All([ + new Assert\Regex([ + 'pattern' => '/^[^.]/', + 'message' => 'The extension should not start with a dot.' + ]) + ])] #[Serializer\Type('array')] private array $extensions = []; diff --git a/webapp/src/Service/SubmissionService.php b/webapp/src/Service/SubmissionService.php index 73f656e9e5..c53cd17e45 100644 --- a/webapp/src/Service/SubmissionService.php +++ b/webapp/src/Service/SubmissionService.php @@ -530,8 +530,7 @@ public function submitSolution( if ($source !== 'shadowing' && $language->getFilterCompilerFiles()) { $matchesExtension = false; foreach ($language->getExtensions() as $extension) { - $extensionLength = strlen($extension); - if (substr($file->getClientOriginalName(), -$extensionLength) === $extension) { + if (str_ends_with($file->getClientOriginalName(), '.' . $extension)) { $matchesExtension = true; break; } diff --git a/webapp/templates/jury/partials/language_form.html.twig b/webapp/templates/jury/partials/language_form.html.twig index 82e9d7b1ad..7409b3958f 100644 --- a/webapp/templates/jury/partials/language_form.html.twig +++ b/webapp/templates/jury/partials/language_form.html.twig @@ -18,7 +18,7 @@ addExtension($extensionsHolder, $addExtensionButton); }); - $extensionsHolder.find('div').each(function() { + $extensionsHolder.find('div:not(.invalid-feedback)').each(function() { addDeleteLink($(this)); }); @@ -37,8 +37,10 @@ var $removeFormButton = $(''); var $inputGroup = $('
'); var $formControl = $extensionDiv.find('.form-control'); + var $feedbackBlock = $extensionDiv.find('.invalid-feedback'); $inputGroup.append($formControl); $inputGroup.append($removeFormButton); + $inputGroup.append($feedbackBlock); $extensionDiv.html($inputGroup); $removeFormButton.on('click', function(e) {