diff --git a/config/packages/nelmio_api_doc.yaml b/config/packages/nelmio_api_doc.yaml index 883316db..f047130b 100644 --- a/config/packages/nelmio_api_doc.yaml +++ b/config/packages/nelmio_api_doc.yaml @@ -32,6 +32,7 @@ nelmio_api_doc: - { alias: UserCreateForm, type: App\Form\API\UserApiCreateForm, groups: [Default, Entity, User, User_Entity] } - { alias: UserEditForm, type: App\Form\API\UserApiEditForm, groups: [Default, Entity, User, User_Entity] } - { alias: User, type: App\Entity\User, groups: [Default] } + - { alias: UserPreference, type: App\Entity\UserPreference, groups: [Default, User_Entity] } - { alias: UserEntity, type: App\Entity\User, groups: [Default, Entity, User, User_Entity] } - { alias: UserCollection, type: App\Entity\User, groups: [Default, Collection, User] } - { alias: TeamEditForm, type: App\Form\API\TeamApiEditForm, groups: [Default, Entity, Team, Team_Entity] } diff --git a/src/API/UserController.php b/src/API/UserController.php index 286e3591..a0aa391f 100644 --- a/src/API/UserController.php +++ b/src/API/UserController.php @@ -13,6 +13,7 @@ namespace App\API; use App\Configuration\SystemConfiguration; use App\Entity\User; +use App\Event\PrepareUserEvent; use App\Form\API\UserApiCreateForm; use App\Form\API\UserApiEditForm; use App\Repository\Query\UserQuery; @@ -26,6 +27,7 @@ use HandcraftedInTheAlps\RestRoutingBundle\Controller\Annotations\RouteResource; use Nelmio\ApiDocBundle\Annotation\Security as ApiSecurity; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; use Swagger\Annotations as SWG; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; @@ -137,7 +139,7 @@ final class UserController extends BaseApiController * @ApiSecurity(name="apiUser") * @ApiSecurity(name="apiToken") */ - public function getAction(int $id): Response + public function getAction(int $id, EventDispatcherInterface $dispatcher): Response { $user = $this->repository->find($id); @@ -149,6 +151,10 @@ final class UserController extends BaseApiController throw new AccessDeniedHttpException('You are not allowed to view this profile'); } + // we need to prepare the user preferences, which is done via an EventSubscriber + $event = new PrepareUserEvent($user); + $dispatcher->dispatch($event); + $view = new View($user, 200); $view->getContext()->setGroups(self::GROUPS_ENTITY); diff --git a/src/Entity/User.php b/src/Entity/User.php index 6f0fedcd..0210200d 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -373,6 +373,45 @@ class User implements UserInterface, EquatableInterface, \Serializable return $this; } + /** + * Read-only list of of all visible user preferences. + * + * @Serializer\VirtualProperty + * @Serializer\SerializedName("preferences"), + * @Serializer\Groups({"User_Entity"}) + * @SWG\Property(type="array", @SWG\Items(ref="#/definitions/UserPreference")) + * + * @internal only for API usage + * @return UserPreference[] + */ + public function getVisiblePreferences(): array + { + // hide all internal preferences, which are either available in other fields + // or which are only used within the Kimai UI + $skip = [ + UserPreference::TIMEZONE, + UserPreference::LOCALE, + UserPreference::SKIN, + 'calendar.initial_view', + 'login.initial_view', + 'reporting.initial_view', + 'theme.collapsed_sidebar', + 'theme.layout', + 'theme.update_browser_title', + 'timesheet.daily_stats', + 'timesheet.export_decimal', + ]; + + $all = []; + foreach ($this->preferences as $preference) { + if ($preference->isEnabled() && !\in_array($preference->getName(), $skip)) { + $all[] = $preference; + } + } + + return $all; + } + /** * @return Collection */ diff --git a/src/Entity/UserPreference.php b/src/Entity/UserPreference.php index 95f33892..0db257a0 100644 --- a/src/Entity/UserPreference.php +++ b/src/Entity/UserPreference.php @@ -11,6 +11,7 @@ namespace App\Entity; use App\Form\Type\YesNoType; use Doctrine\ORM\Mapping as ORM; +use JMS\Serializer\Annotation as Serializer; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\IntegerType; use Symfony\Component\Validator\Constraint; @@ -23,6 +24,8 @@ use Symfony\Component\Validator\Constraints as Assert; * @ORM\UniqueConstraint(columns={"user_id", "name"}) * } * ) + * + * @Serializer\ExclusionPolicy("all") */ class UserPreference { @@ -53,6 +56,9 @@ class UserPreference /** * @var string * + * @Serializer\Expose() + * @Serializer\Groups({"Default"}) + * * @ORM\Column(name="name", type="string", length=50, nullable=false) * @Assert\NotNull() * @Assert\Length(min=2, max=50, allowEmptyString=false) @@ -61,6 +67,9 @@ class UserPreference /** * @var string * + * @Serializer\Expose() + * @Serializer\Groups({"Default"}) + * * @ORM\Column(name="value", type="string", length=255, nullable=true) */ private $value; diff --git a/tests/API/APIControllerBaseTest.php b/tests/API/APIControllerBaseTest.php index a0f3f1fe..43eca410 100644 --- a/tests/API/APIControllerBaseTest.php +++ b/tests/API/APIControllerBaseTest.php @@ -283,6 +283,12 @@ abstract class APIControllerBaseTest extends ControllerBaseTest ]; // embedded meta data + case 'UserPreference': + return [ + 'name' => 'string', + 'value' => '@string', + ]; + case 'CustomerMeta': case 'ProjectMeta': case 'ActivityMeta': @@ -321,6 +327,7 @@ abstract class APIControllerBaseTest extends ControllerBaseTest 'timezone' => 'string', 'accountNumber' => '@string', 'memberships' => ['result' => 'array', 'type' => 'TeamMembership'], + 'preferences' => ['result' => 'array', 'type' => 'UserPreference'], ]; // if a team is embedded