Next major version 2 with PHP 8.1, Symfony 6, Tabler UI, 2FA ... (#2902)
This commit is contained in:
@@ -1,45 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Validator\Constraints;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
|
||||
*/
|
||||
class AllowedHtmlTags extends Constraint
|
||||
{
|
||||
public const DISALLOWED_TAGS_FOUND = 'kimai-allowed-html-tags-00';
|
||||
|
||||
public $tags;
|
||||
|
||||
protected static $errorNames = [
|
||||
self::DISALLOWED_TAGS_FOUND => 'The given value contains disallowed HTML tags.',
|
||||
];
|
||||
|
||||
public $message = 'This string contains invalid HTML tags.';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefaultOption()
|
||||
{
|
||||
return 'tags';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRequiredOptions()
|
||||
{
|
||||
return ['tags'];
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Validator\Constraints;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedValueException;
|
||||
|
||||
class AllowedHtmlTagsValidator extends ConstraintValidator
|
||||
{
|
||||
/**
|
||||
* @param string|mixed $value
|
||||
* @param Constraint $constraint
|
||||
*/
|
||||
public function validate($value, Constraint $constraint)
|
||||
{
|
||||
if (!($constraint instanceof AllowedHtmlTags)) {
|
||||
throw new UnexpectedTypeException($constraint, __NAMESPACE__ . '\AllowedHtmlTags');
|
||||
}
|
||||
|
||||
if (null === $value || '' === $value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_scalar($value) && !(\is_object($value) && method_exists($value, '__toString'))) {
|
||||
throw new UnexpectedValueException($value, 'string');
|
||||
}
|
||||
|
||||
$value = (string) $value;
|
||||
|
||||
if (strip_tags($value, $constraint->tags) !== $value) {
|
||||
$this->context->buildViolation('This string contains invalid HTML tags.')
|
||||
->setTranslationDomain('validators')
|
||||
->setParameter('{{ value }}', $this->formatValue($value))
|
||||
->setCode(AllowedHtmlTags::DISALLOWED_TAGS_FOUND)
|
||||
->addViolation();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ namespace App\Validator\Constraints;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
class ColorChoices extends Constraint
|
||||
final class ColorChoices extends Constraint
|
||||
{
|
||||
public const COLOR_CHOICES_ERROR = 'ui5hffg-dsfef3-1234-5678-2g8jkfr56d84';
|
||||
public const COLOR_CHOICES_NAME_ERROR = 'ui5hffg-dsfef3-1234-5679-2g8jkfr56d84';
|
||||
@@ -21,7 +21,7 @@ class ColorChoices extends Constraint
|
||||
self::COLOR_CHOICES_NAME_ERROR => 'COLOR_CHOICES_NAME_ERROR',
|
||||
];
|
||||
|
||||
public $message = 'The given value {{ value }} is not a valid hexadecimal color.';
|
||||
public $invalidNameMessage = 'The given value {{ name }} is not a valid color name for {{ color }}. Allowed are {{ max }} alpha-numerical characters, including minus and space.';
|
||||
public $maxLength = 20;
|
||||
public string $message = 'The given value {{ value }} is not a valid hexadecimal color.';
|
||||
public string $invalidNameMessage = 'The given value {{ name }} is not a valid color name for {{ color }}. Allowed are {{ max }} alpha-numerical characters, including minus and space.';
|
||||
public int $maxLength = 20;
|
||||
}
|
||||
|
||||
@@ -13,24 +13,19 @@ use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
|
||||
class ColorChoicesValidator extends ConstraintValidator
|
||||
final class ColorChoicesValidator extends ConstraintValidator
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate($value, Constraint $constraint)
|
||||
public function validate(mixed $value, Constraint $constraint): void
|
||||
{
|
||||
if (!$constraint instanceof ColorChoices) {
|
||||
throw new UnexpectedTypeException($constraint, ColorChoices::class);
|
||||
}
|
||||
|
||||
$color = $value;
|
||||
|
||||
if ($color === null || (\is_string($color) && empty(trim($color)))) {
|
||||
if (!\is_string($value) || trim($value) === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
$colors = explode(',', $color);
|
||||
$colors = explode(',', $value);
|
||||
|
||||
foreach ($colors as $color) {
|
||||
$color = explode('|', $color);
|
||||
@@ -44,7 +39,7 @@ class ColorChoicesValidator extends ConstraintValidator
|
||||
$name = $code;
|
||||
}
|
||||
|
||||
if (!\is_string($code) || 1 !== preg_match('/^#[0-9a-fA-F]{6}$/i', $code)) {
|
||||
if (1 !== preg_match('/^#[0-9a-fA-F]{6}$/i', $code)) {
|
||||
$this->context->buildViolation($constraint->message)
|
||||
->setParameter('{{ value }}', $this->formatValue($code))
|
||||
->setCode(ColorChoices::COLOR_CHOICES_ERROR)
|
||||
@@ -60,7 +55,7 @@ class ColorChoicesValidator extends ConstraintValidator
|
||||
$name = str_replace(['-', ' '], '', $name);
|
||||
$length = mb_strlen($name);
|
||||
|
||||
if (!\is_string($name) || $length > $constraint->maxLength || !ctype_alnum($name)) {
|
||||
if ($length > $constraint->maxLength || !preg_match('/^[a-zA-Z0-9]+$/', $name)) {
|
||||
$this->context->buildViolation($constraint->invalidNameMessage)
|
||||
->setParameter('{{ name }}', $this->formatValue($name))
|
||||
->setParameter('{{ color }}', $this->formatValue($code))
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace App\Validator\Constraints;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
class DateTimeFormat extends Constraint
|
||||
final class DateTimeFormat extends Constraint
|
||||
{
|
||||
public const INVALID_FORMAT = 'kimai-datetime-00';
|
||||
|
||||
@@ -19,9 +19,10 @@ class DateTimeFormat extends Constraint
|
||||
self::INVALID_FORMAT => 'The given value is not a valid datetime format.',
|
||||
];
|
||||
|
||||
public $message = 'This datetime format is invalid.';
|
||||
public ?string $separator = null;
|
||||
public ?string $message = 'This datetime format is invalid.';
|
||||
|
||||
public function getTargets()
|
||||
public function getTargets(): string|array
|
||||
{
|
||||
return self::PROPERTY_CONSTRAINT;
|
||||
}
|
||||
|
||||
@@ -13,22 +13,42 @@ use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
|
||||
class DateTimeFormatValidator extends ConstraintValidator
|
||||
final class DateTimeFormatValidator extends ConstraintValidator
|
||||
{
|
||||
/**
|
||||
* @param string|mixed $value
|
||||
* @param string|mixed|null $value
|
||||
* @param Constraint $constraint
|
||||
*/
|
||||
public function validate($value, Constraint $constraint)
|
||||
public function validate(mixed $value, Constraint $constraint): void
|
||||
{
|
||||
if (!($constraint instanceof DateTimeFormat)) {
|
||||
throw new UnexpectedTypeException($constraint, __NAMESPACE__ . '\DateTimeFormat');
|
||||
}
|
||||
|
||||
if ($value === null) {
|
||||
if (!\is_string($value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($constraint->separator === null || $constraint->separator === '') {
|
||||
if (str_contains($value, ',')) {
|
||||
$this->context->buildViolation('The given value should not contain a comma.')
|
||||
->setTranslationDomain('validators')
|
||||
->setCode(DateTimeFormat::INVALID_FORMAT)
|
||||
->addViolation();
|
||||
}
|
||||
|
||||
$this->validateDateTime($value);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (explode($constraint->separator, $value) as $v) {
|
||||
$this->validateDateTime($v);
|
||||
}
|
||||
}
|
||||
|
||||
private function validateDateTime(mixed $value): void
|
||||
{
|
||||
$valid = true;
|
||||
|
||||
if (!\is_string($value)) {
|
||||
|
||||
@@ -11,11 +11,7 @@ namespace App\Validator\Constraints;
|
||||
|
||||
use Symfony\Component\Validator\Constraints\Regex;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
|
||||
*/
|
||||
class Duration extends Regex
|
||||
final class Duration extends Regex
|
||||
{
|
||||
public function __construct($options = null)
|
||||
{
|
||||
@@ -24,7 +20,7 @@ class Duration extends Regex
|
||||
// negative times -? are allowed, because plugins could allow negative times
|
||||
'-?[0-9]{1,}',
|
||||
'-?[0-9]{1,}[,.]{1}[0-9]{1,}',
|
||||
// ASP.NET style time spans - https://momentjs.com/docs/#/durations/
|
||||
// ISO style time spans like 01:37
|
||||
'-?[0-9]{1,}:[0-9]{1,}:[0-9]{1,}',
|
||||
'-?[0-9]{1,}:[0-9]{1,}',
|
||||
// https://en.wikipedia.org/wiki/ISO_8601#Time_intervals
|
||||
|
||||
@@ -11,6 +11,6 @@ namespace App\Validator\Constraints;
|
||||
|
||||
use Symfony\Component\Validator\Constraints\RegexValidator;
|
||||
|
||||
class DurationValidator extends RegexValidator
|
||||
final class DurationValidator extends RegexValidator
|
||||
{
|
||||
}
|
||||
|
||||
@@ -11,11 +11,8 @@ namespace App\Validator\Constraints;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
|
||||
*/
|
||||
class HexColor extends Constraint
|
||||
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
||||
final class HexColor extends Constraint
|
||||
{
|
||||
public const HEX_COLOR_ERROR = 'xd5hffg-dsfef3-426a-83d7-2g8jkfr56d84';
|
||||
|
||||
@@ -23,5 +20,5 @@ class HexColor extends Constraint
|
||||
self::HEX_COLOR_ERROR => 'HEX_COLOR_ERROR',
|
||||
];
|
||||
|
||||
public $message = 'The given value is not a valid hexadecimal color.';
|
||||
public string $message = 'The given value is not a valid hexadecimal color.';
|
||||
}
|
||||
|
||||
@@ -13,12 +13,9 @@ use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
|
||||
class HexColorValidator extends ConstraintValidator
|
||||
final class HexColorValidator extends ConstraintValidator
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate($value, Constraint $constraint)
|
||||
public function validate(mixed $value, Constraint $constraint): void
|
||||
{
|
||||
if (!$constraint instanceof HexColor) {
|
||||
throw new UnexpectedTypeException($constraint, __NAMESPACE__ . '\HexColor');
|
||||
|
||||
@@ -9,14 +9,10 @@
|
||||
|
||||
namespace App\Validator\Constraints;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation\Target;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target({"CLASS", "PROPERTY", "METHOD", "ANNOTATION"})
|
||||
*/
|
||||
class Project extends Constraint
|
||||
#[\Attribute(\Attribute::TARGET_CLASS)]
|
||||
final class Project extends Constraint
|
||||
{
|
||||
public const END_BEFORE_BEGIN_ERROR = 'kimai-project-00';
|
||||
|
||||
@@ -24,9 +20,9 @@ class Project extends Constraint
|
||||
self::END_BEFORE_BEGIN_ERROR => 'End date must not be earlier then start date.',
|
||||
];
|
||||
|
||||
public $message = 'This project has invalid settings.';
|
||||
public string $message = 'This project has invalid settings.';
|
||||
|
||||
public function getTargets()
|
||||
public function getTargets(): string|array
|
||||
{
|
||||
return self::CLASS_CONSTRAINT;
|
||||
}
|
||||
|
||||
@@ -16,26 +16,20 @@ use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
|
||||
class ProjectValidator extends ConstraintValidator
|
||||
final class ProjectValidator extends ConstraintValidator
|
||||
{
|
||||
/**
|
||||
* @var Constraint[]
|
||||
*/
|
||||
private $constraints;
|
||||
|
||||
/**
|
||||
* @param Constraint[] $constraints
|
||||
*/
|
||||
public function __construct(iterable $constraints = [])
|
||||
public function __construct(private iterable $constraints = [])
|
||||
{
|
||||
$this->constraints = $constraints;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Project|mixed $value
|
||||
* @param Constraint $constraint
|
||||
*/
|
||||
public function validate($value, Constraint $constraint)
|
||||
public function validate(mixed $value, Constraint $constraint): void
|
||||
{
|
||||
if (!($constraint instanceof ProjectConstraint)) {
|
||||
throw new UnexpectedTypeException($constraint, ProjectConstraint::class);
|
||||
@@ -47,18 +41,18 @@ class ProjectValidator extends ConstraintValidator
|
||||
|
||||
$this->validateProject($value, $this->context);
|
||||
|
||||
foreach ($this->constraints as $constraint) {
|
||||
foreach ($this->constraints as $innerConstraint) {
|
||||
$this->context
|
||||
->getValidator()
|
||||
->inContext($this->context)
|
||||
->validate($value, $constraint, [Constraint::DEFAULT_GROUP]);
|
||||
->validate($value, $innerConstraint, [Constraint::DEFAULT_GROUP]);
|
||||
}
|
||||
}
|
||||
|
||||
protected function validateProject(Project $project, ExecutionContextInterface $context)
|
||||
protected function validateProject(Project $project, ExecutionContextInterface $context): void
|
||||
{
|
||||
if (null !== $project->getStart() && null !== $project->getEnd() && $project->getStart()->getTimestamp() > $project->getEnd()->getTimestamp()) {
|
||||
$context->buildViolation('End date must not be earlier then start date.')
|
||||
$context->buildViolation(ProjectConstraint::getErrorName(ProjectConstraint::END_BEFORE_BEGIN_ERROR))
|
||||
->atPath('end')
|
||||
->setTranslationDomain('validators')
|
||||
->setCode(ProjectConstraint::END_BEFORE_BEGIN_ERROR)
|
||||
|
||||
@@ -11,15 +11,11 @@ namespace App\Validator\Constraints;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target({"CLASS"})
|
||||
*/
|
||||
class QuickEntryModel extends Constraint
|
||||
final class QuickEntryModel extends Constraint
|
||||
{
|
||||
public const ACTIVITY_REQUIRED = 'quick-entry-model-01';
|
||||
public const PROJECT_REQUIRED = 'quick-entry-model-02';
|
||||
|
||||
public $messageActivityRequired = 'An activity needs to be selected.';
|
||||
public $messageProjectRequired = 'A project needs to be selected.';
|
||||
public string $messageActivityRequired = 'An activity needs to be selected.';
|
||||
public string $messageProjectRequired = 'A project needs to be selected.';
|
||||
}
|
||||
|
||||
@@ -15,12 +15,9 @@ use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
|
||||
class QuickEntryModelValidator extends ConstraintValidator
|
||||
final class QuickEntryModelValidator extends ConstraintValidator
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate($value, Constraint $constraint)
|
||||
public function validate(mixed $value, Constraint $constraint): void
|
||||
{
|
||||
if (!$constraint instanceof QuickEntryModelConstraint) {
|
||||
throw new UnexpectedTypeException($constraint, QuickEntryModelConstraint::class);
|
||||
@@ -30,7 +27,6 @@ class QuickEntryModelValidator extends ConstraintValidator
|
||||
throw new UnexpectedTypeException($value, QuickEntryModel::class);
|
||||
}
|
||||
|
||||
/** @var QuickEntryModel $model */
|
||||
$model = $value;
|
||||
|
||||
if ($model->isPrototype()) {
|
||||
|
||||
@@ -11,10 +11,6 @@ namespace App\Validator\Constraints;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target({"CLASS"})
|
||||
*/
|
||||
class QuickEntryTimesheet extends Constraint
|
||||
final class QuickEntryTimesheet extends Constraint
|
||||
{
|
||||
}
|
||||
|
||||
@@ -15,25 +15,16 @@ use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
|
||||
class QuickEntryTimesheetValidator extends ConstraintValidator
|
||||
final class QuickEntryTimesheetValidator extends ConstraintValidator
|
||||
{
|
||||
/**
|
||||
* @var Constraint[]
|
||||
*/
|
||||
private $constraints;
|
||||
|
||||
/**
|
||||
* @param Constraint[] $constraints
|
||||
*/
|
||||
public function __construct(iterable $constraints)
|
||||
public function __construct(private iterable $constraints)
|
||||
{
|
||||
$this->constraints = $constraints;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate($value, Constraint $constraint)
|
||||
public function validate(mixed $value, Constraint $constraint): void
|
||||
{
|
||||
if (!$constraint instanceof QuickEntryTimesheetConstraint) {
|
||||
throw new UnexpectedTypeException($constraint, QuickEntryTimesheetConstraint::class);
|
||||
@@ -43,19 +34,18 @@ class QuickEntryTimesheetValidator extends ConstraintValidator
|
||||
throw new UnexpectedTypeException($value, TimesheetEntity::class);
|
||||
}
|
||||
|
||||
/** @var TimesheetEntity $timesheet */
|
||||
$timesheet = $value;
|
||||
|
||||
if ($timesheet->getId() === null && $timesheet->getDuration(false) === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($this->constraints as $constraint) {
|
||||
foreach ($this->constraints as $innerConstraint) {
|
||||
$this->context
|
||||
->getValidator()
|
||||
->inContext($this->context)
|
||||
->atPath('duration')
|
||||
->validate($timesheet, $constraint, [Constraint::DEFAULT_GROUP]);
|
||||
->validate($timesheet, $innerConstraint, [Constraint::DEFAULT_GROUP]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,11 +11,8 @@ namespace App\Validator\Constraints;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
|
||||
*/
|
||||
class Role extends Constraint
|
||||
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
||||
final class Role extends Constraint
|
||||
{
|
||||
public const ROLE_ERROR = 'xd5hffg-dsfef3-426a-83d7-1f2d33hs5d84';
|
||||
|
||||
@@ -23,5 +20,5 @@ class Role extends Constraint
|
||||
self::ROLE_ERROR => 'ROLE_ERROR',
|
||||
];
|
||||
|
||||
public $message = 'This value is not a valid role.';
|
||||
public string $message = 'This value is not a valid role.';
|
||||
}
|
||||
|
||||
24
src/Validator/Constraints/RoleName.php
Normal file
24
src/Validator/Constraints/RoleName.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Validator\Constraints;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
||||
final class RoleName extends Constraint
|
||||
{
|
||||
public const ROLE_NAME_ERROR = 'xd5hffg-dsfef3-426a-83d7-1f2d33hs5d85';
|
||||
|
||||
protected static $errorNames = [
|
||||
self::ROLE_NAME_ERROR => 'ROLE_NAME_ERROR',
|
||||
];
|
||||
|
||||
public string $message = 'This value is not a valid role name.';
|
||||
}
|
||||
39
src/Validator/Constraints/RoleNameValidator.php
Normal file
39
src/Validator/Constraints/RoleNameValidator.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Validator\Constraints;
|
||||
|
||||
use App\Security\RoleService;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
|
||||
final class RoleNameValidator extends ConstraintValidator
|
||||
{
|
||||
public function __construct(private RoleService $service)
|
||||
{
|
||||
}
|
||||
|
||||
public function validate(mixed $value, Constraint $constraint): void
|
||||
{
|
||||
if (!$constraint instanceof RoleName) {
|
||||
throw new UnexpectedTypeException($constraint, RoleName::class);
|
||||
}
|
||||
|
||||
// user entity uses uppercase for the roles
|
||||
$roles = $this->service->getAvailableNames();
|
||||
|
||||
if (!\is_string($value) || \in_array($value, $roles, true) || preg_match('/^[A-Z_]{5,}$/', $value) !== 1 || str_contains($value, '__') || str_starts_with($value, '_') || str_ends_with($value, '_')) {
|
||||
$this->context->buildViolation($constraint->message)
|
||||
->setParameter('{{ value }}', $this->formatValue($value))
|
||||
->setCode(RoleName::ROLE_NAME_ERROR)
|
||||
->addViolation();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,22 +14,13 @@ use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
|
||||
class RoleValidator extends ConstraintValidator
|
||||
final class RoleValidator extends ConstraintValidator
|
||||
{
|
||||
/**
|
||||
* @var RoleService
|
||||
*/
|
||||
private $service;
|
||||
|
||||
public function __construct(RoleService $service)
|
||||
public function __construct(private RoleService $service)
|
||||
{
|
||||
$this->service = $service;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate($value, Constraint $constraint)
|
||||
public function validate(mixed $value, Constraint $constraint): void
|
||||
{
|
||||
if (!$constraint instanceof Role) {
|
||||
throw new UnexpectedTypeException($constraint, __NAMESPACE__ . '\Role');
|
||||
@@ -41,11 +32,11 @@ class RoleValidator extends ConstraintValidator
|
||||
$roles = [$roles];
|
||||
}
|
||||
|
||||
// the fos user entity uppercases the roles by default
|
||||
$allowedRoles = array_map('strtoupper', $this->service->getAvailableNames());
|
||||
// user entity uses uppercase for the roles
|
||||
$allowedRoles = $this->service->getAvailableNames();
|
||||
|
||||
foreach ($roles as $role) {
|
||||
if (!\is_string($role) || !\in_array($role, $allowedRoles)) {
|
||||
if (!\is_string($role) || !\in_array($role, $allowedRoles, true)) {
|
||||
$this->context->buildViolation($constraint->message)
|
||||
->setParameter('{{ value }}', $this->formatValue($role))
|
||||
->setCode(Role::ROLE_ERROR)
|
||||
|
||||
@@ -9,14 +9,10 @@
|
||||
|
||||
namespace App\Validator\Constraints;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation\Target;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target({"CLASS", "PROPERTY", "METHOD", "ANNOTATION"})
|
||||
*/
|
||||
class Team extends Constraint
|
||||
#[\Attribute(\Attribute::TARGET_CLASS)]
|
||||
final class Team extends Constraint
|
||||
{
|
||||
public const MISSING_TEAMLEAD = 'kimai-team-001';
|
||||
|
||||
@@ -24,9 +20,9 @@ class Team extends Constraint
|
||||
self::MISSING_TEAMLEAD => 'At least one team leader must be assigned to the team.',
|
||||
];
|
||||
|
||||
public $message = 'The team has invalid settings.';
|
||||
public string $message = 'The team has invalid settings.';
|
||||
|
||||
public function getTargets()
|
||||
public function getTargets(): string|array
|
||||
{
|
||||
return self::CLASS_CONSTRAINT;
|
||||
}
|
||||
|
||||
@@ -14,13 +14,13 @@ use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
|
||||
class TeamValidator extends ConstraintValidator
|
||||
final class TeamValidator extends ConstraintValidator
|
||||
{
|
||||
/**
|
||||
* @param TeamEntity $value
|
||||
* @param Constraint $constraint
|
||||
*/
|
||||
public function validate($value, Constraint $constraint)
|
||||
public function validate(mixed $value, Constraint $constraint): void
|
||||
{
|
||||
if (!($constraint instanceof Team)) {
|
||||
throw new UnexpectedTypeException($constraint, Team::class);
|
||||
@@ -32,7 +32,7 @@ class TeamValidator extends ConstraintValidator
|
||||
|
||||
if (!$value->hasTeamleads()) {
|
||||
$this->context->buildViolation(Team::getErrorName(Team::MISSING_TEAMLEAD))
|
||||
->atPath('teamleads')
|
||||
->atPath('members')
|
||||
->setTranslationDomain('validators')
|
||||
->setCode(Team::MISSING_TEAMLEAD)
|
||||
->addViolation();
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace App\Validator\Constraints;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
class TimeFormat extends Constraint
|
||||
final class TimeFormat extends Constraint
|
||||
{
|
||||
public const INVALID_FORMAT = 'kimai-time-00';
|
||||
|
||||
@@ -19,9 +19,9 @@ class TimeFormat extends Constraint
|
||||
self::INVALID_FORMAT => 'The given value is not a valid time.',
|
||||
];
|
||||
|
||||
public $message = 'This time format is invalid.';
|
||||
public string $message = 'This time format is invalid.';
|
||||
|
||||
public function getTargets()
|
||||
public function getTargets(): string|array
|
||||
{
|
||||
return self::PROPERTY_CONSTRAINT;
|
||||
}
|
||||
|
||||
@@ -14,13 +14,13 @@ use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedValueException;
|
||||
|
||||
class TimeFormatValidator extends ConstraintValidator
|
||||
final class TimeFormatValidator extends ConstraintValidator
|
||||
{
|
||||
/**
|
||||
* @param string|mixed $value
|
||||
* @param Constraint $constraint
|
||||
*/
|
||||
public function validate($value, Constraint $constraint)
|
||||
public function validate(mixed $value, Constraint $constraint): void
|
||||
{
|
||||
if (!($constraint instanceof TimeFormat)) {
|
||||
throw new UnexpectedTypeException($constraint, __NAMESPACE__ . '\TimeFormat');
|
||||
@@ -30,7 +30,7 @@ class TimeFormatValidator extends ConstraintValidator
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_scalar($value) && !(\is_object($value) && method_exists($value, '__toString'))) {
|
||||
if (!\is_scalar($value) && !(\is_object($value) && method_exists($value, '__toString'))) {
|
||||
throw new UnexpectedValueException($value, 'string');
|
||||
}
|
||||
|
||||
|
||||
@@ -9,52 +9,14 @@
|
||||
|
||||
namespace App\Validator\Constraints;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation\Target;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target({"CLASS", "PROPERTY", "METHOD", "ANNOTATION"})
|
||||
*/
|
||||
class Timesheet extends Constraint
|
||||
#[\Attribute(\Attribute::TARGET_CLASS)]
|
||||
final class Timesheet extends Constraint
|
||||
{
|
||||
/** @deprecated since 1.15.3 - use TimesheetBasic::MISSING_BEGIN_ERROR instead */
|
||||
public const MISSING_BEGIN_ERROR = TimesheetBasic::MISSING_BEGIN_ERROR;
|
||||
/** @deprecated since 1.15.3 - use TimesheetBasic::END_BEFORE_BEGIN_ERROR instead */
|
||||
public const END_BEFORE_BEGIN_ERROR = TimesheetBasic::END_BEFORE_BEGIN_ERROR;
|
||||
/** @deprecated since 1.15.3 - use TimesheetBasic::MISSING_ACTIVITY_ERROR instead */
|
||||
public const MISSING_ACTIVITY_ERROR = TimesheetBasic::MISSING_ACTIVITY_ERROR;
|
||||
/** @deprecated since 1.15.3 - use TimesheetBasic::MISSING_PROJECT_ERROR instead */
|
||||
public const MISSING_PROJECT_ERROR = TimesheetBasic::MISSING_PROJECT_ERROR;
|
||||
/** @deprecated since 1.15.3 - use TimesheetBasic::ACTIVITY_PROJECT_MISMATCH_ERROR instead */
|
||||
public const ACTIVITY_PROJECT_MISMATCH_ERROR = TimesheetBasic::ACTIVITY_PROJECT_MISMATCH_ERROR;
|
||||
/** @deprecated since 1.15.3 - use TimesheetBasic::DISABLED_ACTIVITY_ERROR instead */
|
||||
public const DISABLED_ACTIVITY_ERROR = TimesheetBasic::DISABLED_ACTIVITY_ERROR;
|
||||
/** @deprecated since 1.15.3 - use TimesheetBasic::DISABLED_PROJECT_ERROR instead */
|
||||
public const DISABLED_PROJECT_ERROR = TimesheetBasic::DISABLED_PROJECT_ERROR;
|
||||
/** @deprecated since 1.15.3 - use TimesheetBasic::DISABLED_CUSTOMER_ERROR instead */
|
||||
public const DISABLED_CUSTOMER_ERROR = TimesheetBasic::DISABLED_CUSTOMER_ERROR;
|
||||
/** @deprecated since 1.15.3 - use TimesheetBasic::PROJECT_NOT_STARTED instead */
|
||||
public const PROJECT_NOT_STARTED = TimesheetBasic::PROJECT_NOT_STARTED;
|
||||
/** @deprecated since 1.15.3 - use TimesheetBasic::PROJECT_ALREADY_ENDED instead */
|
||||
public const PROJECT_ALREADY_ENDED = TimesheetBasic::PROJECT_ALREADY_ENDED;
|
||||
public string $message = 'This timesheet has invalid settings.';
|
||||
|
||||
protected static $errorNames = [
|
||||
self::MISSING_BEGIN_ERROR => 'You must submit a begin date.',
|
||||
self::END_BEFORE_BEGIN_ERROR => 'End date must not be earlier then start date.',
|
||||
self::MISSING_ACTIVITY_ERROR => 'An activity needs to be selected.',
|
||||
self::MISSING_PROJECT_ERROR => 'A project needs to be selected.',
|
||||
self::ACTIVITY_PROJECT_MISMATCH_ERROR => 'Project mismatch, project specific activity and timesheet project are different.',
|
||||
self::DISABLED_ACTIVITY_ERROR => 'Cannot start a disabled activity.',
|
||||
self::DISABLED_PROJECT_ERROR => 'Cannot start a disabled project.',
|
||||
self::DISABLED_CUSTOMER_ERROR => 'Cannot start a disabled customer.',
|
||||
self::PROJECT_NOT_STARTED => 'The project has not started at that time.',
|
||||
self::PROJECT_ALREADY_ENDED => 'The project is finished at that time.',
|
||||
];
|
||||
|
||||
public $message = 'This timesheet has invalid settings.';
|
||||
|
||||
public function getTargets()
|
||||
public function getTargets(): string|array
|
||||
{
|
||||
return self::CLASS_CONSTRAINT;
|
||||
}
|
||||
|
||||
@@ -9,13 +9,7 @@
|
||||
|
||||
namespace App\Validator\Constraints;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation\Target;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target({"CLASS", "PROPERTY", "METHOD", "ANNOTATION"})
|
||||
*/
|
||||
class TimesheetBasic extends TimesheetConstraint
|
||||
final class TimesheetBasic extends TimesheetConstraint
|
||||
{
|
||||
public const MISSING_BEGIN_ERROR = 'kimai-timesheet-81';
|
||||
public const END_BEFORE_BEGIN_ERROR = 'kimai-timesheet-82';
|
||||
@@ -43,9 +37,9 @@ class TimesheetBasic extends TimesheetConstraint
|
||||
self::PROJECT_DISALLOWS_GLOBAL_ACTIVITY => 'Global activities are forbidden for the selected project.',
|
||||
];
|
||||
|
||||
public $message = 'This timesheet has invalid settings.';
|
||||
public string $message = 'This timesheet has invalid settings.';
|
||||
|
||||
public function getTargets()
|
||||
public function getTargets(): string|array
|
||||
{
|
||||
return self::CLASS_CONSTRAINT;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
namespace App\Validator\Constraints;
|
||||
|
||||
use App\Configuration\SystemConfiguration;
|
||||
use App\Entity\Timesheet as TimesheetEntity;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
@@ -17,36 +18,36 @@ use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
|
||||
final class TimesheetBasicValidator extends ConstraintValidator
|
||||
{
|
||||
/**
|
||||
* @param TimesheetEntity $timesheet
|
||||
* @param Constraint $constraint
|
||||
*/
|
||||
public function validate($timesheet, Constraint $constraint)
|
||||
public function __construct(private SystemConfiguration $systemConfiguration)
|
||||
{
|
||||
}
|
||||
|
||||
public function validate(mixed $value, Constraint $constraint): void
|
||||
{
|
||||
if (!($constraint instanceof TimesheetBasic)) {
|
||||
throw new UnexpectedTypeException($constraint, TimesheetBasic::class);
|
||||
}
|
||||
|
||||
if (!\is_object($timesheet) || !($timesheet instanceof TimesheetEntity)) {
|
||||
throw new UnexpectedTypeException($timesheet, TimesheetEntity::class);
|
||||
if (!\is_object($value) || !($value instanceof TimesheetEntity)) {
|
||||
throw new UnexpectedTypeException($value, TimesheetEntity::class);
|
||||
}
|
||||
|
||||
$this->validateBeginAndEnd($timesheet, $this->context);
|
||||
$this->validateActivityAndProject($timesheet, $this->context);
|
||||
$this->validateBeginAndEnd($value, $this->context);
|
||||
$this->validateActivityAndProject($value, $this->context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TimesheetEntity $timesheet
|
||||
* @param ExecutionContextInterface $context
|
||||
*/
|
||||
protected function validateBeginAndEnd(TimesheetEntity $timesheet, ExecutionContextInterface $context)
|
||||
protected function validateBeginAndEnd(TimesheetEntity $timesheet, ExecutionContextInterface $context): void
|
||||
{
|
||||
$begin = $timesheet->getBegin();
|
||||
$end = $timesheet->getEnd();
|
||||
|
||||
if (null === $begin) {
|
||||
$context->buildViolation('You must submit a begin date.')
|
||||
->atPath('begin')
|
||||
$context->buildViolation(TimesheetBasic::getErrorName(TimesheetBasic::MISSING_BEGIN_ERROR))
|
||||
->atPath('begin_date')
|
||||
->setTranslationDomain('validators')
|
||||
->setCode(TimesheetBasic::MISSING_BEGIN_ERROR)
|
||||
->addViolation();
|
||||
@@ -55,8 +56,8 @@ final class TimesheetBasicValidator extends ConstraintValidator
|
||||
}
|
||||
|
||||
if (null !== $end && $begin > $end) {
|
||||
$context->buildViolation('End date must not be earlier then start date.')
|
||||
->atPath('end')
|
||||
$context->buildViolation(TimesheetBasic::getErrorName(TimesheetBasic::END_BEFORE_BEGIN_ERROR))
|
||||
->atPath('end_date')
|
||||
->setTranslationDomain('validators')
|
||||
->setCode(TimesheetBasic::END_BEFORE_BEGIN_ERROR)
|
||||
->addViolation();
|
||||
@@ -67,10 +68,12 @@ final class TimesheetBasicValidator extends ConstraintValidator
|
||||
* @param TimesheetEntity $timesheet
|
||||
* @param ExecutionContextInterface $context
|
||||
*/
|
||||
protected function validateActivityAndProject(TimesheetEntity $timesheet, ExecutionContextInterface $context)
|
||||
protected function validateActivityAndProject(TimesheetEntity $timesheet, ExecutionContextInterface $context): void
|
||||
{
|
||||
if (null === ($activity = $timesheet->getActivity())) {
|
||||
$context->buildViolation('An activity needs to be selected.')
|
||||
$activity = $timesheet->getActivity();
|
||||
|
||||
if ($this->systemConfiguration->isTimesheetRequiresActivity() && null === $activity) {
|
||||
$context->buildViolation(TimesheetBasic::getErrorName(TimesheetBasic::MISSING_ACTIVITY_ERROR))
|
||||
->atPath('activity')
|
||||
->setTranslationDomain('validators')
|
||||
->setCode(TimesheetBasic::MISSING_ACTIVITY_ERROR)
|
||||
@@ -78,19 +81,21 @@ final class TimesheetBasicValidator extends ConstraintValidator
|
||||
}
|
||||
|
||||
if (null === ($project = $timesheet->getProject())) {
|
||||
$context->buildViolation('A project needs to be selected.')
|
||||
$context->buildViolation(TimesheetBasic::getErrorName(TimesheetBasic::MISSING_PROJECT_ERROR))
|
||||
->atPath('project')
|
||||
->setTranslationDomain('validators')
|
||||
->setCode(TimesheetBasic::MISSING_PROJECT_ERROR)
|
||||
->addViolation();
|
||||
}
|
||||
|
||||
if (null === $activity || null === $project) {
|
||||
$hasActivity = null !== $activity;
|
||||
|
||||
if (null === $project) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (null !== $activity->getProject() && $activity->getProject() !== $project) {
|
||||
$context->buildViolation('Project mismatch, project specific activity and timesheet project are different.')
|
||||
if ($hasActivity && null !== $activity->getProject() && $activity->getProject() !== $project) {
|
||||
$context->buildViolation(TimesheetBasic::getErrorName(TimesheetBasic::ACTIVITY_PROJECT_MISMATCH_ERROR))
|
||||
->atPath('project')
|
||||
->setTranslationDomain('validators')
|
||||
->setCode(TimesheetBasic::ACTIVITY_PROJECT_MISMATCH_ERROR)
|
||||
@@ -100,8 +105,8 @@ final class TimesheetBasicValidator extends ConstraintValidator
|
||||
$timesheetEnd = $timesheet->getEnd();
|
||||
$newOrStarted = null === $timesheetEnd || $timesheet->getId() === null;
|
||||
|
||||
if ($newOrStarted && !$activity->isVisible()) {
|
||||
$context->buildViolation('Cannot start a disabled activity.')
|
||||
if ($newOrStarted && $hasActivity && !$activity->isVisible()) {
|
||||
$context->buildViolation(TimesheetBasic::getErrorName(TimesheetBasic::DISABLED_ACTIVITY_ERROR))
|
||||
->atPath('activity')
|
||||
->setTranslationDomain('validators')
|
||||
->setCode(TimesheetBasic::DISABLED_ACTIVITY_ERROR)
|
||||
@@ -109,7 +114,7 @@ final class TimesheetBasicValidator extends ConstraintValidator
|
||||
}
|
||||
|
||||
if ($newOrStarted && !$project->isVisible()) {
|
||||
$context->buildViolation('Cannot start a disabled project.')
|
||||
$context->buildViolation(TimesheetBasic::getErrorName(TimesheetBasic::DISABLED_PROJECT_ERROR))
|
||||
->atPath('project')
|
||||
->setTranslationDomain('validators')
|
||||
->setCode(TimesheetBasic::DISABLED_PROJECT_ERROR)
|
||||
@@ -117,21 +122,24 @@ final class TimesheetBasicValidator extends ConstraintValidator
|
||||
}
|
||||
|
||||
if ($newOrStarted && !$project->getCustomer()->isVisible()) {
|
||||
$context->buildViolation('Cannot start a disabled customer.')
|
||||
$context->buildViolation(TimesheetBasic::getErrorName(TimesheetBasic::DISABLED_CUSTOMER_ERROR))
|
||||
->atPath('customer')
|
||||
->setTranslationDomain('validators')
|
||||
->setCode(TimesheetBasic::DISABLED_CUSTOMER_ERROR)
|
||||
->addViolation();
|
||||
}
|
||||
|
||||
if (!$project->isGlobalActivities() && $activity->isGlobal()) {
|
||||
$context->buildViolation('Global activities are forbidden for the selected project.')
|
||||
if ($hasActivity && !$project->isGlobalActivities() && $activity->isGlobal()) {
|
||||
$context->buildViolation(TimesheetBasic::getErrorName(TimesheetBasic::PROJECT_DISALLOWS_GLOBAL_ACTIVITY))
|
||||
->atPath('activity')
|
||||
->setTranslationDomain('validators')
|
||||
->setCode(TimesheetBasic::PROJECT_DISALLOWS_GLOBAL_ACTIVITY)
|
||||
->addViolation();
|
||||
}
|
||||
|
||||
$pathStart = 'begin_date';
|
||||
$pathEnd = 'end_date';
|
||||
|
||||
$projectBegin = $project->getStart();
|
||||
$projectEnd = $project->getEnd();
|
||||
|
||||
@@ -139,21 +147,18 @@ final class TimesheetBasicValidator extends ConstraintValidator
|
||||
return;
|
||||
}
|
||||
|
||||
$pathStart = 'begin';
|
||||
$pathEnd = 'end';
|
||||
|
||||
$timesheetStart = $timesheet->getBegin();
|
||||
$timesheetEnd = $timesheet->getEnd();
|
||||
|
||||
if (null !== $timesheetStart) {
|
||||
if (null !== $projectBegin && $timesheetStart->getTimestamp() < $projectBegin->getTimestamp()) {
|
||||
$context->buildViolation('The project has not started at that time.')
|
||||
$context->buildViolation(TimesheetBasic::getErrorName(TimesheetBasic::PROJECT_NOT_STARTED))
|
||||
->atPath($pathStart)
|
||||
->setTranslationDomain('validators')
|
||||
->setCode(TimesheetBasic::PROJECT_NOT_STARTED)
|
||||
->addViolation();
|
||||
} elseif (null !== $projectEnd && $timesheetStart->getTimestamp() > $projectEnd->getTimestamp()) {
|
||||
$context->buildViolation('The project is finished at that time.')
|
||||
$context->buildViolation(TimesheetBasic::getErrorName(TimesheetBasic::PROJECT_ALREADY_ENDED))
|
||||
->atPath($pathStart)
|
||||
->setTranslationDomain('validators')
|
||||
->setCode(TimesheetBasic::PROJECT_ALREADY_ENDED)
|
||||
@@ -163,13 +168,13 @@ final class TimesheetBasicValidator extends ConstraintValidator
|
||||
|
||||
if (null !== $timesheetEnd) {
|
||||
if (null !== $projectEnd && $timesheetEnd->getTimestamp() > $projectEnd->getTimestamp()) {
|
||||
$context->buildViolation('The project is finished at that time.')
|
||||
$context->buildViolation(TimesheetBasic::getErrorName(TimesheetBasic::PROJECT_ALREADY_ENDED))
|
||||
->atPath($pathEnd)
|
||||
->setTranslationDomain('validators')
|
||||
->setCode(TimesheetBasic::PROJECT_ALREADY_ENDED)
|
||||
->addViolation();
|
||||
} elseif (null !== $projectBegin && $timesheetEnd->getTimestamp() < $projectBegin->getTimestamp()) {
|
||||
$context->buildViolation('The project has not started at that time.')
|
||||
$context->buildViolation(TimesheetBasic::getErrorName(TimesheetBasic::PROJECT_NOT_STARTED))
|
||||
->atPath($pathEnd)
|
||||
->setTranslationDomain('validators')
|
||||
->setCode(TimesheetBasic::PROJECT_NOT_STARTED)
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace App\Validator\Constraints;
|
||||
final class TimesheetBudgetUsed extends TimesheetConstraint
|
||||
{
|
||||
// same messages, so we can re-use the validation translation!
|
||||
public $messageRate = 'The budget is completely used.';
|
||||
public $messageTime = 'The budget is completely used.';
|
||||
public $messagePermission = 'Sorry, the budget is used up.';
|
||||
public string $messageRate = 'The budget is completely used.';
|
||||
public string $messageTime = 'The budget is completely used.';
|
||||
public string $messagePermission = 'Sorry, the budget is used up.';
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
namespace App\Validator\Constraints;
|
||||
|
||||
use App\Activity\ActivityStatisticService;
|
||||
use App\Configuration\LocaleService;
|
||||
use App\Configuration\SystemConfiguration;
|
||||
use App\Customer\CustomerStatisticService;
|
||||
use App\Entity\Timesheet;
|
||||
@@ -18,7 +19,7 @@ use App\Project\ProjectStatisticService;
|
||||
use App\Repository\TimesheetRepository;
|
||||
use App\Timesheet\RateServiceInterface;
|
||||
use App\Utils\Duration;
|
||||
use App\Utils\LocaleHelper;
|
||||
use App\Utils\LocaleFormatter;
|
||||
use DateTime;
|
||||
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
@@ -27,30 +28,23 @@ use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
|
||||
final class TimesheetBudgetUsedValidator extends ConstraintValidator
|
||||
{
|
||||
private $customerStatisticService;
|
||||
private $projectStatisticService;
|
||||
private $activityStatisticService;
|
||||
private $timesheetRepository;
|
||||
private $rateService;
|
||||
private $configuration;
|
||||
private $security;
|
||||
|
||||
public function __construct(SystemConfiguration $configuration, CustomerStatisticService $customerStatisticService, ProjectStatisticService $projectStatisticService, ActivityStatisticService $activityStatisticService, TimesheetRepository $timesheetRepository, RateServiceInterface $rateService, AuthorizationCheckerInterface $security)
|
||||
{
|
||||
$this->configuration = $configuration;
|
||||
$this->customerStatisticService = $customerStatisticService;
|
||||
$this->projectStatisticService = $projectStatisticService;
|
||||
$this->activityStatisticService = $activityStatisticService;
|
||||
$this->timesheetRepository = $timesheetRepository;
|
||||
$this->rateService = $rateService;
|
||||
$this->security = $security;
|
||||
public function __construct(
|
||||
private SystemConfiguration $configuration,
|
||||
private CustomerStatisticService $customerStatisticService,
|
||||
private ProjectStatisticService $projectStatisticService,
|
||||
private ActivityStatisticService $activityStatisticService,
|
||||
private TimesheetRepository $timesheetRepository,
|
||||
private RateServiceInterface $rateService,
|
||||
private AuthorizationCheckerInterface $security,
|
||||
private LocaleService $localeService
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Timesheet $timesheet
|
||||
* @param Constraint $constraint
|
||||
*/
|
||||
public function validate($timesheet, Constraint $constraint)
|
||||
public function validate(mixed $timesheet, Constraint $constraint): void
|
||||
{
|
||||
if (!($constraint instanceof TimesheetBudgetUsed)) {
|
||||
throw new UnexpectedTypeException($constraint, TimesheetBudgetUsed::class);
|
||||
@@ -193,10 +187,10 @@ final class TimesheetBudgetUsedValidator extends ConstraintValidator
|
||||
return false;
|
||||
}
|
||||
|
||||
private function addBudgetViolation(TimesheetBudgetUsed $constraint, Timesheet $timesheet, string $field, float $budget, float $rate)
|
||||
private function addBudgetViolation(TimesheetBudgetUsed $constraint, Timesheet $timesheet, string $field, float $budget, float $rate): void
|
||||
{
|
||||
// using the locale of the assigned user is not the best solution, but allows to be independent of the request stack
|
||||
$helper = new LocaleHelper($timesheet->getUser()->getLanguage());
|
||||
$helper = new LocaleFormatter($this->localeService, $timesheet->getUser()->getLanguage());
|
||||
$currency = $timesheet->getProject()->getCustomer()->getCurrency();
|
||||
|
||||
$free = $budget - $rate;
|
||||
@@ -219,7 +213,7 @@ final class TimesheetBudgetUsedValidator extends ConstraintValidator
|
||||
;
|
||||
}
|
||||
|
||||
private function addTimeBudgetViolation(TimesheetBudgetUsed $constraint, string $field, int $budget, int $duration)
|
||||
private function addTimeBudgetViolation(TimesheetBudgetUsed $constraint, string $field, int $budget, int $duration): void
|
||||
{
|
||||
$durationFormat = new Duration();
|
||||
|
||||
|
||||
@@ -17,13 +17,14 @@ final class TimesheetExported extends TimesheetConstraint
|
||||
self::TIMESHEET_EXPORTED => 'This timesheet is already exported.',
|
||||
];
|
||||
|
||||
public $message = 'This timesheet is already exported.';
|
||||
public string $message = 'This timesheet is already exported.';
|
||||
|
||||
/**
|
||||
* @var \DateTime|string|null
|
||||
*/
|
||||
public $now;
|
||||
public null|\DateTime|string $now;
|
||||
|
||||
public function getTargets()
|
||||
public function getTargets(): string|array
|
||||
{
|
||||
return self::CLASS_CONSTRAINT;
|
||||
}
|
||||
|
||||
@@ -17,18 +17,15 @@ use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
|
||||
final class TimesheetExportedValidator extends ConstraintValidator
|
||||
{
|
||||
private $security;
|
||||
|
||||
public function __construct(Security $security)
|
||||
public function __construct(private Security $security)
|
||||
{
|
||||
$this->security = $security;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TimesheetEntity $timesheet
|
||||
* @param Constraint $constraint
|
||||
*/
|
||||
public function validate($timesheet, Constraint $constraint)
|
||||
public function validate(mixed $timesheet, Constraint $constraint): void
|
||||
{
|
||||
if (!($constraint instanceof TimesheetExported)) {
|
||||
throw new UnexpectedTypeException($constraint, TimesheetExported::class);
|
||||
@@ -47,7 +44,7 @@ final class TimesheetExportedValidator extends ConstraintValidator
|
||||
}
|
||||
|
||||
// this was "edit_exported_timesheet" before, but that was wrong, because the first time this
|
||||
// can trigger is the moment when the "export" flag ist set from the "edit form".
|
||||
// can trigger is right when the "export" flag ist set from the "edit form".
|
||||
// most teamleads should not have "edit_exported_timesheet" but only "edit_export_other_timesheet"
|
||||
|
||||
if (null !== $this->security->getUser() && $this->security->isGranted('edit_export', $timesheet)) {
|
||||
|
||||
@@ -17,9 +17,9 @@ final class TimesheetFutureTimes extends TimesheetConstraint
|
||||
self::BEGIN_IN_FUTURE_ERROR => 'The begin date cannot be in the future.',
|
||||
];
|
||||
|
||||
public $message = 'The begin date cannot be in the future.';
|
||||
public string $message = 'The begin date cannot be in the future.';
|
||||
|
||||
public function getTargets()
|
||||
public function getTargets(): string|array
|
||||
{
|
||||
return self::CLASS_CONSTRAINT;
|
||||
}
|
||||
|
||||
@@ -17,21 +17,15 @@ use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
|
||||
final class TimesheetFutureTimesValidator extends ConstraintValidator
|
||||
{
|
||||
/**
|
||||
* @var SystemConfiguration
|
||||
*/
|
||||
private $configuration;
|
||||
|
||||
public function __construct(SystemConfiguration $configuration)
|
||||
public function __construct(private SystemConfiguration $configuration)
|
||||
{
|
||||
$this->configuration = $configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TimesheetEntity $timesheet
|
||||
* @param Constraint $constraint
|
||||
*/
|
||||
public function validate($timesheet, Constraint $constraint)
|
||||
public function validate(mixed $timesheet, Constraint $constraint): void
|
||||
{
|
||||
if (!($constraint instanceof TimesheetFutureTimes)) {
|
||||
throw new UnexpectedTypeException($constraint, TimesheetFutureTimes::class);
|
||||
@@ -51,7 +45,7 @@ final class TimesheetFutureTimesValidator extends ConstraintValidator
|
||||
$allowedDiff = ($this->configuration->getTimesheetDefaultRoundingBegin() * 60) + 60;
|
||||
if (($now->getTimestamp() + $allowedDiff) < $timesheet->getBegin()->getTimestamp()) {
|
||||
$this->context->buildViolation('The begin date cannot be in the future.')
|
||||
->atPath('begin')
|
||||
->atPath('begin_date')
|
||||
->setTranslationDomain('validators')
|
||||
->setCode(TimesheetFutureTimes::BEGIN_IN_FUTURE_ERROR)
|
||||
->addViolation();
|
||||
|
||||
@@ -17,13 +17,13 @@ final class TimesheetLockdown extends TimesheetConstraint
|
||||
self::PERIOD_LOCKED => 'This period is locked, please choose a later date.',
|
||||
];
|
||||
|
||||
public $message = 'This period is locked, please choose a later date.';
|
||||
public string $message = 'This period is locked, please choose a later date.';
|
||||
/**
|
||||
* @var \DateTime|string|null
|
||||
*/
|
||||
public $now;
|
||||
public \DateTime|string|null $now;
|
||||
|
||||
public function getTargets()
|
||||
public function getTargets(): string|array
|
||||
{
|
||||
return self::CLASS_CONSTRAINT;
|
||||
}
|
||||
|
||||
@@ -18,20 +18,15 @@ use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
|
||||
final class TimesheetLockdownValidator extends ConstraintValidator
|
||||
{
|
||||
private $lockdownService;
|
||||
private $security;
|
||||
|
||||
public function __construct(Security $security, LockdownService $lockdownService)
|
||||
public function __construct(private Security $security, private LockdownService $lockdownService)
|
||||
{
|
||||
$this->security = $security;
|
||||
$this->lockdownService = $lockdownService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TimesheetEntity $timesheet
|
||||
* @param Constraint $constraint
|
||||
*/
|
||||
public function validate($timesheet, Constraint $constraint)
|
||||
public function validate(mixed $timesheet, Constraint $constraint): void
|
||||
{
|
||||
if (!($constraint instanceof TimesheetLockdown)) {
|
||||
throw new UnexpectedTypeException($constraint, TimesheetLockdown::class);
|
||||
@@ -78,7 +73,7 @@ final class TimesheetLockdownValidator extends ConstraintValidator
|
||||
|
||||
// raise a violation for all entries before the start of lockdown period
|
||||
$this->context->buildViolation('This period is locked, please choose a later date.')
|
||||
->atPath('begin')
|
||||
->atPath('begin_date')
|
||||
->setTranslationDomain('validators')
|
||||
->setCode(TimesheetLockdown::PERIOD_LOCKED)
|
||||
->addViolation();
|
||||
|
||||
@@ -19,10 +19,10 @@ final class TimesheetLongRunning extends TimesheetConstraint
|
||||
self::MAXIMUM => 'MAXIMUM',
|
||||
];
|
||||
|
||||
public $message = 'Maximum duration of {{ value }} hours exceeded.';
|
||||
public $maximumMessage = 'Maximum duration exceeded.';
|
||||
public string $message = 'Maximum duration of {{ value }} hours exceeded.';
|
||||
public string $maximumMessage = 'Maximum duration exceeded.';
|
||||
|
||||
public function getTargets()
|
||||
public function getTargets(): string|array
|
||||
{
|
||||
return self::CLASS_CONSTRAINT;
|
||||
}
|
||||
|
||||
@@ -17,18 +17,15 @@ use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
|
||||
final class TimesheetLongRunningValidator extends ConstraintValidator
|
||||
{
|
||||
private $systemConfiguration;
|
||||
|
||||
public function __construct(SystemConfiguration $systemConfiguration)
|
||||
public function __construct(private SystemConfiguration $systemConfiguration)
|
||||
{
|
||||
$this->systemConfiguration = $systemConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TimesheetEntity $timesheet
|
||||
* @param Constraint $constraint
|
||||
*/
|
||||
public function validate($timesheet, Constraint $constraint)
|
||||
public function validate(mixed $timesheet, Constraint $constraint): void
|
||||
{
|
||||
if (!($constraint instanceof TimesheetLongRunning)) {
|
||||
throw new UnexpectedTypeException($constraint, TimesheetLongRunning::class);
|
||||
@@ -64,14 +61,14 @@ final class TimesheetLongRunningValidator extends ConstraintValidator
|
||||
// float on purpose, because one second more than the configured minutes is already too long
|
||||
$minutes = $duration / 60;
|
||||
|
||||
if ($minutes < $maxMinutes) {
|
||||
// allow maximum of the exact configured minutes
|
||||
if ($minutes <= $maxMinutes) {
|
||||
return;
|
||||
}
|
||||
|
||||
$format = new \App\Utils\Duration();
|
||||
$hours = $format->format($maxMinutes * 60);
|
||||
|
||||
// raise a violation for all entries before the start of lockdown period
|
||||
$this->context->buildViolation($constraint->message)
|
||||
->setParameter('{{ value }}', $hours)
|
||||
->setTranslationDomain('validators')
|
||||
|
||||
@@ -9,14 +9,10 @@
|
||||
|
||||
namespace App\Validator\Constraints;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation\Target;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target({"CLASS", "PROPERTY", "METHOD", "ANNOTATION"})
|
||||
*/
|
||||
class TimesheetMultiUpdate extends Constraint
|
||||
#[\Attribute(\Attribute::TARGET_CLASS)]
|
||||
final class TimesheetMultiUpdate extends Constraint
|
||||
{
|
||||
public const MISSING_ACTIVITY_ERROR = 'ts-multi-update-84';
|
||||
public const MISSING_PROJECT_ERROR = 'ts-multi-update-85';
|
||||
@@ -36,9 +32,9 @@ class TimesheetMultiUpdate extends Constraint
|
||||
self::HOURLY_RATE_FIXED_RATE => 'Cannot set hourly rate and fixed rate at the same time.',
|
||||
];
|
||||
|
||||
public $message = 'This form has invalid settings.';
|
||||
public string $message = 'This form has invalid settings.';
|
||||
|
||||
public function getTargets()
|
||||
public function getTargets(): string|array
|
||||
{
|
||||
return self::CLASS_CONSTRAINT;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ final class TimesheetMultiUpdateValidator extends ConstraintValidator
|
||||
* @param TimesheetMultiUpdateDTO|mixed $value
|
||||
* @param Constraint $constraint
|
||||
*/
|
||||
public function validate($value, Constraint $constraint)
|
||||
public function validate(mixed $value, Constraint $constraint): void
|
||||
{
|
||||
if (!($constraint instanceof TimesheetMultiUpdateConstraint)) {
|
||||
throw new UnexpectedTypeException($constraint, __NAMESPACE__ . '\TimesheetMultiUpdate');
|
||||
@@ -53,7 +53,7 @@ final class TimesheetMultiUpdateValidator extends ConstraintValidator
|
||||
* @param TimesheetMultiUpdateDTO $dto
|
||||
* @param ExecutionContextInterface $context
|
||||
*/
|
||||
protected function validateActivityAndProject(TimesheetMultiUpdateDTO $dto, ExecutionContextInterface $context)
|
||||
protected function validateActivityAndProject(TimesheetMultiUpdateDTO $dto, ExecutionContextInterface $context): void
|
||||
{
|
||||
$activity = $dto->getActivity();
|
||||
$project = $dto->getProject();
|
||||
|
||||
@@ -9,14 +9,10 @@
|
||||
|
||||
namespace App\Validator\Constraints;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation\Target;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target({"CLASS", "PROPERTY", "METHOD", "ANNOTATION"})
|
||||
*/
|
||||
class TimesheetMultiUser extends Constraint
|
||||
#[\Attribute(\Attribute::TARGET_CLASS)]
|
||||
final class TimesheetMultiUser extends Constraint
|
||||
{
|
||||
public const MISSING_USER_OR_TEAM = 'ts-multi-user-01';
|
||||
|
||||
@@ -24,9 +20,9 @@ class TimesheetMultiUser extends Constraint
|
||||
self::MISSING_USER_OR_TEAM => 'You must select at least one user or team.',
|
||||
];
|
||||
|
||||
public $message = 'This form has invalid settings.';
|
||||
public string $message = 'This form has invalid settings.';
|
||||
|
||||
public function getTargets()
|
||||
public function getTargets(): string|array
|
||||
{
|
||||
return self::CLASS_CONSTRAINT;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ final class TimesheetMultiUserValidator extends ConstraintValidator
|
||||
* @param Timesheet|mixed $value
|
||||
* @param Constraint $constraint
|
||||
*/
|
||||
public function validate($value, Constraint $constraint)
|
||||
public function validate(mixed $value, Constraint $constraint): void
|
||||
{
|
||||
if (!($constraint instanceof TimesheetMultiUser)) {
|
||||
throw new UnexpectedTypeException($constraint, TimesheetMultiUser::class);
|
||||
|
||||
@@ -17,9 +17,9 @@ final class TimesheetOverlapping extends TimesheetConstraint
|
||||
self::RECORD_OVERLAPPING => 'You already have an entry for this time.',
|
||||
];
|
||||
|
||||
public $message = 'You already have an entry for this time.';
|
||||
public string $message = 'You already have an entry for this time.';
|
||||
|
||||
public function getTargets()
|
||||
public function getTargets(): string|array
|
||||
{
|
||||
return self::CLASS_CONSTRAINT;
|
||||
}
|
||||
|
||||
@@ -18,26 +18,15 @@ use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
|
||||
final class TimesheetOverlappingValidator extends ConstraintValidator
|
||||
{
|
||||
/**
|
||||
* @var SystemConfiguration
|
||||
*/
|
||||
private $configuration;
|
||||
/**
|
||||
* @var TimesheetRepository
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
public function __construct(SystemConfiguration $configuration, TimesheetRepository $repository)
|
||||
public function __construct(private SystemConfiguration $configuration, private TimesheetRepository $repository)
|
||||
{
|
||||
$this->configuration = $configuration;
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TimesheetEntity $timesheet
|
||||
* @param Constraint $constraint
|
||||
*/
|
||||
public function validate($timesheet, Constraint $constraint)
|
||||
public function validate(mixed $timesheet, Constraint $constraint): void
|
||||
{
|
||||
if (!($constraint instanceof TimesheetOverlapping)) {
|
||||
throw new UnexpectedTypeException($constraint, TimesheetOverlapping::class);
|
||||
@@ -64,7 +53,7 @@ final class TimesheetOverlappingValidator extends ConstraintValidator
|
||||
}
|
||||
|
||||
$this->context->buildViolation('You already have an entry for this time.')
|
||||
->atPath('begin')
|
||||
->atPath('begin_date')
|
||||
->setTranslationDomain('validators')
|
||||
->setCode(TimesheetOverlapping::RECORD_OVERLAPPING)
|
||||
->addViolation();
|
||||
|
||||
@@ -17,9 +17,9 @@ final class TimesheetRestart extends TimesheetConstraint
|
||||
self::START_DISALLOWED => 'You are not allowed to start this timesheet record.',
|
||||
];
|
||||
|
||||
public $message = 'You are not allowed to start this timesheet record.';
|
||||
public string $message = 'You are not allowed to start this timesheet record.';
|
||||
|
||||
public function getTargets()
|
||||
public function getTargets(): string|array
|
||||
{
|
||||
return self::CLASS_CONSTRAINT;
|
||||
}
|
||||
|
||||
@@ -18,20 +18,15 @@ use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
|
||||
final class TimesheetRestartValidator extends ConstraintValidator
|
||||
{
|
||||
private $trackingModeService;
|
||||
private $security;
|
||||
|
||||
public function __construct(Security $security, TrackingModeService $service)
|
||||
public function __construct(private Security $security, private TrackingModeService $trackingModeService)
|
||||
{
|
||||
$this->security = $security;
|
||||
$this->trackingModeService = $service;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TimesheetEntity $timesheet
|
||||
* @param Constraint $constraint
|
||||
*/
|
||||
public function validate($timesheet, Constraint $constraint)
|
||||
public function validate(mixed $timesheet, Constraint $constraint): void
|
||||
{
|
||||
if (!($constraint instanceof TimesheetRestart)) {
|
||||
throw new UnexpectedTypeException($constraint, TimesheetRestart::class);
|
||||
@@ -57,10 +52,10 @@ final class TimesheetRestartValidator extends ConstraintValidator
|
||||
}
|
||||
|
||||
$mode = $this->trackingModeService->getActiveMode();
|
||||
$path = 'start';
|
||||
$path = 'start_date';
|
||||
|
||||
if ($mode->canEditEnd()) {
|
||||
$path = 'end';
|
||||
$path = 'end_date';
|
||||
} elseif ($mode->canEditDuration()) {
|
||||
$path = 'duration';
|
||||
}
|
||||
|
||||
@@ -17,24 +17,18 @@ use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
|
||||
final class TimesheetValidator extends ConstraintValidator
|
||||
{
|
||||
/**
|
||||
* @var Constraint[]
|
||||
*/
|
||||
private $constraints;
|
||||
|
||||
/**
|
||||
* @param Constraint[] $constraints
|
||||
*/
|
||||
public function __construct(iterable $constraints)
|
||||
public function __construct(private iterable $constraints)
|
||||
{
|
||||
$this->constraints = $constraints;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TimesheetEntity $timesheet
|
||||
* @param Constraint $constraint
|
||||
*/
|
||||
public function validate($timesheet, Constraint $constraint)
|
||||
public function validate(mixed $timesheet, Constraint $constraint): void
|
||||
{
|
||||
if (!($constraint instanceof TimesheetConstraint)) {
|
||||
throw new UnexpectedTypeException($constraint, Timesheet::class);
|
||||
@@ -44,11 +38,11 @@ final class TimesheetValidator extends ConstraintValidator
|
||||
throw new UnexpectedTypeException($timesheet, TimesheetEntity::class);
|
||||
}
|
||||
|
||||
foreach ($this->constraints as $constraint) {
|
||||
foreach ($this->constraints as $innerConstraint) {
|
||||
$this->context
|
||||
->getValidator()
|
||||
->inContext($this->context)
|
||||
->validate($timesheet, $constraint, [Constraint::DEFAULT_GROUP]);
|
||||
->validate($timesheet, $innerConstraint, [Constraint::DEFAULT_GROUP]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,9 +17,9 @@ final class TimesheetZeroDuration extends TimesheetConstraint
|
||||
self::ZERO_DURATION_ERROR => 'Duration cannot be zero.',
|
||||
];
|
||||
|
||||
public $message = 'Duration cannot be zero.';
|
||||
public string $message = 'Duration cannot be zero.';
|
||||
|
||||
public function getTargets()
|
||||
public function getTargets(): string|array
|
||||
{
|
||||
return self::CLASS_CONSTRAINT;
|
||||
}
|
||||
|
||||
@@ -17,18 +17,15 @@ use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
|
||||
final class TimesheetZeroDurationValidator extends ConstraintValidator
|
||||
{
|
||||
private $configuration;
|
||||
|
||||
public function __construct(SystemConfiguration $configuration)
|
||||
public function __construct(private SystemConfiguration $configuration)
|
||||
{
|
||||
$this->configuration = $configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TimesheetEntity $timesheet
|
||||
* @param Constraint $constraint
|
||||
*/
|
||||
public function validate($timesheet, Constraint $constraint)
|
||||
public function validate(mixed $timesheet, Constraint $constraint): void
|
||||
{
|
||||
if (!($constraint instanceof TimesheetZeroDuration)) {
|
||||
throw new UnexpectedTypeException($constraint, TimesheetZeroDuration::class);
|
||||
|
||||
@@ -9,14 +9,10 @@
|
||||
|
||||
namespace App\Validator\Constraints;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation\Target;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target({"CLASS", "PROPERTY", "METHOD", "ANNOTATION"})
|
||||
*/
|
||||
class User extends Constraint
|
||||
#[\Attribute(\Attribute::TARGET_CLASS)]
|
||||
final class User extends Constraint
|
||||
{
|
||||
public const USER_EXISTING_EMAIL = 'kimai-user-00';
|
||||
public const USER_EXISTING_NAME = 'kimai-user-01';
|
||||
@@ -30,9 +26,9 @@ class User extends Constraint
|
||||
self::USER_EXISTING_NAME_AS_EMAIL => 'An equal email is already used.',
|
||||
];
|
||||
|
||||
public $message = 'The user has invalid settings.';
|
||||
public string $message = 'The user has invalid settings.';
|
||||
|
||||
public function getTargets()
|
||||
public function getTargets(): string|array
|
||||
{
|
||||
return self::CLASS_CONSTRAINT;
|
||||
}
|
||||
|
||||
@@ -16,20 +16,17 @@ use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
|
||||
class UserValidator extends ConstraintValidator
|
||||
final class UserValidator extends ConstraintValidator
|
||||
{
|
||||
private $userService;
|
||||
|
||||
public function __construct(UserService $userService)
|
||||
public function __construct(private UserService $userService)
|
||||
{
|
||||
$this->userService = $userService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param UserEntity $value
|
||||
* @param Constraint $constraint
|
||||
*/
|
||||
public function validate($value, Constraint $constraint)
|
||||
public function validate(mixed $value, Constraint $constraint): void
|
||||
{
|
||||
if (!($constraint instanceof User)) {
|
||||
throw new UnexpectedTypeException($constraint, User::class);
|
||||
@@ -42,16 +39,16 @@ class UserValidator extends ConstraintValidator
|
||||
$this->validateUser($value, $this->context);
|
||||
}
|
||||
|
||||
protected function validateUser(UserEntity $user, ExecutionContextInterface $context)
|
||||
protected function validateUser(UserEntity $user, ExecutionContextInterface $context): void
|
||||
{
|
||||
if ($user->getEmail() !== null) {
|
||||
if ($user->hasEmail()) {
|
||||
$this->validateEmailExists($user->getId(), $user->getEmail(), 'email', User::USER_EXISTING_EMAIL, $context);
|
||||
$this->validateUsernameExists($user->getId(), $user->getEmail(), 'email', User::USER_EXISTING_EMAIL_AS_NAME, $context);
|
||||
}
|
||||
|
||||
if ($user->getUsername() !== null) {
|
||||
$this->validateEmailExists($user->getId(), $user->getUsername(), 'username', User::USER_EXISTING_NAME_AS_EMAIL, $context);
|
||||
$this->validateUsernameExists($user->getId(), $user->getUsername(), 'username', User::USER_EXISTING_NAME, $context);
|
||||
if ($user->hasUsername()) {
|
||||
$this->validateEmailExists($user->getId(), $user->getUserIdentifier(), 'username', User::USER_EXISTING_NAME_AS_EMAIL, $context);
|
||||
$this->validateUsernameExists($user->getId(), $user->getUserIdentifier(), 'username', User::USER_EXISTING_NAME, $context);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
namespace App\Validator;
|
||||
|
||||
class ValidationException extends \RuntimeException
|
||||
final class ValidationException extends \RuntimeException
|
||||
{
|
||||
public function __construct(string $message = null)
|
||||
{
|
||||
|
||||
@@ -11,21 +11,14 @@ namespace App\Validator;
|
||||
|
||||
use Symfony\Component\Validator\ConstraintViolationListInterface;
|
||||
|
||||
class ValidationFailedException extends \RuntimeException
|
||||
final class ValidationFailedException extends \RuntimeException
|
||||
{
|
||||
/**
|
||||
* @var ConstraintViolationListInterface
|
||||
*/
|
||||
private $violations;
|
||||
|
||||
public function __construct(ConstraintViolationListInterface $violations, ?string $message = null)
|
||||
public function __construct(private ConstraintViolationListInterface $violations, ?string $message = null)
|
||||
{
|
||||
if ($message === null) {
|
||||
$message = 'Validation failed';
|
||||
}
|
||||
parent::__construct($message, 400);
|
||||
|
||||
$this->violations = $violations;
|
||||
}
|
||||
|
||||
public function getViolations(): ConstraintViolationListInterface
|
||||
|
||||
Reference in New Issue
Block a user