Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 1 addition & 1 deletion appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ The rating depends on the installed text processing backend. See [the rating ove
Learn more about the Nextcloud Ethical AI Rating [in our blog](https://nextcloud.com/blog/nextcloud-ethical-ai-rating/).
]]></description>
<version>5.7.0-alpha.2</version>
<version>5.7.0-alpha.3</version>
<licence>agpl</licence>
<author homepage="https://github.com/ChristophWurst">Christoph Wurst</author>
<author homepage="https://github.com/GretaD">GretaD</author>
Expand Down
4 changes: 4 additions & 0 deletions lib/Account.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ public function getUserId() {
public function getDebug(): bool {
return $this->account->getDebug();
}

public function getImipCreate(): bool {
return $this->account->getImipCreate();
}

/**
* Set the quota percentage
Expand Down
7 changes: 6 additions & 1 deletion lib/Controller/AccountsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,9 @@ public function patchAccount(int $id,
?int $trashRetentionDays = null,
?int $junkMailboxId = null,
?bool $searchBody = null,
?bool $classificationEnabled = null): JSONResponse {
?bool $classificationEnabled = null,
?bool $imipCreate = null,
): JSONResponse {
$account = $this->accountService->find($this->currentUserId, $id);

$dbAccount = $account->getMailAccount();
Expand Down Expand Up @@ -282,6 +284,9 @@ public function patchAccount(int $id,
if ($classificationEnabled !== null) {
$dbAccount->setClassificationEnabled($classificationEnabled);
}
if ($imipCreate !== null) {
$dbAccount->setImipCreate($imipCreate);
}
return new JSONResponse(
new Account($this->accountService->save($dbAccount))
);
Expand Down
9 changes: 9 additions & 0 deletions lib/Db/MailAccount.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@
* @method void setDebug(bool $debug)
* @method bool getClassificationEnabled()
* @method void setClassificationEnabled(bool $classificationEnabled)
* @method bool getImipCreate()
* @method void setImipCreate(bool $value)
*/
class MailAccount extends Entity {
public const SIGNATURE_MODE_PLAIN = 0;
Expand Down Expand Up @@ -190,6 +192,8 @@ class MailAccount extends Entity {
protected bool $debug = false;
protected bool $classificationEnabled = true;

protected bool $imipCreate = false;

/**
* @param array $params
*/
Expand Down Expand Up @@ -253,6 +257,9 @@ public function __construct(array $params = []) {
if (isset($params['classificationEnabled'])) {
$this->setClassificationEnabled($params['classificationEnabled']);
}
if (isset($params['imipCreate'])) {
$this->setImipCreate($params['imipCreate']);
}

$this->addType('inboundPort', 'integer');
$this->addType('outboundPort', 'integer');
Expand All @@ -278,6 +285,7 @@ public function __construct(array $params = []) {
$this->addType('oooFollowsSystem', 'boolean');
$this->addType('debug', 'boolean');
$this->addType('classificationEnabled', 'boolean');
$this->addType('imipCreate', 'boolean');
}

public function getOutOfOfficeFollowsSystem(): bool {
Expand Down Expand Up @@ -327,6 +335,7 @@ public function toJson() {
'outOfOfficeFollowsSystem' => $this->getOutOfOfficeFollowsSystem(),
'debug' => $this->getDebug(),
'classificationEnabled' => $this->getClassificationEnabled(),
'imipCreate' => $this->getImipCreate(),
];

if (!is_null($this->getOutboundHost())) {
Expand Down
37 changes: 37 additions & 0 deletions lib/Migration/Version5007Date20251208000000.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\Mail\Migration;

use Closure;
use OCP\DB\ISchemaWrapper;
use OCP\DB\Types;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;

class Version5007Date20251208000000 extends SimpleMigrationStep {

/**
* @param IOutput $output
* @param Closure(): ISchemaWrapper $schemaClosure
* @param array $options
* @return null|ISchemaWrapper
*/
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
$schema = $schemaClosure();
$accountsTable = $schema->getTable('mail_accounts');
if (!$accountsTable->hasColumn('imip_create')) {
$accountsTable->addColumn('imip_create', Types::BOOLEAN, [
'default' => '0',
'notNull' => false,
]);
}
return $schema;
}
}
28 changes: 13 additions & 15 deletions lib/Service/IMipService.php
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,9 @@
continue;
}

$principalUri = 'principals/users/' . $account->getUserId();
$userId = $account->getUserId();
$recipient = $account->getEmail();
$imipCreate = $account->getImipCreate();

foreach ($filteredMessages as $message) {
/** @var IMAPMessage $imapMessage */
Expand All @@ -138,20 +139,17 @@
try {
// an IMAP message could contain more than one iMIP object
foreach ($imapMessage->scheduling as $schedulingInfo) {
if ($schedulingInfo['method'] === 'REQUEST') {
$processed = $this->calendarManager->handleIMipRequest($principalUri, $sender, $recipient, $schedulingInfo['contents']);
$message->setImipProcessed($processed);
$message->setImipError(!$processed);
} elseif ($schedulingInfo['method'] === 'REPLY') {
$processed = $this->calendarManager->handleIMipReply($principalUri, $sender, $recipient, $schedulingInfo['contents']);
$message->setImipProcessed($processed);
$message->setImipError(!$processed);
} elseif ($schedulingInfo['method'] === 'CANCEL') {
$replyTo = $imapMessage->getReplyTo()->first()?->getEmail();
$processed = $this->calendarManager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $schedulingInfo['contents']);
$message->setImipProcessed($processed);
$message->setImipError(!$processed);
}
$processed = $this->calendarManager->handleIMip(

Check failure on line 142 in lib/Service/IMipService.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-stable31

UndefinedInterfaceMethod

lib/Service/IMipService.php:142:44: UndefinedInterfaceMethod: Method OCP\Calendar\IManager::handleIMip does not exist (see https://psalm.dev/181)
$userId,
$schedulingInfo['contents'],
[

Check failure on line 145 in lib/Service/IMipService.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-stable32

InvalidArgument

lib/Service/IMipService.php:145:8: InvalidArgument: Argument 3 of OCP\Calendar\IManager::handleIMip expects array{absent?: 'create', recipient?: string}, but array{absent: 'create'|'ignore', absentCreateStatus: 'tentative', recipient: string} with additional array shape fields (absentCreateStatus) was provided (see https://psalm.dev/004)

Check failure on line 145 in lib/Service/IMipService.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-master

InvalidArgument

lib/Service/IMipService.php:145:8: InvalidArgument: Argument 3 of OCP\Calendar\IManager::handleIMip expects array{absent?: 'create', recipient?: string}, but array{absent: 'create'|'ignore', absentCreateStatus: 'tentative', recipient: string} with additional array shape fields (absentCreateStatus) was provided (see https://psalm.dev/004)
'recipient' => $recipient,
'absent' => $imipCreate ? 'create' : 'ignore',
'absentCreateStatus' => 'tentative',
],
);
$message->setImipProcessed($processed);
$message->setImipError(!$processed);
}
} catch (Throwable $e) {
$this->logger->error('iMIP message processing failed', [
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "nextcloud-mail",
"version": "5.7.0-alpha.1",
"version": "5.7.0-alpha.3",
"private": true,
"description": "Nextcloud Mail",
"license": "AGPL-3.0-only",
Expand Down
8 changes: 8 additions & 0 deletions src/components/AccountSettings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@
</NcButton>
</div>
</AppSettingsSection>
<AppSettingsSection
v-if="account"
id="calendar-settings"
:name="t('mail', 'Calendar settings')">
<CalendarSettings :account="account" />
</AppSettingsSection>
<AppSettingsSection
v-if="account"
id="classification"
Expand All @@ -79,14 +85,14 @@
id="mail-filters"
:name="t('mail', 'Filters')">
<div id="mail-filters">
<MailFilters :key="account.accountId" ref="mailFilters" :account="account" />

Check warning on line 88 in src/components/AccountSettings.vue

View workflow job for this annotation

GitHub Actions / NPM lint

'mailFilters' is defined as ref, but never used
</div>
</AppSettingsSection>
<AppSettingsSection
v-if="account"
id="quick-actions-settings"
:name="t('mail', 'Quick actions')">
<Settings :key="account.accountId" ref="quickActions" :account="account" />

Check warning on line 95 in src/components/AccountSettings.vue

View workflow job for this annotation

GitHub Actions / NPM lint

'quickActions' is defined as ref, but never used
</AppSettingsSection>
<AppSettingsSection
v-if="account && account.sieveEnabled"
Expand All @@ -95,7 +101,7 @@
<div id="sieve-filter">
<SieveFilterForm
:key="account.accountId"
ref="sieveFilterForm"

Check warning on line 104 in src/components/AccountSettings.vue

View workflow job for this annotation

GitHub Actions / NPM lint

'sieveFilterForm' is defined as ref, but never used
:account="account" />
</div>
</AppSettingsSection>
Expand All @@ -119,7 +125,7 @@
<div id="sieve-settings">
<SieveAccountForm
:key="account.accountId"
ref="sieveAccountForm"

Check warning on line 128 in src/components/AccountSettings.vue

View workflow job for this annotation

GitHub Actions / NPM lint

'sieveAccountForm' is defined as ref, but never used
:account="account" />
</div>
</AppSettingsSection>
Expand All @@ -138,6 +144,7 @@
import AliasSettings from '../components/AliasSettings.vue'
import EditorSettings from '../components/EditorSettings.vue'
import SignatureSettings from '../components/SignatureSettings.vue'
import CalendarSettings from './CalendarSettings.vue'
import CertificateSettings from './CertificateSettings.vue'
import MailFilters from './mailFilter/MailFilters.vue'
import OutOfOfficeForm from './OutOfOfficeForm.vue'
Expand All @@ -161,6 +168,7 @@
AppSettingsDialog,
AppSettingsSection,
AccountDefaultsSettings,
CalendarSettings,
OutOfOfficeForm,
CertificateSettings,
TrashRetentionSettings,
Expand Down Expand Up @@ -203,7 +211,7 @@
},

watch: {
open(newState, oldState) {

Check warning on line 214 in src/components/AccountSettings.vue

View workflow job for this annotation

GitHub Actions / NPM lint

'oldState' is defined but never used
if (newState === true && this.fetchActiveSieveScript === true) {
logger.debug(`Load active sieve script for account ${this.account.accountId}`)
this.fetchActiveSieveScript = false
Expand Down
69 changes: 69 additions & 0 deletions src/components/CalendarSettings.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<!--
- SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
- SPDX-License-Identifier: AGPL-3.0-or-later
-->

<template>
<div>
<NcCheckboxRadioSwitch
Copy link
Member

Choose a reason for hiding this comment

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

missing loading state, which is problematic for multiple clicks in a short time

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

id="imip-create"
:checked="imipCreate"
@update:checked="onToggleImipCreate">
{{ t('mail', 'Automatically create tentative appointments in calendar') }}
</NcCheckboxRadioSwitch>
</div>
</template>

<script>
import { NcCheckboxRadioSwitch } from '@nextcloud/vue'
import { mapStores } from 'pinia'
import Logger from '../logger.js'
import useMainStore from '../store/mainStore.js'

export default {
name: 'CalendarSettings',
components: {
NcCheckboxRadioSwitch,
},

Check failure on line 27 in src/components/CalendarSettings.vue

View workflow job for this annotation

GitHub Actions / NPM lint

Enforce new lines between multi-line properties in Vue components
props: {
account: {
type: Object,
required: true,
},
},

data() {
return {
imipCreate: this.account.imipCreate,
}
},

computed: {
...mapStores(useMainStore),
},

methods: {
async onToggleImipCreate(val) {
const oldVal = this.imipCreate
this.imipCreate = val

try {
await this.mainStore.patchAccount({
account: this.account,
data: {
imipCreate: val,
},
})
Logger.info(`Automatic calendar appointment creation ${val ? 'enabled' : 'disabled'}`)
} catch (error) {
Logger.error(`could not ${val ? 'enable' : 'disable'} automatic calendar appointment creation`, { error })
this.imipCreate = oldVal
throw error
}
},
},
}
</script>

<style lang="scss" scoped>

Check failure on line 68 in src/components/CalendarSettings.vue

View workflow job for this annotation

GitHub Actions / NPM lint

`<style>` is empty. Empty block is not allowed
</style>
Loading
Loading