Просмотр исходного кода

sorting fix, inactive login not possible etc

master
Daniel 10 месяцев назад
Родитель
Сommit
16a6f51176
11 измененных файлов: 102 добавлений и 37 удалений
  1. +1
    -0
      angular/src/app/_components/search-select/search-select.component.ts
  2. +8
    -0
      angular/src/app/_views/trip/trip-list/trip-list.component.ts
  3. +11
    -11
      angular/src/app/app-routing.module.ts
  4. +2
    -1
      angular/src/assets/i18n/en.json
  5. +2
    -1
      httpdocs/src/ApiResource/UserTripApi.php
  6. +14
    -2
      httpdocs/src/Entity/Trip.php
  7. +5
    -0
      httpdocs/src/Entity/User.php
  8. +0
    -5
      httpdocs/src/EventListener/AuthenticationSuccessListener.php
  9. +25
    -16
      httpdocs/src/Security/JwtAuthenticator.php
  10. +3
    -1
      httpdocs/src/State/EntityClassDtoStateProcessor.php
  11. +31
    -0
      httpdocs/src/State/UserTripStateProcessor.php

+ 1
- 0
angular/src/app/_components/search-select/search-select.component.ts Просмотреть файл

@@ -120,6 +120,7 @@ export class SearchSelectComponent implements OnInit, AfterViewInit {
text: 'model.zone',
type: ListComponent.COLUMN_TYPE_TEXT,
subResource: 'zone',
sortingSubResource: 'zone',
field: 'name',
sortable: true,
filterType: FilterBarComponent.FILTER_TYPE_TEXT,


+ 8
- 0
angular/src/app/_views/trip/trip-list/trip-list.component.ts Просмотреть файл

@@ -83,6 +83,14 @@ export class TripListComponent {
sortable: true,
filterType: FilterBarComponent.FILTER_TYPE_DATE,
} as ListColDefinition,
{
name: 'completed',
text: 'trip.completed',
type: ListComponent.COLUMN_TYPE_BOOLEAN,
field: 'completed',
sortable: true,
filterType: FilterBarComponent.FILTER_TYPE_BOOLEAN,
} as ListColDefinition,
{
name: 'createdAt',
text: 'common.created_at',


+ 11
- 11
angular/src/app/app-routing.module.ts Просмотреть файл

@@ -41,7 +41,7 @@ const routes: Routes = [
{
path: ROUTE_DASHBOARD,
component: TwoColumnComponent,
canActivate: [userGuard],
canActivate: [adminGuard],
children: [
{path: '', component: DashboardComponent},
]
@@ -49,7 +49,7 @@ const routes: Routes = [
{
path: ROUTE_BASE_DATA,
component: TwoColumnComponent,
canActivate: [userGuard],
canActivate: [adminGuard],
children: [
{path: '', component: BaseDataComponent},
]
@@ -57,7 +57,7 @@ const routes: Routes = [
{
path: ROUTE_LOCATIONS,
component: TwoColumnComponent,
canActivate: [userGuard],
canActivate: [adminGuard],
children: [
{path: ':id', component: LocationDetailComponent},
]
@@ -65,7 +65,7 @@ const routes: Routes = [
{
path: ROUTE_TRIPS,
component: TwoColumnComponent,
canActivate: [userGuard],
canActivate: [adminGuard],
children: [
{path: '', component: TripComponent},
]
@@ -73,7 +73,7 @@ const routes: Routes = [
{
path: ROUTE_TRIPS,
component: TwoColumnComponent,
canActivate: [userGuard],
canActivate: [adminGuard],
children: [
{path: ':id', component: TripDetailComponent},
]
@@ -81,7 +81,7 @@ const routes: Routes = [
{
path: ROUTE_USER_TRIPS,
component: TwoColumnComponent,
canActivate: [userGuard],
canActivate: [adminGuard],
children: [
{path: '', component: UserTripComponent},
]
@@ -89,7 +89,7 @@ const routes: Routes = [
{
path: ROUTE_USER_TRIPS,
component: TwoColumnComponent,
canActivate: [userGuard],
canActivate: [adminGuard],
children: [
{path: ':id', component: UserTripDetailComponent},
]
@@ -97,7 +97,7 @@ const routes: Routes = [
{
path: ROUTE_ZONES,
component: TwoColumnComponent,
canActivate: [userGuard],
canActivate: [adminGuard],
children: [
{path: ':id', component: ZoneDetailComponent},
]
@@ -105,7 +105,7 @@ const routes: Routes = [
{
path: ROUTE_VESSELS,
component: TwoColumnComponent,
canActivate: [userGuard],
canActivate: [adminGuard],
children: [
{path: ':id', component: VesselDetailComponent},
]
@@ -113,7 +113,7 @@ const routes: Routes = [
{
path: ROUTE_SHIPPING_COMPANIES,
component: TwoColumnComponent,
canActivate: [userGuard],
canActivate: [adminGuard],
children: [
{path: ':id', component: ShippingCompanyDetailComponent},
]
@@ -121,7 +121,7 @@ const routes: Routes = [
{
path: ROUTE_PROFILE,
component: TwoColumnComponent,
canActivate: [userGuard],
canActivate: [adminGuard],
children: [
{path: '', component: ProfileComponent},
]


+ 2
- 1
angular/src/assets/i18n/en.json Просмотреть файл

@@ -40,7 +40,8 @@
"save_itinerary": "Save itinerary",
"assigned_users": "Assigned Pilots",
"save_user_assignments": "Save user assignments",
"events": "Events"
"events": "Events",
"completed": "Completed"
},
"user_trip":
{


+ 2
- 1
httpdocs/src/ApiResource/UserTripApi.php Просмотреть файл

@@ -18,6 +18,7 @@ use App\Filter\CustomJsonFilter;
use App\Filter\CustomJsonOrderFilter;
use App\State\EntityClassDtoStateProcessor;
use App\State\EntityToDtoStateProvider;
use App\State\UserTripStateProcessor;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\PropertyInfo\Type;
use Symfony\Component\Validator\Constraints\NotBlank;
@@ -43,7 +44,7 @@ use Symfony\Component\Validator\Constraints\NotBlank;
],
security: 'is_granted("ROLE_USER")',
provider: EntityToDtoStateProvider::class,
processor: EntityClassDtoStateProcessor::class,
processor: UserTripStateProcessor::class,
stateOptions: new Options(entityClass: UserTrip::class),
)]
#[ApiFilter(SearchFilter::class, properties: [


+ 14
- 2
httpdocs/src/Entity/Trip.php Просмотреть файл

@@ -156,8 +156,20 @@ class Trip
return $this->completed;
}

public function setCompleted(bool $completed): void
{
public function setCompleted(): void
{
$completed = true;
if (count($this->userTrips) <= 0) {
$completed = false;
} else {
/** @var UserTrip $userTrip */
foreach ($this->userTrips as $userTrip) {
if (!$userTrip->isCompleted() || !$userTrip->isApproved()) {
$completed = false;
break;
}
}
}
$this->completed = $completed;
}



+ 5
- 0
httpdocs/src/Entity/User.php Просмотреть файл

@@ -193,6 +193,11 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
$this->isPilot = $isPilot;
}

public function isAdmin()
{
return in_array(User::ROLE_ADMIN, $this->roles, true);
}

public function getCreatedAt(): ?\DateTimeImmutable
{
return $this->createdAt;


+ 0
- 5
httpdocs/src/EventListener/AuthenticationSuccessListener.php Просмотреть файл

@@ -1,9 +1,4 @@
<?php
/**
* @author Daniel Knudsen <d.knudsen@spawntree.de>
* @date 18.01.24
*/


namespace App\EventListener;



+ 25
- 16
httpdocs/src/Security/JwtAuthenticator.php Просмотреть файл

@@ -1,15 +1,8 @@
<?php
// src/Security/JwtAuthenticator.php
namespace App\Security;

use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\IriConverterInterface;
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
use ApiPlatform\State\Provider\ContentNegotiationProvider;
use ApiPlatform\State\SerializerContextBuilderInterface;
use App\ApiResource\UserApi;
use App\Entity\MediaObject;
use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
@@ -21,14 +14,12 @@ use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfonycasts\MicroMapper\MicroMapperInterface;

class JwtAuthenticator extends AbstractAuthenticator
{
public function __construct(
private JWTTokenManagerInterface $jwtManager
private JWTTokenManagerInterface $jwtManager,
private EntityManagerInterface $em
) {}

public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
@@ -51,20 +42,38 @@ class JwtAuthenticator extends AbstractAuthenticator
public function authenticate(Request $request): Passport
{
$data = json_decode($request->getContent(), true);

if (!$data) {
throw new CustomUserMessageAuthenticationException('Invalid JSON.');
throw new CustomUserMessageAuthenticationException('Ungültige JSON-Daten.');
}

$email = $data['email'] ?? '';
$email = $data['email'] ?? '';
$password = $data['password'] ?? '';

$userBadge = new UserBadge($email, function(string $userIdentifier) {
$user = $this->em
->getRepository(User::class)
->findOneBy(['email' => $userIdentifier]);

if (!$user) {
// Standardnachricht für nicht gefundene E-Mail
throw new CustomUserMessageAuthenticationException('Invalid email or password.');
}

if (!$user->isActive()) {
// custom Message, erscheint im JSON-Response
throw new CustomUserMessageAuthenticationException('Your account is currently deactivated.');
}

return $user;
});

return new Passport(
new UserBadge($email),
$userBadge,
new PasswordCredentials($password)
);
}


public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
{
return new JsonResponse([


+ 3
- 1
httpdocs/src/State/EntityClassDtoStateProcessor.php Просмотреть файл

@@ -8,6 +8,7 @@ use ApiPlatform\Doctrine\Orm\State\Options;
use ApiPlatform\Metadata\DeleteOperationInterface;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfonycasts\MicroMapper\MicroMapperInterface;

@@ -16,7 +17,8 @@ class EntityClassDtoStateProcessor implements ProcessorInterface
public function __construct(
#[Autowire(service: PersistProcessor::class)] private ProcessorInterface $persistProcessor,
#[Autowire(service: RemoveProcessor::class)] private ProcessorInterface $removeProcessor,
private MicroMapperInterface $microMapper
private MicroMapperInterface $microMapper,
protected EntityManagerInterface $em
)
{}



+ 31
- 0
httpdocs/src/State/UserTripStateProcessor.php Просмотреть файл

@@ -0,0 +1,31 @@
<?php
/**
* @author Daniel Knudsen <d.knudsen@spawntree.de>
* @date 29.04.25
*/


namespace App\State;


use ApiPlatform\Metadata\Operation;
use App\Entity\Trip;

class UserTripStateProcessor extends EntityClassDtoStateProcessor
{


public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = [])
{
$res = parent::process($data, $operation, $uriVariables, $context);

/** @var Trip $trip */
$trip = $this->em->getRepository(Trip::class)->find($data->tripIri->id);
$trip->setCompleted();
$this->em->flush();

return $res;
}


}

Загрузка…
Отмена
Сохранить