diff options
author | Marcel Klehr <mklehr@gmx.net> | 2023-07-24 12:12:06 +0200 |
---|---|---|
committer | Julien Veyssier <julien-nc@posteo.net> | 2023-08-02 12:37:35 +0200 |
commit | fc9780a41d586e2983f18e128a4095484e5860ac (patch) | |
tree | 800ec46cda50a9e758f6b41f759423a7b82bb0a9 /apps | |
parent | 78856f354f63d3c23920fc3eeb386d6e92d6215d (diff) | |
download | nextcloud-server-fc9780a41d586e2983f18e128a4095484e5860ac.tar.gz nextcloud-server-fc9780a41d586e2983f18e128a4095484e5860ac.zip |
First pass at ai admin settings
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
Diffstat (limited to 'apps')
-rw-r--r-- | apps/settings/appinfo/info.xml | 2 | ||||
-rw-r--r-- | apps/settings/img/ai.svg | 1 | ||||
-rw-r--r-- | apps/settings/lib/Controller/AISettingsController.php | 105 | ||||
-rw-r--r-- | apps/settings/lib/Sections/Admin/ArtificialIntelligence.php | 58 | ||||
-rw-r--r-- | apps/settings/lib/Settings/Admin/ArtificialIntelligence.php | 163 | ||||
-rw-r--r-- | apps/settings/src/components/AdminAI.vue | 83 | ||||
-rw-r--r-- | apps/settings/templates/settings/admin/ai.php | 28 |
7 files changed, 440 insertions, 0 deletions
diff --git a/apps/settings/appinfo/info.xml b/apps/settings/appinfo/info.xml index 92d2928cd31..fefa2fadaef 100644 --- a/apps/settings/appinfo/info.xml +++ b/apps/settings/appinfo/info.xml @@ -19,6 +19,7 @@ <settings> <admin>OCA\Settings\Settings\Admin\Mail</admin> <admin>OCA\Settings\Settings\Admin\Overview</admin> + <admin>OCA\Settings\Settings\Admin\ArtificialIntelligence</admin> <admin>OCA\Settings\Settings\Admin\Server</admin> <admin>OCA\Settings\Settings\Admin\Sharing</admin> <admin>OCA\Settings\Settings\Admin\Security</admin> @@ -27,6 +28,7 @@ <admin-section>OCA\Settings\Sections\Admin\Delegation</admin-section> <admin-section>OCA\Settings\Sections\Admin\Groupware</admin-section> <admin-section>OCA\Settings\Sections\Admin\Overview</admin-section> + <admin-section>OCA\Settings\Sections\Admin\ArtificialIntelligence</admin-section> <admin-section>OCA\Settings\Sections\Admin\Security</admin-section> <admin-section>OCA\Settings\Sections\Admin\Server</admin-section> <admin-section>OCA\Settings\Sections\Admin\Sharing</admin-section> diff --git a/apps/settings/img/ai.svg b/apps/settings/img/ai.svg new file mode 100644 index 00000000000..5d59fd6afe8 --- /dev/null +++ b/apps/settings/img/ai.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12,2A2,2 0 0,1 14,4C14,4.74 13.6,5.39 13,5.73V7H14A7,7 0 0,1 21,14H22A1,1 0 0,1 23,15V18A1,1 0 0,1 22,19H21V20A2,2 0 0,1 19,22H5A2,2 0 0,1 3,20V19H2A1,1 0 0,1 1,18V15A1,1 0 0,1 2,14H3A7,7 0 0,1 10,7H11V5.73C10.4,5.39 10,4.74 10,4A2,2 0 0,1 12,2M7.5,13A2.5,2.5 0 0,0 5,15.5A2.5,2.5 0 0,0 7.5,18A2.5,2.5 0 0,0 10,15.5A2.5,2.5 0 0,0 7.5,13M16.5,13A2.5,2.5 0 0,0 14,15.5A2.5,2.5 0 0,0 16.5,18A2.5,2.5 0 0,0 19,15.5A2.5,2.5 0 0,0 16.5,13Z" /></svg>
\ No newline at end of file diff --git a/apps/settings/lib/Controller/AISettingsController.php b/apps/settings/lib/Controller/AISettingsController.php new file mode 100644 index 00000000000..53aca66c946 --- /dev/null +++ b/apps/settings/lib/Controller/AISettingsController.php @@ -0,0 +1,105 @@ +<?php +/** + * @copyright Copyright (c) 2017 Joas Schilling <coding@schilljs.com> + * @copyright Copyright (c) 2016, ownCloud, Inc. + * + * @author Christoph Wurst <christoph@winzerhof-wurst.at> + * @author Daniel Kesselberg <mail@danielkesselberg.de> + * @author Joas Schilling <coding@schilljs.com> + * @author Lukas Reschke <lukas@statuscode.ch> + * @author Morris Jobke <hey@morrisjobke.de> + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ +namespace OCA\Settings\Controller; + +use OCP\AppFramework\Controller; +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\DataResponse; +use OCP\IConfig; +use OCP\IL10N; +use OCP\IRequest; +use OCP\IURLGenerator; +use OCP\IUserSession; +use OCP\Mail\IMailer; + +class AISettingsController extends Controller { + + /** @var IL10N */ + private $l10n; + /** @var IConfig */ + private $config; + /** @var IUserSession */ + private $userSession; + /** @var IMailer */ + private $mailer; + /** @var IURLGenerator */ + private $urlGenerator; + + /** + * @param string $appName + * @param IRequest $request + * @param IL10N $l10n + * @param IConfig $config + * @param IUserSession $userSession + * @param IURLGenerator $urlGenerator, + * @param IMailer $mailer + */ + public function __construct($appName, + IRequest $request, + IL10N $l10n, + IConfig $config, + IUserSession $userSession, + IURLGenerator $urlGenerator, + IMailer $mailer) { + parent::__construct($appName, $request); + $this->l10n = $l10n; + $this->config = $config; + $this->userSession = $userSession; + $this->urlGenerator = $urlGenerator; + $this->mailer = $mailer; + } + + /** + * Sets the email settings + * + * @PasswordConfirmationRequired + * @AuthorizedAdminSetting(settings=OCA\Settings\Settings\Admin\ArtificialIntelligence) + * + * @param array $settings + * @return DataResponse + */ + public function setAISettings($settings) { + $params = get_defined_vars(); + $configs = []; + foreach ($params as $key => $value) { + $configs[$key] = empty($value) ? null : $value; + } + + // Delete passwords from config in case no auth is specified + if ($params['mail_smtpauth'] !== 1) { + $configs['mail_smtpname'] = null; + $configs['mail_smtppassword'] = null; + } + + $this->config->setSystemValues($configs); + + $this->config->setAppValue('core', 'emailTestSuccessful', '0'); + + return new DataResponse(); + } +} diff --git a/apps/settings/lib/Sections/Admin/ArtificialIntelligence.php b/apps/settings/lib/Sections/Admin/ArtificialIntelligence.php new file mode 100644 index 00000000000..1a25cdf5156 --- /dev/null +++ b/apps/settings/lib/Sections/Admin/ArtificialIntelligence.php @@ -0,0 +1,58 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ +namespace OCA\Settings\Sections\Admin; + +use OCP\IL10N; +use OCP\IURLGenerator; +use OCP\Settings\IIconSection; + +class ArtificialIntelligence implements IIconSection { + + /** @var IL10N */ + private $l; + + /** @var IURLGenerator */ + private $urlGenerator; + + public function __construct(IL10N $l, IURLGenerator $urlGenerator) { + $this->l = $l; + $this->urlGenerator = $urlGenerator; + } + + public function getIcon(): string { + return $this->urlGenerator->imagePath('settings', 'ai.svg'); + } + + public function getID(): string { + return 'ai'; + } + + public function getName(): string { + return $this->l->t('Artificial Intelligence'); + } + + public function getPriority(): int { + return 40; + } +} diff --git a/apps/settings/lib/Settings/Admin/ArtificialIntelligence.php b/apps/settings/lib/Settings/Admin/ArtificialIntelligence.php new file mode 100644 index 00000000000..d33802d6c45 --- /dev/null +++ b/apps/settings/lib/Settings/Admin/ArtificialIntelligence.php @@ -0,0 +1,163 @@ +<?php +/** + * @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net> + * + * @author Marcel Klehr <mklehr@gmx.net> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ +namespace OCA\Settings\Settings\Admin; + +use OCP\AppFramework\Http\TemplateResponse; +use OCP\AppFramework\Services\IInitialState; +use OCP\IConfig; +use OCP\IL10N; +use OCP\IServerContainer; +use OCP\Settings\IDelegatedSettings; +use OCP\SpeechToText\ISpeechToTextManager; +use OCP\TextProcessing\IManager; +use OCP\TextProcessing\IProvider; +use OCP\TextProcessing\ITaskType; +use OCP\Translation\ITranslationManager; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; + +class ArtificialIntelligence implements IDelegatedSettings { + public function __construct( + private IConfig $config, + private IL10N $l, + private IInitialState $initialState, + private ITranslationManager $translationManager, + private ISpeechToTextManager $sttManager, + private IManager $textProcessingManager, + private IServerContainer $container, + ) { + } + + /** + * @return TemplateResponse + */ + public function getForm() { + $translationProviders = []; + $translationPreferences = []; + foreach ($this->translationManager->getProviders() as $provider) { + $translationProviders[] = [ + 'class' => $provider::class, + 'name' => $provider->getName(), + ]; + $translationPreferences[] = $provider::class; + } + + $sttProviders = []; + foreach ($this->sttManager->getProviders() as $provider) { + $sttProviders[] = [ + 'class' => $provider::class, + 'name' => $provider->getName(), + ]; + } + + $textProcessingProviders = []; + /** @var array<class-string<ITaskType>, class-string<IProvider>> $textProcessingSettings */ + $textProcessingSettings = []; + foreach ($this->textProcessingManager->getProviders() as $provider) { + $textProcessingProviders[] = [ + 'class' => $provider::class, + 'name' => $provider->getName(), + 'taskType' => $provider->getTaskType(), + ]; + $textProcessingSettings[$provider->getTaskType()] = $provider::class; + } + $textProcessingTaskTypes = []; + foreach ($textProcessingSettings as $taskTypeClass => $providerClass) { + /** @var ITaskType $taskType */ + try { + $taskType = $this->container->get($taskTypeClass); + } catch (NotFoundExceptionInterface $e) { + continue; + } catch (ContainerExceptionInterface $e) { + continue; + } + $textProcessingTaskTypes[] = [ + 'class' => $taskTypeClass, + 'name' => $taskType->getName(), + 'description' => $taskType->getDescription(), + ]; + } + + $this->initialState->provideInitialState('ai-stt-providers', $sttProviders); + $this->initialState->provideInitialState('ai-translation-providers', $translationProviders); + $this->initialState->provideInitialState('ai-text-processing-providers', $textProcessingProviders); + $this->initialState->provideInitialState('ai-text-processing-task-types', $textProcessingTaskTypes); + + $settings = [ + 'ai.stt_provider' => count($sttProviders) > 0 ? $sttProviders[0]['class'] : null, + 'ai.textprocessing_provider_preferences' => $textProcessingSettings, + 'ai.translation_provider_preferences' => $translationPreferences, + ]; + foreach ($settings as $key => $defaultValue) { + $value = $defaultValue; + $json = $this->config->getAppValue('core', $key, ''); + if ($json !== '') { + $value = json_decode($json, JSON_OBJECT_AS_ARRAY); + switch($key) { + case 'ai.textprocessing_provider_preferences': + // fill $value with $defaultValue values + $value = array_merge($defaultValue, $value); + break; + case 'ai.translation_provider_preferences': + $value += array_diff($defaultValue, $value); // Add entries from $defaultValue that are not in $value to the end of $value + break; + default: + break; + } + } + $settings[$key] = $value; + } + + $this->initialState->provideInitialState('ai-settings', $settings); + + return new TemplateResponse('settings', 'settings/admin/ai'); + } + + /** + * @return string the section ID, e.g. 'sharing' + */ + public function getSection() { + return 'ai'; + } + + /** + * @return int whether the form should be rather on the top or bottom of + * the admin section. The forms are arranged in ascending order of the + * priority values. It is required to return a value between 0 and 100. + * + * E.g.: 70 + */ + public function getPriority() { + return 10; + } + + public function getName(): ?string { + return $this->l->t('Artificial Intelligence'); + } + + public function getAuthorizedAppConfig(): array { + return [ + 'core' => ['/ai_.*/'], + ]; + } +} diff --git a/apps/settings/src/components/AdminAI.vue b/apps/settings/src/components/AdminAI.vue new file mode 100644 index 00000000000..2e1104a49d2 --- /dev/null +++ b/apps/settings/src/components/AdminAI.vue @@ -0,0 +1,83 @@ +<template> + <NcSettingsSection :title="t('settings', 'Artificial Intelligence')" + :description="t('settings', 'Artificial Intelligence features can be implemented by different apps. Here you can set which app should be used for which features.')"> + <h3>{{ t('settings', 'Translations') }}</h3> + <h3>{{ t('settings', 'Speech-To-Text') }}</h3> + <template v-for="provider in sttProviders"> + <NcCheckboxRadioSwitch :key="provider.class" + :checked.sync="settings['ai.stt_provider']" + :value="provider.class" + name="stt_provider" + type="radio">{{ provider.name }}</NcCheckboxRadioSwitch> + </template> + <template v-if="sttProviders.length === 0"> + <NcCheckboxRadioSwitch disabled type="radio">{{ t('settings', 'No apps are currently installed that provide Speech-To-Text functionality') }}</NcCheckboxRadioSwitch> + </template> + <h3>{{ t('settings', 'Text processing') }}</h3> + <template v-for="(type, provider) in settings['ai.textprocessing_provider_preferences']"> + <h4>{{ type }}</h4> + <!--<p>{{ getTaskType(type).description }}</p> + <NcSelect v-model="settings['ai.textprocessing_provider_preferences'][type]" :options="textProcessingProviders.filter(provider => provider.taskType === type)" />--> + </template> + <template v-if="Object.keys(settings['ai.textprocessing_provider_preferences']).length === 0 || !Array.isArray(this.textProcessingTaskTypes)"> + <p>{{ t('settings', 'No apps are currently installed that provide Text processing functionality') }}</p> + </template> + </NcSettingsSection> +</template> + +<script> +import axios from '@nextcloud/axios' +import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch.js' +import NcSettingsSection from '@nextcloud/vue/dist/Components/NcSettingsSection.js' +import { loadState } from '@nextcloud/initial-state' + +import { generateUrl } from '@nextcloud/router' + +export default { + name: 'AdminAI', + components: { + NcCheckboxRadioSwitch, + NcSettingsSection, + }, + data() { + return { + loading: false, + dirty: false, + groups: [], + loadingGroups: false, + sttProviders: loadState('settings', 'ai-stt-providers'), + translationProviders: loadState('settings', 'ai-translation-providers'), + textProcessingProviders: loadState('settings', 'ai-text-processing-providers'), + textProcessingTaskTypes: loadState('settings', 'ai-text-processing-task-types'), + settings: loadState('settings', 'ai-settings'), + } + }, + methods: { + saveChanges() { + this.loading = true + + const data = { + enforced: this.enforced, + enforcedGroups: this.enforcedGroups, + excludedGroups: this.excludedGroups, + } + axios.put(generateUrl('/settings/api/admin/twofactorauth'), data) + .then(resp => resp.data) + .then(state => { + this.state = state + this.dirty = false + }) + .catch(err => { + console.error('could not save changes', err) + }) + .then(() => { this.loading = false }) + }, + getTaskType(type) { + if (!Array.isArray(this.textProcessingTaskTypes)) { + return null + } + return this.textProcessingTaskTypes.find(taskType => taskType === type) + } + }, +} +</script> diff --git a/apps/settings/templates/settings/admin/ai.php b/apps/settings/templates/settings/admin/ai.php new file mode 100644 index 00000000000..fac7ad8d25b --- /dev/null +++ b/apps/settings/templates/settings/admin/ai.php @@ -0,0 +1,28 @@ +<?php +/** + * @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +script('settings', [ + 'vue-settings-admin-ai', +]); +?> + +<div id="ai-settings"> +</div> |