Skip to content

Message: Fix message status update on deletion for sender and receiver - refs BT#21887 #5695

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion assets/vue/components/message/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ export const MESSAGE_TYPE_WALL = 4;
export const MESSAGE_TYPE_GROUP = 5;
export const MESSAGE_TYPE_INVITATION = 6;
export const MESSAGE_TYPE_CONVERSATION = 7;
export const MESSAGE_TYPE_SENDER = 8;

export const MESSAGE_STATUS_DELETED = 3;
export const MESSAGE_STATUS_SENDER_DELETED = 3;
23 changes: 22 additions & 1 deletion assets/vue/services/message.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import makeService from "./api"
import baseService from "./baseService"
import axios from "axios"

// MIGRATION IN PROGRESS. makeService is deprecated
// if you use some method in this service you should try to refactor it with new baseService defining async functions
Expand All @@ -22,7 +23,27 @@ async function countUnreadMessages(params) {
return await baseService.get(`/api/messages?${queryParams}`)
}

async function deleteMessageForUser(messageId, userId) {
return await axios.patch(`/api/messages/${messageId}/delete-for-user`,
{ userId: userId },
{
headers: {
'Content-Type': 'application/json'
}
})
}

async function checkAndUpdateMessageStatus(messageId) {
return await axios.patch(`/api/messages/${messageId}/check-and-update-status`, {}, {
headers: {
'Content-Type': 'application/json'
}
})
}

