aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/composer/composer/autoload_classmap.php3
-rw-r--r--lib/composer/composer/autoload_static.php3
-rw-r--r--lib/l10n/es.js5
-rw-r--r--lib/l10n/es.json5
-rw-r--r--lib/l10n/et_EE.js14
-rw-r--r--lib/l10n/et_EE.json14
-rw-r--r--lib/l10n/lv.js12
-rw-r--r--lib/l10n/lv.json12
-rw-r--r--lib/private/AppConfig.php8
-rw-r--r--lib/private/AppFramework/Middleware/PublicShare/PublicShareMiddleware.php2
-rw-r--r--lib/private/Blurhash/Listener/GenerateBlurhashMetadata.php41
-rw-r--r--lib/private/Files/Cache/Scanner.php10
-rw-r--r--lib/private/Files/ObjectStore/ObjectStoreStorage.php18
-rw-r--r--lib/private/Files/ObjectStore/S3.php12
-rw-r--r--lib/private/Files/ObjectStore/S3ObjectTrait.php45
-rw-r--r--lib/private/Files/Utils/Scanner.php11
-rw-r--r--lib/private/Files/View.php3
-rw-r--r--lib/private/Preview/Generator.php63
-rw-r--r--lib/private/PreviewManager.php29
-rw-r--r--lib/private/Security/Bruteforce/Throttler.php29
-rw-r--r--lib/private/Setup.php2
-rw-r--r--lib/private/TaskProcessing/RemoveOldTasksBackgroundJob.php2
-rw-r--r--lib/public/Calendar/CalendarExportOptions.php68
-rw-r--r--lib/public/Calendar/ICalendarExport.php31
-rw-r--r--lib/public/Calendar/ICalendarIsEnabled.php24
-rw-r--r--lib/public/Files/ObjectStore/IObjectStoreMetaData.php11
-rw-r--r--lib/public/IPreview.php4
-rw-r--r--lib/public/Lock/LockedException.php12
28 files changed, 359 insertions, 134 deletions
diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php
index 5dbba982b15..b936e6d6968 100644
--- a/lib/composer/composer/autoload_classmap.php
+++ b/lib/composer/composer/autoload_classmap.php
@@ -191,6 +191,7 @@ return array(
'OCP\\Cache\\CappedMemoryCache' => $baseDir . '/lib/public/Cache/CappedMemoryCache.php',
'OCP\\Calendar\\BackendTemporarilyUnavailableException' => $baseDir . '/lib/public/Calendar/BackendTemporarilyUnavailableException.php',
'OCP\\Calendar\\CalendarEventStatus' => $baseDir . '/lib/public/Calendar/CalendarEventStatus.php',
+ 'OCP\\Calendar\\CalendarExportOptions' => $baseDir . '/lib/public/Calendar/CalendarExportOptions.php',
'OCP\\Calendar\\Events\\AbstractCalendarObjectEvent' => $baseDir . '/lib/public/Calendar/Events/AbstractCalendarObjectEvent.php',
'OCP\\Calendar\\Events\\CalendarObjectCreatedEvent' => $baseDir . '/lib/public/Calendar/Events/CalendarObjectCreatedEvent.php',
'OCP\\Calendar\\Events\\CalendarObjectDeletedEvent' => $baseDir . '/lib/public/Calendar/Events/CalendarObjectDeletedEvent.php',
@@ -202,6 +203,8 @@ return array(
'OCP\\Calendar\\IAvailabilityResult' => $baseDir . '/lib/public/Calendar/IAvailabilityResult.php',
'OCP\\Calendar\\ICalendar' => $baseDir . '/lib/public/Calendar/ICalendar.php',
'OCP\\Calendar\\ICalendarEventBuilder' => $baseDir . '/lib/public/Calendar/ICalendarEventBuilder.php',
+ 'OCP\\Calendar\\ICalendarExport' => $baseDir . '/lib/public/Calendar/ICalendarExport.php',
+ 'OCP\\Calendar\\ICalendarIsEnabled' => $baseDir . '/lib/public/Calendar/ICalendarIsEnabled.php',
'OCP\\Calendar\\ICalendarIsShared' => $baseDir . '/lib/public/Calendar/ICalendarIsShared.php',
'OCP\\Calendar\\ICalendarIsWritable' => $baseDir . '/lib/public/Calendar/ICalendarIsWritable.php',
'OCP\\Calendar\\ICalendarProvider' => $baseDir . '/lib/public/Calendar/ICalendarProvider.php',
diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php
index 585059cdd20..02284310e01 100644
--- a/lib/composer/composer/autoload_static.php
+++ b/lib/composer/composer/autoload_static.php
@@ -232,6 +232,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\Cache\\CappedMemoryCache' => __DIR__ . '/../../..' . '/lib/public/Cache/CappedMemoryCache.php',
'OCP\\Calendar\\BackendTemporarilyUnavailableException' => __DIR__ . '/../../..' . '/lib/public/Calendar/BackendTemporarilyUnavailableException.php',
'OCP\\Calendar\\CalendarEventStatus' => __DIR__ . '/../../..' . '/lib/public/Calendar/CalendarEventStatus.php',
+ 'OCP\\Calendar\\CalendarExportOptions' => __DIR__ . '/../../..' . '/lib/public/Calendar/CalendarExportOptions.php',
'OCP\\Calendar\\Events\\AbstractCalendarObjectEvent' => __DIR__ . '/../../..' . '/lib/public/Calendar/Events/AbstractCalendarObjectEvent.php',
'OCP\\Calendar\\Events\\CalendarObjectCreatedEvent' => __DIR__ . '/../../..' . '/lib/public/Calendar/Events/CalendarObjectCreatedEvent.php',
'OCP\\Calendar\\Events\\CalendarObjectDeletedEvent' => __DIR__ . '/../../..' . '/lib/public/Calendar/Events/CalendarObjectDeletedEvent.php',
@@ -243,6 +244,8 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\Calendar\\IAvailabilityResult' => __DIR__ . '/../../..' . '/lib/public/Calendar/IAvailabilityResult.php',
'OCP\\Calendar\\ICalendar' => __DIR__ . '/../../..' . '/lib/public/Calendar/ICalendar.php',
'OCP\\Calendar\\ICalendarEventBuilder' => __DIR__ . '/../../..' . '/lib/public/Calendar/ICalendarEventBuilder.php',
+ 'OCP\\Calendar\\ICalendarExport' => __DIR__ . '/../../..' . '/lib/public/Calendar/ICalendarExport.php',
+ 'OCP\\Calendar\\ICalendarIsEnabled' => __DIR__ . '/../../..' . '/lib/public/Calendar/ICalendarIsEnabled.php',
'OCP\\Calendar\\ICalendarIsShared' => __DIR__ . '/../../..' . '/lib/public/Calendar/ICalendarIsShared.php',
'OCP\\Calendar\\ICalendarIsWritable' => __DIR__ . '/../../..' . '/lib/public/Calendar/ICalendarIsWritable.php',
'OCP\\Calendar\\ICalendarProvider' => __DIR__ . '/../../..' . '/lib/public/Calendar/ICalendarProvider.php',
diff --git a/lib/l10n/es.js b/lib/l10n/es.js
index 94e52dbc724..a3cf61509ec 100644
--- a/lib/l10n/es.js
+++ b/lib/l10n/es.js
@@ -364,6 +364,11 @@ OC.L10N.register(
"How many images to generate" : "Cuántas imágenes se generarán",
"Output images" : "Imágenes de salida",
"The generated images" : "Las imágenes generadas",
+ "Generate speech" : "Generar dictado",
+ "Generate speech from a transcript" : "Generar dictado desde una transcripción",
+ "Write transcript that you want the assistant to generate speech from" : "Escriba la transcripción desde la que desea que el asistente genere un dictado",
+ "Output speech" : "Dictado de salida",
+ "The generated speech" : "El dictado generado",
"Free text to text prompt" : "Texto libre a prompt de texto",
"Runs an arbitrary prompt through a language model that returns a reply" : "Ejecuta un prompt arbitrario a través de un modelo de lenguaje que retorna una respuesta",
"Describe a task that you want the assistant to do or ask a question" : "Describa una tarea que quiere que el asistente realice, o, haga una pregunta",
diff --git a/lib/l10n/es.json b/lib/l10n/es.json
index 61f519db2a7..34d4db2eec4 100644
--- a/lib/l10n/es.json
+++ b/lib/l10n/es.json
@@ -362,6 +362,11 @@
"How many images to generate" : "Cuántas imágenes se generarán",
"Output images" : "Imágenes de salida",
"The generated images" : "Las imágenes generadas",
+ "Generate speech" : "Generar dictado",
+ "Generate speech from a transcript" : "Generar dictado desde una transcripción",
+ "Write transcript that you want the assistant to generate speech from" : "Escriba la transcripción desde la que desea que el asistente genere un dictado",
+ "Output speech" : "Dictado de salida",
+ "The generated speech" : "El dictado generado",
"Free text to text prompt" : "Texto libre a prompt de texto",
"Runs an arbitrary prompt through a language model that returns a reply" : "Ejecuta un prompt arbitrario a través de un modelo de lenguaje que retorna una respuesta",
"Describe a task that you want the assistant to do or ask a question" : "Describa una tarea que quiere que el asistente realice, o, haga una pregunta",
diff --git a/lib/l10n/et_EE.js b/lib/l10n/et_EE.js
index d882e0ee79e..e14744dc6ee 100644
--- a/lib/l10n/et_EE.js
+++ b/lib/l10n/et_EE.js
@@ -279,6 +279,7 @@ OC.L10N.register(
"Ensure there is a file called \"%1$s\" in the root of the data directory. It should have the content: \"%2$s\"" : "Palun taga, et andmete juurkaustas leidub fail „%1$s“, mille sisuks on „%2$s“",
"Action \"%s\" not supported or implemented." : "„%s“ tegevus pole toetatud või implementeeritud.",
"Parameters missing in order to complete the request. Missing Parameters: \"%s\"" : "Päringu lõpetamiseks on puudu järgmised parameetrid; „%s“",
+ "ID \"%1$s\" already used by cloud federation provider \"%2$s\"" : "„%1$s“ tunnus on juba kasutusel liitpilve serveris „%2$s“",
"Cloud Federation Provider with ID: \"%s\" does not exist." : "Liitpilve teenusepakkujat tunnusega „%s“ pole olemas.",
"Could not obtain lock type %d on \"%s\"." : "Ei suutnud hankida %d tüüpi lukustust asukohas „%s“.",
"Storage unauthorized. %s" : "Andmeruum on autoriseerimata. %s",
@@ -286,6 +287,16 @@ OC.L10N.register(
"Storage connection error. %s" : "Viga andmeruumi ühenduse loomisel. %s",
"Storage is temporarily not available" : "Salvestusruum pole ajutiselt kättesaadav",
"Storage connection timeout. %s" : "Aegumine andmeruumi ühenduse loomisel. %s",
+ "Transcribe audio" : "Kirjuta heli üles",
+ "Transcribe the things said in an audio" : "Kirjuta üles helifailis kuuldav jutt",
+ "Audio input" : "Helisisend",
+ "The audio to transcribe" : "Üleskirjutatav helifail",
+ "Transcription" : "Üleskirjutus",
+ "The transcribed text" : "Üleskirjutatud tekst",
+ "Context write" : "Kontekstuaalne kirjutamine",
+ "Writes text in a given style based on the provided source material." : "Kirjutab etteantud lähtematerjali lausel teksti üles.",
+ "Writing style" : "Kirjutamisstiil",
+ "Source material" : "Lähtematerjal",
"Generate image" : "Piltide loomine",
"Generate an image from a text prompt" : "Loo tekstisisendist pilt",
"Prompt" : "Sisendvorm",
@@ -299,6 +310,9 @@ OC.L10N.register(
"Write transcript that you want the assistant to generate speech from" : "Kirjuta üles see, mille alusel tahad Abilisel lasta koostada kõne",
"Output speech" : "Kõneväljund",
"The generated speech" : "Koostatud kõne",
+ "Chat" : "Vestle",
+ "Chat with the assistant" : "Vestle Abilisega",
+ "System prompt" : "Süsteemi viip",
"Generate a headline" : "Alapealkirja loomine",
"Generates a possible headline for a text." : "Võimaldab luua teksti põhjal kokkuvõtliku alapealkirja.",
"Original text" : "Lähtetekst",
diff --git a/lib/l10n/et_EE.json b/lib/l10n/et_EE.json
index 295c4b77855..f6c46ab05cb 100644
--- a/lib/l10n/et_EE.json
+++ b/lib/l10n/et_EE.json
@@ -277,6 +277,7 @@
"Ensure there is a file called \"%1$s\" in the root of the data directory. It should have the content: \"%2$s\"" : "Palun taga, et andmete juurkaustas leidub fail „%1$s“, mille sisuks on „%2$s“",
"Action \"%s\" not supported or implemented." : "„%s“ tegevus pole toetatud või implementeeritud.",
"Parameters missing in order to complete the request. Missing Parameters: \"%s\"" : "Päringu lõpetamiseks on puudu järgmised parameetrid; „%s“",
+ "ID \"%1$s\" already used by cloud federation provider \"%2$s\"" : "„%1$s“ tunnus on juba kasutusel liitpilve serveris „%2$s“",
"Cloud Federation Provider with ID: \"%s\" does not exist." : "Liitpilve teenusepakkujat tunnusega „%s“ pole olemas.",
"Could not obtain lock type %d on \"%s\"." : "Ei suutnud hankida %d tüüpi lukustust asukohas „%s“.",
"Storage unauthorized. %s" : "Andmeruum on autoriseerimata. %s",
@@ -284,6 +285,16 @@
"Storage connection error. %s" : "Viga andmeruumi ühenduse loomisel. %s",
"Storage is temporarily not available" : "Salvestusruum pole ajutiselt kättesaadav",
"Storage connection timeout. %s" : "Aegumine andmeruumi ühenduse loomisel. %s",
+ "Transcribe audio" : "Kirjuta heli üles",
+ "Transcribe the things said in an audio" : "Kirjuta üles helifailis kuuldav jutt",
+ "Audio input" : "Helisisend",
+ "The audio to transcribe" : "Üleskirjutatav helifail",
+ "Transcription" : "Üleskirjutus",
+ "The transcribed text" : "Üleskirjutatud tekst",
+ "Context write" : "Kontekstuaalne kirjutamine",
+ "Writes text in a given style based on the provided source material." : "Kirjutab etteantud lähtematerjali lausel teksti üles.",
+ "Writing style" : "Kirjutamisstiil",
+ "Source material" : "Lähtematerjal",
"Generate image" : "Piltide loomine",
"Generate an image from a text prompt" : "Loo tekstisisendist pilt",
"Prompt" : "Sisendvorm",
@@ -297,6 +308,9 @@
"Write transcript that you want the assistant to generate speech from" : "Kirjuta üles see, mille alusel tahad Abilisel lasta koostada kõne",
"Output speech" : "Kõneväljund",
"The generated speech" : "Koostatud kõne",
+ "Chat" : "Vestle",
+ "Chat with the assistant" : "Vestle Abilisega",
+ "System prompt" : "Süsteemi viip",
"Generate a headline" : "Alapealkirja loomine",
"Generates a possible headline for a text." : "Võimaldab luua teksti põhjal kokkuvõtliku alapealkirja.",
"Original text" : "Lähtetekst",
diff --git a/lib/l10n/lv.js b/lib/l10n/lv.js
index e5b635a2bd3..c6eeeaa756c 100644
--- a/lib/l10n/lv.js
+++ b/lib/l10n/lv.js
@@ -31,14 +31,14 @@ OC.L10N.register(
"View profile" : "Skatīt profilu",
"today" : "šodien",
"yesterday" : "vakar",
- "_%n day ago_::_%n days ago_" : ["%n dienas atpakaļ","%n dienas atpakaļ","%n dienām"],
+ "_%n day ago_::_%n days ago_" : ["pirms %n dienām","pirms %n dienas","pirms %n dienām"],
"last month" : "pagājušajā mēnesī",
- "_%n month ago_::_%n months ago_" : ["%n mēneši atpakaļ","%n mēneši atpakaļ","%n mēnešiem"],
+ "_%n month ago_::_%n months ago_" : ["pirms %n mēnešiem","pirms %n mēneša","pirms %n mēnešiem"],
"last year" : "gājušajā gadā",
- "_%n year ago_::_%n years ago_" : ["%n gadiem","%n gadiem","%n gadiem"],
- "_%n hour ago_::_%n hours ago_" : ["%n stundas atpakaļ","%n stundas atpakaļ","%n stundām"],
- "_%n minute ago_::_%n minutes ago_" : ["%n minūtes atpakaļ","%n minūtes atpakaļ","%n minūtēm"],
- "seconds ago" : "sekundēm",
+ "_%n year ago_::_%n years ago_" : ["pirms %n gadiem","pirms %n gada","pirms %n gadiem"],
+ "_%n hour ago_::_%n hours ago_" : ["pirms %n stundām","pirms %n stundas","pirms %n stundām"],
+ "_%n minute ago_::_%n minutes ago_" : ["pirms %n minūtēm","pirms %n minūtes","pirms %n minūtēm"],
+ "seconds ago" : "pirms vairākām sekundēm",
"Empty file" : "Tukša datne",
"File already exists" : "Datne jau pastāv",
"Filename contains at least one invalid character" : "Datnes nosaukums satur vismaz vienu nederīgu rakstzīmi",
diff --git a/lib/l10n/lv.json b/lib/l10n/lv.json
index f71090c5fd4..865458737df 100644
--- a/lib/l10n/lv.json
+++ b/lib/l10n/lv.json
@@ -29,14 +29,14 @@
"View profile" : "Skatīt profilu",
"today" : "šodien",
"yesterday" : "vakar",
- "_%n day ago_::_%n days ago_" : ["%n dienas atpakaļ","%n dienas atpakaļ","%n dienām"],
+ "_%n day ago_::_%n days ago_" : ["pirms %n dienām","pirms %n dienas","pirms %n dienām"],
"last month" : "pagājušajā mēnesī",
- "_%n month ago_::_%n months ago_" : ["%n mēneši atpakaļ","%n mēneši atpakaļ","%n mēnešiem"],
+ "_%n month ago_::_%n months ago_" : ["pirms %n mēnešiem","pirms %n mēneša","pirms %n mēnešiem"],
"last year" : "gājušajā gadā",
- "_%n year ago_::_%n years ago_" : ["%n gadiem","%n gadiem","%n gadiem"],
- "_%n hour ago_::_%n hours ago_" : ["%n stundas atpakaļ","%n stundas atpakaļ","%n stundām"],
- "_%n minute ago_::_%n minutes ago_" : ["%n minūtes atpakaļ","%n minūtes atpakaļ","%n minūtēm"],
- "seconds ago" : "sekundēm",
+ "_%n year ago_::_%n years ago_" : ["pirms %n gadiem","pirms %n gada","pirms %n gadiem"],
+ "_%n hour ago_::_%n hours ago_" : ["pirms %n stundām","pirms %n stundas","pirms %n stundām"],
+ "_%n minute ago_::_%n minutes ago_" : ["pirms %n minūtēm","pirms %n minūtes","pirms %n minūtēm"],
+ "seconds ago" : "pirms vairākām sekundēm",
"Empty file" : "Tukša datne",
"File already exists" : "Datne jau pastāv",
"Filename contains at least one invalid character" : "Datnes nosaukums satur vismaz vienu nederīgu rakstzīmi",
diff --git a/lib/private/AppConfig.php b/lib/private/AppConfig.php
index 092d37c3338..a8a6f689ffa 100644
--- a/lib/private/AppConfig.php
+++ b/lib/private/AppConfig.php
@@ -488,6 +488,14 @@ class AppConfig implements IAppConfig {
* @see VALUE_ARRAY
*/
public function getValueType(string $app, string $key, ?bool $lazy = null): int {
+ $type = self::VALUE_MIXED;
+ $ignorable = $lazy ?? false;
+ $this->matchAndApplyLexiconDefinition($app, $key, $ignorable, $type);
+ if ($type !== self::VALUE_MIXED) {
+ // a modified $type means config key is set in Lexicon
+ return $type;
+ }
+
$this->assertParams($app, $key);
$this->loadConfig($app, $lazy);
diff --git a/lib/private/AppFramework/Middleware/PublicShare/PublicShareMiddleware.php b/lib/private/AppFramework/Middleware/PublicShare/PublicShareMiddleware.php
index 2b3025fccff..b3040673d0f 100644
--- a/lib/private/AppFramework/Middleware/PublicShare/PublicShareMiddleware.php
+++ b/lib/private/AppFramework/Middleware/PublicShare/PublicShareMiddleware.php
@@ -120,7 +120,7 @@ class PublicShareMiddleware extends Middleware {
private function throttle($bruteforceProtectionAction, $token): void {
$ip = $this->request->getRemoteAddress();
- $this->throttler->sleepDelay($ip, $bruteforceProtectionAction);
+ $this->throttler->sleepDelayOrThrowOnMax($ip, $bruteforceProtectionAction);
$this->throttler->registerAttempt($bruteforceProtectionAction, $ip, ['token' => $token]);
}
}
diff --git a/lib/private/Blurhash/Listener/GenerateBlurhashMetadata.php b/lib/private/Blurhash/Listener/GenerateBlurhashMetadata.php
index 16f63594f19..982693bcfe8 100644
--- a/lib/private/Blurhash/Listener/GenerateBlurhashMetadata.php
+++ b/lib/private/Blurhash/Listener/GenerateBlurhashMetadata.php
@@ -19,6 +19,7 @@ use OCP\Files\NotPermittedException;
use OCP\FilesMetadata\AMetadataEvent;
use OCP\FilesMetadata\Event\MetadataBackgroundEvent;
use OCP\FilesMetadata\Event\MetadataLiveEvent;
+use OCP\IPreview;
use OCP\Lock\LockedException;
/**
@@ -27,11 +28,14 @@ use OCP\Lock\LockedException;
* @template-implements IEventListener<AMetadataEvent>
*/
class GenerateBlurhashMetadata implements IEventListener {
- private const RESIZE_BOXSIZE = 30;
-
private const COMPONENTS_X = 4;
private const COMPONENTS_Y = 3;
+ public function __construct(
+ private IPreview $preview,
+ ) {
+ }
+
/**
* @throws NotPermittedException
* @throws GenericFileException
@@ -64,7 +68,9 @@ class GenerateBlurhashMetadata implements IEventListener {
return;
}
- $image = $this->resizedImageFromFile($file);
+ $preview = $this->preview->getPreview($file, 64, 64, cacheResult: false);
+ $image = @imagecreatefromstring($preview->getContent());
+
if (!$image) {
return;
}
@@ -74,35 +80,6 @@ class GenerateBlurhashMetadata implements IEventListener {
}
/**
- * @param File $file
- *
- * @return GdImage|false
- * @throws GenericFileException
- * @throws NotPermittedException
- * @throws LockedException
- */
- private function resizedImageFromFile(File $file): GdImage|false {
- $image = @imagecreatefromstring($file->getContent());
- if ($image === false) {
- return false;
- }
-
- $currX = imagesx($image);
- $currY = imagesy($image);
-
- if ($currX > $currY) {
- $newX = self::RESIZE_BOXSIZE;
- $newY = intval($currY * $newX / $currX);
- } else {
- $newY = self::RESIZE_BOXSIZE;
- $newX = intval($currX * $newY / $currY);
- }
-
- $newImage = @imagescale($image, $newX, $newY);
- return ($newImage !== false) ? $newImage : $image;
- }
-
- /**
* @param GdImage $image
*
* @return string
diff --git a/lib/private/Files/Cache/Scanner.php b/lib/private/Files/Cache/Scanner.php
index 1fb408a0655..b067f70b8cb 100644
--- a/lib/private/Files/Cache/Scanner.php
+++ b/lib/private/Files/Cache/Scanner.php
@@ -210,7 +210,7 @@ class Scanner extends BasicEmitter implements IScanner {
* @var \OC\Files\Cache\CacheEntry $cacheData
*/
$newData = $this->array_diff_assoc_multi($data, $cacheData->getData());
-
+
// make it known to the caller that etag has been changed and needs propagation
if (isset($newData['etag'])) {
$data['etag_changed'] = true;
@@ -351,23 +351,23 @@ class Scanner extends BasicEmitter implements IScanner {
*
*/
protected function array_diff_assoc_multi(array $array1, array $array2) {
-
+
$result = [];
foreach ($array1 as $key => $value) {
-
+
// if $array2 doesn't have the same key, that's a result
if (!array_key_exists($key, $array2)) {
$result[$key] = $value;
continue;
}
-
+
// if $array2's value for the same key is different, that's a result
if ($array2[$key] !== $value && !is_array($value)) {
$result[$key] = $value;
continue;
}
-
+
if (is_array($value)) {
$nestedDiff = $this->array_diff_assoc_multi($value, $array2[$key]);
if (!empty($nestedDiff)) {
diff --git a/lib/private/Files/ObjectStore/ObjectStoreStorage.php b/lib/private/Files/ObjectStore/ObjectStoreStorage.php
index 16ef4e7de63..ebe87399ab4 100644
--- a/lib/private/Files/ObjectStore/ObjectStoreStorage.php
+++ b/lib/private/Files/ObjectStore/ObjectStoreStorage.php
@@ -22,6 +22,7 @@ use OCP\Files\FileInfo;
use OCP\Files\GenericFileException;
use OCP\Files\NotFoundException;
use OCP\Files\ObjectStore\IObjectStore;
+use OCP\Files\ObjectStore\IObjectStoreMetaData;
use OCP\Files\ObjectStore\IObjectStoreMultiPartUpload;
use OCP\Files\Storage\IChunkedFileWrite;
use OCP\Files\Storage\IStorage;
@@ -479,6 +480,11 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common implements IChunkedFil
$mimetypeDetector = \OC::$server->getMimeTypeDetector();
$mimetype = $mimetypeDetector->detectPath($path);
+ $metadata = [
+ 'mimetype' => $mimetype,
+ 'original-storage' => $this->getId(),
+ 'original-path' => $path,
+ ];
$stat['mimetype'] = $mimetype;
$stat['etag'] = $this->getETag($path);
@@ -507,13 +513,21 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common implements IChunkedFil
]);
$size = $writtenSize;
});
- $this->objectStore->writeObject($urn, $countStream, $mimetype);
+ if ($this->objectStore instanceof IObjectStoreMetaData) {
+ $this->objectStore->writeObjectWithMetaData($urn, $countStream, $metadata);
+ } else {
+ $this->objectStore->writeObject($urn, $countStream, $metadata['mimetype']);
+ }
if (is_resource($countStream)) {
fclose($countStream);
}
$stat['size'] = $size;
} else {
- $this->objectStore->writeObject($urn, $stream, $mimetype);
+ if ($this->objectStore instanceof IObjectStoreMetaData) {
+ $this->objectStore->writeObjectWithMetaData($urn, $stream, $metadata);
+ } else {
+ $this->objectStore->writeObject($urn, $stream, $metadata['mimetype']);
+ }
if (is_resource($stream)) {
fclose($stream);
}
diff --git a/lib/private/Files/ObjectStore/S3.php b/lib/private/Files/ObjectStore/S3.php
index e970fb6ac14..23c061db174 100644
--- a/lib/private/Files/ObjectStore/S3.php
+++ b/lib/private/Files/ObjectStore/S3.php
@@ -95,6 +95,16 @@ class S3 implements IObjectStore, IObjectStoreMultiPartUpload, IObjectStoreMetaD
]);
}
+ private function parseS3Metadata(array $metadata): array {
+ $result = [];
+ foreach ($metadata as $key => $value) {
+ if (str_starts_with($key, 'x-amz-meta-')) {
+ $result[substr($key, strlen('x-amz-meta-'))] = $value;
+ }
+ }
+ return $result;
+ }
+
public function getObjectMetaData(string $urn): array {
$object = $this->getConnection()->headObject([
'Bucket' => $this->bucket,
@@ -104,7 +114,7 @@ class S3 implements IObjectStore, IObjectStoreMultiPartUpload, IObjectStoreMetaD
'mtime' => $object['LastModified'],
'etag' => trim($object['ETag'], '"'),
'size' => (int)($object['Size'] ?? $object['ContentLength']),
- ];
+ ] + $this->parseS3Metadata($object['Metadata'] ?? []);
}
public function listObjects(string $prefix = ''): \Iterator {
diff --git a/lib/private/Files/ObjectStore/S3ObjectTrait.php b/lib/private/Files/ObjectStore/S3ObjectTrait.php
index 9d7cfa644e6..61e8158b863 100644
--- a/lib/private/Files/ObjectStore/S3ObjectTrait.php
+++ b/lib/private/Files/ObjectStore/S3ObjectTrait.php
@@ -77,22 +77,32 @@ trait S3ObjectTrait {
return $fh;
}
+ private function buildS3Metadata(array $metadata): array {
+ $result = [];
+ foreach ($metadata as $key => $value) {
+ $result['x-amz-meta-' . $key] = $value;
+ }
+ return $result;
+ }
/**
* Single object put helper
*
* @param string $urn the unified resource name used to identify the object
* @param StreamInterface $stream stream with the data to write
- * @param string|null $mimetype the mimetype to set for the remove object @since 22.0.0
+ * @param array $metaData the metadata to set for the object
* @throws \Exception when something goes wrong, message will be logged
*/
- protected function writeSingle(string $urn, StreamInterface $stream, ?string $mimetype = null): void {
+ protected function writeSingle(string $urn, StreamInterface $stream, array $metaData): void {
+ $mimetype = $metaData['mimetype'] ?? null;
+ unset($metaData['mimetype']);
$this->getConnection()->putObject([
'Bucket' => $this->bucket,
'Key' => $urn,
'Body' => $stream,
'ACL' => 'private',
'ContentType' => $mimetype,
+ 'Metadata' => $this->buildS3Metadata($metaData),
'StorageClass' => $this->storageClass,
] + $this->getSSECParameters());
}
@@ -103,10 +113,12 @@ trait S3ObjectTrait {
*
* @param string $urn the unified resource name used to identify the object
* @param StreamInterface $stream stream with the data to write
- * @param string|null $mimetype the mimetype to set for the remove object
+ * @param array $metaData the metadata to set for the object
* @throws \Exception when something goes wrong, message will be logged
*/
- protected function writeMultiPart(string $urn, StreamInterface $stream, ?string $mimetype = null): void {
+ protected function writeMultiPart(string $urn, StreamInterface $stream, array $metaData): void {
+ $mimetype = $metaData['mimetype'] ?? null;
+ unset($metaData['mimetype']);
$uploader = new MultipartUploader($this->getConnection(), $stream, [
'bucket' => $this->bucket,
'concurrency' => $this->concurrency,
@@ -114,6 +126,7 @@ trait S3ObjectTrait {
'part_size' => $this->uploadPartSize,
'params' => [
'ContentType' => $mimetype,
+ 'Metadata' => $this->buildS3Metadata($metaData),
'StorageClass' => $this->storageClass,
] + $this->getSSECParameters(),
]);
@@ -131,15 +144,15 @@ trait S3ObjectTrait {
}
}
-
- /**
- * @param string $urn the unified resource name used to identify the object
- * @param resource $stream stream with the data to write
- * @param string|null $mimetype the mimetype to set for the remove object @since 22.0.0
- * @throws \Exception when something goes wrong, message will be logged
- * @since 7.0.0
- */
public function writeObject($urn, $stream, ?string $mimetype = null) {
+ $metaData = [];
+ if ($mimetype) {
+ $metaData['mimetype'] = $mimetype;
+ }
+ $this->writeObjectWithMetaData($urn, $stream, $metaData);
+ }
+
+ public function writeObjectWithMetaData(string $urn, $stream, array $metaData): void {
$canSeek = fseek($stream, 0, SEEK_CUR) === 0;
$psrStream = Utils::streamFor($stream);
@@ -154,16 +167,16 @@ trait S3ObjectTrait {
$buffer->seek(0);
if ($buffer->getSize() < $this->putSizeLimit) {
// buffer is fully seekable, so use it directly for the small upload
- $this->writeSingle($urn, $buffer, $mimetype);
+ $this->writeSingle($urn, $buffer, $metaData);
} else {
$loadStream = new Psr7\AppendStream([$buffer, $psrStream]);
- $this->writeMultiPart($urn, $loadStream, $mimetype);
+ $this->writeMultiPart($urn, $loadStream, $metaData);
}
} else {
if ($size < $this->putSizeLimit) {
- $this->writeSingle($urn, $psrStream, $mimetype);
+ $this->writeSingle($urn, $psrStream, $metaData);
} else {
- $this->writeMultiPart($urn, $psrStream, $mimetype);
+ $this->writeMultiPart($urn, $psrStream, $metaData);
}
}
$psrStream->close();
diff --git a/lib/private/Files/Utils/Scanner.php b/lib/private/Files/Utils/Scanner.php
index 4d94629443f..e9ed351b27b 100644
--- a/lib/private/Files/Utils/Scanner.php
+++ b/lib/private/Files/Utils/Scanner.php
@@ -29,6 +29,7 @@ use OCP\Files\Storage\IStorage;
use OCP\Files\StorageNotAvailableException;
use OCP\IDBConnection;
use OCP\Lock\ILockingProvider;
+use OCP\Lock\LockedException;
use Psr\Log\LoggerInterface;
/**
@@ -260,7 +261,15 @@ class Scanner extends PublicEmitter {
try {
$propagator = $storage->getPropagator();
$propagator->beginBatch();
- $scanner->scan($relativePath, $recursive, \OC\Files\Cache\Scanner::REUSE_ETAG | \OC\Files\Cache\Scanner::REUSE_SIZE);
+ try {
+ $scanner->scan($relativePath, $recursive, \OC\Files\Cache\Scanner::REUSE_ETAG | \OC\Files\Cache\Scanner::REUSE_SIZE);
+ } catch (LockedException $e) {
+ if (is_string($e->getReadablePath()) && str_starts_with($e->getReadablePath(), 'scanner::')) {
+ throw new LockedException("scanner::$dir", $e, $e->getExistingLock());
+ } else {
+ throw $e;
+ }
+ }
$cache = $storage->getCache();
if ($cache instanceof Cache) {
// only re-calculate for the root folder we scanned, anything below that is taken care of by the scanner
diff --git a/lib/private/Files/View.php b/lib/private/Files/View.php
index bbad24d3e43..f17ced1611b 100644
--- a/lib/private/Files/View.php
+++ b/lib/private/Files/View.php
@@ -1466,8 +1466,7 @@ class View {
public function addSubMounts(FileInfo $info, $extOnly = false): void {
$mounts = Filesystem::getMountManager()->findIn($info->getPath());
$info->setSubMounts(array_filter($mounts, function (IMountPoint $mount) use ($extOnly) {
- $subStorage = $mount->getStorage();
- return !($extOnly && $subStorage instanceof \OCA\Files_Sharing\SharedStorage);
+ return !($extOnly && $mount instanceof SharedMount);
}));
}
diff --git a/lib/private/Preview/Generator.php b/lib/private/Preview/Generator.php
index b95971d0085..00fc3b43d61 100644
--- a/lib/private/Preview/Generator.php
+++ b/lib/private/Preview/Generator.php
@@ -12,6 +12,7 @@ use OCP\Files\IAppData;
use OCP\Files\InvalidPathException;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
+use OCP\Files\SimpleFS\InMemoryFile;
use OCP\Files\SimpleFS\ISimpleFile;
use OCP\Files\SimpleFS\ISimpleFolder;
use OCP\IConfig;
@@ -43,17 +44,19 @@ class Generator {
* The cache is searched first and if nothing usable was found then a preview is
* generated by one of the providers
*
- * @param File $file
- * @param int $width
- * @param int $height
- * @param bool $crop
- * @param string $mode
- * @param string|null $mimeType
* @return ISimpleFile
* @throws NotFoundException
* @throws \InvalidArgumentException if the preview would be invalid (in case the original image is invalid)
*/
- public function getPreview(File $file, $width = -1, $height = -1, $crop = false, $mode = IPreview::MODE_FILL, $mimeType = null) {
+ public function getPreview(
+ File $file,
+ int $width = -1,
+ int $height = -1,
+ bool $crop = false,
+ string $mode = IPreview::MODE_FILL,
+ ?string $mimeType = null,
+ bool $cacheResult = true,
+ ): ISimpleFile {
$specification = [
'width' => $width,
'height' => $height,
@@ -78,23 +81,19 @@ class Generator {
'mode' => $mode,
'mimeType' => $mimeType,
]);
-
+
// since we only ask for one preview, and the generate method return the last one it created, it returns the one we want
- return $this->generatePreviews($file, [$specification], $mimeType);
+ return $this->generatePreviews($file, [$specification], $mimeType, $cacheResult);
}
/**
* Generates previews of a file
*
- * @param File $file
- * @param non-empty-array $specifications
- * @param string $mimeType
- * @return ISimpleFile the last preview that was generated
* @throws NotFoundException
* @throws \InvalidArgumentException if the preview would be invalid (in case the original image is invalid)
*/
- public function generatePreviews(File $file, array $specifications, $mimeType = null) {
+ public function generatePreviews(File $file, array $specifications, ?string $mimeType = null, bool $cacheResult = true): ISimpleFile {
//Make sure that we can read the file
if (!$file->isReadable()) {
$this->logger->warning('Cannot read file: {path}, skipping preview generation.', ['path' => $file->getPath()]);
@@ -167,7 +166,7 @@ class Generator {
}
$this->logger->warning('Cached preview not found for file {path}, generating a new preview.', ['path' => $file->getPath()]);
- $preview = $this->generatePreview($previewFolder, $maxPreviewImage, $width, $height, $crop, $maxWidth, $maxHeight, $previewVersion);
+ $preview = $this->generatePreview($previewFolder, $maxPreviewImage, $width, $height, $crop, $maxWidth, $maxHeight, $previewVersion, $cacheResult);
// New file, augment our array
$previewFiles[] = $preview;
}
@@ -351,11 +350,10 @@ class Generator {
$path = $this->generatePath($preview->width(), $preview->height(), $crop, $max, $preview->dataMimeType(), $prefix);
try {
- $file = $previewFolder->newFile($path);
if ($preview instanceof IStreamImage) {
- $file->putContent($preview->resource());
+ return $previewFolder->newFile($path, $preview->resource());
} else {
- $file->putContent($preview->data());
+ return $previewFolder->newFile($path, $preview->data());
}
} catch (NotPermittedException $e) {
throw new NotFoundException();
@@ -490,19 +488,20 @@ class Generator {
}
/**
- * @param ISimpleFolder $previewFolder
- * @param ISimpleFile $maxPreview
- * @param int $width
- * @param int $height
- * @param bool $crop
- * @param int $maxWidth
- * @param int $maxHeight
- * @param string $prefix
- * @return ISimpleFile
* @throws NotFoundException
* @throws \InvalidArgumentException if the preview would be invalid (in case the original image is invalid)
*/
- private function generatePreview(ISimpleFolder $previewFolder, IImage $maxPreview, $width, $height, $crop, $maxWidth, $maxHeight, $prefix) {
+ private function generatePreview(
+ ISimpleFolder $previewFolder,
+ IImage $maxPreview,
+ int $width,
+ int $height,
+ bool $crop,
+ int $maxWidth,
+ int $maxHeight,
+ string $prefix,
+ bool $cacheResult,
+ ): ISimpleFile {
$preview = $maxPreview;
if (!$preview->valid()) {
throw new \InvalidArgumentException('Failed to generate preview, failed to load image');
@@ -539,12 +538,14 @@ class Generator {
$path = $this->generatePath($width, $height, $crop, false, $preview->dataMimeType(), $prefix);
try {
- $file = $previewFolder->newFile($path);
- $file->putContent($preview->data());
+ if ($cacheResult) {
+ return $previewFolder->newFile($path, $preview->data());
+ } else {
+ return new InMemoryFile($path, $preview->data());
+ }
} catch (NotPermittedException $e) {
throw new NotFoundException();
}
-
return $file;
}
diff --git a/lib/private/PreviewManager.php b/lib/private/PreviewManager.php
index bc77dcfe483..1f618dab22d 100644
--- a/lib/private/PreviewManager.php
+++ b/lib/private/PreviewManager.php
@@ -145,29 +145,20 @@ class PreviewManager implements IPreview {
return $this->generator;
}
- /**
- * Returns a preview of a file
- *
- * The cache is searched first and if nothing usable was found then a preview is
- * generated by one of the providers
- *
- * @param File $file
- * @param int $width
- * @param int $height
- * @param bool $crop
- * @param string $mode
- * @param string $mimeType
- * @return ISimpleFile
- * @throws NotFoundException
- * @throws \InvalidArgumentException if the preview would be invalid (in case the original image is invalid)
- * @since 11.0.0 - \InvalidArgumentException was added in 12.0.0
- */
- public function getPreview(File $file, $width = -1, $height = -1, $crop = false, $mode = IPreview::MODE_FILL, $mimeType = null) {
+ public function getPreview(
+ File $file,
+ $width = -1,
+ $height = -1,
+ $crop = false,
+ $mode = IPreview::MODE_FILL,
+ $mimeType = null,
+ bool $cacheResult = true,
+ ): ISimpleFile {
$this->throwIfPreviewsDisabled();
$previewConcurrency = $this->getGenerator()->getNumConcurrentPreviews('preview_concurrency_all');
$sem = Generator::guardWithSemaphore(Generator::SEMAPHORE_ID_ALL, $previewConcurrency);
try {
- $preview = $this->getGenerator()->getPreview($file, $width, $height, $crop, $mode, $mimeType);
+ $preview = $this->getGenerator()->getPreview($file, $width, $height, $crop, $mode, $mimeType, $cacheResult);
} finally {
Generator::unguardWithSemaphore($sem);
}
diff --git a/lib/private/Security/Bruteforce/Throttler.php b/lib/private/Security/Bruteforce/Throttler.php
index 21d50848641..065f720ba72 100644
--- a/lib/private/Security/Bruteforce/Throttler.php
+++ b/lib/private/Security/Bruteforce/Throttler.php
@@ -127,6 +127,13 @@ class Throttler implements IThrottler {
*/
public function getDelay(string $ip, string $action = ''): int {
$attempts = $this->getAttempts($ip, $action);
+ return $this->calculateDelay($attempts);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function calculateDelay(int $attempts): int {
if ($attempts === 0) {
return 0;
}
@@ -199,25 +206,29 @@ class Throttler implements IThrottler {
* {@inheritDoc}
*/
public function sleepDelayOrThrowOnMax(string $ip, string $action = ''): int {
- $delay = $this->getDelay($ip, $action);
- if (($delay === self::MAX_DELAY_MS) && $this->getAttempts($ip, $action, 0.5) > $this->config->getSystemValueInt('auth.bruteforce.max-attempts', self::MAX_ATTEMPTS)) {
- $this->logger->info('IP address blocked because it reached the maximum failed attempts in the last 30 minutes [action: {action}, ip: {ip}]', [
+ $attempts = $this->getAttempts($ip, $action, 0.5);
+ if ($attempts > $this->config->getSystemValueInt('auth.bruteforce.max-attempts', self::MAX_ATTEMPTS)) {
+ $this->logger->info('IP address blocked because it reached the maximum failed attempts in the last 30 minutes [action: {action}, attempts: {attempts}, ip: {ip}]', [
'action' => $action,
'ip' => $ip,
+ 'attempts' => $attempts,
]);
// If the ip made too many attempts within the last 30 mins we don't execute anymore
throw new MaxDelayReached('Reached maximum delay');
}
- if ($delay > 100) {
- $this->logger->info('IP address throttled because it reached the attempts limit in the last 30 minutes [action: {action}, delay: {delay}, ip: {ip}]', [
+
+ $attempts = $this->getAttempts($ip, $action);
+ if ($attempts > 10) {
+ $this->logger->info('IP address throttled because it reached the attempts limit in the last 12 hours [action: {action}, attempts: {attempts}, ip: {ip}]', [
'action' => $action,
'ip' => $ip,
- 'delay' => $delay,
+ 'attempts' => $attempts,
]);
}
- if (!$this->config->getSystemValueBool('auth.bruteforce.protection.testing')) {
- usleep($delay * 1000);
+ if ($attempts > 0) {
+ return $this->calculateDelay($attempts);
}
- return $delay;
+
+ return 0;
}
}
diff --git a/lib/private/Setup.php b/lib/private/Setup.php
index 6d3021aff71..959797fb962 100644
--- a/lib/private/Setup.php
+++ b/lib/private/Setup.php
@@ -14,6 +14,7 @@ use Exception;
use InvalidArgumentException;
use OC\Authentication\Token\PublicKeyTokenProvider;
use OC\Authentication\Token\TokenCleanupJob;
+use OC\Core\BackgroundJobs\GenerateMetadataJob;
use OC\Log\Rotate;
use OC\Preview\BackgroundCleanupJob;
use OC\TextProcessing\RemoveOldTasksBackgroundJob;
@@ -495,6 +496,7 @@ class Setup {
$jobList->add(BackgroundCleanupJob::class);
$jobList->add(RemoveOldTasksBackgroundJob::class);
$jobList->add(CleanupDeletedUsers::class);
+ $jobList->add(GenerateMetadataJob::class);
}
/**
diff --git a/lib/private/TaskProcessing/RemoveOldTasksBackgroundJob.php b/lib/private/TaskProcessing/RemoveOldTasksBackgroundJob.php
index c6f26e3aa8b..42d073a024d 100644
--- a/lib/private/TaskProcessing/RemoveOldTasksBackgroundJob.php
+++ b/lib/private/TaskProcessing/RemoveOldTasksBackgroundJob.php
@@ -16,7 +16,7 @@ use OCP\Files\SimpleFS\ISimpleFolder;
use Psr\Log\LoggerInterface;
class RemoveOldTasksBackgroundJob extends TimedJob {
- public const MAX_TASK_AGE_SECONDS = 60 * 60 * 24 * 7 * 4; // 4 weeks
+ public const MAX_TASK_AGE_SECONDS = 60 * 60 * 24 * 30 * 4; // 4 months
private \OCP\Files\IAppData $appData;
public function __construct(
diff --git a/lib/public/Calendar/CalendarExportOptions.php b/lib/public/Calendar/CalendarExportOptions.php
new file mode 100644
index 00000000000..bf21dd85ae4
--- /dev/null
+++ b/lib/public/Calendar/CalendarExportOptions.php
@@ -0,0 +1,68 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCP\Calendar;
+
+/**
+ * Calendar Export Options
+ *
+ * @since 32.0.0
+ */
+final class CalendarExportOptions {
+
+ /** @var 'ical'|'jcal'|'xcal' */
+ private string $format = 'ical';
+ private ?string $rangeStart = null;
+ private ?int $rangeCount = null;
+
+ /**
+ * Gets the export format
+ *
+ * @return 'ical'|'jcal'|'xcal' (defaults to ical)
+ */
+ public function getFormat(): string {
+ return $this->format;
+ }
+
+ /**
+ * Sets the export format
+ *
+ * @param 'ical'|'jcal'|'xcal' $format
+ */
+ public function setFormat(string $format): void {
+ $this->format = $format;
+ }
+
+ /**
+ * Gets the start of the range to export
+ */
+ public function getRangeStart(): ?string {
+ return $this->rangeStart;
+ }
+
+ /**
+ * Sets the start of the range to export
+ */
+ public function setRangeStart(?string $rangeStart): void {
+ $this->rangeStart = $rangeStart;
+ }
+
+ /**
+ * Gets the number of objects to export
+ */
+ public function getRangeCount(): ?int {
+ return $this->rangeCount;
+ }
+
+ /**
+ * Sets the number of objects to export
+ */
+ public function setRangeCount(?int $rangeCount): void {
+ $this->rangeCount = $rangeCount;
+ }
+}
diff --git a/lib/public/Calendar/ICalendarExport.php b/lib/public/Calendar/ICalendarExport.php
new file mode 100644
index 00000000000..61b286e1668
--- /dev/null
+++ b/lib/public/Calendar/ICalendarExport.php
@@ -0,0 +1,31 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCP\Calendar;
+
+use Generator;
+
+/**
+ * ICalendar Interface Extension to export data
+ *
+ * @since 32.0.0
+ */
+interface ICalendarExport {
+
+ /**
+ * Export objects
+ *
+ * @since 32.0.0
+ *
+ * @param CalendarExportOptions|null $options
+ *
+ * @return Generator<\Sabre\VObject\Component\VCalendar>
+ */
+ public function export(?CalendarExportOptions $options): Generator;
+
+}
diff --git a/lib/public/Calendar/ICalendarIsEnabled.php b/lib/public/Calendar/ICalendarIsEnabled.php
new file mode 100644
index 00000000000..868159d208f
--- /dev/null
+++ b/lib/public/Calendar/ICalendarIsEnabled.php
@@ -0,0 +1,24 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCP\Calendar;
+
+/**
+ * ICalendar Interface Extension
+ *
+ * @since 32.0.0
+ */
+interface ICalendarIsEnabled {
+
+ /**
+ * Indicates whether the calendar is enabled
+ *
+ * @since 32.0.0
+ */
+ public function isEnabled(): bool;
+
+}
diff --git a/lib/public/Files/ObjectStore/IObjectStoreMetaData.php b/lib/public/Files/ObjectStore/IObjectStoreMetaData.php
index 8359e83f573..9683873be36 100644
--- a/lib/public/Files/ObjectStore/IObjectStoreMetaData.php
+++ b/lib/public/Files/ObjectStore/IObjectStoreMetaData.php
@@ -9,7 +9,7 @@ namespace OCP\Files\ObjectStore;
/**
* Interface IObjectStoreMetaData
*
- * @psalm-type ObjectMetaData = array{mtime?: \DateTime, etag?: string, size?: int, mimetype?: string, filename?: string}
+ * @psalm-type ObjectMetaData = array{mtime?: \DateTime, etag?: string, size?: int, mimetype?: string, filename?: string, original-path?: string, original-storage?: string}
*
* @since 32.0.0
*/
@@ -35,4 +35,13 @@ interface IObjectStoreMetaData {
* @since 32.0.0
*/
public function listObjects(string $prefix = ''): \Iterator;
+
+ /**
+ * @param string $urn the unified resource name used to identify the object
+ * @param resource $stream stream with the data to write
+ * @param ObjectMetaData $metaData the metadata to set for the object
+ * @throws \Exception when something goes wrong, message will be logged
+ * @since 32.0.0
+ */
+ public function writeObjectWithMetaData(string $urn, $stream, array $metaData): void;
}
diff --git a/lib/public/IPreview.php b/lib/public/IPreview.php
index 6ab4af1b7ca..5a2bcde69f1 100644
--- a/lib/public/IPreview.php
+++ b/lib/public/IPreview.php
@@ -71,12 +71,14 @@ interface IPreview {
* @param bool $crop
* @param string $mode
* @param string $mimeType To force a given mimetype for the file (files_versions needs this)
+ * @param bool $cacheResult Whether or not to cache the preview on the filesystem. Default to true. Can be useful to set to false to limit the amount of stored previews.
* @return ISimpleFile
* @throws NotFoundException
* @throws \InvalidArgumentException if the preview would be invalid (in case the original image is invalid)
* @since 11.0.0 - \InvalidArgumentException was added in 12.0.0
+ * @since 32.0.0 - getPreview($cacheResult) added the $cacheResult argument to the signature
*/
- public function getPreview(File $file, $width = -1, $height = -1, $crop = false, $mode = IPreview::MODE_FILL, $mimeType = null);
+ public function getPreview(File $file, $width = -1, $height = -1, $crop = false, $mode = IPreview::MODE_FILL, $mimeType = null, bool $cacheResult = true);
/**
* Returns true if the passed mime type is supported
diff --git a/lib/public/Lock/LockedException.php b/lib/public/Lock/LockedException.php
index ce41d2d7242..799886a2dbb 100644
--- a/lib/public/Lock/LockedException.php
+++ b/lib/public/Lock/LockedException.php
@@ -24,6 +24,8 @@ class LockedException extends \Exception {
/** @var string|null */
private $existingLock;
+ private ?string $readablePath;
+
/**
* LockedException constructor.
*
@@ -34,6 +36,7 @@ class LockedException extends \Exception {
* @since 8.1.0
*/
public function __construct(string $path, ?\Exception $previous = null, ?string $existingLock = null, ?string $readablePath = null) {
+ $this->readablePath = $readablePath;
if ($readablePath) {
$message = "\"$path\"(\"$readablePath\") is locked";
} else {
@@ -62,4 +65,13 @@ class LockedException extends \Exception {
public function getExistingLock(): ?string {
return $this->existingLock;
}
+
+ /**
+ * @return ?string
+ * @since 32.0.0
+ */
+ public function getReadablePath(): ?string {
+ return $this->readablePath;
+ }
+
}