diff options
Diffstat (limited to 'lib')
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 + ), + ]; + } +} |