Release 2.57 (#5929)
This commit is contained in:
@@ -191,7 +191,7 @@ final class ActivityController extends BaseApiController
|
||||
* Delete activity
|
||||
*
|
||||
* [DANGER] This will also delete ALL linked timesheets.
|
||||
* Do you want to use `PATCH` instead and mark it as inactive with `{visible: false}` instead?
|
||||
* Do you want to use `PATCH` instead and mark it as inactive with `{visible: false}`?
|
||||
*/
|
||||
#[IsGranted('delete', 'activity')]
|
||||
#[OA\Delete(responses: [new OA\Response(response: 204, description: 'Delete one activity')])]
|
||||
|
||||
@@ -11,8 +11,10 @@ namespace App\API;
|
||||
|
||||
use App\Customer\CustomerService;
|
||||
use App\Entity\Customer;
|
||||
use App\Entity\CustomerComment;
|
||||
use App\Entity\CustomerRate;
|
||||
use App\Entity\User;
|
||||
use App\Form\API\CommentApiForm;
|
||||
use App\Form\API\CustomerApiEditForm;
|
||||
use App\Form\API\CustomerRateApiForm;
|
||||
use App\Repository\CustomerRateRepository;
|
||||
@@ -35,6 +37,7 @@ use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
#[OA\Tag(name: 'Customer')]
|
||||
final class CustomerController extends BaseApiController
|
||||
{
|
||||
private const GROUPS_COMMENT = ['Default', 'Not_Expanded'];
|
||||
public const GROUPS_ENTITY = ['Default', 'Entity', 'Customer', 'Customer_Entity'];
|
||||
public const GROUPS_COLLECTION = ['Default', 'Collection', 'Customer'];
|
||||
public const GROUPS_RATE = ['Default', 'Entity', 'Customer_Rate'];
|
||||
@@ -182,7 +185,7 @@ final class CustomerController extends BaseApiController
|
||||
* Delete customer
|
||||
*
|
||||
* [DANGER] This will also delete ALL linked projects, project activities and timesheets.
|
||||
* Do you want to use `PATCH` instead and mark it as inactive with `{visible: false}` instead?
|
||||
* Do you want to use `PATCH` instead and mark it as inactive with `{visible: false}`?
|
||||
*/
|
||||
#[IsGranted('delete', 'customer')]
|
||||
#[OA\Delete(responses: [new OA\Response(response: 204, description: 'Delete one customer')])]
|
||||
@@ -299,4 +302,103 @@ final class CustomerController extends BaseApiController
|
||||
|
||||
return $this->viewHandler->handle($view);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch comments for customer
|
||||
*/
|
||||
#[IsGranted('view', 'customer')]
|
||||
#[IsGranted('comments', 'customer')]
|
||||
#[OA\Response(response: 200, description: 'Returns a collection of customer comments', content: new OA\JsonContent(type: 'array', items: new OA\Items(ref: '#/components/schemas/Comment')))]
|
||||
#[OA\Parameter(name: 'id', description: 'The customer whose comments will be returned', in: 'path', required: true)]
|
||||
#[Route(path: '/{id}/comments', name: 'get_customer_comments', requirements: ['id' => '\d+'], methods: ['GET'])]
|
||||
public function getCommentsAction(#[MapEntity(mapping: ['id' => 'id'])] Customer $customer): Response
|
||||
{
|
||||
$comments = $this->repository->getComments($customer);
|
||||
|
||||
$view = new View($comments, 200);
|
||||
$view->getContext()->setGroups(self::GROUPS_COMMENT);
|
||||
|
||||
return $this->viewHandler->handle($view);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add comment for customer
|
||||
*/
|
||||
#[IsGranted('view', 'customer')]
|
||||
#[IsGranted('comments', 'customer')]
|
||||
#[OA\Post(responses: [new OA\Response(response: 200, description: 'Returns the newly created customer comment', content: new OA\JsonContent(ref: '#/components/schemas/Comment'))])]
|
||||
#[OA\Parameter(name: 'id', description: 'The customer to add the comment for', in: 'path', required: true)]
|
||||
#[OA\RequestBody(required: true, content: new OA\JsonContent(ref: '#/components/schemas/CommentForm'))]
|
||||
#[Route(path: '/{id}/comments', name: 'post_customer_comment', requirements: ['id' => '\d+'], methods: ['POST'])]
|
||||
public function postCommentAction(#[MapEntity(mapping: ['id' => 'id'])] Customer $customer, Request $request): Response
|
||||
{
|
||||
$comment = new CustomerComment($customer);
|
||||
$comment->setCreatedBy($this->getUser());
|
||||
|
||||
$form = $this->createForm(CommentApiForm::class, $comment, [
|
||||
'method' => 'POST',
|
||||
]);
|
||||
|
||||
$form->setData($comment);
|
||||
$form->submit($request->request->all(), false);
|
||||
|
||||
if (false === $form->isValid()) {
|
||||
return $this->viewHandler->handle(new View($form, Response::HTTP_BAD_REQUEST));
|
||||
}
|
||||
|
||||
$this->repository->saveComment($comment);
|
||||
|
||||
$view = new View($comment, 200);
|
||||
$view->getContext()->setGroups(self::GROUPS_COMMENT);
|
||||
|
||||
return $this->viewHandler->handle($view);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pin customer comment
|
||||
*
|
||||
* This toggles the `pinned` status of the given comment.
|
||||
*/
|
||||
#[IsGranted('view', 'customer')]
|
||||
#[IsGranted('edit', 'customer')]
|
||||
#[IsGranted('comments', 'customer')]
|
||||
#[OA\Patch(responses: [new OA\Response(response: 200, description: 'Returns the updated customer comment', content: new OA\JsonContent(ref: '#/components/schemas/Comment'))])]
|
||||
#[OA\Parameter(name: 'id', description: 'The customer whose comment will be pinned or unpinned', in: 'path', required: true)]
|
||||
#[OA\Parameter(name: 'comment', description: 'The comment whose pinned status will be toggled', in: 'path', required: true)]
|
||||
#[Route(path: '/{id}/comments/{comment}/pin', name: 'toggle_customer_comment_pin', requirements: ['id' => '\d+', 'comment' => '\d+'], methods: ['PATCH'])]
|
||||
public function toggleCommentPin(#[MapEntity(mapping: ['id' => 'id'])] Customer $customer, #[MapEntity(mapping: ['comment' => 'id'])] CustomerComment $comment): Response
|
||||
{
|
||||
if ($comment->getCustomer() !== $customer) {
|
||||
throw $this->createAccessDeniedException(\sprintf('Comment %s does not belong to customer %s', $comment->getId(), $customer->getId()));
|
||||
}
|
||||
|
||||
$comment->setPinned(!$comment->isPinned());
|
||||
$this->repository->saveComment($comment);
|
||||
|
||||
$view = new View($comment, 200);
|
||||
$view->getContext()->setGroups(self::GROUPS_COMMENT);
|
||||
|
||||
return $this->viewHandler->handle($view);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete customer comment
|
||||
*/
|
||||
#[IsGranted('view', 'customer')]
|
||||
#[IsGranted('edit', 'customer')]
|
||||
#[IsGranted('comments', 'customer')]
|
||||
#[OA\Delete(responses: [new OA\Response(response: 204, description: 'Returns no content: 204 on successful delete')])]
|
||||
#[OA\Parameter(name: 'id', description: 'The customer whose comment will be removed', in: 'path', required: true)]
|
||||
#[OA\Parameter(name: 'comment', description: 'The comment to remove', in: 'path', required: true)]
|
||||
#[Route(path: '/{id}/comments/{comment}', name: 'delete_customer_comment', requirements: ['id' => '\d+', 'comment' => '\d+'], methods: ['DELETE'])]
|
||||
public function deleteCommentAction(#[MapEntity(mapping: ['id' => 'id'])] Customer $customer, #[MapEntity(mapping: ['comment' => 'id'])] CustomerComment $comment): Response
|
||||
{
|
||||
if ($comment->getCustomer() !== $customer) {
|
||||
throw $this->createAccessDeniedException(\sprintf('Comment %s does not belong to customer %s', $comment->getId(), $customer->getId()));
|
||||
}
|
||||
|
||||
$this->repository->deleteComment($comment);
|
||||
|
||||
return $this->viewHandler->handle(new View(null, Response::HTTP_NO_CONTENT));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,10 @@
|
||||
namespace App\API;
|
||||
|
||||
use App\Entity\Project;
|
||||
use App\Entity\ProjectComment;
|
||||
use App\Entity\ProjectRate;
|
||||
use App\Entity\User;
|
||||
use App\Form\API\CommentApiForm;
|
||||
use App\Form\API\ProjectApiEditForm;
|
||||
use App\Form\API\ProjectRateApiForm;
|
||||
use App\Project\ProjectService;
|
||||
@@ -37,6 +39,7 @@ use Symfony\Component\Validator\Constraints;
|
||||
#[OA\Tag(name: 'Project')]
|
||||
final class ProjectController extends BaseApiController
|
||||
{
|
||||
private const GROUPS_COMMENT = ['Default', 'Not_Expanded'];
|
||||
public const GROUPS_ENTITY = ['Default', 'Entity', 'Project', 'Project_Entity'];
|
||||
public const GROUPS_COLLECTION = ['Default', 'Collection', 'Project'];
|
||||
public const GROUPS_RATE = ['Default', 'Entity', 'Project_Rate'];
|
||||
@@ -238,7 +241,7 @@ final class ProjectController extends BaseApiController
|
||||
* Delete project
|
||||
*
|
||||
* [DANGER] This will also delete ALL linked activities and timesheets.
|
||||
* Do you want to use `PATCH` instead and mark it as inactive with `{visible: false}` instead?
|
||||
* Do you want to use `PATCH` instead and mark it as inactive with `{visible: false}`?
|
||||
*/
|
||||
#[IsGranted('delete', 'project')]
|
||||
#[OA\Delete(responses: [new OA\Response(response: 204, description: 'Delete one project')])]
|
||||
@@ -355,4 +358,103 @@ final class ProjectController extends BaseApiController
|
||||
|
||||
return $this->viewHandler->handle($view);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch comments for project
|
||||
*/
|
||||
#[IsGranted('view', 'project')]
|
||||
#[IsGranted('comments', 'project')]
|
||||
#[OA\Response(response: 200, description: 'Returns a collection of project comments', content: new OA\JsonContent(type: 'array', items: new OA\Items(ref: '#/components/schemas/Comment')))]
|
||||
#[OA\Parameter(name: 'id', description: 'The project whose comments will be returned', in: 'path', required: true)]
|
||||
#[Route(path: '/{id}/comments', name: 'get_project_comments', requirements: ['id' => '\d+'], methods: ['GET'])]
|
||||
public function getCommentsAction(#[MapEntity(mapping: ['id' => 'id'])] Project $project): Response
|
||||
{
|
||||
$comments = $this->repository->getComments($project);
|
||||
|
||||
$view = new View($comments, 200);
|
||||
$view->getContext()->setGroups(self::GROUPS_COMMENT);
|
||||
|
||||
return $this->viewHandler->handle($view);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add comment for project
|
||||
*/
|
||||
#[IsGranted('view', 'project')]
|
||||
#[IsGranted('comments', 'project')]
|
||||
#[OA\Post(responses: [new OA\Response(response: 200, description: 'Returns the newly created project comment', content: new OA\JsonContent(ref: '#/components/schemas/Comment'))])]
|
||||
#[OA\Parameter(name: 'id', description: 'The project to add the comment for', in: 'path', required: true)]
|
||||
#[OA\RequestBody(required: true, content: new OA\JsonContent(ref: '#/components/schemas/CommentForm'))]
|
||||
#[Route(path: '/{id}/comments', name: 'post_project_comment', requirements: ['id' => '\d+'], methods: ['POST'])]
|
||||
public function postCommentAction(#[MapEntity(mapping: ['id' => 'id'])] Project $project, Request $request): Response
|
||||
{
|
||||
$comment = new ProjectComment($project);
|
||||
$comment->setCreatedBy($this->getUser());
|
||||
|
||||
$form = $this->createForm(CommentApiForm::class, $comment, [
|
||||
'method' => 'POST',
|
||||
]);
|
||||
|
||||
$form->setData($comment);
|
||||
$form->submit($request->request->all(), false);
|
||||
|
||||
if (false === $form->isValid()) {
|
||||
return $this->viewHandler->handle(new View($form, Response::HTTP_BAD_REQUEST));
|
||||
}
|
||||
|
||||
$this->repository->saveComment($comment);
|
||||
|
||||
$view = new View($comment, 200);
|
||||
$view->getContext()->setGroups(self::GROUPS_COMMENT);
|
||||
|
||||
return $this->viewHandler->handle($view);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pin project comment
|
||||
*
|
||||
* This toggles the `pinned` status of the given comment.
|
||||
*/
|
||||
#[IsGranted('view', 'project')]
|
||||
#[IsGranted('edit', 'project')]
|
||||
#[IsGranted('comments', 'project')]
|
||||
#[OA\Patch(responses: [new OA\Response(response: 200, description: 'Returns the updated project comment', content: new OA\JsonContent(ref: '#/components/schemas/Comment'))])]
|
||||
#[OA\Parameter(name: 'id', description: 'The project whose comment will be pinned or unpinned', in: 'path', required: true)]
|
||||
#[OA\Parameter(name: 'comment', description: 'The comment whose pinned status will be toggled', in: 'path', required: true)]
|
||||
#[Route(path: '/{id}/comments/{comment}/pin', name: 'toggle_project_comment_pin', requirements: ['id' => '\d+', 'comment' => '\d+'], methods: ['PATCH'])]
|
||||
public function toggleCommentPin(#[MapEntity(mapping: ['id' => 'id'])] Project $project, #[MapEntity(mapping: ['comment' => 'id'])] ProjectComment $comment): Response
|
||||
{
|
||||
if ($comment->getProject() !== $project) {
|
||||
throw $this->createAccessDeniedException(\sprintf('Comment %s does not belong to project %s', $comment->getId(), $project->getId()));
|
||||
}
|
||||
|
||||
$comment->setPinned(!$comment->isPinned());
|
||||
$this->repository->saveComment($comment);
|
||||
|
||||
$view = new View($comment, 200);
|
||||
$view->getContext()->setGroups(self::GROUPS_COMMENT);
|
||||
|
||||
return $this->viewHandler->handle($view);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete project comment
|
||||
*/
|
||||
#[IsGranted('view', 'project')]
|
||||
#[IsGranted('edit', 'project')]
|
||||
#[IsGranted('comments', 'project')]
|
||||
#[OA\Delete(responses: [new OA\Response(response: 204, description: 'Returns no content: 204 on successful delete')])]
|
||||
#[OA\Parameter(name: 'id', description: 'The project whose comment will be removed', in: 'path', required: true)]
|
||||
#[OA\Parameter(name: 'comment', description: 'The comment to remove', in: 'path', required: true)]
|
||||
#[Route(path: '/{id}/comments/{comment}', name: 'delete_project_comment', requirements: ['id' => '\d+', 'comment' => '\d+'], methods: ['DELETE'])]
|
||||
public function deleteCommentAction(#[MapEntity(mapping: ['id' => 'id'])] Project $project, #[MapEntity(mapping: ['comment' => 'id'])] ProjectComment $comment): Response
|
||||
{
|
||||
if ($comment->getProject() !== $project) {
|
||||
throw $this->createAccessDeniedException(\sprintf('Comment %s does not belong to project %s', $comment->getId(), $project->getId()));
|
||||
}
|
||||
|
||||
$this->repository->deleteComment($comment);
|
||||
|
||||
return $this->viewHandler->handle(new View(null, Response::HTTP_NO_CONTENT));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,6 +113,9 @@ final class TimesheetController extends BaseApiController
|
||||
|
||||
if (!$seeAll) {
|
||||
foreach ($userRepository->findByIds($users) as $user) {
|
||||
if (!$this->isGranted('access_user', $user)) {
|
||||
throw $this->createAccessDeniedException('Cannot access user: ' . $user->getId());
|
||||
}
|
||||
$query->addUser($user);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user