diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 3004b98093..c5fea823af 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -68,6 +68,8 @@ use OCA\Mail\Service\UserPreferenceService; use OCA\Mail\SetupChecks\MailConnectionPerformance; use OCA\Mail\SetupChecks\MailTransport; +use OCA\Mail\TaskProcessing\MailSendTask; +use OCA\Mail\TaskProcessing\TaskProcessingProvider; use OCA\Mail\Vendor\Favicon\Favicon; use OCP\AppFramework\App; use OCP\AppFramework\Bootstrap\IBootContext; @@ -164,6 +166,9 @@ public function register(IRegistrationContext $context): void { $context->registerSetupCheck(MailTransport::class); $context->registerSetupCheck(MailConnectionPerformance::class); + $context->registerTaskProcessingProvider(TaskProcessingProvider::class); + $context->registerTaskProcessingTaskType(MailSendTask::class); + // bypass Horde Translation system Horde_Translation::setHandler('Horde_Imap_Client', new HordeTranslationHandler()); Horde_Translation::setHandler('Horde_Mime', new HordeTranslationHandler()); diff --git a/lib/Provider/MailService.php b/lib/Provider/MailService.php index c140adfc9a..106438ab46 100644 --- a/lib/Provider/MailService.php +++ b/lib/Provider/MailService.php @@ -8,7 +8,9 @@ */ namespace OCA\Mail\Provider; +use OCA\Mail\AppInfo\Application; use OCA\Mail\Provider\Command\MessageSend; +use OCA\Mail\TaskProcessing\MailSendTask; use OCP\Mail\Provider\Address; use OCP\Mail\Provider\Exception\SendException; use OCP\Mail\Provider\IAddress; @@ -16,7 +18,8 @@ use OCP\Mail\Provider\IMessageSend; use OCP\Mail\Provider\IService; use OCP\Mail\Provider\Message; - +use OCP\TaskProcessing\IManager as TaskProcessingManager; +use OCP\TaskProcessing\Task; use Psr\Container\ContainerInterface; class MailService implements IService, IMessageSend { @@ -178,12 +181,49 @@ public function initiateMessage(): IMessage { * @since 4.0.0 * * @param IMessage $message mail message object with all required parameters to send a message - * @param array $options array of options reserved for future use + * @param array{sendMode?:'instant'|'queued'|'deferred',sendDelay?:int} $options array of options reserved for future use + * + * $options['sendMode'] can be: + * - 'instant': sends the message immediately (connects to the mail service right away) + * - 'queued': adds the message to the task processing queue to be sent as soon as possible + * - 'deferred': adds the message to the task processing queue to be sent after a delay + * $options['sendDelay'] is only applicable if 'sendMode' is 'deferred' and specifies the delay in seconds before sending the message * * @throws SendException on failure, check message for reason */ #[\Override] public function sendMessage(IMessage $message, array $options = []): void { + // if send mode is not set, default to queued + if (!isset($options['sendMode'])) { + $options['sendMode'] = 'queued'; + } + // if send mode is not instant use task processing this sends the message as soon as possible + if ($options['sendMode'] !== 'instant') { + $taskProcessingManager = $this->container->get(TaskProcessingManager::class); + $availableTaskTypes = $taskProcessingManager->getAvailableTaskTypes(); + // if task processing is available use it + if (isset($availableTaskTypes[MailSendTask::ID])) { + $task = new Task( + MailSendTask::ID, + [ + 'userId' => $this->userId, + 'serviceId' => $this->serviceId, + 'message' => $message, + 'options' => $options, + ], + Application::APP_ID, + null + ); + + if ($options['sendMode'] === 'deferred' && isset($options['sendDelay']) && is_int($options['sendDelay'])) { + $task->setScheduledAt(time() + $options['sendDelay']); + } + + $taskProcessingManager->scheduleTask($task); + return; + } + } + // fallback to instant send /** @var MessageSend $cmd */ $cmd = $this->container->get(MessageSend::class); // perform action diff --git a/lib/TaskProcessing/MailSendProvider.php b/lib/TaskProcessing/MailSendProvider.php new file mode 100644 index 0000000000..f4ed3531a6 --- /dev/null +++ b/lib/TaskProcessing/MailSendProvider.php @@ -0,0 +1,131 @@ + new ShapeDescriptor( + $this->l->t('User ID'), + $this->l->t('The ID of the user sending the email'), + EShapeType::Text + ), + 'serviceId' => new ShapeDescriptor( + $this->l->t('Service ID'), + $this->l->t('The ID of the service/account sending the email'), + EShapeType::Number + ), + 'message' => new ShapeDescriptor( + $this->l->t('Message'), + $this->l->t('The email message to be sent (OCP\Mail\IMessage)'), + EShapeType::Object + ), + 'options' => new ShapeDescriptor( + $this->l->t('Options'), + $this->l->t('Additional options for sending the email'), + EShapeType::Array + ), + ]; + + } + + public function getOptionalOutputShape(): array { + return []; + } + + public function getInputShapeEnumValues(): array { + return []; + } + + public function getInputShapeDefaults(): array { + return []; + } + + public function getOptionalInputShapeEnumValues(): array { + return []; + } + + public function getOptionalInputShapeDefaults(): array { + return []; + } + + public function getOutputShapeEnumValues(): array { + return []; + } + + public function getOptionalOutputShapeEnumValues(): array { + return []; + } + + public function process(?string $userId, array $input, callable $reportProgress): array { + // extract parameters + $userId = $input['userId'] ?? null; + $serviceId = $input['serviceId'] ?? null; + $options = $input['options'] ?? []; + if (isset($input['message'])) { + $message = new Message(); + $message->jsonDeserialize((array)$input['message']); + } else { + $message = null; + } + // validate parameters + if ($userId === null || empty($userId)) { + throw new \InvalidArgumentException('Invalid or missing userId'); + } + if ($serviceId === null || empty($serviceId) || $serviceId <= 0) { + throw new \InvalidArgumentException('Invalid or missing serviceId'); + } + if (!$message instanceof Message) { + throw new \InvalidArgumentException('Invalid or missing message'); + } + // perform task + /** @var MessageSend $cmd */ + $cmd = $this->container->get(MessageSend::class); + $cmd->perform($userId, (string)$serviceId, $message, $options); + + return []; + } +} diff --git a/lib/TaskProcessing/MailSendTask.php b/lib/TaskProcessing/MailSendTask.php new file mode 100644 index 0000000000..dcb3b403ea --- /dev/null +++ b/lib/TaskProcessing/MailSendTask.php @@ -0,0 +1,85 @@ +l = $l10nFactory->get('lib'); + } + + public function getName(): string { + return $this->l->t('Mail Send'); + } + + public function getDescription(): string { + return $this->l->t('Send an email using the mail app.'); + } + + public function getId(): string { + return self::ID; + } + + public function getInputShape(): array { + return [ + 'userId' => new ShapeDescriptor( + $this->l->t('User ID'), + $this->l->t('The ID of the user sending the email'), + EShapeType::Text + ), + 'serviceId' => new ShapeDescriptor( + $this->l->t('Service ID'), + $this->l->t('The ID of the service/account sending the email'), + EShapeType::Number + ), + 'message' => new ShapeDescriptor( + $this->l->t('Message'), + $this->l->t('The email message to be sent (OCP\Mail\IMessage)'), + EShapeType::Object + ), + 'options' => new ShapeDescriptor( + $this->l->t('Options'), + $this->l->t('Additional options for sending the email'), + EShapeType::Array + ), + ]; + } + + public function getOutputShape(): array { + return [ + 'status_code' => new ShapeDescriptor( + $this->l->t('Status Code'), + $this->l->t('The status code of the email sending operation'), + EShapeType::Number + ), + 'status_message' => new ShapeDescriptor( + $this->l->t('Status Message'), + $this->l->t('A message describing the result of the email sending operation'), + EShapeType::Text + ), + ]; + } +}