aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/composer/composer/autoload_classmap.php22
-rw-r--r--lib/composer/composer/autoload_static.php22
-rw-r--r--lib/l10n/br.js1
-rw-r--r--lib/l10n/br.json1
-rw-r--r--lib/l10n/de_DE.js2
-rw-r--r--lib/l10n/de_DE.json2
-rw-r--r--lib/l10n/en_GB.js2
-rw-r--r--lib/l10n/en_GB.json2
-rw-r--r--lib/l10n/ga.js2
-rw-r--r--lib/l10n/ga.json2
-rw-r--r--lib/l10n/gl.js2
-rw-r--r--lib/l10n/gl.json2
-rw-r--r--lib/l10n/hu.js1
-rw-r--r--lib/l10n/hu.json1
-rw-r--r--lib/l10n/nb.js2
-rw-r--r--lib/l10n/nb.json2
-rw-r--r--lib/l10n/pt_BR.js2
-rw-r--r--lib/l10n/pt_BR.json2
-rw-r--r--lib/l10n/sr@latin.js2
-rw-r--r--lib/l10n/sr@latin.json2
-rw-r--r--lib/l10n/sv.js2
-rw-r--r--lib/l10n/sv.json2
-rw-r--r--lib/l10n/zh_TW.js2
-rw-r--r--lib/l10n/zh_TW.json2
-rw-r--r--lib/private/AppFramework/Bootstrap/RegistrationContext.php24
-rw-r--r--lib/private/DB/Connection.php51
-rw-r--r--lib/private/DB/ConnectionAdapter.php4
-rw-r--r--lib/private/DB/QueryBuilder/ExpressionBuilder/PgSqlExpressionBuilder.php2
-rw-r--r--lib/private/DB/QueryBuilder/QueryBuilder.php1
-rw-r--r--lib/private/Files/Template/TemplateManager.php8
-rw-r--r--lib/private/Files/View.php4
-rw-r--r--lib/private/Group/Manager.php13
-rw-r--r--lib/private/Group/MetaData.php25
-rw-r--r--lib/private/Mail/Provider/Manager.php255
-rw-r--r--lib/private/Preview/BackgroundCleanupJob.php2
-rw-r--r--lib/private/Server.php3
-rw-r--r--lib/private/SubAdmin.php5
-rw-r--r--lib/private/TaskProcessing/Db/Task.php22
-rw-r--r--lib/private/TaskProcessing/Db/TaskMapper.php45
-rw-r--r--lib/private/TaskProcessing/Manager.php194
-rw-r--r--lib/private/Template/JSConfigHelper.php6
-rw-r--r--lib/public/Accounts/UserUpdatedEvent.php2
-rw-r--r--lib/public/App/Events/AppDisableEvent.php2
-rw-r--r--lib/public/App/Events/AppEnableEvent.php2
-rw-r--r--lib/public/App/Events/AppUpdateEvent.php2
-rw-r--r--lib/public/AppFramework/Bootstrap/IRegistrationContext.php11
-rw-r--r--lib/public/Collaboration/Reference/RenderReferenceEvent.php6
-rw-r--r--lib/public/Files/Template/BeforeGetTemplatesEvent.php40
-rw-r--r--lib/public/Files/Template/Field.php48
-rw-r--r--lib/public/Files/Template/FieldType.php19
-rw-r--r--lib/public/Files/Template/FileCreatedFromTemplateEvent.php12
-rw-r--r--lib/public/Files/Template/ITemplateManager.php4
-rw-r--r--lib/public/Files/Template/InvalidFieldTypeException.php15
-rw-r--r--lib/public/Files/Template/Template.php12
-rw-r--r--lib/public/IGroupManager.php8
-rw-r--r--lib/public/Mail/Provider/Address.php85
-rw-r--r--lib/public/Mail/Provider/Attachment.php139
-rw-r--r--lib/public/Mail/Provider/Exception/Exception.php18
-rw-r--r--lib/public/Mail/Provider/Exception/SendException.php18
-rw-r--r--lib/public/Mail/Provider/IAddress.php61
-rw-r--r--lib/public/Mail/Provider/IAttachment.php101
-rw-r--r--lib/public/Mail/Provider/IManager.php106
-rw-r--r--lib/public/Mail/Provider/IMessage.php232
-rw-r--r--lib/public/Mail/Provider/IMessageSend.php31
-rw-r--r--lib/public/Mail/Provider/IProvider.php94
-rw-r--r--lib/public/Mail/Provider/IService.php119
-rw-r--r--lib/public/Mail/Provider/Message.php338
-rw-r--r--lib/public/Preview/BeforePreviewFetchedEvent.php5
-rw-r--r--lib/public/TaskProcessing/EShapeType.php32
-rw-r--r--lib/public/TaskProcessing/IManager.php20
-rw-r--r--lib/public/TaskProcessing/IProvider.php48
-rw-r--r--lib/public/TaskProcessing/ShapeDescriptor.php4
-rw-r--r--lib/public/TaskProcessing/ShapeEnumValue.php51
-rw-r--r--lib/public/TaskProcessing/Task.php57
-rw-r--r--lib/public/TaskProcessing/TaskTypes/TextToText.php2
-rw-r--r--lib/public/TaskProcessing/TaskTypes/TextToTextTranslate.php102
76 files changed, 2537 insertions, 57 deletions
diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php
index c074a50f26e..e359a089fe1 100644
--- a/lib/composer/composer/autoload_classmap.php
+++ b/lib/composer/composer/autoload_classmap.php
@@ -435,9 +435,13 @@ return array(
'OCP\\Files\\Storage\\IStorage' => $baseDir . '/lib/public/Files/Storage/IStorage.php',
'OCP\\Files\\Storage\\IStorageFactory' => $baseDir . '/lib/public/Files/Storage/IStorageFactory.php',
'OCP\\Files\\Storage\\IWriteStreamStorage' => $baseDir . '/lib/public/Files/Storage/IWriteStreamStorage.php',
+ 'OCP\\Files\\Template\\BeforeGetTemplatesEvent' => $baseDir . '/lib/public/Files/Template/BeforeGetTemplatesEvent.php',
+ 'OCP\\Files\\Template\\Field' => $baseDir . '/lib/public/Files/Template/Field.php',
+ 'OCP\\Files\\Template\\FieldType' => $baseDir . '/lib/public/Files/Template/FieldType.php',
'OCP\\Files\\Template\\FileCreatedFromTemplateEvent' => $baseDir . '/lib/public/Files/Template/FileCreatedFromTemplateEvent.php',
'OCP\\Files\\Template\\ICustomTemplateProvider' => $baseDir . '/lib/public/Files/Template/ICustomTemplateProvider.php',
'OCP\\Files\\Template\\ITemplateManager' => $baseDir . '/lib/public/Files/Template/ITemplateManager.php',
+ 'OCP\\Files\\Template\\InvalidFieldTypeException' => $baseDir . '/lib/public/Files/Template/InvalidFieldTypeException.php',
'OCP\\Files\\Template\\RegisterTemplateCreatorEvent' => $baseDir . '/lib/public/Files/Template/RegisterTemplateCreatorEvent.php',
'OCP\\Files\\Template\\Template' => $baseDir . '/lib/public/Files/Template/Template.php',
'OCP\\Files\\Template\\TemplateFileCreator' => $baseDir . '/lib/public/Files/Template/TemplateFileCreator.php',
@@ -568,6 +572,18 @@ return array(
'OCP\\Mail\\IEMailTemplate' => $baseDir . '/lib/public/Mail/IEMailTemplate.php',
'OCP\\Mail\\IMailer' => $baseDir . '/lib/public/Mail/IMailer.php',
'OCP\\Mail\\IMessage' => $baseDir . '/lib/public/Mail/IMessage.php',
+ 'OCP\\Mail\\Provider\\Address' => $baseDir . '/lib/public/Mail/Provider/Address.php',
+ 'OCP\\Mail\\Provider\\Attachment' => $baseDir . '/lib/public/Mail/Provider/Attachment.php',
+ 'OCP\\Mail\\Provider\\Exception\\Exception' => $baseDir . '/lib/public/Mail/Provider/Exception/Exception.php',
+ 'OCP\\Mail\\Provider\\Exception\\SendException' => $baseDir . '/lib/public/Mail/Provider/Exception/SendException.php',
+ 'OCP\\Mail\\Provider\\IAddress' => $baseDir . '/lib/public/Mail/Provider/IAddress.php',
+ 'OCP\\Mail\\Provider\\IAttachment' => $baseDir . '/lib/public/Mail/Provider/IAttachment.php',
+ 'OCP\\Mail\\Provider\\IManager' => $baseDir . '/lib/public/Mail/Provider/IManager.php',
+ 'OCP\\Mail\\Provider\\IMessage' => $baseDir . '/lib/public/Mail/Provider/IMessage.php',
+ 'OCP\\Mail\\Provider\\IMessageSend' => $baseDir . '/lib/public/Mail/Provider/IMessageSend.php',
+ 'OCP\\Mail\\Provider\\IProvider' => $baseDir . '/lib/public/Mail/Provider/IProvider.php',
+ 'OCP\\Mail\\Provider\\IService' => $baseDir . '/lib/public/Mail/Provider/IService.php',
+ 'OCP\\Mail\\Provider\\Message' => $baseDir . '/lib/public/Mail/Provider/Message.php',
'OCP\\Migration\\BigIntMigration' => $baseDir . '/lib/public/Migration/BigIntMigration.php',
'OCP\\Migration\\IMigrationStep' => $baseDir . '/lib/public/Migration/IMigrationStep.php',
'OCP\\Migration\\IOutput' => $baseDir . '/lib/public/Migration/IOutput.php',
@@ -737,6 +753,7 @@ return array(
'OCP\\TaskProcessing\\ISynchronousProvider' => $baseDir . '/lib/public/TaskProcessing/ISynchronousProvider.php',
'OCP\\TaskProcessing\\ITaskType' => $baseDir . '/lib/public/TaskProcessing/ITaskType.php',
'OCP\\TaskProcessing\\ShapeDescriptor' => $baseDir . '/lib/public/TaskProcessing/ShapeDescriptor.php',
+ 'OCP\\TaskProcessing\\ShapeEnumValue' => $baseDir . '/lib/public/TaskProcessing/ShapeEnumValue.php',
'OCP\\TaskProcessing\\Task' => $baseDir . '/lib/public/TaskProcessing/Task.php',
'OCP\\TaskProcessing\\TaskTypes\\AudioToText' => $baseDir . '/lib/public/TaskProcessing/TaskTypes/AudioToText.php',
'OCP\\TaskProcessing\\TaskTypes\\ContextWrite' => $baseDir . '/lib/public/TaskProcessing/TaskTypes/ContextWrite.php',
@@ -750,6 +767,7 @@ return array(
'OCP\\TaskProcessing\\TaskTypes\\TextToTextSimplification' => $baseDir . '/lib/public/TaskProcessing/TaskTypes/TextToTextSimplification.php',
'OCP\\TaskProcessing\\TaskTypes\\TextToTextSummary' => $baseDir . '/lib/public/TaskProcessing/TaskTypes/TextToTextSummary.php',
'OCP\\TaskProcessing\\TaskTypes\\TextToTextTopics' => $baseDir . '/lib/public/TaskProcessing/TaskTypes/TextToTextTopics.php',
+ 'OCP\\TaskProcessing\\TaskTypes\\TextToTextTranslate' => $baseDir . '/lib/public/TaskProcessing/TaskTypes/TextToTextTranslate.php',
'OCP\\Teams\\ITeamManager' => $baseDir . '/lib/public/Teams/ITeamManager.php',
'OCP\\Teams\\ITeamResourceProvider' => $baseDir . '/lib/public/Teams/ITeamResourceProvider.php',
'OCP\\Teams\\Team' => $baseDir . '/lib/public/Teams/Team.php',
@@ -1180,6 +1198,8 @@ return array(
'OC\\Core\\Command\\SystemTag\\Delete' => $baseDir . '/core/Command/SystemTag/Delete.php',
'OC\\Core\\Command\\SystemTag\\Edit' => $baseDir . '/core/Command/SystemTag/Edit.php',
'OC\\Core\\Command\\SystemTag\\ListCommand' => $baseDir . '/core/Command/SystemTag/ListCommand.php',
+ 'OC\\Core\\Command\\TaskProcessing\\ListCommand' => $baseDir . '/core/Command/TaskProcessing/ListCommand.php',
+ 'OC\\Core\\Command\\TaskProcessing\\Statistics' => $baseDir . '/core/Command/TaskProcessing/Statistics.php',
'OC\\Core\\Command\\TwoFactorAuth\\Base' => $baseDir . '/core/Command/TwoFactorAuth/Base.php',
'OC\\Core\\Command\\TwoFactorAuth\\Cleanup' => $baseDir . '/core/Command/TwoFactorAuth/Cleanup.php',
'OC\\Core\\Command\\TwoFactorAuth\\Disable' => $baseDir . '/core/Command/TwoFactorAuth/Disable.php',
@@ -1326,6 +1346,7 @@ return array(
'OC\\Core\\Migrations\\Version29000Date20240124132202' => $baseDir . '/core/Migrations/Version29000Date20240124132202.php',
'OC\\Core\\Migrations\\Version29000Date20240131122720' => $baseDir . '/core/Migrations/Version29000Date20240131122720.php',
'OC\\Core\\Migrations\\Version30000Date20240429122720' => $baseDir . '/core/Migrations/Version30000Date20240429122720.php',
+ 'OC\\Core\\Migrations\\Version30000Date20240708160048' => $baseDir . '/core/Migrations/Version30000Date20240708160048.php',
'OC\\Core\\Migrations\\Version30000Date20240717111406' => $baseDir . '/core/Migrations/Version30000Date20240717111406.php',
'OC\\Core\\Notification\\CoreNotifier' => $baseDir . '/core/Notification/CoreNotifier.php',
'OC\\Core\\ResponseDefinitions' => $baseDir . '/core/ResponseDefinitions.php',
@@ -1615,6 +1636,7 @@ return array(
'OC\\Mail\\EMailTemplate' => $baseDir . '/lib/private/Mail/EMailTemplate.php',
'OC\\Mail\\Mailer' => $baseDir . '/lib/private/Mail/Mailer.php',
'OC\\Mail\\Message' => $baseDir . '/lib/private/Mail/Message.php',
+ 'OC\\Mail\\Provider\\Manager' => $baseDir . '/lib/private/Mail/Provider/Manager.php',
'OC\\Memcache\\APCu' => $baseDir . '/lib/private/Memcache/APCu.php',
'OC\\Memcache\\ArrayCache' => $baseDir . '/lib/private/Memcache/ArrayCache.php',
'OC\\Memcache\\CADTrait' => $baseDir . '/lib/private/Memcache/CADTrait.php',
diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php
index 61df306bda3..de92b293fcb 100644
--- a/lib/composer/composer/autoload_static.php
+++ b/lib/composer/composer/autoload_static.php
@@ -468,9 +468,13 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\Files\\Storage\\IStorage' => __DIR__ . '/../../..' . '/lib/public/Files/Storage/IStorage.php',
'OCP\\Files\\Storage\\IStorageFactory' => __DIR__ . '/../../..' . '/lib/public/Files/Storage/IStorageFactory.php',
'OCP\\Files\\Storage\\IWriteStreamStorage' => __DIR__ . '/../../..' . '/lib/public/Files/Storage/IWriteStreamStorage.php',
+ 'OCP\\Files\\Template\\BeforeGetTemplatesEvent' => __DIR__ . '/../../..' . '/lib/public/Files/Template/BeforeGetTemplatesEvent.php',
+ 'OCP\\Files\\Template\\Field' => __DIR__ . '/../../..' . '/lib/public/Files/Template/Field.php',
+ 'OCP\\Files\\Template\\FieldType' => __DIR__ . '/../../..' . '/lib/public/Files/Template/FieldType.php',
'OCP\\Files\\Template\\FileCreatedFromTemplateEvent' => __DIR__ . '/../../..' . '/lib/public/Files/Template/FileCreatedFromTemplateEvent.php',
'OCP\\Files\\Template\\ICustomTemplateProvider' => __DIR__ . '/../../..' . '/lib/public/Files/Template/ICustomTemplateProvider.php',
'OCP\\Files\\Template\\ITemplateManager' => __DIR__ . '/../../..' . '/lib/public/Files/Template/ITemplateManager.php',
+ 'OCP\\Files\\Template\\InvalidFieldTypeException' => __DIR__ . '/../../..' . '/lib/public/Files/Template/InvalidFieldTypeException.php',
'OCP\\Files\\Template\\RegisterTemplateCreatorEvent' => __DIR__ . '/../../..' . '/lib/public/Files/Template/RegisterTemplateCreatorEvent.php',
'OCP\\Files\\Template\\Template' => __DIR__ . '/../../..' . '/lib/public/Files/Template/Template.php',
'OCP\\Files\\Template\\TemplateFileCreator' => __DIR__ . '/../../..' . '/lib/public/Files/Template/TemplateFileCreator.php',
@@ -601,6 +605,18 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\Mail\\IEMailTemplate' => __DIR__ . '/../../..' . '/lib/public/Mail/IEMailTemplate.php',
'OCP\\Mail\\IMailer' => __DIR__ . '/../../..' . '/lib/public/Mail/IMailer.php',
'OCP\\Mail\\IMessage' => __DIR__ . '/../../..' . '/lib/public/Mail/IMessage.php',
+ 'OCP\\Mail\\Provider\\Address' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/Address.php',
+ 'OCP\\Mail\\Provider\\Attachment' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/Attachment.php',
+ 'OCP\\Mail\\Provider\\Exception\\Exception' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/Exception/Exception.php',
+ 'OCP\\Mail\\Provider\\Exception\\SendException' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/Exception/SendException.php',
+ 'OCP\\Mail\\Provider\\IAddress' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/IAddress.php',
+ 'OCP\\Mail\\Provider\\IAttachment' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/IAttachment.php',
+ 'OCP\\Mail\\Provider\\IManager' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/IManager.php',
+ 'OCP\\Mail\\Provider\\IMessage' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/IMessage.php',
+ 'OCP\\Mail\\Provider\\IMessageSend' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/IMessageSend.php',
+ 'OCP\\Mail\\Provider\\IProvider' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/IProvider.php',
+ 'OCP\\Mail\\Provider\\IService' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/IService.php',
+ 'OCP\\Mail\\Provider\\Message' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/Message.php',
'OCP\\Migration\\BigIntMigration' => __DIR__ . '/../../..' . '/lib/public/Migration/BigIntMigration.php',
'OCP\\Migration\\IMigrationStep' => __DIR__ . '/../../..' . '/lib/public/Migration/IMigrationStep.php',
'OCP\\Migration\\IOutput' => __DIR__ . '/../../..' . '/lib/public/Migration/IOutput.php',
@@ -770,6 +786,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\TaskProcessing\\ISynchronousProvider' => __DIR__ . '/../../..' . '/lib/public/TaskProcessing/ISynchronousProvider.php',
'OCP\\TaskProcessing\\ITaskType' => __DIR__ . '/../../..' . '/lib/public/TaskProcessing/ITaskType.php',
'OCP\\TaskProcessing\\ShapeDescriptor' => __DIR__ . '/../../..' . '/lib/public/TaskProcessing/ShapeDescriptor.php',
+ 'OCP\\TaskProcessing\\ShapeEnumValue' => __DIR__ . '/../../..' . '/lib/public/TaskProcessing/ShapeEnumValue.php',
'OCP\\TaskProcessing\\Task' => __DIR__ . '/../../..' . '/lib/public/TaskProcessing/Task.php',
'OCP\\TaskProcessing\\TaskTypes\\AudioToText' => __DIR__ . '/../../..' . '/lib/public/TaskProcessing/TaskTypes/AudioToText.php',
'OCP\\TaskProcessing\\TaskTypes\\ContextWrite' => __DIR__ . '/../../..' . '/lib/public/TaskProcessing/TaskTypes/ContextWrite.php',
@@ -783,6 +800,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\TaskProcessing\\TaskTypes\\TextToTextSimplification' => __DIR__ . '/../../..' . '/lib/public/TaskProcessing/TaskTypes/TextToTextSimplification.php',
'OCP\\TaskProcessing\\TaskTypes\\TextToTextSummary' => __DIR__ . '/../../..' . '/lib/public/TaskProcessing/TaskTypes/TextToTextSummary.php',
'OCP\\TaskProcessing\\TaskTypes\\TextToTextTopics' => __DIR__ . '/../../..' . '/lib/public/TaskProcessing/TaskTypes/TextToTextTopics.php',
+ 'OCP\\TaskProcessing\\TaskTypes\\TextToTextTranslate' => __DIR__ . '/../../..' . '/lib/public/TaskProcessing/TaskTypes/TextToTextTranslate.php',
'OCP\\Teams\\ITeamManager' => __DIR__ . '/../../..' . '/lib/public/Teams/ITeamManager.php',
'OCP\\Teams\\ITeamResourceProvider' => __DIR__ . '/../../..' . '/lib/public/Teams/ITeamResourceProvider.php',
'OCP\\Teams\\Team' => __DIR__ . '/../../..' . '/lib/public/Teams/Team.php',
@@ -1213,6 +1231,8 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Core\\Command\\SystemTag\\Delete' => __DIR__ . '/../../..' . '/core/Command/SystemTag/Delete.php',
'OC\\Core\\Command\\SystemTag\\Edit' => __DIR__ . '/../../..' . '/core/Command/SystemTag/Edit.php',
'OC\\Core\\Command\\SystemTag\\ListCommand' => __DIR__ . '/../../..' . '/core/Command/SystemTag/ListCommand.php',
+ 'OC\\Core\\Command\\TaskProcessing\\ListCommand' => __DIR__ . '/../../..' . '/core/Command/TaskProcessing/ListCommand.php',
+ 'OC\\Core\\Command\\TaskProcessing\\Statistics' => __DIR__ . '/../../..' . '/core/Command/TaskProcessing/Statistics.php',
'OC\\Core\\Command\\TwoFactorAuth\\Base' => __DIR__ . '/../../..' . '/core/Command/TwoFactorAuth/Base.php',
'OC\\Core\\Command\\TwoFactorAuth\\Cleanup' => __DIR__ . '/../../..' . '/core/Command/TwoFactorAuth/Cleanup.php',
'OC\\Core\\Command\\TwoFactorAuth\\Disable' => __DIR__ . '/../../..' . '/core/Command/TwoFactorAuth/Disable.php',
@@ -1359,6 +1379,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Core\\Migrations\\Version29000Date20240124132202' => __DIR__ . '/../../..' . '/core/Migrations/Version29000Date20240124132202.php',
'OC\\Core\\Migrations\\Version29000Date20240131122720' => __DIR__ . '/../../..' . '/core/Migrations/Version29000Date20240131122720.php',
'OC\\Core\\Migrations\\Version30000Date20240429122720' => __DIR__ . '/../../..' . '/core/Migrations/Version30000Date20240429122720.php',
+ 'OC\\Core\\Migrations\\Version30000Date20240708160048' => __DIR__ . '/../../..' . '/core/Migrations/Version30000Date20240708160048.php',
'OC\\Core\\Migrations\\Version30000Date20240717111406' => __DIR__ . '/../../..' . '/core/Migrations/Version30000Date20240717111406.php',
'OC\\Core\\Notification\\CoreNotifier' => __DIR__ . '/../../..' . '/core/Notification/CoreNotifier.php',
'OC\\Core\\ResponseDefinitions' => __DIR__ . '/../../..' . '/core/ResponseDefinitions.php',
@@ -1648,6 +1669,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Mail\\EMailTemplate' => __DIR__ . '/../../..' . '/lib/private/Mail/EMailTemplate.php',
'OC\\Mail\\Mailer' => __DIR__ . '/../../..' . '/lib/private/Mail/Mailer.php',
'OC\\Mail\\Message' => __DIR__ . '/../../..' . '/lib/private/Mail/Message.php',
+ 'OC\\Mail\\Provider\\Manager' => __DIR__ . '/../../..' . '/lib/private/Mail/Provider/Manager.php',
'OC\\Memcache\\APCu' => __DIR__ . '/../../..' . '/lib/private/Memcache/APCu.php',
'OC\\Memcache\\ArrayCache' => __DIR__ . '/../../..' . '/lib/private/Memcache/ArrayCache.php',
'OC\\Memcache\\CADTrait' => __DIR__ . '/../../..' . '/lib/private/Memcache/CADTrait.php',
diff --git a/lib/l10n/br.js b/lib/l10n/br.js
index 74b65c80083..9acd6e9a38d 100644
--- a/lib/l10n/br.js
+++ b/lib/l10n/br.js
@@ -28,6 +28,7 @@ OC.L10N.register(
"Apps" : "Meziant",
"Settings" : "Arventennoù",
"Log out" : "Kuitat",
+ "Accounts" : "Kontoù",
"Email" : "Postel",
"Twitter" : "Twitter",
"Website" : "Lec'hien web",
diff --git a/lib/l10n/br.json b/lib/l10n/br.json
index 4c7e7c89929..c7844acba80 100644
--- a/lib/l10n/br.json
+++ b/lib/l10n/br.json
@@ -26,6 +26,7 @@
"Apps" : "Meziant",
"Settings" : "Arventennoù",
"Log out" : "Kuitat",
+ "Accounts" : "Kontoù",
"Email" : "Postel",
"Twitter" : "Twitter",
"Website" : "Lec'hien web",
diff --git a/lib/l10n/de_DE.js b/lib/l10n/de_DE.js
index cc15b32c111..2ab7e76a79e 100644
--- a/lib/l10n/de_DE.js
+++ b/lib/l10n/de_DE.js
@@ -38,6 +38,7 @@ OC.L10N.register(
"Server version %s or higher is required." : "Server Version %s oder höher wird benötigt.",
"Server version %s or lower is required." : "Server Version %s oder niedriger wird benötigt.",
"Logged in account must be an admin, a sub admin or gotten special right to access this setting" : "Das angemeldete Konto muss ein Administrator, ein Teil-Administrator sein oder ein Sonderrecht haben, um auf diese Einstellung zuzugreifen",
+ "Your current IP address doesn’t allow you to perform admin actions" : "Unter Ihrer aktuellen IP-Adresse sind keine Administrationsaktionen erlaubt",
"Logged in account must be an admin or sub admin" : "Das angemeldete Konto muss ein (Sub-)Administrator sein",
"Logged in account must be an admin" : "Das angemeldete Konto muss ein Administrator sein",
"Wiping of device %s has started" : "Löschen von Gerät %s wurde gestartet",
@@ -87,6 +88,7 @@ OC.L10N.register(
"Invalid path" : "Ungültiger Pfad",
"Failed to create file from template" : "Fehler beim Erstellen der Datei aus Vorlage",
"Templates" : "Vorlagen",
+ "Path contains invalid segments" : "Pfad enthält ungültige Segmente",
"File name is a reserved word" : "Der Dateiname ist ein reserviertes Wort",
"File name contains at least one invalid character" : "Der Dateiname enthält mindestens ein ungültiges Zeichen",
"File name is too long" : "Dateiname ist zu lang",
diff --git a/lib/l10n/de_DE.json b/lib/l10n/de_DE.json
index 33413652199..5d231b0ab0f 100644
--- a/lib/l10n/de_DE.json
+++ b/lib/l10n/de_DE.json
@@ -36,6 +36,7 @@
"Server version %s or higher is required." : "Server Version %s oder höher wird benötigt.",
"Server version %s or lower is required." : "Server Version %s oder niedriger wird benötigt.",
"Logged in account must be an admin, a sub admin or gotten special right to access this setting" : "Das angemeldete Konto muss ein Administrator, ein Teil-Administrator sein oder ein Sonderrecht haben, um auf diese Einstellung zuzugreifen",
+ "Your current IP address doesn’t allow you to perform admin actions" : "Unter Ihrer aktuellen IP-Adresse sind keine Administrationsaktionen erlaubt",
"Logged in account must be an admin or sub admin" : "Das angemeldete Konto muss ein (Sub-)Administrator sein",
"Logged in account must be an admin" : "Das angemeldete Konto muss ein Administrator sein",
"Wiping of device %s has started" : "Löschen von Gerät %s wurde gestartet",
@@ -85,6 +86,7 @@
"Invalid path" : "Ungültiger Pfad",
"Failed to create file from template" : "Fehler beim Erstellen der Datei aus Vorlage",
"Templates" : "Vorlagen",
+ "Path contains invalid segments" : "Pfad enthält ungültige Segmente",
"File name is a reserved word" : "Der Dateiname ist ein reserviertes Wort",
"File name contains at least one invalid character" : "Der Dateiname enthält mindestens ein ungültiges Zeichen",
"File name is too long" : "Dateiname ist zu lang",
diff --git a/lib/l10n/en_GB.js b/lib/l10n/en_GB.js
index 839d46e241d..fe293098c00 100644
--- a/lib/l10n/en_GB.js
+++ b/lib/l10n/en_GB.js
@@ -38,6 +38,7 @@ OC.L10N.register(
"Server version %s or higher is required." : "Server version %s or higher is required.",
"Server version %s or lower is required." : "Server version %s or lower is required.",
"Logged in account must be an admin, a sub admin or gotten special right to access this setting" : "Logged in account must be an admin, a sub admin or gotten special right to access this setting",
+ "Your current IP address doesn’t allow you to perform admin actions" : "Your current IP address doesn’t allow you to perform admin actions",
"Logged in account must be an admin or sub admin" : "Logged in account must be an admin or sub admin",
"Logged in account must be an admin" : "Logged in account must be an admin",
"Wiping of device %s has started" : "Wiping of device %s has started",
@@ -87,6 +88,7 @@ OC.L10N.register(
"Invalid path" : "Invalid path",
"Failed to create file from template" : "Failed to create file from template",
"Templates" : "Templates",
+ "Path contains invalid segments" : "Path contains invalid segments",
"File name is a reserved word" : "File name is a reserved word",
"File name contains at least one invalid character" : "File name contains at least one invalid character",
"File name is too long" : "File name is too long",
diff --git a/lib/l10n/en_GB.json b/lib/l10n/en_GB.json
index 90d48cec7e7..331038ba6db 100644
--- a/lib/l10n/en_GB.json
+++ b/lib/l10n/en_GB.json
@@ -36,6 +36,7 @@
"Server version %s or higher is required." : "Server version %s or higher is required.",
"Server version %s or lower is required." : "Server version %s or lower is required.",
"Logged in account must be an admin, a sub admin or gotten special right to access this setting" : "Logged in account must be an admin, a sub admin or gotten special right to access this setting",
+ "Your current IP address doesn’t allow you to perform admin actions" : "Your current IP address doesn’t allow you to perform admin actions",
"Logged in account must be an admin or sub admin" : "Logged in account must be an admin or sub admin",
"Logged in account must be an admin" : "Logged in account must be an admin",
"Wiping of device %s has started" : "Wiping of device %s has started",
@@ -85,6 +86,7 @@
"Invalid path" : "Invalid path",
"Failed to create file from template" : "Failed to create file from template",
"Templates" : "Templates",
+ "Path contains invalid segments" : "Path contains invalid segments",
"File name is a reserved word" : "File name is a reserved word",
"File name contains at least one invalid character" : "File name contains at least one invalid character",
"File name is too long" : "File name is too long",
diff --git a/lib/l10n/ga.js b/lib/l10n/ga.js
index fdc5016267b..eb6ba637453 100644
--- a/lib/l10n/ga.js
+++ b/lib/l10n/ga.js
@@ -38,6 +38,7 @@ OC.L10N.register(
"Server version %s or higher is required." : "Tá leagan freastalaí %s nó níos airde ag teastáil.",
"Server version %s or lower is required." : "Tá leagan freastalaí %s nó níos ísle ag teastáil.",
"Logged in account must be an admin, a sub admin or gotten special right to access this setting" : "Ní mór gur riarthóir, fo-riarthóir é cuntas logáilte isteach nó go bhfuil ceart speisialta faighte aige chun an socrú seo a rochtain",
+ "Your current IP address doesn’t allow you to perform admin actions" : "Ní cheadaíonn do sheoladh IP reatha duit gníomhartha riaracháin a dhéanamh",
"Logged in account must be an admin or sub admin" : "Ní mór gur riarthóir nó fo-riarthóir é cuntas logáilte isteach",
"Logged in account must be an admin" : "Ní mór gur riarthóir é cuntas logáilte isteach",
"Wiping of device %s has started" : "Tá tús curtha le glanadh an ghléis %s",
@@ -87,6 +88,7 @@ OC.L10N.register(
"Invalid path" : "Conair neamhbhailí",
"Failed to create file from template" : "Theip ar chruthú comhad ón teimpléad",
"Templates" : "Teimpléid",
+ "Path contains invalid segments" : "Tá míreanna neamhbhailí sa chonair",
"File name is a reserved word" : "Focal in áirithe is ea ainm comhaid",
"File name contains at least one invalid character" : "Tá carachtar neamhbhailí amháin ar a laghad san ainm comhaid",
"File name is too long" : "Tá ainm an chomhaid rófhada",
diff --git a/lib/l10n/ga.json b/lib/l10n/ga.json
index 31a35e2c55c..ee5f47e7aec 100644
--- a/lib/l10n/ga.json
+++ b/lib/l10n/ga.json
@@ -36,6 +36,7 @@
"Server version %s or higher is required." : "Tá leagan freastalaí %s nó níos airde ag teastáil.",
"Server version %s or lower is required." : "Tá leagan freastalaí %s nó níos ísle ag teastáil.",
"Logged in account must be an admin, a sub admin or gotten special right to access this setting" : "Ní mór gur riarthóir, fo-riarthóir é cuntas logáilte isteach nó go bhfuil ceart speisialta faighte aige chun an socrú seo a rochtain",
+ "Your current IP address doesn’t allow you to perform admin actions" : "Ní cheadaíonn do sheoladh IP reatha duit gníomhartha riaracháin a dhéanamh",
"Logged in account must be an admin or sub admin" : "Ní mór gur riarthóir nó fo-riarthóir é cuntas logáilte isteach",
"Logged in account must be an admin" : "Ní mór gur riarthóir é cuntas logáilte isteach",
"Wiping of device %s has started" : "Tá tús curtha le glanadh an ghléis %s",
@@ -85,6 +86,7 @@
"Invalid path" : "Conair neamhbhailí",
"Failed to create file from template" : "Theip ar chruthú comhad ón teimpléad",
"Templates" : "Teimpléid",
+ "Path contains invalid segments" : "Tá míreanna neamhbhailí sa chonair",
"File name is a reserved word" : "Focal in áirithe is ea ainm comhaid",
"File name contains at least one invalid character" : "Tá carachtar neamhbhailí amháin ar a laghad san ainm comhaid",
"File name is too long" : "Tá ainm an chomhaid rófhada",
diff --git a/lib/l10n/gl.js b/lib/l10n/gl.js
index 3f981a253d5..9ee4a4e50d4 100644
--- a/lib/l10n/gl.js
+++ b/lib/l10n/gl.js
@@ -38,6 +38,7 @@ OC.L10N.register(
"Server version %s or higher is required." : "Precísase da versión %s ou superior do servidor.",
"Server version %s or lower is required." : "Precísase da versión %s ou inferior do servidor.",
"Logged in account must be an admin, a sub admin or gotten special right to access this setting" : "A conta que accede debe ser de administración, de subadministración ou ter dereitos especiais para acceder a este axuste",
+ "Your current IP address doesn’t allow you to perform admin actions" : "O seu enderezo IP actual non lle permite realizar accións de administración",
"Logged in account must be an admin or sub admin" : "A conta que accede debe ser de administración ou de subadministración",
"Logged in account must be an admin" : "A conta que accede debe ser de administración",
"Wiping of device %s has started" : "Iniciouse a limpeza do dispositivo %s",
@@ -87,6 +88,7 @@ OC.L10N.register(
"Invalid path" : "Ruta incorrecta.",
"Failed to create file from template" : "Produciuse un fallo ao crear un ficheiro a partir do modelo",
"Templates" : "Modelos",
+ "Path contains invalid segments" : "A ruta contén segmentos non válidos",
"File name is a reserved word" : "O nome de ficheiro é unha palabra reservada",
"File name contains at least one invalid character" : "O nome de ficheiro contén algún carácter incorrecto",
"File name is too long" : "O nome de ficheiro é longo de máis",
diff --git a/lib/l10n/gl.json b/lib/l10n/gl.json
index 120cb46fad5..01577f16265 100644
--- a/lib/l10n/gl.json
+++ b/lib/l10n/gl.json
@@ -36,6 +36,7 @@
"Server version %s or higher is required." : "Precísase da versión %s ou superior do servidor.",
"Server version %s or lower is required." : "Precísase da versión %s ou inferior do servidor.",
"Logged in account must be an admin, a sub admin or gotten special right to access this setting" : "A conta que accede debe ser de administración, de subadministración ou ter dereitos especiais para acceder a este axuste",
+ "Your current IP address doesn’t allow you to perform admin actions" : "O seu enderezo IP actual non lle permite realizar accións de administración",
"Logged in account must be an admin or sub admin" : "A conta que accede debe ser de administración ou de subadministración",
"Logged in account must be an admin" : "A conta que accede debe ser de administración",
"Wiping of device %s has started" : "Iniciouse a limpeza do dispositivo %s",
@@ -85,6 +86,7 @@
"Invalid path" : "Ruta incorrecta.",
"Failed to create file from template" : "Produciuse un fallo ao crear un ficheiro a partir do modelo",
"Templates" : "Modelos",
+ "Path contains invalid segments" : "A ruta contén segmentos non válidos",
"File name is a reserved word" : "O nome de ficheiro é unha palabra reservada",
"File name contains at least one invalid character" : "O nome de ficheiro contén algún carácter incorrecto",
"File name is too long" : "O nome de ficheiro é longo de máis",
diff --git a/lib/l10n/hu.js b/lib/l10n/hu.js
index 6f5b7aaa15f..174501303fd 100644
--- a/lib/l10n/hu.js
+++ b/lib/l10n/hu.js
@@ -89,6 +89,7 @@ OC.L10N.register(
"App \"%s\" cannot be installed because it is not compatible with this version of the server." : "A(z) „%s” alkalmazást nem lehet telepíteni, mert nem kompatibilis ezzel a kiszolgálóverzióval.",
"__language_name__" : "Magyar",
"This is an automatically sent email, please do not reply." : "Ez egy automatikusan küldött levél, ne válaszoljon rá.",
+ "Help & privacy" : "Súgó és adatvédelem",
"Appearance and accessibility" : "Megjelenés és akadálymentesítés",
"Apps" : "Alkalmazások",
"Personal settings" : "Személyes beállítások",
diff --git a/lib/l10n/hu.json b/lib/l10n/hu.json
index d1cd54bac41..45e3ebfccb2 100644
--- a/lib/l10n/hu.json
+++ b/lib/l10n/hu.json
@@ -87,6 +87,7 @@
"App \"%s\" cannot be installed because it is not compatible with this version of the server." : "A(z) „%s” alkalmazást nem lehet telepíteni, mert nem kompatibilis ezzel a kiszolgálóverzióval.",
"__language_name__" : "Magyar",
"This is an automatically sent email, please do not reply." : "Ez egy automatikusan küldött levél, ne válaszoljon rá.",
+ "Help & privacy" : "Súgó és adatvédelem",
"Appearance and accessibility" : "Megjelenés és akadálymentesítés",
"Apps" : "Alkalmazások",
"Personal settings" : "Személyes beállítások",
diff --git a/lib/l10n/nb.js b/lib/l10n/nb.js
index f62bcfd18fa..0c465bcd81b 100644
--- a/lib/l10n/nb.js
+++ b/lib/l10n/nb.js
@@ -38,6 +38,7 @@ OC.L10N.register(
"Server version %s or higher is required." : "Serverversjon %s eller høyere kreves.",
"Server version %s or lower is required." : "Serverversjon %s eller lavere kreves.",
"Logged in account must be an admin, a sub admin or gotten special right to access this setting" : "Innlogget konto må være administrator, underadministrator eller ha fått spesiell rett til å få tilgang til denne innstillingen.",
+ "Your current IP address doesn’t allow you to perform admin actions" : "Den nåværende IP-adressen din lar deg ikke utføre administratorhandlinger",
"Logged in account must be an admin or sub admin" : "Innlogget konto må være administrator eller en underadministrator.",
"Logged in account must be an admin" : "Innlogget konto må være administrator",
"Wiping of device %s has started" : "Sikker sletting av enhet %s er startet",
@@ -87,6 +88,7 @@ OC.L10N.register(
"Invalid path" : "Ugyldig filbane",
"Failed to create file from template" : "Oppretting av fil fra mal feilet",
"Templates" : "Maler",
+ "Path contains invalid segments" : "Banen inneholder ugyldige segmenter",
"File name is a reserved word" : "Filnavnet er et reservert ord",
"File name contains at least one invalid character" : "Filnavnet inneholder minst ett ulovlig tegn",
"File name is too long" : "Filnavnet er for langt",
diff --git a/lib/l10n/nb.json b/lib/l10n/nb.json
index bb6c97d6006..c3e66f6fa7d 100644
--- a/lib/l10n/nb.json
+++ b/lib/l10n/nb.json
@@ -36,6 +36,7 @@
"Server version %s or higher is required." : "Serverversjon %s eller høyere kreves.",
"Server version %s or lower is required." : "Serverversjon %s eller lavere kreves.",
"Logged in account must be an admin, a sub admin or gotten special right to access this setting" : "Innlogget konto må være administrator, underadministrator eller ha fått spesiell rett til å få tilgang til denne innstillingen.",
+ "Your current IP address doesn’t allow you to perform admin actions" : "Den nåværende IP-adressen din lar deg ikke utføre administratorhandlinger",
"Logged in account must be an admin or sub admin" : "Innlogget konto må være administrator eller en underadministrator.",
"Logged in account must be an admin" : "Innlogget konto må være administrator",
"Wiping of device %s has started" : "Sikker sletting av enhet %s er startet",
@@ -85,6 +86,7 @@
"Invalid path" : "Ugyldig filbane",
"Failed to create file from template" : "Oppretting av fil fra mal feilet",
"Templates" : "Maler",
+ "Path contains invalid segments" : "Banen inneholder ugyldige segmenter",
"File name is a reserved word" : "Filnavnet er et reservert ord",
"File name contains at least one invalid character" : "Filnavnet inneholder minst ett ulovlig tegn",
"File name is too long" : "Filnavnet er for langt",
diff --git a/lib/l10n/pt_BR.js b/lib/l10n/pt_BR.js
index 07572e10cb5..5edc4c57c84 100644
--- a/lib/l10n/pt_BR.js
+++ b/lib/l10n/pt_BR.js
@@ -38,6 +38,7 @@ OC.L10N.register(
"Server version %s or higher is required." : "É requerido um servidor da versão %s ou superior.",
"Server version %s or lower is required." : "É requerido um servidor da versão %s ou abaixo.",
"Logged in account must be an admin, a sub admin or gotten special right to access this setting" : "A conta conectada deve ser um administrador, um subadministrador ou ter direito especial para acessar esta configuração",
+ "Your current IP address doesn’t allow you to perform admin actions" : "Seu endereço IP atual não permite que você execute ações administrativas",
"Logged in account must be an admin or sub admin" : "A conta conectada deve ser de um administrador ou subadministrador",
"Logged in account must be an admin" : "A conta logada deve ser de um administrador",
"Wiping of device %s has started" : "Limpeza do dispositivo %s iniciou",
@@ -87,6 +88,7 @@ OC.L10N.register(
"Invalid path" : "Diretório inválido",
"Failed to create file from template" : "Falha ao criar arquivo do modelo ",
"Templates" : "Modelos ",
+ "Path contains invalid segments" : "O caminho contém segmentos inválidos",
"File name is a reserved word" : "O nome do arquivo é uma palavra reservada",
"File name contains at least one invalid character" : "O nome do arquivo contém pelo menos um caracter inválido",
"File name is too long" : "O nome do arquivo é muito longo",
diff --git a/lib/l10n/pt_BR.json b/lib/l10n/pt_BR.json
index cd3f55ce20d..1305041a586 100644
--- a/lib/l10n/pt_BR.json
+++ b/lib/l10n/pt_BR.json
@@ -36,6 +36,7 @@
"Server version %s or higher is required." : "É requerido um servidor da versão %s ou superior.",
"Server version %s or lower is required." : "É requerido um servidor da versão %s ou abaixo.",
"Logged in account must be an admin, a sub admin or gotten special right to access this setting" : "A conta conectada deve ser um administrador, um subadministrador ou ter direito especial para acessar esta configuração",
+ "Your current IP address doesn’t allow you to perform admin actions" : "Seu endereço IP atual não permite que você execute ações administrativas",
"Logged in account must be an admin or sub admin" : "A conta conectada deve ser de um administrador ou subadministrador",
"Logged in account must be an admin" : "A conta logada deve ser de um administrador",
"Wiping of device %s has started" : "Limpeza do dispositivo %s iniciou",
@@ -85,6 +86,7 @@
"Invalid path" : "Diretório inválido",
"Failed to create file from template" : "Falha ao criar arquivo do modelo ",
"Templates" : "Modelos ",
+ "Path contains invalid segments" : "O caminho contém segmentos inválidos",
"File name is a reserved word" : "O nome do arquivo é uma palavra reservada",
"File name contains at least one invalid character" : "O nome do arquivo contém pelo menos um caracter inválido",
"File name is too long" : "O nome do arquivo é muito longo",
diff --git a/lib/l10n/sr@latin.js b/lib/l10n/sr@latin.js
index 3487200ee85..2d322157d37 100644
--- a/lib/l10n/sr@latin.js
+++ b/lib/l10n/sr@latin.js
@@ -1,6 +1,8 @@
OC.L10N.register(
"lib",
{
+ "Unknown filetype" : "Nepoznati tip fajla",
+ "Invalid image" : "Neispravna slika",
"Files" : "Фајлови",
"View profile" : "Vidi profil",
"seconds ago" : "pre par sekundi",
diff --git a/lib/l10n/sr@latin.json b/lib/l10n/sr@latin.json
index 84ad4559b0a..0300634c077 100644
--- a/lib/l10n/sr@latin.json
+++ b/lib/l10n/sr@latin.json
@@ -1,4 +1,6 @@
{ "translations": {
+ "Unknown filetype" : "Nepoznati tip fajla",
+ "Invalid image" : "Neispravna slika",
"Files" : "Фајлови",
"View profile" : "Vidi profil",
"seconds ago" : "pre par sekundi",
diff --git a/lib/l10n/sv.js b/lib/l10n/sv.js
index 1bc47ffa0ba..d210f36901c 100644
--- a/lib/l10n/sv.js
+++ b/lib/l10n/sv.js
@@ -38,6 +38,7 @@ OC.L10N.register(
"Server version %s or higher is required." : "Serverversion %s eller nyare krävs.",
"Server version %s or lower is required." : "Serverversion %s eller äldre krävs.",
"Logged in account must be an admin, a sub admin or gotten special right to access this setting" : "Inloggat konto måste vara en admin, underadministratör eller ha tilldelats speciella rättigheter för att komma åt denna inställning",
+ "Your current IP address doesn’t allow you to perform admin actions" : "Din nuvarande IP-adress tillåter dig inte att utföra administratörsåtgärder",
"Logged in account must be an admin or sub admin" : "Inloggat konto måste vara en admin eller underadministratör",
"Logged in account must be an admin" : "Inloggat konto måste vara admin",
"Wiping of device %s has started" : "Rensning av enhet %s har startat",
@@ -87,6 +88,7 @@ OC.L10N.register(
"Invalid path" : "Ogiltig sökväg",
"Failed to create file from template" : "Kunde skapa fil från mall",
"Templates" : "Mallar",
+ "Path contains invalid segments" : "Sökvägen innehåller ogiltiga segment",
"File name is a reserved word" : "Filnamnet är ett reserverat ord",
"File name contains at least one invalid character" : "Filnamnet innehåller minst ett ogiltigt tecken",
"File name is too long" : "Filnamnet är för långt",
diff --git a/lib/l10n/sv.json b/lib/l10n/sv.json
index 2b918b35dc4..3882f550ccb 100644
--- a/lib/l10n/sv.json
+++ b/lib/l10n/sv.json
@@ -36,6 +36,7 @@
"Server version %s or higher is required." : "Serverversion %s eller nyare krävs.",
"Server version %s or lower is required." : "Serverversion %s eller äldre krävs.",
"Logged in account must be an admin, a sub admin or gotten special right to access this setting" : "Inloggat konto måste vara en admin, underadministratör eller ha tilldelats speciella rättigheter för att komma åt denna inställning",
+ "Your current IP address doesn’t allow you to perform admin actions" : "Din nuvarande IP-adress tillåter dig inte att utföra administratörsåtgärder",
"Logged in account must be an admin or sub admin" : "Inloggat konto måste vara en admin eller underadministratör",
"Logged in account must be an admin" : "Inloggat konto måste vara admin",
"Wiping of device %s has started" : "Rensning av enhet %s har startat",
@@ -85,6 +86,7 @@
"Invalid path" : "Ogiltig sökväg",
"Failed to create file from template" : "Kunde skapa fil från mall",
"Templates" : "Mallar",
+ "Path contains invalid segments" : "Sökvägen innehåller ogiltiga segment",
"File name is a reserved word" : "Filnamnet är ett reserverat ord",
"File name contains at least one invalid character" : "Filnamnet innehåller minst ett ogiltigt tecken",
"File name is too long" : "Filnamnet är för långt",
diff --git a/lib/l10n/zh_TW.js b/lib/l10n/zh_TW.js
index cb5bf6eddc3..4fac3736cac 100644
--- a/lib/l10n/zh_TW.js
+++ b/lib/l10n/zh_TW.js
@@ -38,6 +38,7 @@ OC.L10N.register(
"Server version %s or higher is required." : "需要伺服器版本 %s 或更高",
"Server version %s or lower is required." : "需要伺服器版本 %s 或更低",
"Logged in account must be an admin, a sub admin or gotten special right to access this setting" : "登入帳號必須為管理員、子管理員或有可存取此設定的特殊權限的使用者",
+ "Your current IP address doesn’t allow you to perform admin actions" : "您目前的 IP 位置不允許您執行管理動作",
"Logged in account must be an admin or sub admin" : "登入的帳號必須要是管理員或是子管理員",
"Logged in account must be an admin" : "登入的帳號必須有管理員權限",
"Wiping of device %s has started" : "已開始抹除裝置 %s ",
@@ -87,6 +88,7 @@ OC.L10N.register(
"Invalid path" : "無效的路徑",
"Failed to create file from template" : "無法從範本建立檔案",
"Templates" : "範本",
+ "Path contains invalid segments" : "路徑包含無效的部份",
"File name is a reserved word" : "檔案名稱是保留字",
"File name contains at least one invalid character" : "檔案名稱含有不允許的字元",
"File name is too long" : "檔案名稱太長",
diff --git a/lib/l10n/zh_TW.json b/lib/l10n/zh_TW.json
index cd5c36d72b8..5fe0528c607 100644
--- a/lib/l10n/zh_TW.json
+++ b/lib/l10n/zh_TW.json
@@ -36,6 +36,7 @@
"Server version %s or higher is required." : "需要伺服器版本 %s 或更高",
"Server version %s or lower is required." : "需要伺服器版本 %s 或更低",
"Logged in account must be an admin, a sub admin or gotten special right to access this setting" : "登入帳號必須為管理員、子管理員或有可存取此設定的特殊權限的使用者",
+ "Your current IP address doesn’t allow you to perform admin actions" : "您目前的 IP 位置不允許您執行管理動作",
"Logged in account must be an admin or sub admin" : "登入的帳號必須要是管理員或是子管理員",
"Logged in account must be an admin" : "登入的帳號必須有管理員權限",
"Wiping of device %s has started" : "已開始抹除裝置 %s ",
@@ -85,6 +86,7 @@
"Invalid path" : "無效的路徑",
"Failed to create file from template" : "無法從範本建立檔案",
"Templates" : "範本",
+ "Path contains invalid segments" : "路徑包含無效的部份",
"File name is a reserved word" : "檔案名稱是保留字",
"File name contains at least one invalid character" : "檔案名稱含有不允許的字元",
"File name is too long" : "檔案名稱太長",
diff --git a/lib/private/AppFramework/Bootstrap/RegistrationContext.php b/lib/private/AppFramework/Bootstrap/RegistrationContext.php
index df03d59ebfa..f59d5b55706 100644
--- a/lib/private/AppFramework/Bootstrap/RegistrationContext.php
+++ b/lib/private/AppFramework/Bootstrap/RegistrationContext.php
@@ -26,6 +26,7 @@ use OCP\Dashboard\IWidget;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\Template\ICustomTemplateProvider;
use OCP\Http\WellKnown\IHandler;
+use OCP\Mail\Provider\IProvider as IMailProvider;
use OCP\Notification\INotifier;
use OCP\Profile\ILinkAction;
use OCP\Search\IProvider;
@@ -148,6 +149,9 @@ class RegistrationContext {
/** @var ServiceRegistration<\OCP\TaskProcessing\ITaskType>[] */
private array $taskProcessingTaskTypes = [];
+
+ /** @var ServiceRegistration<IMailProvider>[] */
+ private $mailProviders = [];
public function __construct(LoggerInterface $logger) {
$this->logger = $logger;
@@ -411,6 +415,13 @@ class RegistrationContext {
$taskProcessingTaskTypeClass
);
}
+
+ public function registerMailProvider(string $class): void {
+ $this->context->registerMailProvider(
+ $this->appId,
+ $class
+ );
+ }
};
}
@@ -603,6 +614,12 @@ class RegistrationContext {
public function registerTaskProcessingTaskType(string $appId, string $taskProcessingTaskTypeClass) {
$this->taskProcessingTaskTypes[] = new ServiceRegistration($appId, $taskProcessingTaskTypeClass);
}
+ /**
+ * @psalm-param class-string<IMailProvider> $migratorClass
+ */
+ public function registerMailProvider(string $appId, string $class): void {
+ $this->mailProviders[] = new ServiceRegistration($appId, $class);
+ }
/**
* @param App[] $apps
@@ -948,4 +965,11 @@ class RegistrationContext {
public function getTaskProcessingTaskTypes(): array {
return $this->taskProcessingTaskTypes;
}
+
+ /**
+ * @return ServiceRegistration<IMailProvider>[]
+ */
+ public function getMailProviders(): array {
+ return $this->mailProviders;
+ }
}
diff --git a/lib/private/DB/Connection.php b/lib/private/DB/Connection.php
index e584690f95d..3cdd5fd06c0 100644
--- a/lib/private/DB/Connection.php
+++ b/lib/private/DB/Connection.php
@@ -69,6 +69,9 @@ class Connection extends PrimaryReadReplicaConnection {
/** @var array<string, int> */
protected $tableDirtyWrites = [];
+ protected bool $logDbException = false;
+ private ?array $transactionBacktrace = null;
+
protected bool $logRequestId;
protected string $requestId;
@@ -101,6 +104,7 @@ class Connection extends PrimaryReadReplicaConnection {
$this->logger = Server::get(LoggerInterface::class);
$this->logRequestId = $this->systemConfig->getValue('db.log_request_id', false);
+ $this->logDbException = $this->systemConfig->getValue('db.log_exceptions', false);
$this->requestId = Server::get(IRequestId::class)->getId();
/** @var \OCP\Profiler\IProfiler */
@@ -304,7 +308,12 @@ class Connection extends PrimaryReadReplicaConnection {
$sql = $this->finishQuery($sql);
$this->queriesExecuted++;
$this->logQueryToFile($sql);
- return parent::executeQuery($sql, $params, $types, $qcp);
+ try {
+ return parent::executeQuery($sql, $params, $types, $qcp);
+ } catch (\Exception $e) {
+ $this->logDatabaseException($e);
+ throw $e;
+ }
}
/**
@@ -346,7 +355,12 @@ class Connection extends PrimaryReadReplicaConnection {
$sql = $this->finishQuery($sql);
$this->queriesExecuted++;
$this->logQueryToFile($sql);
- return (int)parent::executeStatement($sql, $params, $types);
+ try {
+ return (int)parent::executeStatement($sql, $params, $types);
+ } catch (\Exception $e) {
+ $this->logDatabaseException($e);
+ throw $e;
+ }
}
protected function logQueryToFile(string $sql): void {
@@ -419,11 +433,21 @@ class Connection extends PrimaryReadReplicaConnection {
* @deprecated 15.0.0 - use unique index and "try { $db->insert() } catch (UniqueConstraintViolationException $e) {}" instead, because it is more reliable and does not have the risk for deadlocks - see https://github.com/nextcloud/server/pull/12371
*/
public function insertIfNotExist($table, $input, ?array $compare = null) {
- return $this->adapter->insertIfNotExist($table, $input, $compare);
+ try {
+ return $this->adapter->insertIfNotExist($table, $input, $compare);
+ } catch (\Exception $e) {
+ $this->logDatabaseException($e);
+ throw $e;
+ }
}
public function insertIgnoreConflict(string $table, array $values) : int {
- return $this->adapter->insertIgnoreConflict($table, $values);
+ try {
+ return $this->adapter->insertIgnoreConflict($table, $values);
+ } catch (\Exception $e) {
+ $this->logDatabaseException($e);
+ throw $e;
+ }
}
private function getType($value) {
@@ -682,6 +706,7 @@ class Connection extends PrimaryReadReplicaConnection {
public function beginTransaction() {
if (!$this->inTransaction()) {
+ $this->transactionBacktrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
$this->transactionActiveSince = microtime(true);
}
return parent::beginTransaction();
@@ -691,6 +716,7 @@ class Connection extends PrimaryReadReplicaConnection {
$result = parent::commit();
if ($this->getTransactionNestingLevel() === 0) {
$timeTook = microtime(true) - $this->transactionActiveSince;
+ $this->transactionBacktrace = null;
$this->transactionActiveSince = null;
if ($timeTook > 1) {
$this->logger->debug('Transaction took ' . $timeTook . 's', ['exception' => new \Exception('Transaction took ' . $timeTook . 's')]);
@@ -703,6 +729,7 @@ class Connection extends PrimaryReadReplicaConnection {
$result = parent::rollBack();
if ($this->getTransactionNestingLevel() === 0) {
$timeTook = microtime(true) - $this->transactionActiveSince;
+ $this->transactionBacktrace = null;
$this->transactionActiveSince = null;
if ($timeTook > 1) {
$this->logger->debug('Transaction rollback took longer than 1s: ' . $timeTook, ['exception' => new \Exception('Long running transaction rollback')]);
@@ -759,4 +786,20 @@ class Connection extends PrimaryReadReplicaConnection {
/** @var ServerInfoAwareConnection $this->_conn */
return $this->_conn->getServerVersion();
}
+
+ /**
+ * Log a database exception if enabled
+ *
+ * @param \Exception $exception
+ * @return void
+ */
+ public function logDatabaseException(\Exception $exception): void {
+ if ($this->logDbException) {
+ if ($exception instanceof Exception\UniqueConstraintViolationException) {
+ $this->logger->info($exception->getMessage(), ['exception' => $exception, 'transaction' => $this->transactionBacktrace]);
+ } else {
+ $this->logger->error($exception->getMessage(), ['exception' => $exception, 'transaction' => $this->transactionBacktrace]);
+ }
+ }
+ }
}
diff --git a/lib/private/DB/ConnectionAdapter.php b/lib/private/DB/ConnectionAdapter.php
index b7225169e4c..88083711195 100644
--- a/lib/private/DB/ConnectionAdapter.php
+++ b/lib/private/DB/ConnectionAdapter.php
@@ -240,4 +240,8 @@ class ConnectionAdapter implements IDBConnection {
public function getServerVersion(): string {
return $this->inner->getServerVersion();
}
+
+ public function logDatabaseException(\Exception $exception) {
+ $this->inner->logDatabaseException($exception);
+ }
}
diff --git a/lib/private/DB/QueryBuilder/ExpressionBuilder/PgSqlExpressionBuilder.php b/lib/private/DB/QueryBuilder/ExpressionBuilder/PgSqlExpressionBuilder.php
index 1a162f04a49..53a566a7eb6 100644
--- a/lib/private/DB/QueryBuilder/ExpressionBuilder/PgSqlExpressionBuilder.php
+++ b/lib/private/DB/QueryBuilder/ExpressionBuilder/PgSqlExpressionBuilder.php
@@ -23,7 +23,7 @@ class PgSqlExpressionBuilder extends ExpressionBuilder {
public function castColumn($column, $type): IQueryFunction {
switch ($type) {
case IQueryBuilder::PARAM_INT:
- return new QueryFunction('CAST(' . $this->helper->quoteColumnName($column) . ' AS INT)');
+ return new QueryFunction('CAST(' . $this->helper->quoteColumnName($column) . ' AS BIGINT)');
case IQueryBuilder::PARAM_STR:
return new QueryFunction('CAST(' . $this->helper->quoteColumnName($column) . ' AS TEXT)');
default:
diff --git a/lib/private/DB/QueryBuilder/QueryBuilder.php b/lib/private/DB/QueryBuilder/QueryBuilder.php
index 0ea223cd89c..4c4786f02b6 100644
--- a/lib/private/DB/QueryBuilder/QueryBuilder.php
+++ b/lib/private/DB/QueryBuilder/QueryBuilder.php
@@ -253,6 +253,7 @@ class QueryBuilder implements IQueryBuilder {
// `IQueryBuilder->execute` never wrapped the exception, but `executeQuery` and `executeStatement` do
/** @var \Doctrine\DBAL\Exception $previous */
$previous = $e->getPrevious();
+
throw $previous;
}
}
diff --git a/lib/private/Files/Template/TemplateManager.php b/lib/private/Files/Template/TemplateManager.php
index 8362298b831..8b20e86930a 100644
--- a/lib/private/Files/Template/TemplateManager.php
+++ b/lib/private/Files/Template/TemplateManager.php
@@ -18,6 +18,7 @@ use OCP\Files\GenericFileException;
use OCP\Files\IRootFolder;
use OCP\Files\Node;
use OCP\Files\NotFoundException;
+use OCP\Files\Template\BeforeGetTemplatesEvent;
use OCP\Files\Template\FileCreatedFromTemplateEvent;
use OCP\Files\Template\ICustomTemplateProvider;
use OCP\Files\Template\ITemplateManager;
@@ -127,10 +128,11 @@ class TemplateManager implements ITemplateManager {
/**
* @param string $filePath
* @param string $templateId
+ * @param array $templateFields
* @return array
* @throws GenericFileException
*/
- public function createFromTemplate(string $filePath, string $templateId = '', string $templateType = 'user'): array {
+ public function createFromTemplate(string $filePath, string $templateId = '', string $templateType = 'user', array $templateFields = []): array {
$userFolder = $this->rootFolder->getUserFolder($this->userId);
try {
$userFolder->get($filePath);
@@ -157,7 +159,7 @@ class TemplateManager implements ITemplateManager {
$template->copy($targetFile->getPath());
}
}
- $this->eventDispatcher->dispatchTyped(new FileCreatedFromTemplateEvent($template, $targetFile));
+ $this->eventDispatcher->dispatchTyped(new FileCreatedFromTemplateEvent($template, $targetFile, $templateFields));
return $this->formatFile($userFolder->get($filePath));
} catch (\Exception $e) {
$this->logger->error($e->getMessage(), ['exception' => $e]);
@@ -204,6 +206,8 @@ class TemplateManager implements ITemplateManager {
}
}
+ $this->eventDispatcher->dispatchTyped(new BeforeGetTemplatesEvent($templates));
+
return $templates;
}
diff --git a/lib/private/Files/View.php b/lib/private/Files/View.php
index 323266e8cfa..0e5e433ccb6 100644
--- a/lib/private/Files/View.php
+++ b/lib/private/Files/View.php
@@ -1358,6 +1358,10 @@ class View {
if ($mount instanceof MoveableMount && $internalPath === '') {
$data['permissions'] |= \OCP\Constants::PERMISSION_DELETE;
}
+ if ($internalPath === '' && $data['name']) {
+ $data['name'] = basename($path);
+ }
+
$ownerId = $storage->getOwner($internalPath);
$owner = null;
if ($ownerId !== null && $ownerId !== false) {
diff --git a/lib/private/Group/Manager.php b/lib/private/Group/Manager.php
index 092c56fe00a..cad8e76fbbd 100644
--- a/lib/private/Group/Manager.php
+++ b/lib/private/Group/Manager.php
@@ -8,6 +8,7 @@
namespace OC\Group;
use OC\Hooks\PublicEmitter;
+use OC\Settings\AuthorizedGroupMapper;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Group\Backend\IBatchMethodsBackend;
use OCP\Group\Backend\ICreateNamedGroupBackend;
@@ -333,6 +334,18 @@ class Manager extends PublicEmitter implements IGroupManager {
return $this->isInGroup($userId, 'admin');
}
+ public function isDelegatedAdmin(string $userId): bool {
+ if (!$this->remoteAddress->allowsAdminActions()) {
+ return false;
+ }
+
+ // Check if the user as admin delegation for users listing
+ $authorizedGroupMapper = \OCP\Server::get(AuthorizedGroupMapper::class);
+ $user = $this->userManager->get($userId);
+ $authorizedClasses = $authorizedGroupMapper->findAllClassesForUser($user);
+ return in_array(\OCA\Settings\Settings\Admin\Users::class, $authorizedClasses, true);
+ }
+
/**
* Checks if a userId is in a group
*
diff --git a/lib/private/Group/MetaData.php b/lib/private/Group/MetaData.php
index 638dc184812..da553c89a7b 100644
--- a/lib/private/Group/MetaData.php
+++ b/lib/private/Group/MetaData.php
@@ -17,33 +17,22 @@ class MetaData {
public const SORT_USERCOUNT = 1; // May have performance issues on LDAP backends
public const SORT_GROUPNAME = 2;
- /** @var string */
- protected $user;
- /** @var bool */
- protected $isAdmin;
/** @var array */
protected $metaData = [];
- /** @var GroupManager */
- protected $groupManager;
/** @var int */
protected $sorting = self::SORT_NONE;
- /** @var IUserSession */
- protected $userSession;
/**
* @param string $user the uid of the current user
* @param bool $isAdmin whether the current users is an admin
*/
public function __construct(
- string $user,
- bool $isAdmin,
- IGroupManager $groupManager,
- IUserSession $userSession
+ private string $user,
+ private bool $isAdmin,
+ private bool $isDelegatedAdmin,
+ private IGroupManager $groupManager,
+ private IUserSession $userSession
) {
- $this->user = $user;
- $this->isAdmin = $isAdmin;
- $this->groupManager = $groupManager;
- $this->userSession = $userSession;
}
/**
@@ -162,11 +151,11 @@ class MetaData {
* @return IGroup[]
*/
public function getGroups(string $search = ''): array {
- if ($this->isAdmin) {
+ if ($this->isAdmin || $this->isDelegatedAdmin) {
return $this->groupManager->search($search);
} else {
$userObject = $this->userSession->getUser();
- if ($userObject !== null) {
+ if ($userObject !== null && $this->groupManager instanceof GroupManager) {
$groups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($userObject);
} else {
$groups = [];
diff --git a/lib/private/Mail/Provider/Manager.php b/lib/private/Mail/Provider/Manager.php
new file mode 100644
index 00000000000..244aa86d68d
--- /dev/null
+++ b/lib/private/Mail/Provider/Manager.php
@@ -0,0 +1,255 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OC\Mail\Provider;
+
+use OC\AppFramework\Bootstrap\Coordinator;
+use OCP\Mail\Provider\IManager;
+use OCP\Mail\Provider\IProvider;
+use OCP\Mail\Provider\IService;
+use Psr\Container\ContainerInterface;
+use Psr\Log\LoggerInterface;
+use Throwable;
+
+class Manager implements IManager {
+
+ protected ?array $providersCollection = null;
+
+ public function __construct(
+ private Coordinator $coordinator,
+ private ContainerInterface $container,
+ private LoggerInterface $logger,
+ ) {
+ }
+
+ /**
+ * Determine if any mail providers are registered
+ *
+ * @since 30.0.0
+ *
+ * @return bool
+ */
+ public function has(): bool {
+
+ // return true if collection has any providers
+ return !empty($this->providers());
+
+ }
+
+ /**
+ * Retrieve a count of how many mail providers are registered
+ *
+ * @since 30.0.0
+ *
+ * @return int
+ */
+ public function count(): int {
+
+ // return count of providers in collection
+ return count($this->providers());
+
+ }
+
+ /**
+ * Retrieve which mail providers are registered
+ *
+ * @since 30.0.0
+ *
+ * @return array<string,string> collection of provider id and label ['jmap' => 'JMap Connector']
+ */
+ public function types(): array {
+
+ // construct types collection
+ $types = [];
+ // extract id and name from providers collection
+ foreach ($this->providers() as $entry) {
+ $types[$entry->id()] = $entry->label();
+ }
+ // return types collection
+ return $types;
+
+ }
+
+ /**
+ * Retrieve all registered mail providers
+ *
+ * @since 30.0.0
+ *
+ * @return array<string,IProvider> collection of provider id and object ['jmap' => IProviderObject]
+ */
+ public function providers(): array {
+
+ // evaluate if we already have a cached collection of providers and return the collection if we do
+ if (is_array($this->providersCollection)) {
+ return $this->providersCollection;
+ }
+ // retrieve server registration context
+ $context = $this->coordinator->getRegistrationContext();
+ // evaluate if registration context was returned
+ if ($context === null) {
+ return [];
+ }
+ // initilize cached collection
+ $this->providersCollection = [];
+ // iterate through all registered mail providers
+ foreach ($context->getMailProviders() as $entry) {
+ try {
+ /** @var IProvider $provider */
+ // object provider
+ $provider = $this->container->get($entry->getService());
+ // add provider to cache collection
+ $this->providersCollection[$provider->id()] = $provider;
+ } catch (Throwable $e) {
+ $this->logger->error(
+ 'Could not load mail provider ' . $entry->getService() . ': ' . $e->getMessage(),
+ ['exception' => $e]
+ );
+ }
+ }
+ // return mail provider collection
+ return $this->providersCollection;
+
+ }
+
+ /**
+ * Retrieve a provider with a specific id
+ *
+ * @since 30.0.0
+ *
+ * @param string $providerId provider id
+ *
+ * @return IProvider|null
+ */
+ public function findProviderById(string $providerId): IProvider | null {
+
+ // evaluate if we already have a cached collection of providers
+ if (!is_array($this->providersCollection)) {
+ $this->providers();
+ }
+
+ if (isset($this->providersCollection[$providerId])) {
+ return $this->providersCollection[$providerId];
+ }
+ // return null if provider was not found
+ return null;
+
+ }
+
+ /**
+ * Retrieve all services for all registered mail providers
+ *
+ * @since 30.0.0
+ *
+ * @param string $userId user id
+ *
+ * @return array<string,array<string,IService>> collection of provider id, service id and object ['jmap' => ['Service1' => IServiceObject]]
+ */
+ public function services(string $userId): array {
+
+ // initilize collection
+ $services = [];
+ // retrieve and iterate through mail providers
+ foreach ($this->providers() as $entry) {
+ // retrieve collection of services
+ $mailServices = $entry->listServices($userId);
+ // evaluate if mail services collection is not empty and add results to services collection
+ if (!empty($mailServices)) {
+ $services[$entry->id()] = $mailServices;
+ }
+ }
+ // return collection
+ return $services;
+
+ }
+
+ /**
+ * Retrieve a service with a specific id
+ *
+ * @since 30.0.0
+ *
+ * @param string $userId user id
+ * @param string $serviceId service id
+ * @param string $providerId provider id
+ *
+ * @return IService|null returns service object or null if none found
+ */
+ public function findServiceById(string $userId, string $serviceId, ?string $providerId = null): IService | null {
+
+ // evaluate if provider id was specified
+ if ($providerId !== null) {
+ // find provider
+ $provider = $this->findProviderById($providerId);
+ // evaluate if provider was found
+ if ($provider instanceof IProvider) {
+ // find service with specific id
+ $service = $provider->findServiceById($userId, $serviceId);
+ // evaluate if mail service was found
+ if ($service instanceof IService) {
+ return $service;
+ }
+ }
+ } else {
+ // retrieve and iterate through mail providers
+ foreach ($this->providers() as $provider) {
+ // find service with specific id
+ $service = $provider->findServiceById($userId, $serviceId);
+ // evaluate if mail service was found
+ if ($service instanceof IService) {
+ return $service;
+ }
+ }
+ }
+
+ // return null if no match was found
+ return null;
+
+ }
+
+ /**
+ * Retrieve a service for a specific mail address
+ * returns first service with specific primary address
+ *
+ * @since 30.0.0
+ *
+ * @param string $userId user id
+ * @param string $address mail address (e.g. test@example.com)
+ * @param string $providerId provider id
+ *
+ * @return IService|null returns service object or null if none found
+ */
+ public function findServiceByAddress(string $userId, string $address, ?string $providerId = null): IService | null {
+
+ // evaluate if provider id was specified
+ if ($providerId !== null) {
+ // find provider
+ $provider = $this->findProviderById($providerId);
+ // evaluate if provider was found
+ if ($provider instanceof IProvider) {
+ // find service with specific mail address
+ $service = $provider->findServiceByAddress($userId, $address);
+ // evaluate if mail service was found
+ if ($service instanceof IService) {
+ return $service;
+ }
+ }
+ } else {
+ // retrieve and iterate through mail providers
+ foreach ($this->providers() as $provider) {
+ // find service with specific mail address
+ $service = $provider->findServiceByAddress($userId, $address);
+ // evaluate if mail service was found
+ if ($service instanceof IService) {
+ return $service;
+ }
+ }
+ }
+ // return null if no match was found
+ return null;
+
+ }
+}
diff --git a/lib/private/Preview/BackgroundCleanupJob.php b/lib/private/Preview/BackgroundCleanupJob.php
index 49ff01486a3..deadcd007b1 100644
--- a/lib/private/Preview/BackgroundCleanupJob.php
+++ b/lib/private/Preview/BackgroundCleanupJob.php
@@ -73,6 +73,8 @@ class BackgroundCleanupJob extends TimedJob {
->where(
$qb->expr()->isNull('b.fileid')
)->andWhere(
+ $qb->expr()->eq('a.storage', $qb->createNamedParameter($this->previewFolder->getStorageId()))
+ )->andWhere(
$qb->expr()->eq('a.parent', $qb->createNamedParameter($this->previewFolder->getId()))
)->andWhere(
$qb->expr()->like('a.name', $qb->createNamedParameter('__%'))
diff --git a/lib/private/Server.php b/lib/private/Server.php
index cfbb6dc317f..3d156454920 100644
--- a/lib/private/Server.php
+++ b/lib/private/Server.php
@@ -1026,6 +1026,9 @@ class Server extends ServerContainer implements IServerContainer {
/** @deprecated 19.0.0 */
$this->registerDeprecatedAlias('Mailer', IMailer::class);
+ /** @since 30.0.0 */
+ $this->registerAlias(\OCP\Mail\Provider\IManager::class, \OC\Mail\Provider\Manager::class);
+
/** @deprecated 21.0.0 */
$this->registerDeprecatedAlias('LDAPProvider', ILDAPProvider::class);
diff --git a/lib/private/SubAdmin.php b/lib/private/SubAdmin.php
index 9ffa0ab03a3..c025ab7b012 100644
--- a/lib/private/SubAdmin.php
+++ b/lib/private/SubAdmin.php
@@ -233,6 +233,11 @@ class SubAdmin extends PublicEmitter implements ISubAdmin {
return true;
}
+ // Check if the user is already an admin
+ if ($this->groupManager->isDelegatedAdmin($user->getUID())) {
+ return true;
+ }
+
$qb = $this->dbConn->getQueryBuilder();
$result = $qb->select('gid')
diff --git a/lib/private/TaskProcessing/Db/Task.php b/lib/private/TaskProcessing/Db/Task.php
index 9fc999faf1a..6787c11344a 100644
--- a/lib/private/TaskProcessing/Db/Task.php
+++ b/lib/private/TaskProcessing/Db/Task.php
@@ -39,6 +39,12 @@ use OCP\TaskProcessing\Task as OCPTask;
* @method string getWebhookUri()
* @method setWebhookMethod(string $webhookMethod)
* @method string getWebhookMethod()
+ * @method setScheduledAt(int $scheduledAt)
+ * @method int getScheduledAt()
+ * @method setStartedAt(int $startedAt)
+ * @method int getStartedAt()
+ * @method setEndedAt(int $endedAt)
+ * @method int getEndedAt()
*/
class Task extends Entity {
protected $lastUpdated;
@@ -54,16 +60,19 @@ class Task extends Entity {
protected $progress;
protected $webhookUri;
protected $webhookMethod;
+ protected $scheduledAt;
+ protected $startedAt;
+ protected $endedAt;
/**
* @var string[]
*/
- public static array $columns = ['id', 'last_updated', 'type', 'input', 'output', 'status', 'user_id', 'app_id', 'custom_id', 'completion_expected_at', 'error_message', 'progress', 'webhook_uri', 'webhook_method'];
+ public static array $columns = ['id', 'last_updated', 'type', 'input', 'output', 'status', 'user_id', 'app_id', 'custom_id', 'completion_expected_at', 'error_message', 'progress', 'webhook_uri', 'webhook_method', 'scheduled_at', 'started_at', 'ended_at'];
/**
* @var string[]
*/
- public static array $fields = ['id', 'lastUpdated', 'type', 'input', 'output', 'status', 'userId', 'appId', 'customId', 'completionExpectedAt', 'errorMessage', 'progress', 'webhookUri', 'webhookMethod'];
+ public static array $fields = ['id', 'lastUpdated', 'type', 'input', 'output', 'status', 'userId', 'appId', 'customId', 'completionExpectedAt', 'errorMessage', 'progress', 'webhookUri', 'webhookMethod', 'scheduledAt', 'startedAt', 'endedAt'];
public function __construct() {
@@ -82,6 +91,9 @@ class Task extends Entity {
$this->addType('progress', 'float');
$this->addType('webhookUri', 'string');
$this->addType('webhookMethod', 'string');
+ $this->addType('scheduledAt', 'integer');
+ $this->addType('startedAt', 'integer');
+ $this->addType('endedAt', 'integer');
}
public function toRow(): array {
@@ -107,6 +119,9 @@ class Task extends Entity {
'progress' => $task->getProgress(),
'webhookUri' => $task->getWebhookUri(),
'webhookMethod' => $task->getWebhookMethod(),
+ 'scheduledAt' => $task->getScheduledAt(),
+ 'startedAt' => $task->getStartedAt(),
+ 'endedAt' => $task->getEndedAt(),
]);
return $taskEntity;
}
@@ -126,6 +141,9 @@ class Task extends Entity {
$task->setProgress($this->getProgress());
$task->setWebhookUri($this->getWebhookUri());
$task->setWebhookMethod($this->getWebhookMethod());
+ $task->setScheduledAt($this->getScheduledAt());
+ $task->setStartedAt($this->getStartedAt());
+ $task->setEndedAt($this->getEndedAt());
return $task;
}
}
diff --git a/lib/private/TaskProcessing/Db/TaskMapper.php b/lib/private/TaskProcessing/Db/TaskMapper.php
index da3910dcb3d..2bdee4fa134 100644
--- a/lib/private/TaskProcessing/Db/TaskMapper.php
+++ b/lib/private/TaskProcessing/Db/TaskMapper.php
@@ -137,6 +137,51 @@ class TaskMapper extends QBMapper {
}
/**
+ * @param string|null $userId
+ * @param string|null $taskType
+ * @param string|null $appId
+ * @param string|null $customId
+ * @param int|null $status
+ * @param int|null $scheduleAfter
+ * @param int|null $endedBefore
+ * @return list<Task>
+ * @throws Exception
+ */
+ public function findTasks(
+ ?string $userId, ?string $taskType = null, ?string $appId = null, ?string $customId = null,
+ ?int $status = null, ?int $scheduleAfter = null, ?int $endedBefore = null): array {
+ $qb = $this->db->getQueryBuilder();
+ $qb->select(Task::$columns)
+ ->from($this->tableName);
+
+ // empty string: no userId filter
+ if ($userId !== '') {
+ $qb->where($qb->expr()->eq('user_id', $qb->createPositionalParameter($userId)));
+ }
+ if ($taskType !== null) {
+ $qb->andWhere($qb->expr()->eq('type', $qb->createPositionalParameter($taskType)));
+ }
+ if ($appId !== null) {
+ $qb->andWhere($qb->expr()->eq('app_id', $qb->createPositionalParameter($appId)));
+ }
+ if ($customId !== null) {
+ $qb->andWhere($qb->expr()->eq('custom_id', $qb->createPositionalParameter($customId)));
+ }
+ if ($status !== null) {
+ $qb->andWhere($qb->expr()->eq('status', $qb->createPositionalParameter($status, IQueryBuilder::PARAM_INT)));
+ }
+ if ($scheduleAfter !== null) {
+ $qb->andWhere($qb->expr()->isNotNull('scheduled_at'));
+ $qb->andWhere($qb->expr()->gt('scheduled_at', $qb->createPositionalParameter($scheduleAfter, IQueryBuilder::PARAM_INT)));
+ }
+ if ($endedBefore !== null) {
+ $qb->andWhere($qb->expr()->isNotNull('ended_at'));
+ $qb->andWhere($qb->expr()->lt('ended_at', $qb->createPositionalParameter($endedBefore, IQueryBuilder::PARAM_INT)));
+ }
+ return array_values($this->findEntities($qb));
+ }
+
+ /**
* @param int $timeout
* @return int the number of deleted tasks
* @throws Exception
diff --git a/lib/private/TaskProcessing/Manager.php b/lib/private/TaskProcessing/Manager.php
index 714a23ce5e2..cb1e4e1e041 100644
--- a/lib/private/TaskProcessing/Manager.php
+++ b/lib/private/TaskProcessing/Manager.php
@@ -50,6 +50,7 @@ use OCP\TaskProcessing\IProvider;
use OCP\TaskProcessing\ISynchronousProvider;
use OCP\TaskProcessing\ITaskType;
use OCP\TaskProcessing\ShapeDescriptor;
+use OCP\TaskProcessing\ShapeEnumValue;
use OCP\TaskProcessing\Task;
use OCP\TaskProcessing\TaskTypes\AudioToText;
use OCP\TaskProcessing\TaskTypes\TextToImage;
@@ -70,11 +71,12 @@ class Manager implements IManager {
/** @var list<IProvider>|null */
private ?array $providers = null;
- /** @var array<string,array{name: string, description: string, inputShape: array<string, ShapeDescriptor>, optionalInputShape: array<string, ShapeDescriptor>, outputShape: array<string, ShapeDescriptor>, optionalOutputShape: array<string, ShapeDescriptor>}>|null */
+ /**
+ * @var array<array-key,array{name: string, description: string, inputShape: ShapeDescriptor[], inputShapeEnumValues: ShapeEnumValue[][], inputShapeDefaults: array<array-key, numeric|string>, optionalInputShape: ShapeDescriptor[], optionalInputShapeEnumValues: ShapeEnumValue[][], optionalInputShapeDefaults: array<array-key, numeric|string>, outputShape: ShapeDescriptor[], outputShapeEnumValues: ShapeEnumValue[][], optionalOutputShape: ShapeDescriptor[], optionalOutputShapeEnumValues: ShapeEnumValue[][]}>
+ */
private ?array $availableTaskTypes = null;
private IAppData $appData;
-
public function __construct(
private IConfig $config,
private Coordinator $coordinator,
@@ -95,9 +97,7 @@ class Manager implements IManager {
$this->appData = $appDataFactory->get('core');
}
- /**
- * @return IProvider[]
- */
+
private function _getTextProcessingProviders(): array {
$oldProviders = $this->textProcessingManager->getProviders();
$newProviders = [];
@@ -155,6 +155,30 @@ class Manager implements IManager {
throw new ProcessingException($e->getMessage(), 0, $e);
}
}
+
+ 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 [];
+ }
};
$newProviders[$provider->getId()] = $provider;
}
@@ -289,6 +313,30 @@ class Manager implements IManager {
}
return ['images' => array_map(fn (ISimpleFile $file) => $file->getContent(), $files)];
}
+
+ 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 [];
+ }
};
$newProviders[$newProvider->getId()] = $newProvider;
}
@@ -351,6 +399,30 @@ class Manager implements IManager {
}
return ['output' => $result];
}
+
+ 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 [];
+ }
};
$newProviders[$newProvider->getId()] = $newProvider;
}
@@ -410,6 +482,7 @@ class Manager implements IManager {
\OCP\TaskProcessing\TaskTypes\TextToTextFormalization::ID => \OCP\Server::get(\OCP\TaskProcessing\TaskTypes\TextToTextFormalization::class),
\OCP\TaskProcessing\TaskTypes\TextToTextSimplification::ID => \OCP\Server::get(\OCP\TaskProcessing\TaskTypes\TextToTextSimplification::class),
\OCP\TaskProcessing\TaskTypes\TextToTextChat::ID => \OCP\Server::get(\OCP\TaskProcessing\TaskTypes\TextToTextChat::class),
+ \OCP\TaskProcessing\TaskTypes\TextToTextTranslate::ID => \OCP\Server::get(\OCP\TaskProcessing\TaskTypes\TextToTextTranslate::class),
\OCP\TaskProcessing\TaskTypes\TextToImage::ID => \OCP\Server::get(\OCP\TaskProcessing\TaskTypes\TextToImage::class),
\OCP\TaskProcessing\TaskTypes\AudioToText::ID => \OCP\Server::get(\OCP\TaskProcessing\TaskTypes\AudioToText::class),
\OCP\TaskProcessing\TaskTypes\ContextWrite::ID => \OCP\Server::get(\OCP\TaskProcessing\TaskTypes\ContextWrite::class),
@@ -439,21 +512,40 @@ class Manager implements IManager {
/**
* @param ShapeDescriptor[] $spec
+ * @param array<array-key, string|numeric> $defaults
+ * @param array<array-key, ShapeEnumValue[]> $enumValues
* @param array $io
+ * @param bool $optional
* @return void
* @throws ValidationException
*/
- private function validateInput(array $spec, array $io, bool $optional = false): void {
+ private static function validateInput(array $spec, array $defaults, array $enumValues, array $io, bool $optional = false): void {
foreach ($spec as $key => $descriptor) {
$type = $descriptor->getShapeType();
if (!isset($io[$key])) {
if ($optional) {
continue;
}
+ if (isset($defaults[$key])) {
+ if (EShapeType::getScalarType($type) !== $type) {
+ throw new ValidationException('Provider tried to set a default value for a non-scalar slot');
+ }
+ if (EShapeType::isFileType($type)) {
+ throw new ValidationException('Provider tried to set a default value for a slot that is not text or number');
+ }
+ $type->validateInput($defaults[$key]);
+ continue;
+ }
throw new ValidationException('Missing key: "' . $key . '"');
}
try {
$type->validateInput($io[$key]);
+ if ($type === EShapeType::Enum) {
+ if (!isset($enumValues[$key])) {
+ throw new ValidationException('Provider did not provide enum values for an enum slot: "' . $key .'"');
+ }
+ $type->validateEnum($io[$key], $enumValues[$key]);
+ }
} catch (ValidationException $e) {
throw new ValidationException('Failed to validate input key "' . $key . '": ' . $e->getMessage());
}
@@ -461,13 +553,26 @@ class Manager implements IManager {
}
/**
+ * Takes task input data and replaces fileIds with File objects
+ *
+ * @param array<array-key, list<numeric|string>|numeric|string> $input
+ * @param array<array-key, numeric|string> ...$defaultSpecs the specs
+ * @return array<array-key, list<numeric|string>|numeric|string>
+ */
+ public function fillInputDefaults(array $input, ...$defaultSpecs): array {
+ $spec = array_reduce($defaultSpecs, fn ($carry, $spec) => array_merge($carry, $spec), []);
+ return array_merge($spec, $input);
+ }
+
+ /**
* @param ShapeDescriptor[] $spec
+ * @param array<array-key, ShapeEnumValue[]> $enumValues
* @param array $io
* @param bool $optional
* @return void
* @throws ValidationException
*/
- private function validateOutputWithFileIds(array $spec, array $io, bool $optional = false): void {
+ private static function validateOutputWithFileIds(array $spec, array $enumValues, array $io, bool $optional = false): void {
foreach ($spec as $key => $descriptor) {
$type = $descriptor->getShapeType();
if (!isset($io[$key])) {
@@ -478,6 +583,9 @@ class Manager implements IManager {
}
try {
$type->validateOutputWithFileIds($io[$key]);
+ if (isset($enumValues[$key])) {
+ $type->validateEnum($io[$key], $enumValues[$key]);
+ }
} catch (ValidationException $e) {
throw new ValidationException('Failed to validate output key "' . $key . '": ' . $e->getMessage());
}
@@ -486,12 +594,13 @@ class Manager implements IManager {
/**
* @param ShapeDescriptor[] $spec
+ * @param array<array-key, ShapeEnumValue[]> $enumValues
* @param array $io
* @param bool $optional
* @return void
* @throws ValidationException
*/
- private function validateOutputWithFileData(array $spec, array $io, bool $optional = false): void {
+ private static function validateOutputWithFileData(array $spec, array $enumValues, array $io, bool $optional = false): void {
foreach ($spec as $key => $descriptor) {
$type = $descriptor->getShapeType();
if (!isset($io[$key])) {
@@ -502,6 +611,9 @@ class Manager implements IManager {
}
try {
$type->validateOutputWithFileData($io[$key]);
+ if (isset($enumValues[$key])) {
+ $type->validateEnum($io[$key], $enumValues[$key]);
+ }
} catch (ValidationException $e) {
throw new ValidationException('Failed to validate output key "' . $key . '": ' . $e->getMessage());
}
@@ -569,10 +681,16 @@ class Manager implements IManager {
$availableTaskTypes[$provider->getTaskTypeId()] = [
'name' => $taskType->getName(),
'description' => $taskType->getDescription(),
- 'inputShape' => $taskType->getInputShape(),
'optionalInputShape' => $provider->getOptionalInputShape(),
+ 'inputShapeEnumValues' => $provider->getInputShapeEnumValues(),
+ 'inputShapeDefaults' => $provider->getInputShapeDefaults(),
+ 'inputShape' => $taskType->getInputShape(),
+ 'optionalInputShapeEnumValues' => $provider->getOptionalInputShapeEnumValues(),
+ 'optionalInputShapeDefaults' => $provider->getOptionalInputShapeDefaults(),
'outputShape' => $taskType->getOutputShape(),
+ 'outputShapeEnumValues' => $provider->getOutputShapeEnumValues(),
'optionalOutputShape' => $provider->getOptionalOutputShape(),
+ 'optionalOutputShapeEnumValues' => $provider->getOptionalOutputShapeEnumValues(),
];
}
@@ -592,10 +710,14 @@ class Manager implements IManager {
}
$taskTypes = $this->getAvailableTaskTypes();
$inputShape = $taskTypes[$task->getTaskTypeId()]['inputShape'];
+ $inputShapeDefaults = $taskTypes[$task->getTaskTypeId()]['inputShapeDefaults'];
+ $inputShapeEnumValues = $taskTypes[$task->getTaskTypeId()]['inputShapeEnumValues'];
$optionalInputShape = $taskTypes[$task->getTaskTypeId()]['optionalInputShape'];
+ $optionalInputShapeEnumValues = $taskTypes[$task->getTaskTypeId()]['optionalInputShapeEnumValues'];
+ $optionalInputShapeDefaults = $taskTypes[$task->getTaskTypeId()]['optionalInputShapeDefaults'];
// validate input
- $this->validateInput($inputShape, $task->getInput());
- $this->validateInput($optionalInputShape, $task->getInput(), true);
+ $this->validateInput($inputShape, $inputShapeDefaults, $inputShapeEnumValues, $task->getInput());
+ $this->validateInput($optionalInputShape, $optionalInputShapeDefaults, $optionalInputShapeEnumValues, $task->getInput(), true);
// authenticate access to mentioned files
$ids = [];
foreach ($inputShape + $optionalInputShape as $key => $descriptor) {
@@ -614,8 +736,11 @@ class Manager implements IManager {
$this->validateUserAccessToFile($fileId, $task->getUserId());
}
// remove superfluous keys and set input
- $task->setInput($this->removeSuperfluousArrayKeys($task->getInput(), $inputShape, $optionalInputShape));
+ $input = $this->removeSuperfluousArrayKeys($task->getInput(), $inputShape, $optionalInputShape);
+ $inputWithDefaults = $this->fillInputDefaults($input, $inputShapeDefaults, $optionalInputShapeDefaults);
+ $task->setInput($inputWithDefaults);
$task->setStatus(Task::STATUS_SCHEDULED);
+ $task->setScheduledAt(time());
$provider = $this->getPreferredProvider($task->getTaskTypeId());
// calculate expected completion time
$completionExpectedAt = new \DateTime('now');
@@ -656,6 +781,7 @@ class Manager implements IManager {
return;
}
$task->setStatus(Task::STATUS_CANCELLED);
+ $task->setEndedAt(time());
$taskEntity = \OC\TaskProcessing\Db\Task::fromPublicTask($task);
try {
$this->taskMapper->update($taskEntity);
@@ -671,6 +797,10 @@ class Manager implements IManager {
if ($task->getStatus() === Task::STATUS_CANCELLED) {
return false;
}
+ // only set the start time if the task is going from scheduled to running
+ if ($task->getstatus() === Task::STATUS_SCHEDULED) {
+ $task->setStartedAt(time());
+ }
$task->setStatus(Task::STATUS_RUNNING);
$task->setProgress($progress);
$taskEntity = \OC\TaskProcessing\Db\Task::fromPublicTask($task);
@@ -691,20 +821,23 @@ class Manager implements IManager {
}
if ($error !== null) {
$task->setStatus(Task::STATUS_FAILED);
+ $task->setEndedAt(time());
$task->setErrorMessage($error);
$this->logger->warning('A TaskProcessing ' . $task->getTaskTypeId() . ' task with id ' . $id . ' failed with the following message: ' . $error);
} elseif ($result !== null) {
$taskTypes = $this->getAvailableTaskTypes();
$outputShape = $taskTypes[$task->getTaskTypeId()]['outputShape'];
+ $outputShapeEnumValues = $taskTypes[$task->getTaskTypeId()]['outputShapeEnumValues'];
$optionalOutputShape = $taskTypes[$task->getTaskTypeId()]['optionalOutputShape'];
+ $optionalOutputShapeEnumValues = $taskTypes[$task->getTaskTypeId()]['optionalOutputShapeEnumValues'];
try {
// validate output
if (!$isUsingFileIds) {
- $this->validateOutputWithFileData($outputShape, $result);
- $this->validateOutputWithFileData($optionalOutputShape, $result, true);
+ $this->validateOutputWithFileData($outputShape, $outputShapeEnumValues, $result);
+ $this->validateOutputWithFileData($optionalOutputShape, $optionalOutputShapeEnumValues, $result, true);
} else {
- $this->validateOutputWithFileIds($outputShape, $result);
- $this->validateOutputWithFileIds($optionalOutputShape, $result, true);
+ $this->validateOutputWithFileIds($outputShape, $outputShapeEnumValues, $result);
+ $this->validateOutputWithFileIds($optionalOutputShape, $optionalOutputShapeEnumValues, $result, true);
}
$output = $this->removeSuperfluousArrayKeys($result, $outputShape, $optionalOutputShape);
// extract raw data and put it in files, replace it with file ids
@@ -725,21 +858,25 @@ class Manager implements IManager {
$task->setOutput($output);
$task->setProgress(1);
$task->setStatus(Task::STATUS_SUCCESSFUL);
+ $task->setEndedAt(time());
} catch (ValidationException $e) {
$task->setProgress(1);
$task->setStatus(Task::STATUS_FAILED);
+ $task->setEndedAt(time());
$error = 'The task was processed successfully but the provider\'s output doesn\'t pass validation against the task type\'s outputShape spec and/or the provider\'s own optionalOutputShape spec';
$task->setErrorMessage($error);
$this->logger->error($error, ['exception' => $e]);
} catch (NotPermittedException $e) {
$task->setProgress(1);
$task->setStatus(Task::STATUS_FAILED);
+ $task->setEndedAt(time());
$error = 'The task was processed successfully but storing the output in a file failed';
$task->setErrorMessage($error);
$this->logger->error($error, ['exception' => $e]);
} catch (InvalidPathException|\OCP\Files\NotFoundException $e) {
$task->setProgress(1);
$task->setStatus(Task::STATUS_FAILED);
+ $task->setEndedAt(time());
$error = 'The task was processed successfully but the result file could not be found';
$task->setErrorMessage($error);
$this->logger->error($error, ['exception' => $e]);
@@ -839,6 +976,20 @@ class Manager implements IManager {
}
}
+ public function getTasks(
+ ?string $userId, ?string $taskTypeId = null, ?string $appId = null, ?string $customId = null,
+ ?int $status = null, ?int $scheduleAfter = null, ?int $endedBefore = null
+ ): array {
+ try {
+ $taskEntities = $this->taskMapper->findTasks($userId, $taskTypeId, $appId, $customId, $status, $scheduleAfter, $endedBefore);
+ return array_map(fn ($taskEntity): Task => $taskEntity->toPublicTask(), $taskEntities);
+ } catch (\OCP\DB\Exception $e) {
+ throw new \OCP\TaskProcessing\Exception\Exception('There was a problem finding the tasks', 0, $e);
+ } catch (\JsonException $e) {
+ throw new \OCP\TaskProcessing\Exception\Exception('There was a problem parsing JSON after finding the tasks', 0, $e);
+ }
+ }
+
public function getUserTasksByApp(?string $userId, string $appId, ?string $customId = null): array {
try {
$taskEntities = $this->taskMapper->findUserTasksByApp($userId, $appId, $customId);
@@ -904,9 +1055,6 @@ class Manager implements IManager {
$inputShape = $taskTypes[$task->getTaskTypeId()]['inputShape'];
$optionalInputShape = $taskTypes[$task->getTaskTypeId()]['optionalInputShape'];
$input = $task->getInput();
- // validate input, again for good measure (should have been validated in scheduleTask)
- $this->validateInput($inputShape, $input);
- $this->validateInput($optionalInputShape, $input, true);
$input = $this->removeSuperfluousArrayKeys($input, $inputShape, $optionalInputShape);
$input = $this->fillInputFileData($task->getUserId(), $input, $inputShape, $optionalInputShape);
return $input;
@@ -926,6 +1074,14 @@ class Manager implements IManager {
* @throws Exception
*/
public function setTaskStatus(Task $task, int $status): void {
+ $currentTaskStatus = $task->getStatus();
+ if ($currentTaskStatus === Task::STATUS_SCHEDULED && $status === Task::STATUS_RUNNING) {
+ $task->setStartedAt(time());
+ } elseif ($currentTaskStatus === Task::STATUS_RUNNING && ($status === Task::STATUS_FAILED || $status === Task::STATUS_CANCELLED)) {
+ $task->setEndedAt(time());
+ } elseif ($currentTaskStatus === Task::STATUS_UNKNOWN && $status === Task::STATUS_SCHEDULED) {
+ $task->setScheduledAt(time());
+ }
$task->setStatus($status);
$taskEntity = \OC\TaskProcessing\Db\Task::fromPublicTask($task);
$this->taskMapper->update($taskEntity);
diff --git a/lib/private/Template/JSConfigHelper.php b/lib/private/Template/JSConfigHelper.php
index 963c158a793..eb253042f19 100644
--- a/lib/private/Template/JSConfigHelper.php
+++ b/lib/private/Template/JSConfigHelper.php
@@ -12,6 +12,7 @@ use OC\Authentication\Token\IProvider;
use OC\CapabilitiesManager;
use OC\Files\FilenameValidator;
use OC\Share\Share;
+use OCA\Provisioning_API\Controller\AUserData;
use OCP\App\AppPathNotFoundException;
use OCP\App\IAppManager;
use OCP\Authentication\Exceptions\ExpiredTokenException;
@@ -133,6 +134,9 @@ class JSConfigHelper {
$capabilities = $this->capabilitiesManager->getCapabilities(false, true);
+ $userFirstDay = $this->config->getUserValue($uid, 'core', AUserData::USER_FIELD_FIRST_DAY_OF_WEEK, null);
+ $firstDay = (int)($userFirstDay ?? $this->l->l('firstday', null));
+
$config = [
/** @deprecated 30.0.0 - use files capabilities instead */
'blacklist_files_regex' => FileInfo::BLACKLIST_FILES_REGEX,
@@ -220,7 +224,7 @@ class JSConfigHelper {
$this->l->t('Nov.'),
$this->l->t('Dec.')
]),
- "firstDay" => json_encode($this->l->l('firstday', null)),
+ "firstDay" => json_encode($firstDay),
"_oc_config" => json_encode($config),
"oc_appconfig" => json_encode([
'core' => [
diff --git a/lib/public/Accounts/UserUpdatedEvent.php b/lib/public/Accounts/UserUpdatedEvent.php
index 8be8a31b08f..fe9ea62a302 100644
--- a/lib/public/Accounts/UserUpdatedEvent.php
+++ b/lib/public/Accounts/UserUpdatedEvent.php
@@ -12,6 +12,8 @@ use OCP\EventDispatcher\Event;
use OCP\IUser;
/**
+ * This event is triggered when the account data of a user was updated.
+ *
* @since 28.0.0
*/
class UserUpdatedEvent extends Event {
diff --git a/lib/public/App/Events/AppDisableEvent.php b/lib/public/App/Events/AppDisableEvent.php
index 00a74663667..5d16d42785e 100644
--- a/lib/public/App/Events/AppDisableEvent.php
+++ b/lib/public/App/Events/AppDisableEvent.php
@@ -11,6 +11,8 @@ namespace OCP\App\Events;
use OCP\EventDispatcher\Event;
/**
+ * This event is triggered when an app is disabled.
+ *
* @since 27.0.0
*/
class AppDisableEvent extends Event {
diff --git a/lib/public/App/Events/AppEnableEvent.php b/lib/public/App/Events/AppEnableEvent.php
index 70d42263d3e..fe1523d6a83 100644
--- a/lib/public/App/Events/AppEnableEvent.php
+++ b/lib/public/App/Events/AppEnableEvent.php
@@ -11,6 +11,8 @@ namespace OCP\App\Events;
use OCP\EventDispatcher\Event;
/**
+ * This event is triggered when an app is enabled.
+ *
* @since 27.0.0
*/
class AppEnableEvent extends Event {
diff --git a/lib/public/App/Events/AppUpdateEvent.php b/lib/public/App/Events/AppUpdateEvent.php
index de88d65b68b..344e7def080 100644
--- a/lib/public/App/Events/AppUpdateEvent.php
+++ b/lib/public/App/Events/AppUpdateEvent.php
@@ -11,6 +11,8 @@ namespace OCP\App\Events;
use OCP\EventDispatcher\Event;
/**
+ * This event is triggered when an app is updated.
+ *
* @since 27.0.0
*/
class AppUpdateEvent extends Event {
diff --git a/lib/public/AppFramework/Bootstrap/IRegistrationContext.php b/lib/public/AppFramework/Bootstrap/IRegistrationContext.php
index b86f7bcd76d..57e76f268d9 100644
--- a/lib/public/AppFramework/Bootstrap/IRegistrationContext.php
+++ b/lib/public/AppFramework/Bootstrap/IRegistrationContext.php
@@ -17,6 +17,7 @@ use OCP\Collaboration\Reference\IReferenceProvider;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\Template\ICustomTemplateProvider;
use OCP\IContainer;
+use OCP\Mail\Provider\IProvider as IMailProvider;
use OCP\Notification\INotifier;
use OCP\Preview\IProviderV2;
use OCP\SpeechToText\ISpeechToTextProvider;
@@ -412,4 +413,14 @@ interface IRegistrationContext {
* @since 30.0.0
*/
public function registerTaskProcessingTaskType(string $taskProcessingTaskTypeClass): void;
+
+ /**
+ * Register a mail provider
+ *
+ * @param string $class
+ * @psalm-param class-string<IMailProvider> $class
+ * @since 30.0.0
+ */
+ public function registerMailProvider(string $class): void;
+
}
diff --git a/lib/public/Collaboration/Reference/RenderReferenceEvent.php b/lib/public/Collaboration/Reference/RenderReferenceEvent.php
index 09778a20d2a..692098dbf60 100644
--- a/lib/public/Collaboration/Reference/RenderReferenceEvent.php
+++ b/lib/public/Collaboration/Reference/RenderReferenceEvent.php
@@ -11,8 +11,10 @@ namespace OCP\Collaboration\Reference;
use OCP\EventDispatcher\Event;
/**
- * Event that apps can emit on their page rendering to trigger loading of aditional
- * scripts for reference widget rendering
+ * Event emitted when apps might render references like link previews or smart picker widgets.
+ *
+ * This can be used to inject scripts for extending that.
+ * Further details can be found in the :ref:`Reference providers` deep dive.
*
* @since 25.0.0
*/
diff --git a/lib/public/Files/Template/BeforeGetTemplatesEvent.php b/lib/public/Files/Template/BeforeGetTemplatesEvent.php
new file mode 100644
index 00000000000..006163c5f7f
--- /dev/null
+++ b/lib/public/Files/Template/BeforeGetTemplatesEvent.php
@@ -0,0 +1,40 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\Files\Template;
+
+use OCP\EventDispatcher\Event;
+
+/**
+ * @since 30.0.0
+ */
+class BeforeGetTemplatesEvent extends Event {
+ /** @var array<Template> */
+ private array $templates;
+
+ /**
+ * @param array<Template> $templates
+ *
+ * @since 30.0.0
+ */
+ public function __construct(array $templates) {
+ parent::__construct();
+
+ $this->templates = $templates;
+ }
+
+ /**
+ * @return array<Template>
+ *
+ * @since 30.0.0
+ */
+ public function getTemplates(): array {
+ return $this->templates;
+ }
+}
diff --git a/lib/public/Files/Template/Field.php b/lib/public/Files/Template/Field.php
new file mode 100644
index 00000000000..ce0ed3176d9
--- /dev/null
+++ b/lib/public/Files/Template/Field.php
@@ -0,0 +1,48 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\Files\Template;
+
+/**
+ * @since 30.0.0
+ */
+class Field implements \JsonSerializable {
+ private string $index;
+ private string $content;
+ private FieldType $type;
+ private ?string $alias;
+ private ?int $id;
+ private ?string $tag;
+
+ /**
+ * @since 30.0.0
+ */
+ public function __construct(string $index, string $content, FieldType $type, ?string $alias = null, ?int $id = null, ?string $tag = null) {
+ $this->index = $index;
+ $this->alias = $alias;
+ $this->type = $type;
+ $this->id = $id;
+ $this->tag = $tag;
+ $this->content = $content;
+ }
+
+ /**
+ * @since 30.0.0
+ */
+ public function jsonSerialize(): array {
+ return [
+ "index" => $this->index,
+ "content" => $this->content,
+ "type" => $this->type->value,
+ "alias" => $this->alias,
+ "id" => $this->id,
+ "tag" => $this->tag,
+ ];
+ }
+}
diff --git a/lib/public/Files/Template/FieldType.php b/lib/public/Files/Template/FieldType.php
new file mode 100644
index 00000000000..ff3080406a7
--- /dev/null
+++ b/lib/public/Files/Template/FieldType.php
@@ -0,0 +1,19 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\Files\Template;
+
+/**
+ * @since 30.0.0
+ */
+enum FieldType: string {
+ case RichText = "rich-text";
+ case CheckBox = "checkbox";
+ case DropDownList = "drop-down-list";
+ case Picture = "picture";
+ case Date = "date";
+}
diff --git a/lib/public/Files/Template/FileCreatedFromTemplateEvent.php b/lib/public/Files/Template/FileCreatedFromTemplateEvent.php
index ed585504f70..0636d1dc251 100644
--- a/lib/public/Files/Template/FileCreatedFromTemplateEvent.php
+++ b/lib/public/Files/Template/FileCreatedFromTemplateEvent.php
@@ -17,15 +17,17 @@ use OCP\Files\File;
class FileCreatedFromTemplateEvent extends Event {
private $template;
private $target;
+ private $templateFields;
/**
* @param File|null $template
* @param File $target
* @since 21.0.0
*/
- public function __construct(?File $template, File $target) {
+ public function __construct(?File $template, File $target, array $templateFields) {
$this->template = $template;
$this->target = $target;
+ $this->templateFields = $templateFields;
}
/**
@@ -37,6 +39,14 @@ class FileCreatedFromTemplateEvent extends Event {
}
/**
+ * @return array
+ * @since 30.0.0
+ */
+ public function getTemplateFields(): array {
+ return $this->templateFields;
+ }
+
+ /**
* @return File
* @since 21.0.0
*/
diff --git a/lib/public/Files/Template/ITemplateManager.php b/lib/public/Files/Template/ITemplateManager.php
index 5adcc0ded25..94e0db935e7 100644
--- a/lib/public/Files/Template/ITemplateManager.php
+++ b/lib/public/Files/Template/ITemplateManager.php
@@ -67,9 +67,11 @@ interface ITemplateManager {
/**
* @param string $filePath
* @param string $templateId
+ * @param string $templateType
+ * @param array $templateFields Since 30.0.0
* @return array
* @throws GenericFileException
* @since 21.0.0
*/
- public function createFromTemplate(string $filePath, string $templateId = '', string $templateType = 'user'): array;
+ public function createFromTemplate(string $filePath, string $templateId = '', string $templateType = 'user', array $templateFields = []): array;
}
diff --git a/lib/public/Files/Template/InvalidFieldTypeException.php b/lib/public/Files/Template/InvalidFieldTypeException.php
new file mode 100644
index 00000000000..a0c5297526c
--- /dev/null
+++ b/lib/public/Files/Template/InvalidFieldTypeException.php
@@ -0,0 +1,15 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\Files\Template;
+
+/**
+ * Exception for invalid template field type
+ * @since 30.0.0
+ */
+class InvalidFieldTypeException extends \Exception {
+}
diff --git a/lib/public/Files/Template/Template.php b/lib/public/Files/Template/Template.php
index 94f5cec268f..634935d212e 100644
--- a/lib/public/Files/Template/Template.php
+++ b/lib/public/Files/Template/Template.php
@@ -24,6 +24,8 @@ final class Template implements \JsonSerializable {
private $hasPreview = false;
/** @var string|null */
private $previewUrl = null;
+ /** @var array */
+ private $fields = [];
/**
* @since 21.0.0
@@ -49,6 +51,13 @@ final class Template implements \JsonSerializable {
}
/**
+ * @since 30.0.0
+ */
+ public function setFields(array $fields): void {
+ $this->fields = $fields;
+ }
+
+ /**
* @since 21.0.0
*/
public function jsonSerialize(): array {
@@ -64,7 +73,8 @@ final class Template implements \JsonSerializable {
'size' => $this->file->getSize(),
'type' => $this->file->getType(),
'hasPreview' => $this->hasPreview,
- 'previewUrl' => $this->previewUrl
+ 'previewUrl' => $this->previewUrl,
+ 'fields' => $this->fields
];
}
}
diff --git a/lib/public/IGroupManager.php b/lib/public/IGroupManager.php
index d4ea3f70722..ee88990df79 100644
--- a/lib/public/IGroupManager.php
+++ b/lib/public/IGroupManager.php
@@ -115,6 +115,14 @@ interface IGroupManager {
public function isAdmin($userId);
/**
+ * Checks if a userId is eligible to users administration delegation
+ * @param string $userId
+ * @return bool if delegated admin
+ * @since 30.0.0
+ */
+ public function isDelegatedAdmin(string $userId): bool;
+
+ /**
* Checks if a userId is in a group
* @param string $userId
* @param string $group
diff --git a/lib/public/Mail/Provider/Address.php b/lib/public/Mail/Provider/Address.php
new file mode 100644
index 00000000000..9cd2859a8a5
--- /dev/null
+++ b/lib/public/Mail/Provider/Address.php
@@ -0,0 +1,85 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCP\Mail\Provider;
+
+/**
+ * Mail Address Object
+ *
+ * This object is used to define the address and label of a email address
+ *
+ * @since 30.0.0
+ *
+ */
+class Address implements \OCP\Mail\Provider\IAddress {
+
+ /**
+ * initialize the mail address object
+ *
+ * @since 30.0.0
+ *
+ * @param string|null $address mail address (e.g test@example.com)
+ * @param string|null $label mail address label/name
+ */
+ public function __construct(
+ protected ?string $address = null,
+ protected ?string $label = null
+ ) {
+ }
+
+ /**
+ * sets the mail address
+ *
+ * @since 30.0.0
+ *
+ * @param string $value mail address (e.g. test@example.com)
+ *
+ * @return self return this object for command chaining
+ */
+ public function setAddress(string $value): self {
+ $this->address = $value;
+ return $this;
+ }
+
+ /**
+ * gets the mail address
+ *
+ * @since 30.0.0
+ *
+ * @return string|null returns the mail address or null if one is not set
+ */
+ public function getAddress(): string | null {
+ return $this->address;
+ }
+
+ /**
+ * sets the mail address label/name
+ *
+ * @since 30.0.0
+ *
+ * @param string $value mail address label/name
+ *
+ * @return self return this object for command chaining
+ */
+ public function setLabel(string $value): self {
+ $this->label = $value;
+ return $this;
+ }
+
+ /**
+ * gets the mail address label/name
+ *
+ * @since 30.0.0
+ *
+ * @return string|null returns the mail address label/name or null if one is not set
+ */
+ public function getLabel(): string | null {
+ return $this->label;
+ }
+
+}
diff --git a/lib/public/Mail/Provider/Attachment.php b/lib/public/Mail/Provider/Attachment.php
new file mode 100644
index 00000000000..d7790a3bbc6
--- /dev/null
+++ b/lib/public/Mail/Provider/Attachment.php
@@ -0,0 +1,139 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCP\Mail\Provider;
+
+/**
+ * Mail Attachment Object
+ *
+ * This object is used to define the parameters of a mail attachment
+ *
+ * @since 30.0.0
+ *
+ */
+class Attachment implements \OCP\Mail\Provider\IAttachment {
+
+ /**
+ * initialize the mail attachment object
+ *
+ * @since 30.0.0
+ *
+ * @param string|null $contents binary contents of file
+ * @param string|null $name file name (e.g example.txt)
+ * @param string|null $type mime type (e.g. text/plain)
+ * @param bool $embedded embedded status of the attachment, default is false
+ */
+ public function __construct(
+ protected ?string $contents,
+ protected ?string $name,
+ protected ?string $type,
+ protected bool $embedded = false
+ ) {
+ }
+
+ /**
+ * sets the attachment file name
+ *
+ * @since 30.0.0
+ *
+ * @param string $value file name (e.g example.txt)
+ *
+ * @return self return this object for command chaining
+ */
+ public function setName(string $value): self {
+ $this->name = $value;
+ return $this;
+ }
+
+ /**
+ * gets the attachment file name
+ *
+ * @since 30.0.0
+ *
+ * @return string | null returns the attachment file name or null if not set
+ */
+ public function getName(): string | null {
+ return $this->name;
+ }
+
+ /**
+ * sets the attachment mime type
+ *
+ * @since 30.0.0
+ *
+ * @param string $value mime type (e.g. text/plain)
+ *
+ * @return self return this object for command chaining
+ */
+ public function setType(string $value): self {
+ $this->type = $value;
+ return $this;
+ }
+
+ /**
+ * gets the attachment mime type
+ *
+ * @since 30.0.0
+ *
+ * @return string | null returns the attachment mime type or null if not set
+ */
+ public function getType(): string | null {
+ return $this->type;
+ }
+
+ /**
+ * sets the attachment contents (actual data)
+ *
+ * @since 30.0.0
+ *
+ * @param string $value binary contents of file
+ *
+ * @return self return this object for command chaining
+ */
+ public function setContents(string $value): self {
+ $this->contents = $value;
+ return $this;
+ }
+
+ /**
+ * gets the attachment contents (actual data)
+ *
+ * @since 30.0.0
+ *
+ * @return string | null returns the attachment contents or null if not set
+ */
+ public function getContents(): string | null {
+ return $this->contents;
+ }
+
+ /**
+ * sets the embedded status of the attachment
+ *
+ * @since 30.0.0
+ *
+ * @param bool $value true - embedded / false - not embedded
+ *
+ * @return self return this object for command chaining
+ */
+ public function setEmbedded(bool $value): self {
+ $this->embedded = $value;
+ return $this;
+ }
+
+ /**
+ * gets the embedded status of the attachment
+ *
+ * @since 30.0.0
+ *
+ * @return bool embedded status of the attachment
+ */
+ public function getEmbedded(): bool {
+ return $this->embedded;
+ }
+
+}
diff --git a/lib/public/Mail/Provider/Exception/Exception.php b/lib/public/Mail/Provider/Exception/Exception.php
new file mode 100644
index 00000000000..7514c72a869
--- /dev/null
+++ b/lib/public/Mail/Provider/Exception/Exception.php
@@ -0,0 +1,18 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCP\Mail\Provider\Exception;
+
+/**
+ * Mail Provider Base Exception
+ *
+ * @since 30.0.0
+ */
+class Exception extends \Exception {
+
+}
diff --git a/lib/public/Mail/Provider/Exception/SendException.php b/lib/public/Mail/Provider/Exception/SendException.php
new file mode 100644
index 00000000000..fba0903cbb3
--- /dev/null
+++ b/lib/public/Mail/Provider/Exception/SendException.php
@@ -0,0 +1,18 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCP\Mail\Provider\Exception;
+
+/**
+ * Mail Provider Send Exception
+ *
+ * @since 30.0.0
+ */
+class SendException extends Exception {
+
+}
diff --git a/lib/public/Mail/Provider/IAddress.php b/lib/public/Mail/Provider/IAddress.php
new file mode 100644
index 00000000000..b980f31150b
--- /dev/null
+++ b/lib/public/Mail/Provider/IAddress.php
@@ -0,0 +1,61 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCP\Mail\Provider;
+
+/**
+ * Mail Address Interface
+ *
+ * This interface is a base requirement of methods and functionality used to construct a mail address object
+ *
+ * @since 30.0.0
+ *
+ */
+interface IAddress {
+
+ /**
+ * sets the mail address
+ *
+ * @since 30.0.0
+ *
+ * @param string $value mail address (test@example.com)
+ *
+ * @return self return this object for command chaining
+ */
+ public function setAddress(string $value): self;
+
+ /**
+ * gets the mail address
+ *
+ * @since 30.0.0
+ *
+ * @return string returns the mail address
+ */
+ public function getAddress(): string | null;
+
+ /**
+ * sets the mail address label/name
+ *
+ * @since 30.0.0
+ *
+ * @param string $value mail address label/name
+ *
+ * @return self return this object for command chaining
+ */
+ public function setLabel(string $value): self;
+
+ /**
+ * gets the mail address label/name
+ *
+ * @since 30.0.0
+ *
+ * @return string returns the mail address label/name
+ */
+ public function getLabel(): string | null;
+
+}
diff --git a/lib/public/Mail/Provider/IAttachment.php b/lib/public/Mail/Provider/IAttachment.php
new file mode 100644
index 00000000000..b5bdffc0e81
--- /dev/null
+++ b/lib/public/Mail/Provider/IAttachment.php
@@ -0,0 +1,101 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCP\Mail\Provider;
+
+/**
+ * Mail Attachment Interface
+ *
+ * This interface is used for defining individual attachments that are attached to a message
+ *
+ * @since 30.0.0
+ *
+ */
+interface IAttachment {
+
+ /**
+ * sets the attachment file name
+ *
+ * @since 30.0.0
+ *
+ * @param string $value file name (e.g example.txt)
+ *
+ * @return self return this object for command chaining
+ */
+ public function setName(string $value): self;
+
+ /**
+ * gets the attachment file name
+ *
+ * @since 30.0.0
+ *
+ * @return string | null returns the attachment file name or null if one is not set
+ */
+ public function getName(): string | null;
+
+ /**
+ * sets the attachment mime type
+ *
+ * @since 30.0.0
+ *
+ * @param string $value mime type (e.g. text/plain)
+ *
+ * @return self return this object for command chaining
+ */
+ public function setType(string $value): self;
+
+ /**
+ * gets the attachment mime type
+ *
+ * @since 30.0.0
+ *
+ * @return string | null returns the attachment mime type or null if not set
+ */
+ public function getType(): string | null;
+
+ /**
+ * sets the attachment contents (actual data)
+ *
+ * @since 30.0.0
+ *
+ * @param string $value binary contents of file
+ *
+ * @return self return this object for command chaining
+ */
+ public function setContents(string $value): self;
+
+ /**
+ * gets the attachment contents (actual data)
+ *
+ * @since 30.0.0
+ *
+ * @return string | null returns the attachment contents or null if not set
+ */
+ public function getContents(): string | null;
+
+ /**
+ * sets the embedded status of the attachment
+ *
+ * @since 30.0.0
+ *
+ * @param bool $value true - embedded / false - not embedded
+ *
+ * @return self return this object for command chaining
+ */
+ public function setEmbedded(bool $value): self;
+
+ /**
+ * gets the embedded status of the attachment
+ *
+ * @since 30.0.0
+ *
+ * @return bool embedded status of the attachment
+ */
+ public function getEmbedded(): bool;
+
+}
diff --git a/lib/public/Mail/Provider/IManager.php b/lib/public/Mail/Provider/IManager.php
new file mode 100644
index 00000000000..4c06cfff87c
--- /dev/null
+++ b/lib/public/Mail/Provider/IManager.php
@@ -0,0 +1,106 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCP\Mail\Provider;
+
+/**
+ * Mail Provider Manager Interface
+ *
+ * This interface is a base requirement of methods and functionality used to construct a mail provider manager object
+ *
+ * @since 30.0.0
+ *
+ */
+interface IManager {
+
+ /**
+ * determine if any mail providers are registered
+ *
+ * @since 30.0.0
+ *
+ * @return bool
+ */
+ public function has(): bool;
+
+ /**
+ * retrieve a count of how many mail providers are registered
+ *
+ * @since 30.0.0
+ *
+ * @return int
+ */
+ public function count(): int;
+
+ /**
+ * retrieve which mail providers are registered
+ *
+ * @since 30.0.0
+ *
+ * @return array<string,String> collection of provider id and label ['jmap' => 'JMap Connector']
+ */
+ public function types(): array;
+
+ /**
+ * retrieve all registered mail providers
+ *
+ * @since 30.0.0
+ *
+ * @return array<string,IProvider> collection of provider id and object ['jmap' => IProviderObject]
+ */
+ public function providers(): array;
+
+ /**
+ * retrieve a provider with a specific id
+ *
+ * @since 30.0.0
+ *
+ * @param string $providerId provider id
+ *
+ * @return IProvider|null
+ */
+ public function findProviderById(string $providerId): IProvider | null;
+
+ /**
+ * retrieve all services for all registered mail providers
+ *
+ * @since 30.0.0
+ *
+ * @param string $userId user id
+ *
+ * @return array<string,array<string,IService>> collection of provider id, service id and object ['jmap' => ['Service1' => IServiceObject]]
+ */
+ public function services(string $userId): array;
+
+ /**
+ * retrieve a service with a specific id
+ *
+ * @since 30.0.0
+ *
+ * @param string $userId user id
+ * @param string $serviceId service id
+ * @param string $providerId provider id
+ *
+ * @return IService|null returns service object or null if none found
+ */
+ public function findServiceById(string $userId, string $serviceId, ?string $providerId = null): IService | null;
+
+ /**
+ * retrieve a service for a specific mail address
+ * returns first service with specific primary address
+ *
+ * @since 30.0.0
+ *
+ * @param string $userId user id
+ * @param string $address mail address (e.g. test@example.com)
+ * @param string $providerId provider id
+ *
+ * @return IService|null returns service object or null if none found
+ */
+ public function findServiceByAddress(string $userId, string $address, ?string $providerId = null): IService | null;
+
+}
diff --git a/lib/public/Mail/Provider/IMessage.php b/lib/public/Mail/Provider/IMessage.php
new file mode 100644
index 00000000000..71021981362
--- /dev/null
+++ b/lib/public/Mail/Provider/IMessage.php
@@ -0,0 +1,232 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCP\Mail\Provider;
+
+/**
+ * Mail Message Interface
+ *
+ * This interface is a base requirement of methods and functionality used to construct a mail message object
+ *
+ * @since 30.0.0
+ *
+ */
+interface IMessage {
+
+ /**
+ * arbitrary unique text string identifying this message
+ *
+ * @since 30.0.0
+ *
+ * @return string id of this message
+ */
+ public function id(): string;
+
+ /**
+ * sets the sender of this message
+ *
+ * @since 30.0.0
+ *
+ * @param IAddress $value sender's mail address object
+ *
+ * @return self return this object for command chaining
+ */
+ public function setFrom(IAddress $value): self;
+
+ /**
+ * gets the sender of this message
+ *
+ * @since 30.0.0
+ *
+ * @param IAddress|null sender's mail address object
+ */
+ public function getFrom(): IAddress | null;
+
+ /**
+ * sets the sender's reply to address of this message
+ *
+ * @since 30.0.0
+ *
+ * @param IAddress $value senders's reply to mail address object
+ *
+ * @return self return this object for command chaining
+ */
+ public function setReplyTo(IAddress $value): self;
+
+ /**
+ * gets the sender's reply to address of this message
+ *
+ * @since 30.0.0
+ *
+ * @param IAddress|null sender's reply to mail address object
+ */
+ public function getReplyTo(): IAddress | null;
+
+ /**
+ * sets the recipient(s) of this message
+ *
+ * @since 30.0.0
+ *
+ * @param IAddress ...$value collection of or one or more mail address objects
+ *
+ * @return self return this object for command chaining
+ */
+ public function setTo(IAddress ...$value): self;
+
+ /**
+ * gets the recipient(s) of this message
+ *
+ * @since 30.0.0
+ *
+ * @param array<int,IAddress> collection of all recipient mail address objects
+ */
+ public function getTo(): array;
+
+ /**
+ * sets the copy to recipient(s) of this message
+ *
+ * @since 30.0.0
+ *
+ * @param IAddress ...$value collection of or one or more mail address objects
+ *
+ * @return self return this object for command chaining
+ */
+ public function setCc(IAddress ...$value): self;
+
+ /**
+ * gets the copy to recipient(s) of this message
+ *
+ * @since 30.0.0
+ *
+ * @param array<int,IAddress> collection of all copied recipient mail address objects
+ */
+ public function getCc(): array;
+
+ /**
+ * sets the blind copy to recipient(s) of this message
+ *
+ * @since 30.0.0
+ *
+ * @param IAddress ...$value collection of or one or more mail address objects
+ *
+ * @return self return this object for command chaining
+ */
+ public function setBcc(IAddress ...$value): self;
+
+ /**
+ * gets the blind copy to recipient(s) of this message
+ *
+ * @since 30.0.0
+ *
+ * @param array<int,IAddress> collection of all blind copied recipient mail address objects
+ */
+ public function getBcc(): array;
+
+ /**
+ * sets the subject of this message
+ *
+ * @since 30.0.0
+ *
+ * @param string $value subject of mail message
+ *
+ * @return self return this object for command chaining
+ */
+ public function setSubject(string $value): self;
+
+ /**
+ * gets the subject of this message
+ *
+ * @since 30.0.0
+ *
+ * @param string|null subject of message or null if one is not set
+ */
+ public function getSubject(): string | null;
+
+ /**
+ * sets the plain text or html body of this message
+ *
+ * @since 30.0.0
+ *
+ * @param string $value text or html body of message
+ * @param bool $html html flag - true for html
+ *
+ * @return self return this object for command chaining
+ */
+ public function setBody(string $value, bool $html): self;
+
+ /**
+ * gets either the html or plain text body of this message
+ *
+ * html body will be returned over plain text if html body exists
+ *
+ * @since 30.0.0
+ *
+ * @param string|null html/plain body of this message or null if one is not set
+ */
+ public function getBody(): string | null;
+
+ /**
+ * sets the html body of this message
+ *
+ * @since 30.0.0
+ *
+ * @param string $value html body of message
+ *
+ * @return self return this object for command chaining
+ */
+ public function setBodyHtml(string $value): self;
+
+ /**
+ * gets the html body of this message
+ *
+ * @since 30.0.0
+ *
+ * @param string|null html body of this message or null if one is not set
+ */
+ public function getBodyHtml(): string | null;
+
+ /**
+ * sets the plain text body of this message
+ *
+ * @since 30.0.0
+ *
+ * @param string $value plain text body of message
+ *
+ * @return self return this object for command chaining
+ */
+ public function setBodyPlain(string $value): self;
+
+ /**
+ * gets the plain text body of this message
+ *
+ * @since 30.0.0
+ *
+ * @param string|null plain text body of this message or null if one is not set
+ */
+ public function getBodyPlain(): string | null;
+
+ /**
+ * sets the attachments of this message
+ *
+ * @since 30.0.0
+ *
+ * @param IAttachment ...$value collection of or one or more mail attachment objects
+ *
+ * @return self return this object for command chaining
+ */
+ public function setAttachments(IAttachment ...$value): self;
+
+ /**
+ * gets the attachments of this message
+ *
+ * @since 30.0.0
+ *
+ * @return array<int,IAttachment> collection of all mail attachment objects
+ */
+ public function getAttachments(): array;
+}
diff --git a/lib/public/Mail/Provider/IMessageSend.php b/lib/public/Mail/Provider/IMessageSend.php
new file mode 100644
index 00000000000..e68f18cf95c
--- /dev/null
+++ b/lib/public/Mail/Provider/IMessageSend.php
@@ -0,0 +1,31 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCP\Mail\Provider;
+
+/**
+ * Mail Message Send Interface
+ *
+ * This interface is the required set of methods and functionality used to extend IService with message sending functionality
+ *
+ * @since 30.0.0
+ *
+ */
+interface IMessageSend {
+
+ /**
+ * send an outbound message
+ *
+ * @since 30.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
+ */
+ public function sendMessage(IMessage $message, array $option = []): void;
+
+}
diff --git a/lib/public/Mail/Provider/IProvider.php b/lib/public/Mail/Provider/IProvider.php
new file mode 100644
index 00000000000..47d6002f9b0
--- /dev/null
+++ b/lib/public/Mail/Provider/IProvider.php
@@ -0,0 +1,94 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCP\Mail\Provider;
+
+/**
+ * Mail Provider Interface
+ *
+ * This interface is a base requirement of methods and functionality used to construct a mail provider object
+ *
+ * @since 30.0.0
+ *
+ */
+interface IProvider {
+
+ /**
+ * arbitrary unique text string identifying this provider
+ *
+ * @since 30.0.0
+ *
+ * @return string id of this provider (e.g. UUID or 'IMAP/SMTP' or anything else)
+ */
+ public function id(): string;
+
+ /**
+ * localized human friendly name of this provider
+ *
+ * @since 30.0.0
+ *
+ * @return string label/name of this provider (e.g. Plain Old IMAP/SMTP)
+ */
+ public function label(): string;
+
+ /**
+ * determine if any services are configured for a specific user
+ *
+ * @since 30.0.0
+ *
+ * @param string $userId user id
+ *
+ * @return bool true if any services are configure for the user
+ */
+ public function hasServices(string $userId): bool;
+
+ /**
+ * retrieve collection of services for a specific user
+ *
+ * @param string $userId user id
+ *
+ * @since 30.0.0
+ *
+ * @return array<string,IService> collection of service id and object ['1' => IServiceObject]
+ */
+ public function listServices(string $userId): array;
+
+ /**
+ * retrieve a service with a specific id
+ *
+ * @since 30.0.0
+ *
+ * @param string $userId user id
+ * @param string $serviceId service id
+ *
+ * @return IService|null returns service object or null if none found
+ */
+ public function findServiceById(string $userId, string $serviceId): IService | null;
+
+ /**
+ * retrieve a service for a specific mail address
+ *
+ * @since 30.0.0
+ *
+ * @param string $userId user id
+ * @param string $address mail address (e.g. test@example.com)
+ *
+ * @return IService|null returns service object or null if none found
+ */
+ public function findServiceByAddress(string $userId, string $address): IService | null;
+
+ /**
+ * construct a new empty service object
+ *
+ * @since 30.0.0
+ *
+ * @return IService blank service object
+ */
+ public function initiateService(): IService;
+
+}
diff --git a/lib/public/Mail/Provider/IService.php b/lib/public/Mail/Provider/IService.php
new file mode 100644
index 00000000000..a8fd73749bb
--- /dev/null
+++ b/lib/public/Mail/Provider/IService.php
@@ -0,0 +1,119 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCP\Mail\Provider;
+
+/**
+ * Mail Service Interface
+ *
+ * This interface is a base requirement of methods and functionality used to construct a mail service object
+ *
+ * @since 30.0.0
+ *
+ */
+interface IService {
+
+ /**
+ * arbitrary unique text string identifying this service
+ *
+ * @since 30.0.0
+ *
+ * @return string id of this service (e.g. 1 or service1 or anything else)
+ */
+ public function id(): string;
+
+ /**
+ * checks if a service is able of performing an specific action
+ *
+ * @since 4.0.0
+ *
+ * @param string $value required ability e.g. 'MessageSend'
+ *
+ * @return bool true/false if ability is supplied and found in collection
+ */
+ public function capable(string $value): bool;
+
+ /**
+ * retrieves a collection of what actions a service can perfrom
+ *
+ * @since 4.0.0
+ *
+ * @return array collection of abilities otherwise empty collection
+ */
+ public function capabilities(): array;
+
+ /**
+ * gets the localized human frendly name of this service
+ *
+ * @since 30.0.0
+ *
+ * @return string label/name of service (e.g. ACME Company Mail Service)
+ */
+ public function getLabel(): string;
+
+ /**
+ * sets the localized human frendly name of this service
+ *
+ * @since 30.0.0
+ *
+ * @param string $value label/name of service (e.g. ACME Company Mail Service)
+ *
+ * @return self return this object for command chaining
+ */
+ public function setLabel(string $value): self;
+
+ /**
+ * gets the primary mailing address for this service
+ *
+ * @since 30.0.0
+ *
+ * @return IAddress mail address object
+ */
+ public function getPrimaryAddress(): IAddress;
+
+ /**
+ * sets the primary mailing address for this service
+ *
+ * @since 30.0.0
+ *
+ * @param IAddress $value mail address object
+ *
+ * @return self return this object for command chaining
+ */
+ public function setPrimaryAddress(IAddress $value): self;
+
+ /**
+ * gets the secondary mailing addresses (aliases) collection for this service
+ *
+ * @since 30.0.0
+ *
+ * @return array<int, IAddress> collection of mail address objects
+ */
+ public function getSecondaryAddresses(): array;
+
+ /**
+ * sets the secondary mailing addresses (aliases) for this service
+ *
+ * @since 30.0.0
+ *
+ * @param IAddress ...$value collection of one or more mail address objects
+ *
+ * @return self return this object for command chaining
+ */
+ public function setSecondaryAddresses(IAddress ...$value): self;
+
+ /**
+ * construct a new empty message object
+ *
+ * @since 30.0.0
+ *
+ * @return IMessage blank message object
+ */
+ public function initiateMessage(): IMessage;
+
+}
diff --git a/lib/public/Mail/Provider/Message.php b/lib/public/Mail/Provider/Message.php
new file mode 100644
index 00000000000..58638963640
--- /dev/null
+++ b/lib/public/Mail/Provider/Message.php
@@ -0,0 +1,338 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCP\Mail\Provider;
+
+/**
+ * Mail Message Object
+ *
+ * This object is used to define a mail message that can be used to transfer data to a provider
+ *
+ * @since 30.0.0
+ *
+ */
+class Message implements \OCP\Mail\Provider\IMessage {
+
+ /**
+ * initialize the mail message object
+ *
+ * @since 30.0.0
+ *
+ * @param array $data message data array
+ */
+ public function __construct(
+ protected array $data = [],
+ ) {
+ }
+
+ /**
+ * arbitrary unique text string identifying this message
+ *
+ * @since 30.0.0
+ *
+ * @return string id of this message
+ */
+ public function id(): string {
+ // return id of message
+ return (isset($this->data['id'])) ? $this->data['id'] : '';
+ }
+
+ /**
+ * sets the sender of this message
+ *
+ * @since 30.0.0
+ *
+ * @param IAddress $value sender's mail address object
+ *
+ * @return self return this object for command chaining
+ */
+ public function setFrom(IAddress $value): self {
+ // create or update field in data store with value
+ $this->data['from'] = $value;
+ // return this object for command chaining
+ return $this;
+ }
+
+ /**
+ * gets the sender of this message
+ *
+ * @since 30.0.0
+ *
+ * @param IAddress|null sender's mail address object
+ */
+ public function getFrom(): IAddress | null {
+ // evaluate if data store field exists and return value(s) or null otherwise
+ return (isset($this->data['from'])) ? $this->data['from'] : null;
+ }
+
+ /**
+ * sets the sender's reply to address of this message
+ *
+ * @since 30.0.0
+ *
+ * @param IAddress $value senders's reply to mail address object
+ *
+ * @return self return this object for command chaining
+ */
+ public function setReplyTo(IAddress $value): self {
+ // create or update field in data store with value
+ $this->data['replyTo'] = $value;
+ // return this object for command chaining
+ return $this;
+ }
+
+ /**
+ * gets the sender's reply to address of this message
+ *
+ * @since 30.0.0
+ *
+ * @param IAddress|null sender's reply to mail address object
+ */
+ public function getReplyTo(): IAddress | null {
+ // evaluate if data store field exists and return value(s) or null otherwise
+ return (isset($this->data['replyTo'])) ? $this->data['replyTo'] : null;
+ }
+
+ /**
+ * sets the recipient(s) of this message
+ *
+ * @since 30.0.0
+ *
+ * @param IAddress ...$value collection of or one or more mail address objects
+ *
+ * @return self return this object for command chaining
+ */
+ public function setTo(IAddress ...$value): self {
+ // create or update field in data store with value
+ $this->data['to'] = $value;
+ // return this object for command chaining
+ return $this;
+ }
+
+ /**
+ * gets the recipient(s) of this message
+ *
+ * @since 30.0.0
+ *
+ * @param array<int,IAddress> collection of all recipient mail address objects
+ */
+ public function getTo(): array {
+ // evaluate if data store field exists and return value(s) or empty collection
+ return (isset($this->data['to'])) ? $this->data['to'] : [];
+ }
+
+ /**
+ * sets the copy to recipient(s) of this message
+ *
+ * @since 30.0.0
+ *
+ * @param IAddress ...$value collection of or one or more mail address objects
+ *
+ * @return self return this object for command chaining
+ */
+ public function setCc(IAddress ...$value): self {
+ // create or update field in data store with value
+ $this->data['cc'] = $value;
+ // return this object for command chaining
+ return $this;
+ }
+
+ /**
+ * gets the copy to recipient(s) of this message
+ *
+ * @since 30.0.0
+ *
+ * @param array<int,IAddress> collection of all copied recipient mail address objects
+ */
+ public function getCc(): array {
+ // evaluate if data store field exists and return value(s) or empty collection
+ return (isset($this->data['cc'])) ? $this->data['cc'] : [];
+ }
+
+ /**
+ * sets the blind copy to recipient(s) of this message
+ *
+ * @since 30.0.0
+ *
+ * @param IAddress ...$value collection of or one or more mail address objects
+ *
+ * @return self return this object for command chaining
+ */
+ public function setBcc(IAddress ...$value): self {
+ // create or update field in data store with value
+ $this->data['bcc'] = $value;
+ // return this object for command chaining
+ return $this;
+ }
+
+ /**
+ * gets the blind copy to recipient(s) of this message
+ *
+ * @since 30.0.0
+ *
+ * @param array<int,IAddress> collection of all blind copied recipient mail address objects
+ */
+ public function getBcc(): array {
+ // evaluate if data store field exists and return value(s) or empty collection
+ return (isset($this->data['bcc'])) ? $this->data['bcc'] : [];
+ }
+
+ /**
+ * sets the subject of this message
+ *
+ * @since 30.0.0
+ *
+ * @param string $value subject of mail message
+ *
+ * @return self return this object for command chaining
+ */
+ public function setSubject(string $value): self {
+ // create or update field in data store with value
+ $this->data['subject'] = $value;
+ // return this object for command chaining
+ return $this;
+ }
+
+ /**
+ * gets the subject of this message
+ *
+ * @since 30.0.0
+ *
+ * @param string|null subject of message or null if one is not set
+ */
+ public function getSubject(): string | null {
+ // evaluate if data store field exists and return value(s) or null otherwise
+ return (isset($this->data['subject'])) ? $this->data['subject'] : null;
+ }
+
+ /**
+ * sets the plain text or html body of this message
+ *
+ * @since 30.0.0
+ *
+ * @param string $value text or html body of message
+ * @param bool $html html flag - true for html
+ *
+ * @return self return this object for command chaining
+ */
+ public function setBody(string $value, bool $html = false): self {
+ // evaluate html flag and create or update appropriate field in data store with value
+ if ($html) {
+ $this->data['bodyHtml'] = $value;
+ } else {
+ $this->data['bodyPlain'] = $value;
+ }
+ // return this object for command chaining
+ return $this;
+ }
+
+ /**
+ * gets either the html or plain text body of this message
+ *
+ * html body will be returned over plain text if html body exists
+ *
+ * @since 30.0.0
+ *
+ * @param string|null html/plain body of this message or null if one is not set
+ */
+ public function getBody(): string | null {
+ // evaluate if data store field(s) exists and return value
+ if (isset($this->data['bodyHtml'])) {
+ return $this->data['bodyHtml'];
+ } elseif (isset($this->data['bodyPlain'])) {
+ return $this->data['bodyPlain'];
+ }
+ // return null if data fields did not exist in data store
+ return null;
+ }
+
+ /**
+ * sets the html body of this message
+ *
+ * @since 30.0.0
+ *
+ * @param string $value html body of message
+ *
+ * @return self return this object for command chaining
+ */
+ public function setBodyHtml(string $value): self {
+ // create or update field in data store with value
+ $this->data['bodyHtml'] = $value;
+ // return this object for command chaining
+ return $this;
+ }
+
+ /**
+ * gets the html body of this message
+ *
+ * @since 30.0.0
+ *
+ * @param string|null html body of this message or null if one is not set
+ */
+ public function getBodyHtml(): string | null {
+ // evaluate if data store field exists and return value(s) or null otherwise
+ return (isset($this->data['bodyHtml'])) ? $this->data['bodyHtml'] : null;
+ }
+
+ /**
+ * sets the plain text body of this message
+ *
+ * @since 30.0.0
+ *
+ * @param string $value plain text body of message
+ *
+ * @return self return this object for command chaining
+ */
+ public function setBodyPlain(string $value): self {
+ // create or update field in data store with value
+ $this->data['bodyPlain'] = $value;
+ // return this object for command chaining
+ return $this;
+ }
+
+ /**
+ * gets the plain text body of this message
+ *
+ * @since 30.0.0
+ *
+ * @param string|null plain text body of this message or null if one is not set
+ */
+ public function getBodyPlain(): string | null {
+ // evaluate if data store field exists and return value(s) or null otherwise
+ return (isset($this->data['bodyPlain'])) ? $this->data['bodyPlain'] : null;
+ }
+
+ /**
+ * sets the attachments of this message
+ *
+ * @since 30.0.0
+ *
+ * @param IAttachment ...$value collection of or one or more mail attachment objects
+ *
+ * @return self return this object for command chaining
+ */
+ public function setAttachments(IAttachment ...$value): self {
+ // create or update field in data store with value
+ $this->data['attachments'] = $value;
+ // return this object for command chaining
+ return $this;
+ }
+
+ /**
+ * gets the attachments of this message
+ *
+ * @since 30.0.0
+ *
+ * @return array<int,IAttachment> collection of all mail attachment objects
+ */
+ public function getAttachments(): array {
+ // evaluate if data store field exists and return value(s) or null otherwise
+ return (isset($this->data['attachments'])) ? $this->data['attachments'] : [];
+ }
+
+}
diff --git a/lib/public/Preview/BeforePreviewFetchedEvent.php b/lib/public/Preview/BeforePreviewFetchedEvent.php
index ac3c8c13b55..398358d5905 100644
--- a/lib/public/Preview/BeforePreviewFetchedEvent.php
+++ b/lib/public/Preview/BeforePreviewFetchedEvent.php
@@ -11,7 +11,12 @@ use OCP\Files\Node;
use OCP\IPreview;
/**
+ * Emitted before a file preview is being fetched.
+ *
+ * It can be used to block preview rendering by throwing a ``OCP\Files\NotFoundException``
+ *
* @since 25.0.1
+ * @since 28.0.0 the constructor arguments ``$width``, ``$height``, ``$crop`` and ``$mode`` are no longer nullable.
*/
class BeforePreviewFetchedEvent extends \OCP\EventDispatcher\Event {
/**
diff --git a/lib/public/TaskProcessing/EShapeType.php b/lib/public/TaskProcessing/EShapeType.php
index 059f9d0c3c7..cd8d6d837da 100644
--- a/lib/public/TaskProcessing/EShapeType.php
+++ b/lib/public/TaskProcessing/EShapeType.php
@@ -23,6 +23,7 @@ enum EShapeType: int {
case Audio = 3;
case Video = 4;
case File = 5;
+ case Enum = 6;
case ListOfNumbers = 10;
case ListOfTexts = 11;
case ListOfImages = 12;
@@ -32,11 +33,33 @@ enum EShapeType: int {
/**
* @param mixed $value
+ * @param ShapeEnumValue[] $enumValues
+ * @return void
+ * @throws ValidationException
+ * @since 30.0.0
+ */
+ public function validateEnum(mixed $value, array $enumValues): void {
+ if ($this !== EShapeType::Enum) {
+ throw new ValidationException('Provider provided enum values for non-enum slot');
+ }
+ foreach ($enumValues as $enumValue) {
+ if ($value === $enumValue->getValue()) {
+ return;
+ }
+ }
+ throw new ValidationException('Wrong value given for Enum slot. Got "' . $value . '", but expected one of the provided enum values: "' . implode('", "', array_map(fn ($enumValue) => $enumValue->getValue(), $enumValues)) . '"');
+ }
+
+ /**
+ * @param mixed $value
* @return void
* @throws ValidationException
* @since 30.0.0
*/
private function validateNonFileType(mixed $value): void {
+ if ($this === EShapeType::Enum && !is_string($value)) {
+ throw new ValidationException('Non-text item provided for Enum slot');
+ }
if ($this === EShapeType::Text && !is_string($value)) {
throw new ValidationException('Non-text item provided for Text slot');
}
@@ -159,4 +182,13 @@ enum EShapeType: int {
public static function getScalarType(EShapeType $type): EShapeType {
return EShapeType::from($type->value % 10);
}
+
+ /**
+ * @param EShapeType $type
+ * @return bool
+ * @since 30.0.0
+ */
+ public static function isFileType(EShapeType $type): bool {
+ return in_array(EShapeType::getScalarType($type), [EShapeType::File, EShapeType::Image, EShapeType::Audio, EShapeType::Video], true);
+ }
}
diff --git a/lib/public/TaskProcessing/IManager.php b/lib/public/TaskProcessing/IManager.php
index c68ad1afbac..e3e6b3be09d 100644
--- a/lib/public/TaskProcessing/IManager.php
+++ b/lib/public/TaskProcessing/IManager.php
@@ -46,7 +46,7 @@ interface IManager {
public function getPreferredProvider(string $taskType);
/**
- * @return array<string,array{name: string, description: string, inputShape: ShapeDescriptor[], optionalInputShape: ShapeDescriptor[], outputShape: ShapeDescriptor[], optionalOutputShape: ShapeDescriptor[]}>
+ * @return array<array-key,array{name: string, description: string, inputShape: ShapeDescriptor[], inputShapeEnumValues: ShapeEnumValue[][], inputShapeDefaults: array<array-key, numeric|string>, optionalInputShape: ShapeDescriptor[], optionalInputShapeEnumValues: ShapeEnumValue[][], optionalInputShapeDefaults: array<array-key, numeric|string>, outputShape: ShapeDescriptor[], outputShapeEnumValues: ShapeEnumValue[][], optionalOutputShape: ShapeDescriptor[], optionalOutputShapeEnumValues: ShapeEnumValue[][]}>
* @since 30.0.0
*/
public function getAvailableTaskTypes(): array;
@@ -141,6 +141,24 @@ interface IManager {
public function getUserTasks(?string $userId, ?string $taskTypeId = null, ?string $customId = null): array;
/**
+ * @param string|null $userId The user id that scheduled the task
+ * @param string|null $taskTypeId The task type id to filter by
+ * @param string|null $appId The app ID of the app that submitted the task
+ * @param string|null $customId The custom task ID
+ * @param int|null $status The task status
+ * @param int|null $scheduleAfter Minimum schedule time filter
+ * @param int|null $endedBefore Maximum ending time filter
+ * @return list<Task>
+ * @throws Exception If the query failed
+ * @throws NotFoundException If the task could not be found
+ * @since 30.0.0
+ */
+ public function getTasks(
+ ?string $userId, ?string $taskTypeId = null, ?string $appId = null, ?string $customId = null,
+ ?int $status = null, ?int $scheduleAfter = null, ?int $endedBefore = null
+ ): array;
+
+ /**
* @param string|null $userId
* @param string $appId
* @param string|null $customId
diff --git a/lib/public/TaskProcessing/IProvider.php b/lib/public/TaskProcessing/IProvider.php
index 68a708ca834..a4e752216c7 100644
--- a/lib/public/TaskProcessing/IProvider.php
+++ b/lib/public/TaskProcessing/IProvider.php
@@ -58,4 +58,52 @@ interface IProvider {
* @psalm-return ShapeDescriptor[]
*/
public function getOptionalOutputShape(): array;
+
+ /**
+ * Returns the option list for each input shape ENUM slot
+ *
+ * @since 30.0.0
+ * @psalm-return ShapeEnumValue[][]
+ */
+ public function getInputShapeEnumValues(): array;
+
+ /**
+ * Returns the default values for input shape slots
+ *
+ * @since 30.0.0
+ * @psalm-return array<array-key, string|numeric>
+ */
+ public function getInputShapeDefaults(): array;
+
+ /**
+ * Returns the option list for each optional input shape ENUM slot
+ *
+ * @since 30.0.0
+ * @psalm-return ShapeEnumValue[][]
+ */
+ public function getOptionalInputShapeEnumValues(): array;
+
+ /**
+ * Returns the default values for optional input shape slots
+ *
+ * @since 30.0.0
+ * @psalm-return array<array-key, string|numeric>
+ */
+ public function getOptionalInputShapeDefaults(): array;
+
+ /**
+ * Returns the option list for each output shape ENUM slot
+ *
+ * @since 30.0.0
+ * @psalm-return ShapeEnumValue[][]
+ */
+ public function getOutputShapeEnumValues(): array;
+
+ /**
+ * Returns the option list for each optional output shape ENUM slot
+ *
+ * @since 30.0.0
+ * @psalm-return ShapeEnumValue[][]
+ */
+ public function getOptionalOutputShapeEnumValues(): array;
}
diff --git a/lib/public/TaskProcessing/ShapeDescriptor.php b/lib/public/TaskProcessing/ShapeDescriptor.php
index 5759b260865..19e57c8a91d 100644
--- a/lib/public/TaskProcessing/ShapeDescriptor.php
+++ b/lib/public/TaskProcessing/ShapeDescriptor.php
@@ -49,11 +49,11 @@ class ShapeDescriptor implements \JsonSerializable {
}
/**
- * @return array{name: string, description: string, type: "Number"|"Text"|"Audio"|"Image"|"Video"|"File"|"ListOfNumbers"|"ListOfTexts"|"ListOfImages"|"ListOfAudios"|"ListOfVideos"|"ListOfFiles"}
+ * @return array{name: string, description: string, type: "Number"|"Text"|"Audio"|"Image"|"Video"|"File"|"Enum"|"ListOfNumbers"|"ListOfTexts"|"ListOfImages"|"ListOfAudios"|"ListOfVideos"|"ListOfFiles"}
* @since 30.0.0
*/
public function jsonSerialize(): array {
- /** @var "Number"|"Text"|"Audio"|"Image"|"Video"|"File"|"ListOfNumbers"|"ListOfTexts"|"ListOfImages"|"ListOfAudios"|"ListOfVideos"|"ListOfFiles" $type */
+ /** @var "Number"|"Text"|"Audio"|"Image"|"Video"|"File"|"Enum"|"ListOfNumbers"|"ListOfTexts"|"ListOfImages"|"ListOfAudios"|"ListOfVideos"|"ListOfFiles" $type */
$type = $this->getShapeType()->name;
return [
'name' => $this->getName(),
diff --git a/lib/public/TaskProcessing/ShapeEnumValue.php b/lib/public/TaskProcessing/ShapeEnumValue.php
new file mode 100644
index 00000000000..bc500524304
--- /dev/null
+++ b/lib/public/TaskProcessing/ShapeEnumValue.php
@@ -0,0 +1,51 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCP\TaskProcessing;
+
+/**
+ * Data object for input output shape enum slot value
+ * @since 30.0.0
+ */
+class ShapeEnumValue implements \JsonSerializable {
+ /**
+ * @param string $name
+ * @param string $value
+ * @since 30.0.0
+ */
+ public function __construct(
+ private string $name,
+ private string $value,
+ ) {
+ }
+
+ /**
+ * @return string
+ * @since 30.0.0
+ */
+ public function getName(): string {
+ return $this->name;
+ }
+
+ /**
+ * @return string
+ * @since 30.0.0
+ */
+ public function getValue(): string {
+ return $this->value;
+ }
+
+ /**
+ * @return array{name: string, value: string}
+ * @since 30.0.0
+ */
+ public function jsonSerialize(): array {
+ return [
+ 'name' => $this->getName(),
+ 'value' => $this->getValue(),
+ ];
+ }
+}
diff --git a/lib/public/TaskProcessing/Task.php b/lib/public/TaskProcessing/Task.php
index 44834c3b846..db8e4d7fab5 100644
--- a/lib/public/TaskProcessing/Task.php
+++ b/lib/public/TaskProcessing/Task.php
@@ -63,6 +63,10 @@ final class Task implements \JsonSerializable {
*/
protected int $status = self::STATUS_UNKNOWN;
+ protected ?int $scheduledAt = null;
+ protected ?int $startedAt = null;
+ protected ?int $endedAt = null;
+
/**
* @param string $taskTypeId
* @param array<string,list<numeric|string>|numeric|string> $input
@@ -201,7 +205,55 @@ final class Task implements \JsonSerializable {
}
/**
- * @psalm-return array{id: ?int, lastUpdated: int, type: string, status: 'STATUS_CANCELLED'|'STATUS_FAILED'|'STATUS_SUCCESSFUL'|'STATUS_RUNNING'|'STATUS_SCHEDULED'|'STATUS_UNKNOWN', userId: ?string, appId: string, input: array<array-key, list<numeric|string>|numeric|string>, output: ?array<array-key, list<numeric|string>|numeric|string>, customId: ?string, completionExpectedAt: ?int, progress: ?float}
+ * @return int|null
+ * @since 30.0.0
+ */
+ final public function getScheduledAt(): ?int {
+ return $this->scheduledAt;
+ }
+
+ /**
+ * @param int|null $scheduledAt
+ * @since 30.0.0
+ */
+ final public function setScheduledAt(?int $scheduledAt): void {
+ $this->scheduledAt = $scheduledAt;
+ }
+
+ /**
+ * @return int|null
+ * @since 30.0.0
+ */
+ final public function getStartedAt(): ?int {
+ return $this->startedAt;
+ }
+
+ /**
+ * @param int|null $startedAt
+ * @since 30.0.0
+ */
+ final public function setStartedAt(?int $startedAt): void {
+ $this->startedAt = $startedAt;
+ }
+
+ /**
+ * @return int|null
+ * @since 30.0.0
+ */
+ final public function getEndedAt(): ?int {
+ return $this->endedAt;
+ }
+
+ /**
+ * @param int|null $endedAt
+ * @since 30.0.0
+ */
+ final public function setEndedAt(?int $endedAt): void {
+ $this->endedAt = $endedAt;
+ }
+
+ /**
+ * @psalm-return array{id: ?int, lastUpdated: int, type: string, status: 'STATUS_CANCELLED'|'STATUS_FAILED'|'STATUS_SUCCESSFUL'|'STATUS_RUNNING'|'STATUS_SCHEDULED'|'STATUS_UNKNOWN', userId: ?string, appId: string, input: array<array-key, list<numeric|string>|numeric|string>, output: ?array<array-key, list<numeric|string>|numeric|string>, customId: ?string, completionExpectedAt: ?int, progress: ?float, scheduledAt: ?int, startedAt: ?int, endedAt: ?int}
* @since 30.0.0
*/
final public function jsonSerialize(): array {
@@ -217,6 +269,9 @@ final class Task implements \JsonSerializable {
'customId' => $this->getCustomId(),
'completionExpectedAt' => $this->getCompletionExpectedAt()?->getTimestamp(),
'progress' => $this->getProgress(),
+ 'scheduledAt' => $this->getScheduledAt(),
+ 'startedAt' => $this->getStartedAt(),
+ 'endedAt' => $this->getEndedAt(),
];
}
diff --git a/lib/public/TaskProcessing/TaskTypes/TextToText.php b/lib/public/TaskProcessing/TaskTypes/TextToText.php
index 7ac7f2eca05..92eaf5629e8 100644
--- a/lib/public/TaskProcessing/TaskTypes/TextToText.php
+++ b/lib/public/TaskProcessing/TaskTypes/TextToText.php
@@ -51,7 +51,7 @@ class TextToText implements ITaskType {
* @since 30.0.0
*/
public function getDescription(): string {
- return $this->l->t('Runs an arbitrary prompt through a language model that retuns a reply');
+ return $this->l->t('Runs an arbitrary prompt through a language model that returns a reply');
}
/**
diff --git a/lib/public/TaskProcessing/TaskTypes/TextToTextTranslate.php b/lib/public/TaskProcessing/TaskTypes/TextToTextTranslate.php
new file mode 100644
index 00000000000..11b71ec3eb2
--- /dev/null
+++ b/lib/public/TaskProcessing/TaskTypes/TextToTextTranslate.php
@@ -0,0 +1,102 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\TaskProcessing\TaskTypes;
+
+use OCP\IL10N;
+use OCP\L10N\IFactory;
+use OCP\TaskProcessing\EShapeType;
+use OCP\TaskProcessing\ITaskType;
+use OCP\TaskProcessing\ShapeDescriptor;
+
+/**
+ * This is the task processing task type for generic text processing
+ * @since 30.0.0
+ */
+class TextToTextTranslate implements ITaskType {
+ /**
+ * @since 30.0.0
+ */
+ public const ID = 'core:text2text:translate';
+
+ private IL10N $l;
+
+ /**
+ * @param IFactory $l10nFactory
+ * @since 30.0.0
+ */
+ public function __construct(
+ IFactory $l10nFactory,
+ ) {
+ $this->l = $l10nFactory->get('core');
+ }
+
+
+ /**
+ * @inheritDoc
+ * @since 30.0.0
+ */
+ public function getName(): string {
+ return $this->l->t('Translate');
+ }
+
+ /**
+ * @inheritDoc
+ * @since 30.0.0
+ */
+ public function getDescription(): string {
+ return $this->l->t('Translate text from one language to another');
+ }
+
+ /**
+ * @return string
+ * @since 30.0.0
+ */
+ public function getId(): string {
+ return self::ID;
+ }
+
+ /**
+ * @return ShapeDescriptor[]
+ * @since 30.0.0
+ */
+ public function getInputShape(): array {
+ return [
+ 'input' => new ShapeDescriptor(
+ $this->l->t('Origin text'),
+ $this->l->t('The text to translate'),
+ EShapeType::Text
+ ),
+ 'origin_language' => new ShapeDescriptor(
+ $this->l->t('Origin language'),
+ $this->l->t('The language of the origin text'),
+ EShapeType::Enum
+ ),
+ 'target_language' => new ShapeDescriptor(
+ $this->l->t('Target language'),
+ $this->l->t('The desired language to translate the origin text in'),
+ EShapeType::Enum
+ ),
+ ];
+ }
+
+ /**
+ * @return ShapeDescriptor[]
+ * @since 30.0.0
+ */
+ public function getOutputShape(): array {
+ return [
+ 'output' => new ShapeDescriptor(
+ $this->l->t('Result'),
+ $this->l->t('The translated text'),
+ EShapeType::Text
+ ),
+ ];
+ }
+}