Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@

namespace ILIAS\UI\Implementation\Component\Input\Field;

use ILIAS\Language\Language;
use ILIAS\UI\Component as C;
use ILIAS\Data\Factory as DataFactory;
use ILIAS\Data\DateFormat\DateFormat;
use ILIAS\UI\Implementation\Component\ComponentHelper;
use ILIAS\UI\Implementation\Component\Input\InputData;
use ILIAS\UI\Implementation\Component\JavaScriptBindable;
use DateTimeImmutable;
use ILIAS\Refinery\Custom\Transformation;
Expand All @@ -40,6 +42,8 @@ class DateTime extends FormInput implements C\Input\Field\DateTime

public const TIME_FORMAT = 'HH:mm';

protected Language $lng;

protected DateFormat $format;
protected ?DateTimeImmutable $min_date = null;
protected ?DateTimeImmutable $max_date = null;
Expand All @@ -55,10 +59,12 @@ class DateTime extends FormInput implements C\Input\Field\DateTime
public function __construct(
DataFactory $data_factory,
\ILIAS\Refinery\Factory $refinery,
Language $lng,
string $label,
?string $byline
) {
parent::__construct($data_factory, $refinery, $label, $byline);
$this->lng = $lng;

$this->format = $data_factory->dateFormat()->standard();

Expand Down Expand Up @@ -97,6 +103,28 @@ public function withValue($value): self
return parent::withValue($value);
}

/**
* @inheritdoc
*
* User input may be unparseable as a datetime (e.g. a 5-digit year submitted
* by the browser). Surface that as a soft validation error rather than letting
* the strict isClientSideValueOk check raise InvalidArgumentException out of
* withValue. See Mantis #46714.
*/
public function withInput(InputData $input): self
{
if (!$this->isDisabled() && $this->getName() !== null) {
$value = $input->getOr($this->getName(), null);
if (is_string($value) && $value !== '' && !$this->isClientSideValueOk($value)) {
$clone = clone $this;
$clone->value = $value;
$clone->content = $this->data_factory->error($this->lng->txt('exc_date_not_valid'));
return $clone->withError($this->lng->txt('exc_date_not_valid'));
}
}
return parent::withInput($input);
}

public function withFormat(DateFormat $format): self
{
$clone = clone $this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ public function multiSelect(string $label, array $options, ?string $byline = nul
*/
public function dateTime(string $label, ?string $byline = null): I\DateTime
{
return new DateTime($this->data_factory, $this->refinery, $label, $byline);
return new DateTime($this->data_factory, $this->refinery, $this->lng, $label, $byline);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -714,12 +714,18 @@ protected function internalRenderDateTimeField(F\DateTime $component, RendererIn

$this->applyValue($component, $tpl, function (?string $value) use ($dt_type) {
if ($value !== null) {
$value = new \DateTimeImmutable($value);
return $value->format(match ($dt_type) {
self::TYPE_DATETIME => self::HTML5_NATIVE_DATETIME_FORMAT,
self::TYPE_DATE => self::HTML5_NATIVE_DATE_FORMAT,
self::TYPE_TIME => self::HTML5_NATIVE_TIME_FORMAT,
});
try {
$datetime = new \DateTimeImmutable($value);
return $datetime->format(match ($dt_type) {
self::TYPE_DATETIME => self::HTML5_NATIVE_DATETIME_FORMAT,
self::TYPE_DATE => self::HTML5_NATIVE_DATE_FORMAT,
self::TYPE_TIME => self::HTML5_NATIVE_TIME_FORMAT,
});
} catch (\Exception) {
// Display unparseable user input verbatim so the
// submitter can correct it (Mantis #46714).
return $value;
}
}
return null;
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,39 @@ public function testWithInvalidValue(): void
->withValue("this is no datetime...");
}

/**
* Mantis #46714: a browser may submit a 5-digit year (e.g. "12345-01-01 10:00"),
* which PHP's DateTimeImmutable cannot parse. withInput must produce a soft
* validation error rather than letting InvalidArgumentException escape.
*/
public function testWithInputFiveDigitYearProducesErrorNotException(): void
{
$datetime = $this->factory->datetime('label', 'byline')
->withUseTime(true)
->withNameFrom($this->name_source);
$post_data = new DefInputData(['name_0' => '12345-01-01 10:00']);

$clone = $datetime->withInput($post_data);

$this->assertNotNull($clone->getError());
$content = $clone->getContent();
$this->assertTrue($content->isError());
$this->assertSame('12345-01-01 10:00', $clone->getValue());
}

public function testWithInputUnparseableValueRendersRawValue(): void
{
$datetime = $this->factory->datetime('label', 'byline')
->withUseTime(true)
->withNameFrom($this->name_source);
$post_data = new DefInputData(['name_0' => '12345-01-01 10:00']);

$clone = $datetime->withInput($post_data);

$html = $this->getDefaultRenderer()->render($clone);
$this->assertStringContainsString('12345-01-01 10:00', $html);
}

public function testRender(): void
{
$datetime = $this->factory->dateTime('label', 'byline');
Expand Down
Loading