Skip to content
Draft
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
5 changes: 5 additions & 0 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,11 @@
'url' => '/api/settings/allownewaccounts',
'verb' => 'POST'
],
[
'name' => 'settings#setAllowNewMailAliases',
'url' => '/api/settings/allownewaliases',
'verb' => 'POST'
],
[
'name' => 'settings#setEnabledLlmProcessing',
'url' => '/api/settings/llm',
Expand Down
5 changes: 5 additions & 0 deletions lib/Controller/PageController.php
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,11 @@ public function index(): TemplateResponse {
$this->config->getAppValue('mail', 'allow_new_mail_accounts', 'yes') === 'yes'
);

$this->initialStateService->provideInitialState(
'allow-new-aliases',
$this->config->getAppValue('mail', 'allow_new_mail_aliases', 'yes') === 'yes'
);

$this->initialStateService->provideInitialState(
'llm_summaries_available',
$this->aiIntegrationsService->isLlmProcessingEnabled() && $this->aiIntegrationsService->isLlmAvailable(SummaryTaskType::class)
Expand Down
5 changes: 5 additions & 0 deletions lib/Controller/SettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@ public function setAllowNewMailAccounts(bool $allowed): void {
$this->config->setAppValue('mail', 'allow_new_mail_accounts', $allowed ? 'yes' : 'no');
}

public function setAllowNewMailAliases(bool $allowed): JSONResponse {
$this->config->setAppValue('mail', 'allow_new_mail_aliases', $allowed ? 'yes' : 'no');
return new JSONResponse([]);
}

public function setEnabledLlmProcessing(bool $enabled): JSONResponse {
$this->config->setAppValue('mail', 'llm_processing', $enabled ? 'yes' : 'no');
return new JSONResponse([]);
Expand Down
10 changes: 9 additions & 1 deletion lib/Service/AliasesService.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,21 @@
use OCA\Mail\Db\MailAccountMapper;
use OCA\Mail\Exception\ClientException;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\IConfig;

class AliasesService {
/** @var AliasMapper */
private $aliasMapper;

/** @var MailAccountMapper */
private $mailAccountMapper;
/** @var IConfig */
private $config;

public function __construct(AliasMapper $aliasMapper, MailAccountMapper $mailAccountMapper) {
public function __construct(AliasMapper $aliasMapper, MailAccountMapper $mailAccountMapper, IConfig $config) {
$this->aliasMapper = $aliasMapper;
$this->mailAccountMapper = $mailAccountMapper;
$this->config = $config;
}

/**
Expand Down Expand Up @@ -67,6 +71,10 @@ public function findByAliasAndUserId(string $aliasEmail, string $userId): Alias
* @throws DoesNotExistException
*/
public function create(string $userId, int $accountId, string $alias, string $aliasName): Alias {
if ($this->config->getAppValue('mail', 'allow_new_mail_aliases', 'yes') === 'no') {
throw new ClientException('Creating aliases has been disabled by the administrator.');
}

$this->mailAccountMapper->find($userId, $accountId);

$aliasEntity = new Alias();
Expand Down
6 changes: 6 additions & 0 deletions lib/Settings/AdminSettings.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ public function getForm() {
$this->config->getAppValue('mail', 'allow_new_mail_accounts', 'yes') === 'yes'
);

$this->initialStateService->provideInitialState(
Application::APP_ID,
'allow_new_mail_aliases',
$this->config->getAppValue('mail', 'allow_new_mail_aliases', 'yes') === 'yes'
);

$this->initialStateService->provideInitialState(
Application::APP_ID,
'layout_message_view',
Expand Down
17 changes: 16 additions & 1 deletion src/components/AccountSettings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@
:additional-trap-elements="trapElements"
:name="t('mail', 'Account settings')"
@update:open="updateOpen">
<AppSettingsSection
<AppSettingsSection v-if="showProviderAppPassword"

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

View workflow job for this annotation

GitHub Actions / NPM lint

Expected a linebreak before this attribute
id="provider-app-password"
:name="t('mail', 'IMAP access / password')">
<ProviderAppPassword :account="account" :provider-id="account.managedByProvider" />

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

View workflow job for this annotation

GitHub Actions / NPM lint

The '<ProviderAppPassword>' component has been used, but not defined
</AppSettingsSection>
<AppSettingsSection v-if="allowNewAliases"

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

View workflow job for this annotation

GitHub Actions / NPM lint

Expected a linebreak before this attribute
id="alias-settings"
:name="t('mail', 'Aliases')">
<AliasSettings :account="account" @rename-primary-alias="scrollToAccountSettings" />
Expand Down Expand Up @@ -85,14 +90,14 @@
id="mail-filters"
:name="t('mail', 'Filters')">
<div id="mail-filters">
<MailFilters :key="account.accountId" ref="mailFilters" :account="account" />

Check warning on line 93 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 100 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 @@ -101,7 +106,7 @@
<div id="sieve-filter">
<SieveFilterForm
:key="account.accountId"
ref="sieveFilterForm"

Check warning on line 109 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 @@ -125,7 +130,7 @@
<div id="sieve-settings">
<SieveAccountForm
:key="account.accountId"
ref="sieveAccountForm"

Check warning on line 133 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 Down Expand Up @@ -208,11 +213,21 @@

email() {
return this.account.emailAddress
},

Check failure on line 216 in src/components/AccountSettings.vue

View workflow job for this annotation

GitHub Actions / NPM lint

Enforce new lines between multi-line properties in Vue components
allowNewAliases() {
return this.mainStore.getPreference('allow-new-aliases', true)
},

Check failure on line 219 in src/components/AccountSettings.vue

View workflow job for this annotation

GitHub Actions / NPM lint

Enforce new lines between multi-line properties in Vue components
showProviderAppPassword() {
// Show the password reset section if:
// 1. Account is managed by a provider (managedByProvider is set)
// 2. Provider supports app passwords
return this.account.managedByProvider
&& this.account.providerCapabilities?.appPasswords === true
},
Comment on lines +220 to +226
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I believe this is unrelated to your feature. The property does not exist

},

watch: {
open(newState, oldState) {

Check warning on line 230 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
6 changes: 4 additions & 2 deletions src/components/AliasSettings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@

<div v-if="!account.provisioningId" class="aliases-controls">
<ButtonVue
v-if="!showForm"
v-if="!showForm && allowNewAliases"
type="primary"
:aria-label="t('mail', 'Add alias')"
@click="showForm = true">
Expand Down Expand Up @@ -124,8 +124,10 @@
...mapStores(useMainStore),
aliases() {
return this.account.aliases
},

Check failure on line 127 in src/components/AliasSettings.vue

View workflow job for this annotation

GitHub Actions / NPM lint

Enforce new lines between multi-line properties in Vue components

allowNewAliases() {
return this.mainStore.getPreference('allow-new-aliases', true)
},

Check failure on line 130 in src/components/AliasSettings.vue

View workflow job for this annotation

GitHub Actions / NPM lint

Enforce new lines between multi-line properties in Vue components
accountAlias() {
return {
alias: this.account.emailAddress,
Expand Down
17 changes: 17 additions & 0 deletions src/components/settings/AdminSettings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,18 @@
</p>
</article>
</div>
<div class="app-description">
<h3>{{ t('mail', 'Allow aliases') }}</h3>
<article>
<p>
<NcCheckboxRadioSwitch :checked.sync="allowNewMailAliases"

Check failure on line 143 in src/components/settings/AdminSettings.vue

View workflow job for this annotation

GitHub Actions / NPM lint

Using `checked` is deprecated - use `model-value` or `v-model` instead
type="switch"
@update:checked="updateAllowNewMailAliases">
{{ t('mail', 'Allow users to create mail aliases') }}
</NcCheckboxRadioSwitch>
</p>
</article>
</div>
<div
v-if="isLlmSummaryConfigured"
class="app-description">
Expand Down Expand Up @@ -300,6 +312,7 @@
setImportanceClassificationEnabledByDefault,
setLayoutMessageView,
updateAllowNewMailAccounts,
updateAllowNewMailAliases,
updateEnabledSmartReply,
updateLlmEnabled,
updateProvisioningSettings,
Expand Down Expand Up @@ -368,6 +381,7 @@
},

allowNewMailAccounts: loadState('mail', 'allow_new_mail_accounts', true),
allowNewMailAliases: loadState('mail', 'allow_new_mail_aliases', true),
isLlmSummaryConfigured: loadState('mail', 'enabled_llm_summary_backend'),
isLlmEnabled: loadState('mail', 'llm_processing', true),
isLlmFreePromptConfigured: loadState('mail', 'enabled_llm_free_prompt_backend'),
Expand Down Expand Up @@ -430,7 +444,10 @@

async updateAllowNewMailAccounts(checked) {
await updateAllowNewMailAccounts(checked)
},

Check failure on line 447 in src/components/settings/AdminSettings.vue

View workflow job for this annotation

GitHub Actions / NPM lint

Enforce new lines between multi-line properties in Vue components
async updateAllowNewMailAliases(checked) {
await updateAllowNewMailAliases(checked)
},

async updateLlmEnabled(checked) {
await updateLlmEnabled(checked)
Expand Down
4 changes: 4 additions & 0 deletions src/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ export default function initAfterAppCreation() {
key: 'allow-new-accounts',
value: loadState('mail', 'allow-new-accounts', true),
})
mainStore.savePreferenceMutation({
key: 'allow-new-aliases',
value: loadState('mail', 'allow-new-aliases', true),
})
mainStore.savePreferenceMutation({
key: 'password-is-unavailable',
value: loadState('mail', 'password-is-unavailable', false),
Expand Down
8 changes: 8 additions & 0 deletions src/service/SettingsService.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ export function updateAllowNewMailAccounts(allowed) {
return axios.post(url, data).then((resp) => resp.data)
}

export function updateAllowNewMailAliases(allowed) {
const url = generateUrl('/apps/mail/api/settings/allownewaliases')
const data = {
allowed,
}
return axios.post(url, data).then((resp) => resp.data)
}

export async function updateLlmEnabled(enabled) {
const url = generateUrl('/apps/mail/api/settings/llm')
const data = {
Expand Down
7 changes: 6 additions & 1 deletion tests/Unit/Controller/AliasesControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IConfig;

class AliasesControllerTest extends TestCase {
private $controller;
Expand Down Expand Up @@ -46,8 +47,12 @@ public function setUp(): void {

$this->aliasMapper = $this->createMock(AliasMapper::class);
$this->mailAccountMapper = $this->createMock(MailAccountMapper::class);
$config = $this->createMock(IConfig::class);
$config->method('getAppValue')
->with('mail', 'allow_new_mail_aliases', 'yes')
->willReturn('yes');

$this->aliasService = new AliasesService($this->aliasMapper, $this->mailAccountMapper);
$this->aliasService = new AliasesService($this->aliasMapper, $this->mailAccountMapper, $config);
$this->controller = new AliasesController($this->appName, $this->request, $this->aliasService, $this->userId);
}

Expand Down
5 changes: 3 additions & 2 deletions tests/Unit/Controller/PageControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ public function testIndex(): void {
['version', '0.0.0', '26.0.0'],
['app.mail.attachment-size-limit', 0, 123],
]);
$this->config->expects($this->exactly(7))
$this->config->expects($this->exactly(8))
->method('getAppValue')
->withConsecutive(
[ 'mail', 'installed_version' ],
Expand All @@ -279,6 +279,7 @@ public function testIndex(): void {
['mail', 'microsoft_oauth_tenant_id' ],
['core', 'backgroundjobs_mode', 'ajax' ],
['mail', 'allow_new_mail_accounts', 'yes'],
['mail', 'allow_new_mail_aliases', 'yes'],
)->willReturnOnConsecutiveCalls(
$this->returnValue('1.2.3'),
$this->returnValue('threaded'),
Expand All @@ -287,7 +288,7 @@ public function testIndex(): void {
$this->returnValue(''),
$this->returnValue('cron'),
$this->returnValue('yes'),
$this->returnValue('no')
$this->returnValue('yes'),
);
$this->aiIntegrationsService->expects(self::exactly(4))
->method('isLlmProcessingEnabled')
Expand Down
29 changes: 28 additions & 1 deletion tests/Unit/Service/AliasesServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use OCA\Mail\Exception\ClientException;
use OCA\Mail\Service\AliasesService;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\IConfig;

class AliasesServiceTest extends TestCase {
/** @var AliasesService */
Expand All @@ -28,15 +29,24 @@ class AliasesServiceTest extends TestCase {
/** @var MailAccountMapper */
private $mailAccountMapper;

/** @var IConfig */
private $config;

protected function setUp(): void {
parent::setUp();

$this->aliasMapper = $this->createMock(AliasMapper::class);
$this->mailAccountMapper = $this->createMock(MailAccountMapper::class);
$this->config = $this->createMock(IConfig::class);

$this->config->method('getAppValue')
->with('mail', 'allow_new_mail_aliases', 'yes')
->willReturn('yes');

$this->service = new AliasesService(
$this->aliasMapper,
$this->mailAccountMapper
$this->mailAccountMapper,
$this->config,
);
}

Expand Down Expand Up @@ -123,6 +133,23 @@ public function testCreateForbiddenAccountId(): void {
);
}

public function testCreateDisabledByAdmin(): void {
$this->expectException(ClientException::class);
$this->expectExceptionMessage('Creating aliases has been disabled by the administrator.');

$this->config->expects(self::once())
->method('getAppValue')
->with('mail', 'allow_new_mail_aliases', 'yes')
->willReturn('no');

$this->mailAccountMapper->expects(self::never())
->method('find');
$this->aliasMapper->expects(self::never())
->method('insert');

$this->service->create(300, 200, 'jane@doe.com', 'Jane Doe');
}

public function testDelete(): void {
$entity = new Alias();
$entity->setId(101);
Expand Down
7 changes: 6 additions & 1 deletion tests/Unit/Settings/AdminSettingsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public function testGetSection() {
}

public function testGetForm() {
$this->serviceMock->getParameter('initialStateService')->expects($this->exactly(14))
$this->serviceMock->getParameter('initialStateService')->expects($this->exactly(15))
->method('provideInitialState')
->withConsecutive(
[
Expand All @@ -55,6 +55,11 @@ public function testGetForm() {
'allow_new_mail_accounts',
$this->anything()
],
[
Application::APP_ID,
'allow_new_mail_aliases',
$this->anything()
],
[
Application::APP_ID,
'layout_message_view',
Expand Down
Loading