diff --git a/src/Exception/DatabaseException.php b/src/Exception/DatabaseException.php new file mode 100644 index 000000000..4a3130ab8 --- /dev/null +++ b/src/Exception/DatabaseException.php @@ -0,0 +1,31 @@ +name] + )] + #[IdParameter(type: 'schedule', schema: new Schema(type: 'integer', example: 123))] + #[SuccessResponse( + description: 'Id of deleted schedule', + content: new IdJson('ID of deleted schedule') + )] + #[DefaultResponses([ + HttpResponseCodes::UNAUTHORIZED, + HttpResponseCodes::NOT_FOUND, + ])] + public function deleteSchedule(int $id): JsonResponse + { + $this->scheduleService->deleteSchedule($id); + + return $this->jsonResponse(['id' => $id]); + } +} diff --git a/src/Schedule/Controller/Element/CollectionController.php b/src/Schedule/Controller/Element/CollectionController.php index 4b0ad9526..eff362184 100644 --- a/src/Schedule/Controller/Element/CollectionController.php +++ b/src/Schedule/Controller/Element/CollectionController.php @@ -18,11 +18,9 @@ use OpenApi\Attributes\Get; use Pimcore\Bundle\StudioBackendBundle\Controller\AbstractApiController; -use Pimcore\Bundle\StudioBackendBundle\Note\Attributes\Response\Property\NoteCollection; use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attributes\Content\ItemsJson; use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attributes\Parameters\Path\ElementTypeParameter; use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attributes\Parameters\Path\IdParameter; -use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attributes\Response\Content\CollectionJson; use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attributes\Response\DefaultResponses; use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attributes\Response\SuccessResponse; use Pimcore\Bundle\StudioBackendBundle\OpenApi\Config\Tags; @@ -60,7 +58,7 @@ public function __construct( #[ElementTypeParameter] #[IdParameter(type: 'element')] #[SuccessResponse( - description: 'Paginated schedules', + description: 'List of schedules', content: new ItemsJson(Schedule::class) )] #[DefaultResponses([ @@ -72,8 +70,6 @@ public function getSchedules( int $id ): JsonResponse { - $tasks = $this->scheduleService->listSchedules($elementType, $id); - - return $this->jsonResponse(['items' => $tasks]); + return $this->jsonResponse(['items' => $this->scheduleService->listSchedules($elementType, $id)]); } } diff --git a/src/Schedule/Controller/Element/CreateController.php b/src/Schedule/Controller/Element/CreateController.php new file mode 100644 index 000000000..bc174a224 --- /dev/null +++ b/src/Schedule/Controller/Element/CreateController.php @@ -0,0 +1,73 @@ +name] + )] + #[ElementTypeParameter] + #[IdParameter(type: 'element')] + #[SuccessResponse( + description: 'Created schedule', + content: new JsonContent(ref: Schedule::class) + )] + #[DefaultResponses([ + HttpResponseCodes::UNAUTHORIZED, + ])] + public function createSchedule(string $elementType, int $id): JsonResponse + { + return $this->jsonResponse($this->scheduleService->createSchedule($elementType, $id)); + } +} diff --git a/src/Schedule/Controller/Element/UpdateController.php b/src/Schedule/Controller/Element/UpdateController.php new file mode 100644 index 000000000..6d682e04c --- /dev/null +++ b/src/Schedule/Controller/Element/UpdateController.php @@ -0,0 +1,86 @@ +name] + )] + #[ElementTypeParameter] + #[IdParameter(type: 'element')] + #[ElementScheduleRequestBody] + #[SuccessResponse( + description: 'List of schedules', + content: new ItemsJson(Schedule::class) + )] + #[DefaultResponses([ + HttpResponseCodes::UNAUTHORIZED, + HttpResponseCodes::NOT_FOUND + ])] + public function updateSchedules( + string $elementType, + int $id, + #[MapRequestPayload] UpdateElementSchedules $updateElementSchedules + ): JsonResponse + { + $this->scheduleService->updateSchedules($elementType, $id, $updateElementSchedules); + + return $this->jsonResponse(['items' => $this->scheduleService->listSchedules($elementType, $id)]); + } +} diff --git a/src/Schedule/Repository/ScheduleRepository.php b/src/Schedule/Repository/ScheduleRepository.php index 3a9602560..e9929a1f6 100644 --- a/src/Schedule/Repository/ScheduleRepository.php +++ b/src/Schedule/Repository/ScheduleRepository.php @@ -16,24 +16,146 @@ namespace Pimcore\Bundle\StudioBackendBundle\Schedule\Repository; +use Carbon\Carbon; +use Doctrine\DBAL\ArrayParameterType; +use Doctrine\DBAL\Exception; +use Pimcore\Bundle\StaticResolverBundle\Db\DbResolverInterface; use Pimcore\Bundle\StaticResolverBundle\Models\Element\ServiceResolverInterface; +use Pimcore\Bundle\StaticResolverBundle\Models\Schedule\TaskResolverInterface; +use Pimcore\Bundle\StudioBackendBundle\Exception\DatabaseException; +use Pimcore\Bundle\StudioBackendBundle\Exception\ElementNotFoundException; +use Pimcore\Bundle\StudioBackendBundle\Exception\NotAuthorizedException; +use Pimcore\Bundle\StudioBackendBundle\Schedule\Request\UpdateElementSchedules; +use Pimcore\Bundle\StudioBackendBundle\Security\Service\SecurityServiceInterface; use Pimcore\Bundle\StudioBackendBundle\Util\Traits\ElementProviderTrait; use Pimcore\Model\Schedule\Task; +/** + * @internal + */ final readonly class ScheduleRepository implements ScheduleRepositoryInterface { use ElementProviderTrait; - public function __construct(private ServiceResolverInterface $serviceResolver) + public function __construct( + private ServiceResolverInterface $serviceResolver, + private DbResolverInterface $dbResolver, + private TaskResolverInterface $taskResolver, + private SecurityServiceInterface $securityService, + ) + { + } + + /** + * @throws NotAuthorizedException + */ + public function createSchedule(string $elementType, int $id): Task + { + $user = $this->securityService->getCurrentUser(); + + $task = new Task(); + $task->setCtype($elementType); + $task->setCid($id); + $task->setDate(Carbon::today()->getTimestamp()); + $task->setActive(true); + $task->setUserId($user->getId()); + $task->save(); + + return $task; + } + + /** + * @throws ElementNotFoundException + */ + public function getSchedule(int $id): Task { + $task = $this->taskResolver->getById($id); + + if (!$task) { + throw new ElementNotFoundException($id, 'Task'); + } + return $task; } /** * @return array + * @throws ElementNotFoundException */ public function listSchedules(string $elementType, int $id): array { return $this->getElement($this->serviceResolver, $elementType, $id)->getScheduledTasks(); } + + /** + * @throws DatabaseException + */ + public function updateSchedules( + string $elementType, + int $id, + UpdateElementSchedules $updateElementSchedules + ): void + { + $schedules = $updateElementSchedules->getSchedules(); + + $currentTasks = []; + foreach ($schedules as $schedule) { + $task = $this->taskResolver->getById($schedule->getId()); + + if (!$task) { + continue; + } + + $currentTasks[] = $task->getId(); + $task->setCid($id); + $task->setCtype($elementType); + $task->setDate($schedule->getDate()); + $task->setAction($schedule->getAction()); + $task->setVersion($schedule->getVersion()); + $task->setActive($schedule->isActive()); + $task->save(); + } + + + $this->deleteObsoleteTasks($currentTasks, $id); + } + + /** + * @throws ElementNotFoundException|DatabaseException + */ + public function deleteSchedule(int $id): void + { + $task = $this->getSchedule($id); + + $queryBuilder = $this->dbResolver->get()->createQueryBuilder(); + + $queryBuilder->delete('schedule_tasks') + ->where('id = :id') + ->setParameter('id', $task->getId()); + + try { + $queryBuilder->executeStatement(); + } catch (Exception) { + throw new DatabaseException(); + } + } + + /** + * @throws DatabaseException + */ + private function deleteObsoleteTasks(array $currentTasks, int $cid): void + { + $queryBuilder = $this->dbResolver->get()->createQueryBuilder(); + + $queryBuilder->delete('schedule_tasks') + ->where('id NOT IN (:ids) AND cid = :cid') + ->setParameter('ids', $currentTasks, ArrayParameterType::INTEGER) + ->setParameter('cid', $cid); + + try { + $queryBuilder->executeStatement(); + } catch (Exception) { + throw new DatabaseException(); + } + } } diff --git a/src/Schedule/Repository/ScheduleRepositoryInterface.php b/src/Schedule/Repository/ScheduleRepositoryInterface.php index dab660a6c..b129b34ee 100644 --- a/src/Schedule/Repository/ScheduleRepositoryInterface.php +++ b/src/Schedule/Repository/ScheduleRepositoryInterface.php @@ -16,7 +16,40 @@ namespace Pimcore\Bundle\StudioBackendBundle\Schedule\Repository; +use Pimcore\Bundle\StudioBackendBundle\Exception\DatabaseException; +use Pimcore\Bundle\StudioBackendBundle\Exception\ElementNotFoundException; +use Pimcore\Bundle\StudioBackendBundle\Exception\NotAuthorizedException; +use Pimcore\Bundle\StudioBackendBundle\Schedule\Request\UpdateElementSchedules; +use Pimcore\Model\Schedule\Task; + +/** + * @internal + */ interface ScheduleRepositoryInterface { + /** + * @throws NotAuthorizedException + */ + public function createSchedule(string $elementType, int $id): Task; + + /** + * @throws ElementNotFoundException + */ + public function getSchedule(int $id): Task; + public function listSchedules(string $elementType, int $id): array; + + /** + * @throws DatabaseException + */ + public function updateSchedules( + string $elementType, + int $id, + UpdateElementSchedules $updateElementSchedules + ): void; + + /** + * @throws ElementNotFoundException|DatabaseException + */ + public function deleteSchedule(int $id): void; } diff --git a/src/Schedule/Request/UpdateElementSchedules.php b/src/Schedule/Request/UpdateElementSchedules.php new file mode 100644 index 000000000..4b3f0a2f4 --- /dev/null +++ b/src/Schedule/Request/UpdateElementSchedules.php @@ -0,0 +1,50 @@ + */ + private array $schedules; + + public function __construct( + array $items + ) { + $this->schedules = array_map(static function (array $scheduleData) { + return new UpdateSchedule( + $scheduleData['id'], + $scheduleData['date'], + $scheduleData['action'], + $scheduleData['version'], + $scheduleData['active'], + ); + }, $items); + } + + /** + * @return array + */ + public function getSchedules(): array + { + return $this->schedules; + } +} diff --git a/src/Schedule/Schema/Schedule.php b/src/Schedule/Schema/Schedule.php index 3f2b33466..f0d5fa737 100644 --- a/src/Schedule/Schema/Schedule.php +++ b/src/Schedule/Schema/Schedule.php @@ -66,12 +66,12 @@ public function getDate(): int return $this->date; } - public function getAction(): string + public function getAction(): ?string { return $this->action; } - public function getVersion(): int + public function getVersion(): ?int { return $this->version; } diff --git a/src/Schedule/Schema/UpdateSchedule.php b/src/Schedule/Schema/UpdateSchedule.php new file mode 100644 index 000000000..65d942ac1 --- /dev/null +++ b/src/Schedule/Schema/UpdateSchedule.php @@ -0,0 +1,72 @@ +id; + } + + public function getDate(): int + { + return $this->date; + } + + public function getAction(): ?string + { + return $this->action; + } + + public function getVersion(): ?int + { + return $this->version; + } + + public function isActive(): bool + { + return $this->active; + } +} diff --git a/src/Schedule/Service/ScheduleService.php b/src/Schedule/Service/ScheduleService.php index 7c63b550d..3ad16c10c 100644 --- a/src/Schedule/Service/ScheduleService.php +++ b/src/Schedule/Service/ScheduleService.php @@ -16,9 +16,13 @@ namespace Pimcore\Bundle\StudioBackendBundle\Schedule\Service; +use Pimcore\Bundle\StudioBackendBundle\Exception\DatabaseException; +use Pimcore\Bundle\StudioBackendBundle\Exception\ElementNotFoundException; +use Pimcore\Bundle\StudioBackendBundle\Exception\NotAuthorizedException; use Pimcore\Bundle\StudioBackendBundle\Schedule\Event\ScheduleEvent; use Pimcore\Bundle\StudioBackendBundle\Schedule\Hydrator\ScheduleHydratorInterface; use Pimcore\Bundle\StudioBackendBundle\Schedule\Repository\ScheduleRepositoryInterface; +use Pimcore\Bundle\StudioBackendBundle\Schedule\Request\UpdateElementSchedules; use Pimcore\Bundle\StudioBackendBundle\Schedule\Schema\Schedule; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -35,6 +39,16 @@ public function __construct( { } + /** + * @throws NotAuthorizedException|ElementNotFoundException + */ + public function createSchedule(string $elementType, int $id): Schedule + { + $task = $this->scheduleRepository->createSchedule($elementType, $id); + + return $this->getSchedule($task->getId()); + } + /** * @return array */ @@ -57,4 +71,38 @@ public function listSchedules(string $elementType, int $id): array return $schedules; } + + /** + * @throws DatabaseException + */ + public function updateSchedules( + string $elementType, + int $id, + UpdateElementSchedules $updateElementSchedules + ): void + { + $this->scheduleRepository->updateSchedules($elementType, $id, $updateElementSchedules); + } + + /** + * @throws ElementNotFoundException|DatabaseException + */ + public function deleteSchedule(int $id): void + { + $this->scheduleRepository->deleteSchedule($id); + } + + private function getSchedule(int $id): Schedule + { + $task = $this->scheduleRepository->getSchedule($id); + + $schedule = $this->scheduleHydrator->hydrate($task); + + $this->eventDispatcher->dispatch( + new ScheduleEvent($schedule), + ScheduleEvent::EVENT_NAME + ); + + return $schedule; + } } diff --git a/src/Schedule/Service/ScheduleServiceInterface.php b/src/Schedule/Service/ScheduleServiceInterface.php index d55297455..e63a7fc9b 100644 --- a/src/Schedule/Service/ScheduleServiceInterface.php +++ b/src/Schedule/Service/ScheduleServiceInterface.php @@ -16,10 +16,35 @@ namespace Pimcore\Bundle\StudioBackendBundle\Schedule\Service; +use Pimcore\Bundle\StudioBackendBundle\Exception\DatabaseException; +use Pimcore\Bundle\StudioBackendBundle\Exception\ElementNotFoundException; +use Pimcore\Bundle\StudioBackendBundle\Exception\NotAuthorizedException; +use Pimcore\Bundle\StudioBackendBundle\Schedule\Request\UpdateElementSchedules; +use Pimcore\Bundle\StudioBackendBundle\Schedule\Schema\Schedule; + /** * @internal */ interface ScheduleServiceInterface { + /** + * @throws NotAuthorizedException|ElementNotFoundException + */ + public function createSchedule(string $elementType, int $id): Schedule; + public function listSchedules(string $elementType, int $id): array; + + /** + * @throws DatabaseException + */ + public function updateSchedules( + string $elementType, + int $id, + UpdateElementSchedules $updateElementSchedules + ): void; + + /** + * @throws ElementNotFoundException|DatabaseException + */ + public function deleteSchedule(int $id): void; }