diff --git a/apps/settings/appinfo/info.xml b/apps/settings/appinfo/info.xml index 7864405e292bd..2ec40fd758d7b 100644 --- a/apps/settings/appinfo/info.xml +++ b/apps/settings/appinfo/info.xml @@ -19,6 +19,7 @@ OCA\Settings\Settings\Admin\Mail OCA\Settings\Settings\Admin\Overview + OCA\Settings\Settings\Admin\ArtificialIntelligence OCA\Settings\Settings\Admin\Server OCA\Settings\Settings\Admin\Sharing OCA\Settings\Settings\Admin\Security @@ -27,6 +28,7 @@ OCA\Settings\Sections\Admin\Delegation OCA\Settings\Sections\Admin\Groupware OCA\Settings\Sections\Admin\Overview + OCA\Settings\Sections\Admin\ArtificialIntelligence OCA\Settings\Sections\Admin\Security OCA\Settings\Sections\Admin\Server OCA\Settings\Sections\Admin\Sharing diff --git a/apps/settings/appinfo/routes.php b/apps/settings/appinfo/routes.php index 938842dd576c4..e238510b1a75f 100644 --- a/apps/settings/appinfo/routes.php +++ b/apps/settings/appinfo/routes.php @@ -75,6 +75,7 @@ ['name' => 'ChangePassword#changeUserPassword', 'url' => '/settings/users/changepassword', 'verb' => 'POST' , 'root' => ''], ['name' => 'TwoFactorSettings#index', 'url' => '/settings/api/admin/twofactorauth', 'verb' => 'GET' , 'root' => ''], ['name' => 'TwoFactorSettings#update', 'url' => '/settings/api/admin/twofactorauth', 'verb' => 'PUT' , 'root' => ''], + ['name' => 'AISettings#update', 'url' => '/settings/api/admin/ai', 'verb' => 'PUT' , 'root' => ''], ['name' => 'Help#help', 'url' => '/settings/help/{mode}', 'verb' => 'GET', 'defaults' => ['mode' => ''] , 'root' => ''], diff --git a/apps/settings/composer/composer/autoload_classmap.php b/apps/settings/composer/composer/autoload_classmap.php index 32de1ff6d2a7e..a2d62b530179e 100644 --- a/apps/settings/composer/composer/autoload_classmap.php +++ b/apps/settings/composer/composer/autoload_classmap.php @@ -16,6 +16,7 @@ 'OCA\\Settings\\Activity\\Setting' => $baseDir . '/../lib/Activity/Setting.php', 'OCA\\Settings\\AppInfo\\Application' => $baseDir . '/../lib/AppInfo/Application.php', 'OCA\\Settings\\BackgroundJobs\\VerifyUserData' => $baseDir . '/../lib/BackgroundJobs/VerifyUserData.php', + 'OCA\\Settings\\Controller\\AISettingsController' => $baseDir . '/../lib/Controller/AISettingsController.php', 'OCA\\Settings\\Controller\\AdminSettingsController' => $baseDir . '/../lib/Controller/AdminSettingsController.php', 'OCA\\Settings\\Controller\\AppSettingsController' => $baseDir . '/../lib/Controller/AppSettingsController.php', 'OCA\\Settings\\Controller\\AuthSettingsController' => $baseDir . '/../lib/Controller/AuthSettingsController.php', @@ -42,6 +43,7 @@ 'OCA\\Settings\\Search\\AppSearch' => $baseDir . '/../lib/Search/AppSearch.php', 'OCA\\Settings\\Search\\SectionSearch' => $baseDir . '/../lib/Search/SectionSearch.php', 'OCA\\Settings\\Sections\\Admin\\Additional' => $baseDir . '/../lib/Sections/Admin/Additional.php', + 'OCA\\Settings\\Sections\\Admin\\ArtificialIntelligence' => $baseDir . '/../lib/Sections/Admin/ArtificialIntelligence.php', 'OCA\\Settings\\Sections\\Admin\\Delegation' => $baseDir . '/../lib/Sections/Admin/Delegation.php', 'OCA\\Settings\\Sections\\Admin\\Groupware' => $baseDir . '/../lib/Sections/Admin/Groupware.php', 'OCA\\Settings\\Sections\\Admin\\Overview' => $baseDir . '/../lib/Sections/Admin/Overview.php', @@ -56,6 +58,7 @@ 'OCA\\Settings\\Service\\AuthorizedGroupService' => $baseDir . '/../lib/Service/AuthorizedGroupService.php', 'OCA\\Settings\\Service\\NotFoundException' => $baseDir . '/../lib/Service/NotFoundException.php', 'OCA\\Settings\\Service\\ServiceException' => $baseDir . '/../lib/Service/ServiceException.php', + 'OCA\\Settings\\Settings\\Admin\\ArtificialIntelligence' => $baseDir . '/../lib/Settings/Admin/ArtificialIntelligence.php', 'OCA\\Settings\\Settings\\Admin\\Delegation' => $baseDir . '/../lib/Settings/Admin/Delegation.php', 'OCA\\Settings\\Settings\\Admin\\Mail' => $baseDir . '/../lib/Settings/Admin/Mail.php', 'OCA\\Settings\\Settings\\Admin\\Overview' => $baseDir . '/../lib/Settings/Admin/Overview.php', diff --git a/apps/settings/composer/composer/autoload_static.php b/apps/settings/composer/composer/autoload_static.php index 57235766a7c5e..c76e3d84ae317 100644 --- a/apps/settings/composer/composer/autoload_static.php +++ b/apps/settings/composer/composer/autoload_static.php @@ -31,6 +31,7 @@ class ComposerStaticInitSettings 'OCA\\Settings\\Activity\\Setting' => __DIR__ . '/..' . '/../lib/Activity/Setting.php', 'OCA\\Settings\\AppInfo\\Application' => __DIR__ . '/..' . '/../lib/AppInfo/Application.php', 'OCA\\Settings\\BackgroundJobs\\VerifyUserData' => __DIR__ . '/..' . '/../lib/BackgroundJobs/VerifyUserData.php', + 'OCA\\Settings\\Controller\\AISettingsController' => __DIR__ . '/..' . '/../lib/Controller/AISettingsController.php', 'OCA\\Settings\\Controller\\AdminSettingsController' => __DIR__ . '/..' . '/../lib/Controller/AdminSettingsController.php', 'OCA\\Settings\\Controller\\AppSettingsController' => __DIR__ . '/..' . '/../lib/Controller/AppSettingsController.php', 'OCA\\Settings\\Controller\\AuthSettingsController' => __DIR__ . '/..' . '/../lib/Controller/AuthSettingsController.php', @@ -57,6 +58,7 @@ class ComposerStaticInitSettings 'OCA\\Settings\\Search\\AppSearch' => __DIR__ . '/..' . '/../lib/Search/AppSearch.php', 'OCA\\Settings\\Search\\SectionSearch' => __DIR__ . '/..' . '/../lib/Search/SectionSearch.php', 'OCA\\Settings\\Sections\\Admin\\Additional' => __DIR__ . '/..' . '/../lib/Sections/Admin/Additional.php', + 'OCA\\Settings\\Sections\\Admin\\ArtificialIntelligence' => __DIR__ . '/..' . '/../lib/Sections/Admin/ArtificialIntelligence.php', 'OCA\\Settings\\Sections\\Admin\\Delegation' => __DIR__ . '/..' . '/../lib/Sections/Admin/Delegation.php', 'OCA\\Settings\\Sections\\Admin\\Groupware' => __DIR__ . '/..' . '/../lib/Sections/Admin/Groupware.php', 'OCA\\Settings\\Sections\\Admin\\Overview' => __DIR__ . '/..' . '/../lib/Sections/Admin/Overview.php', @@ -71,6 +73,7 @@ class ComposerStaticInitSettings 'OCA\\Settings\\Service\\AuthorizedGroupService' => __DIR__ . '/..' . '/../lib/Service/AuthorizedGroupService.php', 'OCA\\Settings\\Service\\NotFoundException' => __DIR__ . '/..' . '/../lib/Service/NotFoundException.php', 'OCA\\Settings\\Service\\ServiceException' => __DIR__ . '/..' . '/../lib/Service/ServiceException.php', + 'OCA\\Settings\\Settings\\Admin\\ArtificialIntelligence' => __DIR__ . '/..' . '/../lib/Settings/Admin/ArtificialIntelligence.php', 'OCA\\Settings\\Settings\\Admin\\Delegation' => __DIR__ . '/..' . '/../lib/Settings/Admin/Delegation.php', 'OCA\\Settings\\Settings\\Admin\\Mail' => __DIR__ . '/..' . '/../lib/Settings/Admin/Mail.php', 'OCA\\Settings\\Settings\\Admin\\Overview' => __DIR__ . '/..' . '/../lib/Settings/Admin/Overview.php', diff --git a/apps/settings/img/ai.svg b/apps/settings/img/ai.svg new file mode 100644 index 0000000000000..5d59fd6afe84d --- /dev/null +++ b/apps/settings/img/ai.svg @@ -0,0 +1 @@ + \ 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 0000000000000..7f016d79c2595 --- /dev/null +++ b/apps/settings/lib/Controller/AISettingsController.php @@ -0,0 +1,70 @@ + + * + * @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 + * + */ +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; +use function GuzzleHttp\Promise\queue; + +class AISettingsController extends Controller { + + /** + * @param string $appName + * @param IRequest $request + * @param IConfig $config + */ + public function __construct( + $appName, + IRequest $request, + private IConfig $config, + ) { + parent::__construct($appName, $request); + } + + /** + * Sets the email settings + * + * @AuthorizedAdminSetting(settings=OCA\Settings\Settings\Admin\ArtificialIntelligence) + * + * @param array $settings + * @return DataResponse + */ + public function update($settings) { + $keys = ['ai.stt_provider', 'ai.textprocessing_provider_preferences', 'ai.translation_provider_preferences']; + foreach ($keys as $key) { + if (!isset($settings[$key])) { + continue; + } + $this->config->setAppValue('core', $key, json_encode($settings[$key])); + } + + 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 0000000000000..1a25cdf515672 --- /dev/null +++ b/apps/settings/lib/Sections/Admin/ArtificialIntelligence.php @@ -0,0 +1,58 @@ + + * + * @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 . + * + */ +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 0000000000000..eb1983690a5a1 --- /dev/null +++ b/apps/settings/lib/Settings/Admin/ArtificialIntelligence.php @@ -0,0 +1,166 @@ + + * + * @author Marcel Klehr + * + * @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 . + * + */ +namespace OCA\Settings\Settings\Admin; + +use OCP\AppFramework\Http\TemplateResponse; +use OCP\AppFramework\Services\IInitialState; +use OCP\IConfig; +use OCP\IL10N; +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\ContainerInterface; +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 ContainerInterface $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> $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, true); + 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/admin.js b/apps/settings/src/admin.js index 8bdfa1d0770f1..6d84405c8b857 100644 --- a/apps/settings/src/admin.js +++ b/apps/settings/src/admin.js @@ -227,7 +227,7 @@ window.addEventListener('DOMContentLoaded', () => { OC.SetupChecks.checkSetup(), OC.SetupChecks.checkGeneric(), OC.SetupChecks.checkWOFF2Loading(OC.filePath('core', '', 'fonts/NotoSans-Regular-latin.woff2'), OC.theme.docPlaceholderUrl), - OC.SetupChecks.checkDataProtected() + OC.SetupChecks.checkDataProtected(), ).then((check1, check2, check3, check4, check5, check6, check7, check8, check9, check10, check11) => { const messages = [].concat(check1, check2, check3, check4, check5, check6, check7, check8, check9, check10, check11) const $el = $('#postsetupchecks') diff --git a/apps/settings/src/components/AdminAI.vue b/apps/settings/src/components/AdminAI.vue new file mode 100644 index 0000000000000..8bcaac43019c8 --- /dev/null +++ b/apps/settings/src/components/AdminAI.vue @@ -0,0 +1,173 @@ + + + + diff --git a/apps/settings/src/components/AdminDelegation/GroupSelect.vue b/apps/settings/src/components/AdminDelegation/GroupSelect.vue index 82b5e51fb4502..915935167604a 100644 --- a/apps/settings/src/components/AdminDelegation/GroupSelect.vue +++ b/apps/settings/src/components/AdminDelegation/GroupSelect.vue @@ -1,6 +1,6 @@