feat(core): add CLI management for tenant system stores
Signed-off-by: Sebastian <krupinski01@gmail.com>
This commit is contained in:
@@ -80,8 +80,8 @@ final class CompositionController extends ControllerAbstract {
|
|||||||
if (!isset($data['action']) || !is_string($data['action'])) {
|
if (!isset($data['action']) || !is_string($data['action'])) {
|
||||||
throw new InvalidArgumentException('Invalid parameter: action must be a string');
|
throw new InvalidArgumentException('Invalid parameter: action must be a string');
|
||||||
}
|
}
|
||||||
if (!in_array($data['action'], ['fresh', 'reply', 'forward'], true)) {
|
if (!in_array($data['action'], ['fresh', 'reply', 'replyAll', 'forward'], true)) {
|
||||||
throw new InvalidArgumentException('Invalid parameter: action must be one of fresh, reply, forward');
|
throw new InvalidArgumentException('Invalid parameter: action must be one of fresh, reply, replyAll, forward');
|
||||||
}
|
}
|
||||||
if (!isset($data['sender']) || !is_array($data['sender'])) {
|
if (!isset($data['sender']) || !is_array($data['sender'])) {
|
||||||
throw new InvalidArgumentException('Invalid parameter: sender must be an array');
|
throw new InvalidArgumentException('Invalid parameter: sender must be an array');
|
||||||
@@ -89,8 +89,8 @@ final class CompositionController extends ControllerAbstract {
|
|||||||
if (!isset($data['message']) || !is_array($data['message'])) {
|
if (!isset($data['message']) || !is_array($data['message'])) {
|
||||||
throw new InvalidArgumentException('Invalid parameter: message must be an array');
|
throw new InvalidArgumentException('Invalid parameter: message must be an array');
|
||||||
}
|
}
|
||||||
if (($data['action'] === 'reply' || $data['action'] === 'forward') && (!isset($data['source']) || !is_string($data['source']) || $data['source'] === '')) {
|
if (($data['action'] === 'reply' || $data['action'] === 'replyAll' || $data['action'] === 'forward') && (!isset($data['source']) || !is_string($data['source']) || $data['source'] === '')) {
|
||||||
throw new InvalidArgumentException('Invalid parameter: source must be a non-empty string for reply or forward action');
|
throw new InvalidArgumentException('Invalid parameter: source must be a non-empty string for reply, replyAll, or forward action');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->compositionManager->stage($tenantId, $userId, $data['identifier'], $data['action'], $data['sender'], $data['message'], $data['source'] ?? null);
|
return $this->compositionManager->stage($tenantId, $userId, $data['identifier'], $data['action'], $data['sender'], $data['message'], $data['source'] ?? null);
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ const props = defineProps<{
|
|||||||
// Emits
|
// Emits
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
reply: [entity: EntityObject]
|
reply: [entity: EntityObject]
|
||||||
|
replyAll: [entity: EntityObject]
|
||||||
forward: [entity: EntityObject]
|
forward: [entity: EntityObject]
|
||||||
move: [entity: EntityObject]
|
move: [entity: EntityObject]
|
||||||
delete: [entity: EntityObject]
|
delete: [entity: EntityObject]
|
||||||
@@ -107,6 +108,12 @@ const handleReply = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleReplyAll = () => {
|
||||||
|
if (props.entity) {
|
||||||
|
emit('replyAll', props.entity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const handleForward = () => {
|
const handleForward = () => {
|
||||||
if (props.entity) {
|
if (props.entity) {
|
||||||
emit('forward', props.entity)
|
emit('forward', props.entity)
|
||||||
@@ -176,6 +183,7 @@ const handleViewerDownload = (item: MediaViewerItem) => {
|
|||||||
:security-level="effectiveSecurityLevel"
|
:security-level="effectiveSecurityLevel"
|
||||||
:is-security-overridden="overrideSecurityLevel !== null"
|
:is-security-overridden="overrideSecurityLevel !== null"
|
||||||
@reply="handleReply"
|
@reply="handleReply"
|
||||||
|
@reply-all="handleReplyAll"
|
||||||
@forward="handleForward"
|
@forward="handleForward"
|
||||||
@move="handleMove"
|
@move="handleMove"
|
||||||
@delete="handleDelete"
|
@delete="handleDelete"
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ defineEmits<{
|
|||||||
</v-btn>
|
</v-btn>
|
||||||
|
|
||||||
<v-toolbar-title>
|
<v-toolbar-title>
|
||||||
{{ mode === ComposerMode.Reply ? 'Reply' : mode === ComposerMode.Forward ? 'Forward' : 'New Message' }}
|
{{ mode === ComposerMode.Reply ? 'Reply' : mode === ComposerMode.ReplyAll ? 'Reply All' : mode === ComposerMode.Forward ? 'Forward' : 'New Message' }}
|
||||||
</v-toolbar-title>
|
</v-toolbar-title>
|
||||||
|
|
||||||
<v-spacer />
|
<v-spacer />
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ const props = defineProps<Props>()
|
|||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
reply: []
|
reply: []
|
||||||
|
replyAll: []
|
||||||
forward: []
|
forward: []
|
||||||
move: []
|
move: []
|
||||||
delete: []
|
delete: []
|
||||||
@@ -47,7 +48,7 @@ const currentSecurityLevel = computed(() => {
|
|||||||
<v-btn
|
<v-btn
|
||||||
icon="mdi-reply-all"
|
icon="mdi-reply-all"
|
||||||
variant="text"
|
variant="text"
|
||||||
@click="emit('reply')"
|
@click="emit('replyAll')"
|
||||||
>
|
>
|
||||||
<v-icon>mdi-reply-all</v-icon>
|
<v-icon>mdi-reply-all</v-icon>
|
||||||
<v-tooltip activator="parent" location="bottom">Reply All</v-tooltip>
|
<v-tooltip activator="parent" location="bottom">Reply All</v-tooltip>
|
||||||
|
|||||||
@@ -167,6 +167,8 @@ const handleMessageComposeFresh = () => mailUiStore.openComposer()
|
|||||||
|
|
||||||
const handleMessageComposeReply = (message: EntityObject) => mailUiStore.openComposer(message, ComposerMode.Reply)
|
const handleMessageComposeReply = (message: EntityObject) => mailUiStore.openComposer(message, ComposerMode.Reply)
|
||||||
|
|
||||||
|
const handleMessageComposeReplyAll = (message: EntityObject) => mailUiStore.openComposer(message, ComposerMode.ReplyAll)
|
||||||
|
|
||||||
const handleMessageComposeForward = (message: EntityObject) => mailUiStore.openComposer(message, ComposerMode.Forward)
|
const handleMessageComposeForward = (message: EntityObject) => mailUiStore.openComposer(message, ComposerMode.Forward)
|
||||||
|
|
||||||
const handleMessageComposeClose = () => mailUiStore.closeComposer()
|
const handleMessageComposeClose = () => mailUiStore.closeComposer()
|
||||||
@@ -339,6 +341,7 @@ const handleMessageSelectionDelete = () => mailUiStore.deleteSelectedMessages()
|
|||||||
:entity="selectedMessage"
|
:entity="selectedMessage"
|
||||||
@compose="handleMessageComposeFresh"
|
@compose="handleMessageComposeFresh"
|
||||||
@reply="handleMessageComposeReply"
|
@reply="handleMessageComposeReply"
|
||||||
|
@reply-all="handleMessageComposeReplyAll"
|
||||||
@forward="handleMessageComposeForward"
|
@forward="handleMessageComposeForward"
|
||||||
@move="handleMessageMove"
|
@move="handleMessageMove"
|
||||||
@delete="handleMessageDelete"
|
@delete="handleMessageDelete"
|
||||||
|
|||||||
@@ -118,7 +118,8 @@ export const useMailCompositionStore = defineStore('mailCompositionStore', () =>
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
activeDraft.value = buildDraft(mode, sender, source)
|
const ownAddresses = senderIdentities.value.map(identity => identity.address)
|
||||||
|
activeDraft.value = buildDraft(mode, sender, source, ownAddresses)
|
||||||
activeDraft.value.action = mode
|
activeDraft.value.action = mode
|
||||||
|
|
||||||
stageDraft()
|
stageDraft()
|
||||||
@@ -558,9 +559,9 @@ export const useMailCompositionStore = defineStore('mailCompositionStore', () =>
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
function buildDraft(mode: ComposerMode, sender: ComposerSenderIdentity, source?: EntityObject | MessageAddressObject | null): ComposerDraft {
|
function buildDraft(mode: ComposerMode, sender: ComposerSenderIdentity, source: EntityObject | MessageAddressObject | null | undefined, ownAddresses: string[]): ComposerDraft {
|
||||||
const composition = createIdentifier()
|
const composition = createIdentifier()
|
||||||
const freshMessage = buildMessage(mode, source)
|
const freshMessage = buildMessage(mode, source, ownAddresses)
|
||||||
const freshAttachments = buildAttachments(composition, mode, source)
|
const freshAttachments = buildAttachments(composition, mode, source)
|
||||||
const sourceIdentifier = source instanceof EntityObject ? source.identifier : null
|
const sourceIdentifier = source instanceof EntityObject ? source.identifier : null
|
||||||
return {
|
return {
|
||||||
@@ -576,7 +577,7 @@ function buildDraft(mode: ComposerMode, sender: ComposerSenderIdentity, source?:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildMessage(mode: ComposerMode, source: EntityObject | MessageAddressObject | null | undefined): ComposerDraftMessage {
|
function buildMessage(mode: ComposerMode, source: EntityObject | MessageAddressObject | null | undefined, ownAddresses: string[] = []): ComposerDraftMessage {
|
||||||
|
|
||||||
if (!source) {
|
if (!source) {
|
||||||
return emptyMessage()
|
return emptyMessage()
|
||||||
@@ -603,12 +604,28 @@ function buildMessage(mode: ComposerMode, source: EntityObject | MessageAddressO
|
|||||||
const sentAt = sourceMessage.sent || source.created || ''
|
const sentAt = sourceMessage.sent || source.created || ''
|
||||||
const sentLabel = sentAt ? new Date(sentAt).toLocaleString() : 'an unknown time'
|
const sentLabel = sentAt ? new Date(sentAt).toLocaleString() : 'an unknown time'
|
||||||
|
|
||||||
if (mode === ComposerMode.Reply) {
|
if (mode === ComposerMode.Reply || mode === ComposerMode.ReplyAll) {
|
||||||
const replyToAddr = sourceMessage.replyTo?.[0] ?? sourceMessage.from ?? null
|
const replyToAddr = sourceMessage.replyTo?.[0] ?? sourceMessage.from ?? null
|
||||||
const replyToAddress = replyToAddr?.address || ''
|
const replyToAddress = replyToAddr?.address || ''
|
||||||
|
const to: MessageAddressInterface[] = replyToAddress ? [{ address: replyToAddress, label: replyToAddr?.label }] : []
|
||||||
|
|
||||||
|
let cc: MessageAddressInterface[] = []
|
||||||
|
if (mode === ComposerMode.ReplyAll) {
|
||||||
|
const excluded = new Set([replyToAddress.toLowerCase(), ...ownAddresses.map(addr => addr.toLowerCase())])
|
||||||
|
const seen = new Set<string>()
|
||||||
|
cc = [...(sourceMessage.to || []), ...(sourceMessage.cc || [])].filter(recipient => {
|
||||||
|
const address = recipient.address?.toLowerCase() || ''
|
||||||
|
if (!address || excluded.has(address) || seen.has(address)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
seen.add(address)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
to: replyToAddress ? [{ address: replyToAddress, label: replyToAddr?.label }] : [],
|
to,
|
||||||
cc: [],
|
cc,
|
||||||
bcc: [],
|
bcc: [],
|
||||||
subject: /^Re:/i.test(originalSubject) ? originalSubject : `Re: ${originalSubject}`,
|
subject: /^Re:/i.test(originalSubject) ? originalSubject : `Re: ${originalSubject}`,
|
||||||
body: {
|
body: {
|
||||||
@@ -631,7 +648,7 @@ function buildMessage(mode: ComposerMode, source: EntityObject | MessageAddressO
|
|||||||
}
|
}
|
||||||
|
|
||||||
function buildAttachments(composition: string, mode: ComposerMode, source?: EntityObject | MessageAddressObject | null): Record<string, ComposerDraftAttachment> {
|
function buildAttachments(composition: string, mode: ComposerMode, source?: EntityObject | MessageAddressObject | null): Record<string, ComposerDraftAttachment> {
|
||||||
if (mode === ComposerMode.Fresh || mode === ComposerMode.Reply || !(source instanceof EntityObject)) {
|
if (mode === ComposerMode.Fresh || mode === ComposerMode.Reply || mode === ComposerMode.ReplyAll || !(source instanceof EntityObject)) {
|
||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import type { MessageAddressInterface } from "@MailManager/types/message"
|
|||||||
export enum ComposerMode {
|
export enum ComposerMode {
|
||||||
Fresh = 'fresh',
|
Fresh = 'fresh',
|
||||||
Reply = 'reply',
|
Reply = 'reply',
|
||||||
|
ReplyAll = 'replyAll',
|
||||||
Forward = 'forward',
|
Forward = 'forward',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user