export const messageService = {
create,
countUnreadMessages,
}
deleteMessageForUser,
checkAndUpdateMessageStatus,
};
8 changes: 7 additions & 1 deletion assets/vue/views/message/MessageCreate.vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,18 @@ const message = ref({
const isLoading = ref(false)

const onSubmit = async (messageToSend) => {
if (!messageToSend.receivers || messageToSend.receivers.length === 0) {
notification.showErrorNotification("You must add at least one recipient.")
return
}

isLoading.value = true

try {
await messageService.create(messageToSend)
notification.showSuccessNotification("Message sent succesfully.")
} catch (error) {
notification.showErrorNotification(error)
notification.showErrorNotification(error.message || "Error sending message.")
} finally {
isLoading.value = false
}
Expand Down
21 changes: 18 additions & 3 deletions assets/vue/views/message/MessageList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ import DataTable from "primevue/datatable"
import Column from "primevue/column"
import { useConfirm } from "primevue/useconfirm"
import { useQuery } from "@vue/apollo-composable"
import { MESSAGE_STATUS_DELETED, MESSAGE_TYPE_INBOX } from "../../components/message/constants"
import { MESSAGE_STATUS_SENDER_DELETED, MESSAGE_TYPE_INBOX, MESSAGE_TYPE_SENDER } from "../../components/message/constants"
import { GET_USER_MESSAGE_TAGS } from "../../graphql/queries/MessageTag"
import { useNotification } from "../../composables/notification"
import { useMessageRelUserStore } from "../../store/messageRelUserStore"
Expand All @@ -226,6 +226,7 @@ import SectionHeader from "../../components/layout/SectionHeader.vue"
import InputGroup from "primevue/inputgroup"
import InputText from "primevue/inputtext"
import BaseAppLink from "../../components/basecomponents/BaseAppLink.vue"
import { messageService } from "../../services/message"

const route = useRoute()
const router = useRouter()
Expand Down Expand Up @@ -367,6 +368,8 @@ function showInbox() {
"order[sendDate]": "desc",
itemsPerPage: initialRowsPerPage,
page: 1,
'receivers.receiver': securityStore.user["@id"],
'receivers.receiverType': MESSAGE_TYPE_INBOX,
}

loadMessages()
Expand All @@ -381,6 +384,7 @@ function showInboxByTag(tag) {
"order[sendDate]": "desc",
itemsPerPage: initialRowsPerPage,
page: 1,
'receivers.receiverType': MESSAGE_TYPE_INBOX,
}

loadMessages()
Expand All @@ -396,6 +400,7 @@ function showUnread() {
"receivers.read": false,
itemsPerPage: initialRowsPerPage,
page: 1,
'receivers.receiverType': MESSAGE_TYPE_INBOX,
}

loadMessages()
Expand All @@ -408,6 +413,7 @@ function showSent() {

fetchPayload = {
sender: securityStore.user["@id"],
"receivers.receiverType": MESSAGE_TYPE_SENDER,
"order[sendDate]": "desc",
itemsPerPage: initialRowsPerPage,
page: 1,
Expand Down Expand Up @@ -449,17 +455,26 @@ function findMyReceiver(message) {
return receivers.find(({ receiver }) => receiver["@id"] === securityStore.user["@id"])
}

function extractUserId(apiId) {
return apiId.split('/').pop()
}

async function deleteMessage(message) {
try {
const userId = extractUserId(securityStore.user["@id"])
const messageId = extractUserId(message["@id"])

if (message.sender["@id"] === securityStore.user["@id"]) {
message.status = MESSAGE_STATUS_DELETED
await store.dispatch("message/update", message)
await messageService.deleteMessageForUser(messageId, userId)
} else {
const myReceiver = findMyReceiver(message)
if (myReceiver) {
await store.dispatch("messagereluser/del", myReceiver)
}
}

await messageService.checkAndUpdateMessageStatus(messageId)

notification.showSuccessNotification(t("Message deleted"))
await messageRelUserStore.findUnreadCount()
loadMessages()
Expand Down
2 changes: 1 addition & 1 deletion src/CoreBundle/DataProvider/Extension/MessageExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ private function addWhere(QueryBuilder $qb, string $resourceClass): void

$qb->setParameters([
'current' => $user,
'deleted' => Message::MESSAGE_STATUS_DELETED,
'deleted' => Message::MESSAGE_STATUS_SENDER_DELETED,
// 'currentList' => [$user->getId()],
'inbox' => Message::MESSAGE_TYPE_INBOX,
// 'outbox' => Message::MESSAGE_TYPE_OUTBOX,
Expand Down
37 changes: 25 additions & 12 deletions src/CoreBundle/Entity/Message.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use ApiPlatform\Metadata\Delete;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Patch;
use ApiPlatform\Metadata\Post;
use ApiPlatform\Metadata\Put;
use Chamilo\CoreBundle\Filter\SearchOrFilter;
Expand Down Expand Up @@ -51,6 +52,20 @@
provider: MessageByGroupStateProvider::class
),
new Post(securityPostDenormalize: "is_granted('CREATE', object)"),
new Patch(
uriTemplate: '/messages/{id}/delete-for-user',
inputFormats: ['json' => ['application/json']],
security: "is_granted('ROLE_USER')",
output: false,
processor: MessageProcessor::class,
),
new Patch(
uriTemplate: '/messages/{id}/check-and-update-status',
inputFormats: ['json' => ['application/json']],
security: "is_granted('ROLE_USER')",
output: false,
processor: MessageProcessor::class,
),
],
normalizationContext: [
'groups' => ['message:read'],
Expand All @@ -62,17 +77,15 @@
processor: MessageProcessor::class,
)]
#[ApiFilter(filterClass: OrderFilter::class, properties: ['title', 'sendDate'])]
#[ApiFilter(
filterClass: SearchFilter::class,
properties: [
'msgType' => 'exact',
'status' => 'exact',
'sender' => 'exact',
'receivers.receiver' => 'exact',
'receivers.tags.tag' => 'exact',
'parent' => 'exact',
]
)]
#[ApiFilter(SearchFilter::class, properties: [

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.

'msgType' => 'exact',
'status' => 'exact',
'sender' => 'exact',
'receivers.receiver' => 'exact',
'receivers.receiverType' => 'exact',
'receivers.tags.tag' => 'exact',
'parent' => 'exact',
])]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line indented incorrectly; expected at least 4 spaces, found 0

#[ApiFilter(
BooleanFilter::class,
properties: ['receivers.read']
Expand All @@ -85,7 +98,7 @@ class Message
public const MESSAGE_TYPE_INVITATION = 6;
public const MESSAGE_TYPE_CONVERSATION = 7;
// status
public const MESSAGE_STATUS_DELETED = 3;
public const MESSAGE_STATUS_SENDER_DELETED = 3;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line indented incorrectly; expected 8 spaces, found 4

public const MESSAGE_STATUS_DRAFT = 4;
public const MESSAGE_STATUS_INVITATION_PENDING = 5;
public const MESSAGE_STATUS_INVITATION_ACCEPTED = 6;
Expand Down
1 change: 1 addition & 0 deletions src/CoreBundle/Entity/MessageRelUser.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class MessageRelUser
{
public const TYPE_TO = 1;
public const TYPE_CC = 2;
public const TYPE_SENDER = 8;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line indented incorrectly; expected 8 spaces, found 4


#[Groups(['message_rel_user:read'])]
#[ORM\Column(name: 'id', type: 'integer')]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public function up(Schema $schema): void

$newTypeQueries[] = sprintf(
'UPDATE message SET status = %d WHERE msg_type = %d',
Message::MESSAGE_STATUS_DELETED,
Message::MESSAGE_STATUS_SENDER_DELETED,
self::OLD_MESSAGE_STATUS_DELETED
);
$newTypeQueries[] = sprintf(
Expand Down
4 changes: 2 additions & 2 deletions src/CoreBundle/Repository/MessageRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public function findByGroupId(int $groupId)
$qb->where('m.group = :groupId')
->andWhere('m.status NOT IN (:excludedStatuses)')
->setParameter('groupId', $groupId)
->setParameter('excludedStatuses', [Message::MESSAGE_STATUS_DRAFT, Message::MESSAGE_STATUS_DELETED])
->setParameter('excludedStatuses', [Message::MESSAGE_STATUS_DRAFT, Message::MESSAGE_STATUS_SENDER_DELETED])
->orderBy('m.id', 'ASC')
;

Expand Down Expand Up @@ -304,7 +304,7 @@ public function deleteTopicAndChildren(int $groupId, int $topicId): void

/** @var Message $message */
foreach ($messages as $message) {
$message->setMsgType(Message::MESSAGE_STATUS_DELETED);
$message->setMsgType(Message::MESSAGE_STATUS_SENDER_DELETED);
$entityManager->persist($message);
}

Expand Down
75 changes: 71 additions & 4 deletions src/CoreBundle/State/MessageProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

use ApiPlatform\Metadata\DeleteOperationInterface;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\Metadata\Patch;
use ApiPlatform\Metadata\Post;
use ApiPlatform\State\ProcessorInterface;
use Chamilo\CoreBundle\Entity\Message;
Expand All @@ -16,6 +17,8 @@
use Chamilo\CoreBundle\Repository\ResourceNodeRepository;
use Doctrine\ORM\EntityManagerInterface;
use Notification;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\HttpFoundation\RequestStack;
use Vich\UploaderBundle\Storage\FlysystemStorage;

final class MessageProcessor implements ProcessorInterface
Expand All @@ -26,6 +29,8 @@ public function __construct(
private readonly FlysystemStorage $storage,
private readonly EntityManagerInterface $entityManager,
private readonly ResourceNodeRepository $resourceNodeRepository,
private readonly Security $security,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line indented incorrectly; expected 4 spaces, found 8

private readonly RequestStack $requestStack
) {}

public function process($data, Operation $operation, array $uriVariables = [], array $context = [])
Expand All @@ -34,6 +39,14 @@ public function process($data, Operation $operation, array $uriVariables = [], a
return $this->removeProcessor->process($data, $operation, $uriVariables, $context);
}

if ($operation instanceof Patch && str_contains($operation->getUriTemplate(), 'delete-for-user')) {
return $this->processDeleteForUser($data);
}

if ($operation instanceof Patch && str_contains($operation->getUriTemplate(), 'check-and-update-status')) {
return $this->checkAndUpdateMessageStatus($data);
}

/** @var Message $message */
$message = $this->persistProcessor->process($data, $operation, $uriVariables, $context);

Expand All @@ -47,12 +60,66 @@ public function process($data, Operation $operation, array $uriVariables = [], a
}
}

$user = $this->security->getUser();
if (!$user) {
throw new \LogicException('User not found.');
}
$messageRelUser = new MessageRelUser();
$messageRelUser->setMessage($message);
$messageRelUser->setReceiver($user);
$messageRelUser->setReceiverType(MessageRelUser::TYPE_SENDER);
$this->entityManager->persist($messageRelUser);

if ($message->getMsgType() === Message::MESSAGE_TYPE_INBOX) {
$this->saveNotificationForInboxMessage($message);
}

$this->entityManager->flush();

if ($operation instanceof Post) {
if (Message::MESSAGE_TYPE_INBOX === $message->getMsgType()) {
$this->saveNotificationForInboxMessage($message);
}
return $message;
}

private function processDeleteForUser($data): Message
{
/** @var Message $message */
$message = $data;

$request = $this->requestStack->getCurrentRequest();
if (!$request) {
throw new \LogicException('Cannot get current request');
}

$requestData = json_decode($request->getContent(), true);
if (!isset($requestData['userId'])) {
throw new \InvalidArgumentException('The field userId is required.');
}

$userId = $requestData['userId'];
$messageRelUserRepository = $this->entityManager->getRepository(MessageRelUser::class);
$messageRelUser = $messageRelUserRepository->findOneBy([
'message' => $message,
'receiver' => $userId,
]);

if ($messageRelUser) {
$this->entityManager->remove($messageRelUser);
$this->entityManager->flush();
}

return $message;
}

private function checkAndUpdateMessageStatus($data): Message
{
/** @var Message $message */
$message = $data;

$messageRelUserRepository = $this->entityManager->getRepository(MessageRelUser::class);
$remainingReceivers = $messageRelUserRepository->count(['message' => $message]);

if ($remainingReceivers === 0) {
$message->setStatus(Message::MESSAGE_STATUS_SENDER_DELETED);
$this->entityManager->flush();
}

return $message;
Expand Down
Loading