Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/server.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/composer/composer/autoload_classmap.php2
-rw-r--r--lib/composer/composer/autoload_static.php2
-rw-r--r--lib/l10n/es.js18
-rw-r--r--lib/l10n/es.json18
-rw-r--r--lib/private/Accounts/AccountManager.php11
-rw-r--r--lib/private/Console/TimestampFormatter.php5
-rw-r--r--lib/private/DB/BacktraceDebugStack.php34
-rw-r--r--lib/private/DB/Connection.php3
-rw-r--r--lib/private/DB/DbDataCollector.php5
-rw-r--r--lib/private/DB/MigrationService.php39
-rw-r--r--lib/private/Files/Cache/Storage.php17
-rw-r--r--lib/private/Files/Cache/StorageGlobal.php32
-rw-r--r--lib/private/Files/Config/MountProviderCollection.php4
-rw-r--r--lib/private/Files/FileInfo.php2
-rw-r--r--lib/private/Files/Mount/Manager.php19
-rw-r--r--lib/private/Files/Node/Folder.php82
-rw-r--r--lib/private/Files/Node/LazyFolder.php12
-rw-r--r--lib/private/Files/Node/LazyRoot.php4
-rw-r--r--lib/private/Files/Node/LazyUserFolder.php26
-rw-r--r--lib/private/Files/Node/Node.php40
-rw-r--r--lib/private/Files/Node/Root.php85
-rw-r--r--lib/private/Files/SetupManager.php102
-rw-r--r--lib/private/Files/Utils/PathHelper.php71
-rw-r--r--lib/private/Files/View.php198
-rw-r--r--lib/private/Group/Group.php3
-rw-r--r--lib/private/Preview/Generator.php9
-rw-r--r--lib/private/Preview/Imaginary.php24
-rw-r--r--lib/private/Share20/Manager.php14
-rw-r--r--lib/private/Streamer.php1
-rw-r--r--lib/private/TemplateLayout.php13
-rw-r--r--lib/private/legacy/OC_App.php12
-rw-r--r--lib/private/legacy/OC_Files.php1
-rw-r--r--lib/private/legacy/OC_Helper.php4
-rw-r--r--lib/private/legacy/OC_Util.php7
-rw-r--r--lib/public/Files/Config/IMountProviderCollection.php4
-rw-r--r--lib/public/Files/IRootFolder.php13
-rw-r--r--lib/public/Share/IManager.php4
-rw-r--r--lib/public/User/Events/UserLiveStatusEvent.php31
-rw-r--r--lib/public/Util.php8
39 files changed, 657 insertions, 322 deletions
diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php
index 9243f0c6edb..60814aa4b9a 100644
--- a/lib/composer/composer/autoload_classmap.php
+++ b/lib/composer/composer/autoload_classmap.php
@@ -1027,6 +1027,7 @@ return array(
'OC\\DB\\AdapterOCI8' => $baseDir . '/lib/private/DB/AdapterOCI8.php',
'OC\\DB\\AdapterPgSql' => $baseDir . '/lib/private/DB/AdapterPgSql.php',
'OC\\DB\\AdapterSqlite' => $baseDir . '/lib/private/DB/AdapterSqlite.php',
+ 'OC\\DB\\BacktraceDebugStack' => $baseDir . '/lib/private/DB/BacktraceDebugStack.php',
'OC\\DB\\Connection' => $baseDir . '/lib/private/DB/Connection.php',
'OC\\DB\\ConnectionAdapter' => $baseDir . '/lib/private/DB/ConnectionAdapter.php',
'OC\\DB\\ConnectionFactory' => $baseDir . '/lib/private/DB/ConnectionFactory.php',
@@ -1210,6 +1211,7 @@ return array(
'OC\\Files\\Type\\Detection' => $baseDir . '/lib/private/Files/Type/Detection.php',
'OC\\Files\\Type\\Loader' => $baseDir . '/lib/private/Files/Type/Loader.php',
'OC\\Files\\Type\\TemplateManager' => $baseDir . '/lib/private/Files/Type/TemplateManager.php',
+ 'OC\\Files\\Utils\\PathHelper' => $baseDir . '/lib/private/Files/Utils/PathHelper.php',
'OC\\Files\\Utils\\Scanner' => $baseDir . '/lib/private/Files/Utils/Scanner.php',
'OC\\Files\\View' => $baseDir . '/lib/private/Files/View.php',
'OC\\ForbiddenException' => $baseDir . '/lib/private/ForbiddenException.php',
diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php
index 2c59850a4f0..47dbd7e9ef5 100644
--- a/lib/composer/composer/autoload_static.php
+++ b/lib/composer/composer/autoload_static.php
@@ -1056,6 +1056,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OC\\DB\\AdapterOCI8' => __DIR__ . '/../../..' . '/lib/private/DB/AdapterOCI8.php',
'OC\\DB\\AdapterPgSql' => __DIR__ . '/../../..' . '/lib/private/DB/AdapterPgSql.php',
'OC\\DB\\AdapterSqlite' => __DIR__ . '/../../..' . '/lib/private/DB/AdapterSqlite.php',
+ 'OC\\DB\\BacktraceDebugStack' => __DIR__ . '/../../..' . '/lib/private/DB/BacktraceDebugStack.php',
'OC\\DB\\Connection' => __DIR__ . '/../../..' . '/lib/private/DB/Connection.php',
'OC\\DB\\ConnectionAdapter' => __DIR__ . '/../../..' . '/lib/private/DB/ConnectionAdapter.php',
'OC\\DB\\ConnectionFactory' => __DIR__ . '/../../..' . '/lib/private/DB/ConnectionFactory.php',
@@ -1239,6 +1240,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OC\\Files\\Type\\Detection' => __DIR__ . '/../../..' . '/lib/private/Files/Type/Detection.php',
'OC\\Files\\Type\\Loader' => __DIR__ . '/../../..' . '/lib/private/Files/Type/Loader.php',
'OC\\Files\\Type\\TemplateManager' => __DIR__ . '/../../..' . '/lib/private/Files/Type/TemplateManager.php',
+ 'OC\\Files\\Utils\\PathHelper' => __DIR__ . '/../../..' . '/lib/private/Files/Utils/PathHelper.php',
'OC\\Files\\Utils\\Scanner' => __DIR__ . '/../../..' . '/lib/private/Files/Utils/Scanner.php',
'OC\\Files\\View' => __DIR__ . '/../../..' . '/lib/private/Files/View.php',
'OC\\ForbiddenException' => __DIR__ . '/../../..' . '/lib/private/ForbiddenException.php',
diff --git a/lib/l10n/es.js b/lib/l10n/es.js
index 1bdb387aba3..be4367a2335 100644
--- a/lib/l10n/es.js
+++ b/lib/l10n/es.js
@@ -2,6 +2,7 @@ OC.L10N.register(
"lib",
{
"Cannot write into \"config\" directory!" : "No se puede escribir en la carpeta \"config\"!",
+ "This can usually be fixed by giving the web server write access to the config directory." : "Normalmente esto se puede arreglar dando al servidor web acceso de escritura a la carpeta config.",
"But, if you prefer to keep config.php file read only, set the option \"config_is_read_only\" to true in it." : "Pero, si prefieres mantener el archivo config.php como solo de lectura, establece la opción \"config_is_read_only\" a true en él.",
"See %s" : "Ver %s",
"The files of the app %1$s were not replaced correctly. Make sure it is a version compatible with the server." : "Los archivos de la app %1$s no se han reemplazado correctamente. Asegúrate de que es una versión compatible con el servidor.",
@@ -211,19 +212,36 @@ OC.L10N.register(
"Authentication error" : "Error de autenticación",
"Token expired. Please reload page." : "Token caducado. Por favor, recarge la página.",
"No database drivers (sqlite, mysql, or postgresql) installed." : "No están instalados los drivers de BBDD (sqlite, mysql, o postgresql)",
+ "Cannot write into \"config\" directory." : "No se puede escribir en la carpeta «config».",
+ "This can usually be fixed by giving the web server write access to the config directory. See %s" : "Normalmente esteo se puede arreglar dando al servidor web acceso de escritura a la carpeta config. Véase %s",
"Or, if you prefer to keep config.php file read only, set the option \"config_is_read_only\" to true in it. See %s" : "O, si prefieres mantener el archivo config.php como de solo lectura, marca la opción \"config_is_read_only\" a 'true' en él. Ver %s",
+ "Cannot write into \"apps\" directory." : "No se puede escribir en la carpeta «apps».",
+ "This can usually be fixed by giving the web server write access to the apps directory or disabling the App Store in the config file." : "Normalmente esto se puede arreglar dando al servidor web acceso de escritura a la carpeta de apps o desactivando la App Store en el archivo de configuración.",
"Cannot create \"data\" directory." : "No se puede crear la carpeta \"data\"",
+ "This can usually be fixed by giving the web server write access to the root directory. See %s" : "Normalmente esto se puede arreglar dando al servidor web acceso de escritura a la carpeta raíz. Véase %s",
+ "Permissions can usually be fixed by giving the web server write access to the root directory. See %s." : "Los permisos normalmente se pueden arreglar dando al servidor web acceso de escritura a la carpeta raíz. Véase %s",
+ "Your data directory is not writable." : "No se puede escribir en tu carpeta de datos.",
+ "Setting locale to %s failed." : "Fallo al configurar el idioma a %s.",
+ "Please install one of these locales on your system and restart your web server." : "Por favor, instala uno de estos idiomas en tu sistema y reinicia tu servidor web.",
"PHP module %s not installed." : "El módulo PHP %s no está instalado.",
"Please ask your server administrator to install the module." : "Consulte al administrador de su servidor para instalar el módulo.",
"PHP setting \"%s\" is not set to \"%s\"." : "La opción PHP \"%s\" no es \"%s\".",
"Adjusting this setting in php.ini will make Nextcloud run again" : "Ajustar esta configuración en php.ini hará que Nextcloud funcione de nuevo",
+ "<code>mbstring.func_overload</code> is set to <code>%s</code> instead of the expected value <code>0</code>." : "<code>mbstring.func_overload</code> está establecida como <code>%s</code> en lugar del valor esperado: <code>0</code>.",
+ "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini." : "Para arreglar este problema, establece <code>mbstring.func_overload</code> en<code>0</code> en tu php.ini.",
"libxml2 2.7.0 is at least required. Currently %s is installed." : "libxml2 2.7.0 es requerido en esta o en versiones superiores. Ahora mismo tienes instalada %s.",
"To fix this issue update your libxml2 version and restart your web server." : "Para corregir este error, actualiza la versión de tu libxml2 y reinicia el servidor web.",
"PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP está aparentemente configurado para eliminar bloques de documentos en línea. Esto hará que varias aplicaciones principales estén inaccesibles.",
"This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Probablemente esto venga a causa de la caché o un acelerador, tales como Zend OPcache o eAccelerator.",
"PHP modules have been installed, but they are still listed as missing?" : "Los módulos PHP se han instalado, pero aparecen listados como si faltaran",
"Please ask your server administrator to restart the web server." : "Consulte al administrador de su servidor para reiniciar el servidor web.",
+ "PostgreSQL >= 9 required." : "PostgreSQL >= 9 requerido.",
+ "Please upgrade your database version." : "Por favor, actualiza la versión de tu base de datos.",
+ "Your data directory is readable by other users." : "Tu carpeta de datos puede ser leído por otros usuarios.",
"Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Por favor, cambia los permisos a 0770 para que el directorio no se pueda mostrar a otros usuarios.",
+ "Your data directory must be an absolute path." : "Tu carpeta de datos debe ser una ruta absoluta.",
+ "Check the value of \"datadirectory\" in your configuration." : "Comprueba el valor de «datadirectory» en tu configuración.",
+ "Your data directory is invalid." : "Tu carpeta de datos es inválida.",
"Ensure there is a file called \".ocdata\" in the root of the data directory." : "Asegúrate de que existe un archivo llamado \".ocdata\" en la raíz del directorio de datos.",
"Action \"%s\" not supported or implemented." : "La acción \"%s\" no está soportada o implementada.",
"Authentication failed, wrong token or provider ID given" : "La autentificación ha fallado. Se ha dado un token o una ID de proveedor erróneos.",
diff --git a/lib/l10n/es.json b/lib/l10n/es.json
index 8f3e4a15f4f..81d1b451606 100644
--- a/lib/l10n/es.json
+++ b/lib/l10n/es.json
@@ -1,5 +1,6 @@
{ "translations": {
"Cannot write into \"config\" directory!" : "No se puede escribir en la carpeta \"config\"!",
+ "This can usually be fixed by giving the web server write access to the config directory." : "Normalmente esto se puede arreglar dando al servidor web acceso de escritura a la carpeta config.",
"But, if you prefer to keep config.php file read only, set the option \"config_is_read_only\" to true in it." : "Pero, si prefieres mantener el archivo config.php como solo de lectura, establece la opción \"config_is_read_only\" a true en él.",
"See %s" : "Ver %s",
"The files of the app %1$s were not replaced correctly. Make sure it is a version compatible with the server." : "Los archivos de la app %1$s no se han reemplazado correctamente. Asegúrate de que es una versión compatible con el servidor.",
@@ -209,19 +210,36 @@
"Authentication error" : "Error de autenticación",
"Token expired. Please reload page." : "Token caducado. Por favor, recarge la página.",
"No database drivers (sqlite, mysql, or postgresql) installed." : "No están instalados los drivers de BBDD (sqlite, mysql, o postgresql)",
+ "Cannot write into \"config\" directory." : "No se puede escribir en la carpeta «config».",
+ "This can usually be fixed by giving the web server write access to the config directory. See %s" : "Normalmente esteo se puede arreglar dando al servidor web acceso de escritura a la carpeta config. Véase %s",
"Or, if you prefer to keep config.php file read only, set the option \"config_is_read_only\" to true in it. See %s" : "O, si prefieres mantener el archivo config.php como de solo lectura, marca la opción \"config_is_read_only\" a 'true' en él. Ver %s",
+ "Cannot write into \"apps\" directory." : "No se puede escribir en la carpeta «apps».",
+ "This can usually be fixed by giving the web server write access to the apps directory or disabling the App Store in the config file." : "Normalmente esto se puede arreglar dando al servidor web acceso de escritura a la carpeta de apps o desactivando la App Store en el archivo de configuración.",
"Cannot create \"data\" directory." : "No se puede crear la carpeta \"data\"",
+ "This can usually be fixed by giving the web server write access to the root directory. See %s" : "Normalmente esto se puede arreglar dando al servidor web acceso de escritura a la carpeta raíz. Véase %s",
+ "Permissions can usually be fixed by giving the web server write access to the root directory. See %s." : "Los permisos normalmente se pueden arreglar dando al servidor web acceso de escritura a la carpeta raíz. Véase %s",
+ "Your data directory is not writable." : "No se puede escribir en tu carpeta de datos.",
+ "Setting locale to %s failed." : "Fallo al configurar el idioma a %s.",
+ "Please install one of these locales on your system and restart your web server." : "Por favor, instala uno de estos idiomas en tu sistema y reinicia tu servidor web.",
"PHP module %s not installed." : "El módulo PHP %s no está instalado.",
"Please ask your server administrator to install the module." : "Consulte al administrador de su servidor para instalar el módulo.",
"PHP setting \"%s\" is not set to \"%s\"." : "La opción PHP \"%s\" no es \"%s\".",
"Adjusting this setting in php.ini will make Nextcloud run again" : "Ajustar esta configuración en php.ini hará que Nextcloud funcione de nuevo",
+ "<code>mbstring.func_overload</code> is set to <code>%s</code> instead of the expected value <code>0</code>." : "<code>mbstring.func_overload</code> está establecida como <code>%s</code> en lugar del valor esperado: <code>0</code>.",
+ "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini." : "Para arreglar este problema, establece <code>mbstring.func_overload</code> en<code>0</code> en tu php.ini.",
"libxml2 2.7.0 is at least required. Currently %s is installed." : "libxml2 2.7.0 es requerido en esta o en versiones superiores. Ahora mismo tienes instalada %s.",
"To fix this issue update your libxml2 version and restart your web server." : "Para corregir este error, actualiza la versión de tu libxml2 y reinicia el servidor web.",
"PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP está aparentemente configurado para eliminar bloques de documentos en línea. Esto hará que varias aplicaciones principales estén inaccesibles.",
"This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Probablemente esto venga a causa de la caché o un acelerador, tales como Zend OPcache o eAccelerator.",
"PHP modules have been installed, but they are still listed as missing?" : "Los módulos PHP se han instalado, pero aparecen listados como si faltaran",
"Please ask your server administrator to restart the web server." : "Consulte al administrador de su servidor para reiniciar el servidor web.",
+ "PostgreSQL >= 9 required." : "PostgreSQL >= 9 requerido.",
+ "Please upgrade your database version." : "Por favor, actualiza la versión de tu base de datos.",
+ "Your data directory is readable by other users." : "Tu carpeta de datos puede ser leído por otros usuarios.",
"Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Por favor, cambia los permisos a 0770 para que el directorio no se pueda mostrar a otros usuarios.",
+ "Your data directory must be an absolute path." : "Tu carpeta de datos debe ser una ruta absoluta.",
+ "Check the value of \"datadirectory\" in your configuration." : "Comprueba el valor de «datadirectory» en tu configuración.",
+ "Your data directory is invalid." : "Tu carpeta de datos es inválida.",
"Ensure there is a file called \".ocdata\" in the root of the data directory." : "Asegúrate de que existe un archivo llamado \".ocdata\" en la raíz del directorio de datos.",
"Action \"%s\" not supported or implemented." : "La acción \"%s\" no está soportada o implementada.",
"Authentication failed, wrong token or provider ID given" : "La autentificación ha fallado. Se ha dado un token o una ID de proveedor erróneos.",
diff --git a/lib/private/Accounts/AccountManager.php b/lib/private/Accounts/AccountManager.php
index 127adc9ef38..5792ba1dc5d 100644
--- a/lib/private/Accounts/AccountManager.php
+++ b/lib/private/Accounts/AccountManager.php
@@ -41,6 +41,7 @@ use libphonenumber\PhoneNumber;
use libphonenumber\PhoneNumberFormat;
use libphonenumber\PhoneNumberUtil;
use OC\Profile\TProfileHelper;
+use OC\Cache\CappedMemoryCache;
use OCA\Settings\BackgroundJobs\VerifyUserData;
use OCP\Accounts\IAccount;
use OCP\Accounts\IAccountManager;
@@ -116,6 +117,7 @@ class AccountManager implements IAccountManager {
private $crypto;
/** @var IFactory */
private $l10nfactory;
+ private CappedMemoryCache $internalCache;
public function __construct(
IDBConnection $connection,
@@ -142,6 +144,7 @@ class AccountManager implements IAccountManager {
$this->crypto = $crypto;
// DIing IL10N results in a dependency loop
$this->l10nfactory = $factory;
+ $this->internalCache = new CappedMemoryCache();
}
/**
@@ -763,7 +766,12 @@ class AccountManager implements IAccountManager {
}
public function getAccount(IUser $user): IAccount {
- return $this->parseAccountData($user, $this->getUser($user));
+ if ($this->internalCache->hasKey($user->getUID())) {
+ return $this->internalCache->get($user->getUID());
+ }
+ $account = $this->parseAccountData($user, $this->getUser($user));
+ $this->internalCache->set($user->getUID(), $account);
+ return $account;
}
public function updateAccount(IAccount $account): void {
@@ -813,5 +821,6 @@ class AccountManager implements IAccountManager {
}
$this->updateUser($account->getUser(), $data, true);
+ $this->internalCache->set($account->getUser()->getUID(), $account);
}
}
diff --git a/lib/private/Console/TimestampFormatter.php b/lib/private/Console/TimestampFormatter.php
index 59e480b39e8..c25ed578805 100644
--- a/lib/private/Console/TimestampFormatter.php
+++ b/lib/private/Console/TimestampFormatter.php
@@ -99,6 +99,11 @@ class TimestampFormatter implements OutputFormatterInterface {
* log timezone and dateformat, e.g. "2015-06-23T17:24:37+02:00"
*/
public function format($message) {
+ if (!$this->formatter->isDecorated()) {
+ // Don't add anything to the output when we shouldn't
+ return $this->formatter->format($message);
+ }
+
$timeZone = $this->config->getSystemValue('logtimezone', 'UTC');
$timeZone = $timeZone !== null ? new \DateTimeZone($timeZone) : null;
diff --git a/lib/private/DB/BacktraceDebugStack.php b/lib/private/DB/BacktraceDebugStack.php
new file mode 100644
index 00000000000..be37e5a35da
--- /dev/null
+++ b/lib/private/DB/BacktraceDebugStack.php
@@ -0,0 +1,34 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2022 Robin Appelman <robin@icewind.nl>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OC\DB;
+
+use Doctrine\DBAL\Logging\DebugStack;
+
+class BacktraceDebugStack extends DebugStack {
+ public function startQuery($sql, ?array $params = null, ?array $types = null) {
+ parent::startQuery($sql, $params, $types);
+ $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
+ $this->queries[$this->currentQuery]['backtrace'] = $backtrace;
+ }
+}
diff --git a/lib/private/DB/Connection.php b/lib/private/DB/Connection.php
index 2e38b1ddf5e..22c2bbbb793 100644
--- a/lib/private/DB/Connection.php
+++ b/lib/private/DB/Connection.php
@@ -42,7 +42,6 @@ use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Exception;
use Doctrine\DBAL\Exception\ConstraintViolationException;
use Doctrine\DBAL\Exception\NotNullConstraintViolationException;
-use Doctrine\DBAL\Logging\DebugStack;
use Doctrine\DBAL\Platforms\MySQLPlatform;
use Doctrine\DBAL\Platforms\OraclePlatform;
use Doctrine\DBAL\Platforms\PostgreSQL94Platform;
@@ -113,7 +112,7 @@ class Connection extends \Doctrine\DBAL\Connection {
if ($profiler->isEnabled()) {
$this->dbDataCollector = new DbDataCollector($this);
$profiler->add($this->dbDataCollector);
- $debugStack = new DebugStack();
+ $debugStack = new BacktraceDebugStack();
$this->dbDataCollector->setDebugStack($debugStack);
$this->_config->setSQLLogger($debugStack);
}
diff --git a/lib/private/DB/DbDataCollector.php b/lib/private/DB/DbDataCollector.php
index d708955b10e..60e3dbe797d 100644
--- a/lib/private/DB/DbDataCollector.php
+++ b/lib/private/DB/DbDataCollector.php
@@ -25,14 +25,13 @@ declare(strict_types = 1);
namespace OC\DB;
-use Doctrine\DBAL\Logging\DebugStack;
use Doctrine\DBAL\Types\ConversionException;
use Doctrine\DBAL\Types\Type;
use OC\AppFramework\Http\Request;
use OCP\AppFramework\Http\Response;
class DbDataCollector extends \OCP\DataCollector\AbstractDataCollector {
- protected ?DebugStack $debugStack = null;
+ protected ?BacktraceDebugStack $debugStack = null;
private Connection $connection;
/**
@@ -42,7 +41,7 @@ class DbDataCollector extends \OCP\DataCollector\AbstractDataCollector {
$this->connection = $connection;
}
- public function setDebugStack(DebugStack $debugStack, $name = 'default'): void {
+ public function setDebugStack(BacktraceDebugStack $debugStack, $name = 'default'): void {
$this->debugStack = $debugStack;
}
diff --git a/lib/private/DB/MigrationService.php b/lib/private/DB/MigrationService.php
index 046e3a4924b..92f45dfdbe8 100644
--- a/lib/private/DB/MigrationService.php
+++ b/lib/private/DB/MigrationService.php
@@ -27,7 +27,6 @@
*/
namespace OC\DB;
-use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Platforms\OraclePlatform;
use Doctrine\DBAL\Platforms\PostgreSQL94Platform;
use Doctrine\DBAL\Schema\Index;
@@ -424,10 +423,10 @@ class MigrationService {
foreach ($toBeExecuted as $version) {
try {
$this->executeStep($version, $schemaOnly);
- } catch (DriverException $e) {
+ } catch (\Exception $e) {
// The exception itself does not contain the name of the migration,
// so we wrap it here, to make debugging easier.
- throw new \Exception('Database error when running migration ' . $to . ' for app ' . $this->getApp(), 0, $e);
+ throw new \Exception('Database error when running migration ' . $version . ' for app ' . $this->getApp() . PHP_EOL. $e->getMessage(), 0, $e);
}
}
}
@@ -560,9 +559,13 @@ class MigrationService {
* - Primary key names must be set or the table name 23 chars or shorter
*
* Data constraints:
+ * - Tables need a primary key (Not specific to Oracle, but required for performant clustering support)
* - Columns with "NotNull" can not have empty string as default value
* - Columns with "NotNull" can not have number 0 as default value
* - Columns with type "bool" (which is in fact integer of length 1) can not be "NotNull" as it can not store 0/false
+ * - Columns with type "string" can not be longer than 4.000 characters, use "text" instead
+ *
+ * @see https://github.com/nextcloud/documentation/blob/master/developer_manual/basics/storage/database.rst
*
* @param Schema $sourceSchema
* @param Schema $targetSchema
@@ -583,20 +586,30 @@ class MigrationService {
}
foreach ($table->getColumns() as $thing) {
- if ((!$sourceTable instanceof Table || !$sourceTable->hasColumn($thing->getName())) && \strlen($thing->getName()) > 30) {
- throw new \InvalidArgumentException('Column name "' . $table->getName() . '"."' . $thing->getName() . '" is too long.');
- }
+ // If the table doesn't exist OR if the column doesn't exist in the table
+ if (!$sourceTable instanceof Table || !$sourceTable->hasColumn($thing->getName())) {
+ if (\strlen($thing->getName()) > 30) {
+ throw new \InvalidArgumentException('Column name "' . $table->getName() . '"."' . $thing->getName() . '" is too long.');
+ }
- if ((!$sourceTable instanceof Table || !$sourceTable->hasColumn($thing->getName())) && $thing->getNotnull() && $thing->getDefault() === ''
- && $sourceTable instanceof Table && !$sourceTable->hasColumn($thing->getName())) {
- throw new \InvalidArgumentException('Column "' . $table->getName() . '"."' . $thing->getName() . '" is NotNull, but has empty string or null as default.');
- }
+ if ($thing->getNotnull() && $thing->getDefault() === ''
+ && $sourceTable instanceof Table && !$sourceTable->hasColumn($thing->getName())) {
+ throw new \InvalidArgumentException('Column "' . $table->getName() . '"."' . $thing->getName() . '" is NotNull, but has empty string or null as default.');
+ }
+
+ if ($thing->getNotnull() && $thing->getType()->getName() === Types::BOOLEAN) {
+ throw new \InvalidArgumentException('Column "' . $table->getName() . '"."' . $thing->getName() . '" is type Bool and also NotNull, so it can not store "false".');
+ }
- if ((!$sourceTable instanceof Table || !$sourceTable->hasColumn($thing->getName())) && $thing->getNotnull() && $thing->getType()->getName() === Types::BOOLEAN) {
- throw new \InvalidArgumentException('Column "' . $table->getName() . '"."' . $thing->getName() . '" is type Bool and also NotNull, so it can not store "false".');
+ $sourceColumn = null;
+ } else {
+ $sourceColumn = $sourceTable->getColumn($thing->getName());
}
- if ($thing->getLength() > 4000 && $thing->getType()->getName() === Types::STRING) {
+ // If the column was just created OR the length changed OR the type changed
+ // we will NOT detect invalid length if the column is not modified
+ if (($sourceColumn === null || $sourceColumn->getLength() !== $thing->getLength() || $sourceColumn->getType()->getName() !== Types::STRING)
+ && $thing->getLength() > 4000 && $thing->getType()->getName() === Types::STRING) {
throw new \InvalidArgumentException('Column "' . $table->getName() . '"."' . $thing->getName() . '" is type String, but exceeding the 4.000 length limit.');
}
}
diff --git a/lib/private/Files/Cache/Storage.php b/lib/private/Files/Cache/Storage.php
index 2de2c2f84d7..fb9e5500658 100644
--- a/lib/private/Files/Cache/Storage.php
+++ b/lib/private/Files/Cache/Storage.php
@@ -126,19 +126,9 @@ class Storage {
* @param int $numericId
* @return string|null either the storage id string or null if the numeric id is not known
*/
- public static function getStorageId($numericId) {
- $query = \OC::$server->getDatabaseConnection()->getQueryBuilder();
- $query->select('id')
- ->from('storages')
- ->where($query->expr()->eq('numeric_id', $query->createNamedParameter($numericId)));
- $result = $query->execute();
- $row = $result->fetch();
- $result->closeCursor();
- if ($row) {
- return $row['id'];
- } else {
- return null;
- }
+ public static function getStorageId(int $numericId): ?string {
+ $storage = self::getGlobalCache()->getStorageInfoByNumericId($numericId);
+ return $storage['id'] ?? null;
}
/**
@@ -240,6 +230,7 @@ class Storage {
->from('mounts')
->where($query->expr()->eq('mount_id', $query->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)));
$storageIds = $query->executeQuery()->fetchAll(\PDO::FETCH_COLUMN);
+ $storageIds = array_unique($storageIds);
$query = $db->getQueryBuilder();
$query->delete('filecache')
diff --git a/lib/private/Files/Cache/StorageGlobal.php b/lib/private/Files/Cache/StorageGlobal.php
index 7162f8e4908..a898c435415 100644
--- a/lib/private/Files/Cache/StorageGlobal.php
+++ b/lib/private/Files/Cache/StorageGlobal.php
@@ -41,8 +41,10 @@ class StorageGlobal {
/** @var IDBConnection */
private $connection;
- /** @var array[] */
+ /** @var array<string, array> */
private $cache = [];
+ /** @var array<int, array> */
+ private $numericIdCache = [];
public function __construct(IDBConnection $connection) {
$this->connection = $connection;
@@ -68,7 +70,7 @@ class StorageGlobal {
* @param string $storageId
* @return array|null
*/
- public function getStorageInfo($storageId) {
+ public function getStorageInfo(string $storageId): ?array {
if (!isset($this->cache[$storageId])) {
$builder = $this->connection->getQueryBuilder();
$query = $builder->select(['id', 'numeric_id', 'available', 'last_checked'])
@@ -81,9 +83,33 @@ class StorageGlobal {
if ($row) {
$this->cache[$storageId] = $row;
+ $this->numericIdCache[(int)$row['numeric_id']] = $row;
}
}
- return isset($this->cache[$storageId]) ? $this->cache[$storageId] : null;
+ return $this->cache[$storageId] ?? null;
+ }
+
+ /**
+ * @param int $numericId
+ * @return array|null
+ */
+ public function getStorageInfoByNumericId(int $numericId): ?array {
+ if (!isset($this->numericIdCache[$numericId])) {
+ $builder = $this->connection->getQueryBuilder();
+ $query = $builder->select(['id', 'numeric_id', 'available', 'last_checked'])
+ ->from('storages')
+ ->where($builder->expr()->eq('numeric_id', $builder->createNamedParameter($numericId)));
+
+ $result = $query->execute();
+ $row = $result->fetch();
+ $result->closeCursor();
+
+ if ($row) {
+ $this->numericIdCache[$numericId] = $row;
+ $this->cache[$row['id']] = $row;
+ }
+ }
+ return $this->numericIdCache[$numericId] ?? null;
}
public function clearCache() {
diff --git a/lib/private/Files/Config/MountProviderCollection.php b/lib/private/Files/Config/MountProviderCollection.php
index 2b0acf7d69d..334fce15d9e 100644
--- a/lib/private/Files/Config/MountProviderCollection.php
+++ b/lib/private/Files/Config/MountProviderCollection.php
@@ -97,10 +97,10 @@ class MountProviderCollection implements IMountProviderCollection, Emitter {
return $this->getUserMountsForProviders($user, $this->providers);
}
- public function getUserMountsForProviderClass(IUser $user, string $mountProviderClass): array {
+ public function getUserMountsForProviderClasses(IUser $user, array $mountProviderClasses): array {
$providers = array_filter(
$this->providers,
- fn (IMountProvider $mountProvider) => (get_class($mountProvider) === $mountProviderClass)
+ fn (IMountProvider $mountProvider) => (in_array(get_class($mountProvider), $mountProviderClasses))
);
return $this->getUserMountsForProviders($user, $providers);
}
diff --git a/lib/private/Files/FileInfo.php b/lib/private/Files/FileInfo.php
index 2f361fc051d..6389544184f 100644
--- a/lib/private/Files/FileInfo.php
+++ b/lib/private/Files/FileInfo.php
@@ -254,7 +254,7 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess {
*/
public function getType() {
if (!isset($this->data['type'])) {
- $this->data['type'] = ($this->getMimetype() === 'httpd/unix-directory') ? self::TYPE_FOLDER : self::TYPE_FILE;
+ $this->data['type'] = ($this->getMimetype() === self::MIMETYPE_FOLDER) ? self::TYPE_FOLDER : self::TYPE_FILE;
}
return $this->data['type'];
}
diff --git a/lib/private/Files/Mount/Manager.php b/lib/private/Files/Mount/Manager.php
index ecd97760f17..69285018d17 100644
--- a/lib/private/Files/Mount/Manager.php
+++ b/lib/private/Files/Mount/Manager.php
@@ -158,7 +158,6 @@ class Manager implements IMountManager {
* @return IMountPoint[]
*/
public function findByStorageId(string $id): array {
- \OC_Util::setupFS();
if (\strlen($id) > 64) {
$id = md5($id);
}
@@ -204,4 +203,22 @@ class Manager implements IMountManager {
public function getSetupManager(): SetupManager {
return $this->setupManager;
}
+
+ /**
+ * Return all mounts in a path from a specific mount provider
+ *
+ * @param string $path
+ * @param string[] $mountProviders
+ * @return MountPoint[]
+ */
+ public function getMountsByMountProvider(string $path, array $mountProviders) {
+ $this->getSetupManager()->setupForProvider($path, $mountProviders);
+ if (in_array('', $mountProviders)) {
+ return $this->mounts;
+ } else {
+ return array_filter($this->mounts, function ($mount) use ($mountProviders) {
+ return in_array($mount->getMountProvider(), $mountProviders);
+ });
+ }
+ }
}
diff --git a/lib/private/Files/Node/Folder.php b/lib/private/Files/Node/Folder.php
index 400fd6bedcc..9c15f0edf41 100644
--- a/lib/private/Files/Node/Folder.php
+++ b/lib/private/Files/Node/Folder.php
@@ -36,6 +36,7 @@ use OC\Files\Cache\Wrapper\CacheJail;
use OC\Files\Search\SearchComparison;
use OC\Files\Search\SearchOrder;
use OC\Files\Search\SearchQuery;
+use OC\Files\Utils\PathHelper;
use OCP\Files\Cache\ICacheEntry;
use OCP\Files\FileInfo;
use OCP\Files\Mount\IMountPoint;
@@ -76,17 +77,7 @@ class Folder extends Node implements \OCP\Files\Folder {
* @return string|null
*/
public function getRelativePath($path) {
- if ($this->path === '' or $this->path === '/') {
- return $this->normalizePath($path);
- }
- if ($path === $this->path) {
- return '/';
- } elseif (strpos($path, $this->path . '/') !== 0) {
- return null;
- } else {
- $path = substr($path, strlen($this->path));
- return $this->normalizePath($path);
- }
+ return PathHelper::getRelativePath($this->getPath(), $path);
}
/**
@@ -106,10 +97,10 @@ class Folder extends Node implements \OCP\Files\Folder {
* @throws \OCP\Files\NotFoundException
*/
public function getDirectoryListing() {
- $folderContent = $this->view->getDirectoryContent($this->path);
+ $folderContent = $this->view->getDirectoryContent($this->path, '', $this->getFileInfo());
return array_map(function (FileInfo $info) {
- if ($info->getMimetype() === 'httpd/unix-directory') {
+ if ($info->getMimetype() === FileInfo::MIMETYPE_FOLDER) {
return new Folder($this->root, $this->view, $info->getPath(), $info);
} else {
return new File($this->root, $this->view, $info->getPath(), $info);
@@ -342,70 +333,7 @@ class Folder extends Node implements \OCP\Files\Folder {
* @return \OC\Files\Node\Node[]
*/
public function getById($id) {
- $mountCache = $this->root->getUserMountCache();
- if (strpos($this->getPath(), '/', 1) > 0) {
- [, $user] = explode('/', $this->getPath());
- } else {
- $user = null;
- }
- $mountsContainingFile = $mountCache->getMountsForFileId((int)$id, $user);
-
- // when a user has access trough the same storage trough multiple paths
- // (such as an external storage that is both mounted for a user and shared to the user)
- // the mount cache will only hold a single entry for the storage
- // this can lead to issues as the different ways the user has access to a storage can have different permissions
- //
- // so instead of using the cached entries directly, we instead filter the current mounts by the rootid of the cache entry
-
- $mountRootIds = array_map(function ($mount) {
- return $mount->getRootId();
- }, $mountsContainingFile);
- $mountRootPaths = array_map(function ($mount) {
- return $mount->getRootInternalPath();
- }, $mountsContainingFile);
- $mountRoots = array_combine($mountRootIds, $mountRootPaths);
-
- $mounts = $this->root->getMountsIn($this->path);
- $mounts[] = $this->root->getMount($this->path);
-
- $mountsContainingFile = array_filter($mounts, function ($mount) use ($mountRoots) {
- return isset($mountRoots[$mount->getStorageRootId()]);
- });
-
- if (count($mountsContainingFile) === 0) {
- if ($user === $this->getAppDataDirectoryName()) {
- return $this->getByIdInRootMount((int)$id);
- }
- return [];
- }
-
- $nodes = array_map(function (IMountPoint $mount) use ($id, $mountRoots) {
- $rootInternalPath = $mountRoots[$mount->getStorageRootId()];
- $cacheEntry = $mount->getStorage()->getCache()->get((int)$id);
- if (!$cacheEntry) {
- return null;
- }
-
- // cache jails will hide the "true" internal path
- $internalPath = ltrim($rootInternalPath . '/' . $cacheEntry->getPath(), '/');
- $pathRelativeToMount = substr($internalPath, strlen($rootInternalPath));
- $pathRelativeToMount = ltrim($pathRelativeToMount, '/');
- $absolutePath = rtrim($mount->getMountPoint() . $pathRelativeToMount, '/');
- return $this->root->createNode($absolutePath, new \OC\Files\FileInfo(
- $absolutePath, $mount->getStorage(), $cacheEntry->getPath(), $cacheEntry, $mount,
- \OC::$server->getUserManager()->get($mount->getStorage()->getOwner($pathRelativeToMount))
- ));
- }, $mountsContainingFile);
-
- $nodes = array_filter($nodes);
-
- $folders = array_filter($nodes, function (Node $node) {
- return $this->getRelativePath($node->getPath());
- });
- usort($folders, function ($a, $b) {
- return $b->getPath() <=> $a->getPath();
- });
- return $folders;
+ return $this->root->getByIdInPath((int)$id, $this->getPath());
}
protected function getAppDataDirectoryName(): string {
diff --git a/lib/private/Files/Node/LazyFolder.php b/lib/private/Files/Node/LazyFolder.php
index 45451e5c53c..7d5038e85a2 100644
--- a/lib/private/Files/Node/LazyFolder.php
+++ b/lib/private/Files/Node/LazyFolder.php
@@ -25,6 +25,7 @@ declare(strict_types=1);
*/
namespace OC\Files\Node;
+use OC\Files\Utils\PathHelper;
use OCP\Constants;
/**
@@ -382,13 +383,6 @@ class LazyFolder implements \OCP\Files\Folder {
/**
* @inheritDoc
*/
- public function getRelativePath($path) {
- return $this->__call(__FUNCTION__, func_get_args());
- }
-
- /**
- * @inheritDoc
- */
public function isSubNode($node) {
return $this->__call(__FUNCTION__, func_get_args());
}
@@ -518,4 +512,8 @@ class LazyFolder implements \OCP\Files\Folder {
public function getUploadTime(): int {
return $this->__call(__FUNCTION__, func_get_args());
}
+
+ public function getRelativePath($path) {
+ return PathHelper::getRelativePath($this->getPath(), $path);
+ }
}
diff --git a/lib/private/Files/Node/LazyRoot.php b/lib/private/Files/Node/LazyRoot.php
index c4d61f653e4..c01b9fdbb83 100644
--- a/lib/private/Files/Node/LazyRoot.php
+++ b/lib/private/Files/Node/LazyRoot.php
@@ -39,4 +39,8 @@ class LazyRoot extends LazyFolder implements IRootFolder {
public function getUserFolder($userId) {
return $this->__call(__FUNCTION__, func_get_args());
}
+
+ public function getByIdInPath(int $id, string $path) {
+ return $this->__call(__FUNCTION__, func_get_args());
+ }
}
diff --git a/lib/private/Files/Node/LazyUserFolder.php b/lib/private/Files/Node/LazyUserFolder.php
index 4c9e89ce233..d91759117c1 100644
--- a/lib/private/Files/Node/LazyUserFolder.php
+++ b/lib/private/Files/Node/LazyUserFolder.php
@@ -29,28 +29,38 @@ use OCP\Files\NotFoundException;
use OCP\IUser;
class LazyUserFolder extends LazyFolder {
- private IRootFolder $rootFolder;
+ private IRootFolder $root;
private IUser $user;
+ private string $path;
public function __construct(IRootFolder $rootFolder, IUser $user) {
- $this->rootFolder = $rootFolder;
+ $this->root = $rootFolder;
$this->user = $user;
+ $this->path = '/' . $user->getUID() . '/files';
parent::__construct(function () use ($user) {
try {
- return $this->rootFolder->get('/' . $user->getUID() . '/files');
+ return $this->root->get('/' . $user->getUID() . '/files');
} catch (NotFoundException $e) {
- if (!$this->rootFolder->nodeExists('/' . $user->getUID())) {
- $this->rootFolder->newFolder('/' . $user->getUID());
+ if (!$this->root->nodeExists('/' . $user->getUID())) {
+ $this->root->newFolder('/' . $user->getUID());
}
- return $this->rootFolder->newFolder('/' . $user->getUID() . '/files');
+ return $this->root->newFolder('/' . $user->getUID() . '/files');
}
}, [
- 'path' => '/' . $user->getUID() . '/files',
+ 'path' => $this->path,
'permissions' => Constants::PERMISSION_ALL,
]);
}
public function get($path) {
- return $this->rootFolder->get('/' . $this->user->getUID() . '/files/' . rtrim($path, '/'));
+ return $this->root->get('/' . $this->user->getUID() . '/files/' . ltrim($path, '/'));
+ }
+
+ /**
+ * @param int $id
+ * @return \OC\Files\Node\Node[]
+ */
+ public function getById($id) {
+ return $this->root->getByIdInPath((int)$id, $this->getPath());
}
}
diff --git a/lib/private/Files/Node/Node.php b/lib/private/Files/Node/Node.php
index b939bfce945..c8975154059 100644
--- a/lib/private/Files/Node/Node.php
+++ b/lib/private/Files/Node/Node.php
@@ -31,6 +31,7 @@ namespace OC\Files\Node;
use OC\Files\Filesystem;
use OC\Files\Mount\MoveableMount;
+use OC\Files\Utils\PathHelper;
use OCP\Files\FileInfo;
use OCP\Files\InvalidPathException;
use OCP\Files\NotFoundException;
@@ -153,12 +154,11 @@ class Node implements \OCP\Files\Node {
}
}
- /**
- * @return \OC\Files\Storage\Storage
- * @throws \OCP\Files\NotFoundException
- */
public function getStorage() {
- [$storage,] = $this->view->resolvePath($this->path);
+ $storage = $this->getMountPoint()->getStorage();
+ if (!$storage) {
+ throw new \Exception("No storage for node");
+ }
return $storage;
}
@@ -173,8 +173,7 @@ class Node implements \OCP\Files\Node {
* @return string
*/
public function getInternalPath() {
- [, $internalPath] = $this->view->resolvePath($this->path);
- return $internalPath;
+ return $this->getFileInfo()->getInternalPath();
}
/**
@@ -298,23 +297,7 @@ class Node implements \OCP\Files\Node {
* @return string
*/
protected function normalizePath($path) {
- if ($path === '' or $path === '/') {
- return '/';
- }
- //no windows style slashes
- $path = str_replace('\\', '/', $path);
- //add leading slash
- if ($path[0] !== '/') {
- $path = '/' . $path;
- }
- //remove duplicate slashes
- while (strpos($path, '//') !== false) {
- $path = str_replace('//', '/', $path);
- }
- //remove trailing slash
- $path = rtrim($path, '/');
-
- return $path;
+ return PathHelper::normalizePath($path);
}
/**
@@ -447,6 +430,15 @@ class Node implements \OCP\Files\Node {
if (!$this->view->rename($this->path, $targetPath)) {
throw new NotPermittedException('Could not move ' . $this->path . ' to ' . $targetPath);
}
+
+ $mountPoint = $this->getMountPoint();
+ if ($mountPoint) {
+ // update the cached fileinfo with the new (internal) path
+ /** @var \OC\Files\FileInfo $oldFileInfo */
+ $oldFileInfo = $this->getFileInfo();
+ $this->fileInfo = new \OC\Files\FileInfo($targetPath, $oldFileInfo->getStorage(), $mountPoint->getInternalPath($targetPath), $oldFileInfo->getData(), $mountPoint, $oldFileInfo->getOwner());
+ }
+
$targetNode = $this->root->get($targetPath);
$this->sendHooks(['postRename'], [$this, $targetNode]);
$this->sendHooks(['postWrite'], [$targetNode]);
diff --git a/lib/private/Files/Node/Root.php b/lib/private/Files/Node/Root.php
index a8b36ee7731..7592d4caf37 100644
--- a/lib/private/Files/Node/Root.php
+++ b/lib/private/Files/Node/Root.php
@@ -33,8 +33,10 @@
namespace OC\Files\Node;
use OC\Cache\CappedMemoryCache;
+use OC\Files\FileInfo;
use OC\Files\Mount\Manager;
use OC\Files\Mount\MountPoint;
+use OC\Files\Utils\PathHelper;
use OC\Files\View;
use OC\Hooks\PublicEmitter;
use OC\User\NoUserException;
@@ -42,6 +44,7 @@ use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\Config\IUserMountCache;
use OCP\Files\Events\Node\FilesystemTornDownEvent;
use OCP\Files\IRootFolder;
+use OCP\Files\Mount\IMountPoint;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
use OCP\IUser;
@@ -214,7 +217,7 @@ class Root extends Folder implements IRootFolder {
/**
* @param string $targetPath
- * @return \OC\Files\Node\Node
+ * @return Node
* @throws \OCP\Files\NotPermittedException
*/
public function rename($targetPath) {
@@ -227,7 +230,7 @@ class Root extends Folder implements IRootFolder {
/**
* @param string $targetPath
- * @return \OC\Files\Node\Node
+ * @return Node
* @throws \OCP\Files\NotPermittedException
*/
public function copy($targetPath) {
@@ -402,4 +405,82 @@ class Root extends Folder implements IRootFolder {
public function getUserMountCache() {
return $this->userMountCache;
}
+
+ /**
+ * @param int $id
+ * @return Node[]
+ */
+ public function getByIdInPath(int $id, string $path): array {
+ $mountCache = $this->getUserMountCache();
+ if (strpos($path, '/', 1) > 0) {
+ [, $user] = explode('/', $path);
+ } else {
+ $user = null;
+ }
+ $mountsContainingFile = $mountCache->getMountsForFileId($id, $user);
+
+ // when a user has access trough the same storage trough multiple paths
+ // (such as an external storage that is both mounted for a user and shared to the user)
+ // the mount cache will only hold a single entry for the storage
+ // this can lead to issues as the different ways the user has access to a storage can have different permissions
+ //
+ // so instead of using the cached entries directly, we instead filter the current mounts by the rootid of the cache entry
+
+ $mountRootIds = array_map(function ($mount) {
+ return $mount->getRootId();
+ }, $mountsContainingFile);
+ $mountRootPaths = array_map(function ($mount) {
+ return $mount->getRootInternalPath();
+ }, $mountsContainingFile);
+ $mountProviders = array_unique(array_map(function ($mount) {
+ return $mount->getMountProvider();
+ }, $mountsContainingFile));
+ $mountRoots = array_combine($mountRootIds, $mountRootPaths);
+
+ $mounts = $this->mountManager->getMountsByMountProvider($path, $mountProviders);
+
+ $mountsContainingFile = array_filter($mounts, function ($mount) use ($mountRoots) {
+ return isset($mountRoots[$mount->getStorageRootId()]);
+ });
+
+ if (count($mountsContainingFile) === 0) {
+ if ($user === $this->getAppDataDirectoryName()) {
+ $folder = $this->get($path);
+ if ($folder instanceof Folder) {
+ return $folder->getByIdInRootMount($id);
+ } else {
+ throw new \Exception("getByIdInPath with non folder");
+ }
+ }
+ return [];
+ }
+
+ $nodes = array_map(function (IMountPoint $mount) use ($id, $mountRoots) {
+ $rootInternalPath = $mountRoots[$mount->getStorageRootId()];
+ $cacheEntry = $mount->getStorage()->getCache()->get($id);
+ if (!$cacheEntry) {
+ return null;
+ }
+
+ // cache jails will hide the "true" internal path
+ $internalPath = ltrim($rootInternalPath . '/' . $cacheEntry->getPath(), '/');
+ $pathRelativeToMount = substr($internalPath, strlen($rootInternalPath));
+ $pathRelativeToMount = ltrim($pathRelativeToMount, '/');
+ $absolutePath = rtrim($mount->getMountPoint() . $pathRelativeToMount, '/');
+ return $this->createNode($absolutePath, new FileInfo(
+ $absolutePath, $mount->getStorage(), $cacheEntry->getPath(), $cacheEntry, $mount,
+ \OC::$server->getUserManager()->get($mount->getStorage()->getOwner($pathRelativeToMount))
+ ));
+ }, $mountsContainingFile);
+
+ $nodes = array_filter($nodes);
+
+ $folders = array_filter($nodes, function (Node $node) use ($path) {
+ return PathHelper::getRelativePath($path, $node->getPath()) !== null;
+ });
+ usort($folders, function ($a, $b) {
+ return $b->getPath() <=> $a->getPath();
+ });
+ return $folders;
+ }
}
diff --git a/lib/private/Files/SetupManager.php b/lib/private/Files/SetupManager.php
index da50983da32..d091b5c5e35 100644
--- a/lib/private/Files/SetupManager.php
+++ b/lib/private/Files/SetupManager.php
@@ -40,6 +40,7 @@ use OC_Util;
use OCP\Constants;
use OCP\Diagnostics\IEventLogger;
use OCP\EventDispatcher\IEventDispatcher;
+use OCP\Files\Config\IHomeMountProvider;
use OCP\Files\Config\IMountProvider;
use OCP\Files\Config\IUserMountCache;
use OCP\Files\Events\InvalidateMountCacheEvent;
@@ -263,6 +264,11 @@ class SetupManager {
return strpos($mount->getMountPoint(), $userRoot) === 0;
});
$this->userMountCache->registerMounts($user, $mounts);
+
+ $cacheDuration = $this->config->getSystemValueInt('fs_mount_cache_duration', 5 * 60);
+ if ($cacheDuration > 0) {
+ $this->cache->set($user->getUID(), true, $cacheDuration);
+ }
}
/**
@@ -321,25 +327,32 @@ class SetupManager {
}
/**
- * Set up the filesystem for the specified path
+ * Get the user to setup for a path or `null` if the root needs to be setup
+ *
+ * @param string $path
+ * @return IUser|null
*/
- public function setupForPath(string $path, bool $includeChildren = false): void {
+ private function getUserForPath(string $path) {
if (substr_count($path, '/') < 2) {
if ($user = $this->userSession->getUser()) {
- $this->setupForUser($user);
+ return $user;
} else {
- $this->setupRoot();
+ return null;
}
- return;
} elseif (strpos($path, '/appdata_' . \OC_Util::getInstanceId()) === 0 || strpos($path, '/files_external/') === 0) {
- $this->setupRoot();
- return;
+ return null;
} else {
[, $userId] = explode('/', $path);
}
- $user = $this->userManager->get($userId);
+ return $this->userManager->get($userId);
+ }
+ /**
+ * Set up the filesystem for the specified path
+ */
+ public function setupForPath(string $path, bool $includeChildren = false): void {
+ $user = $this->getUserForPath($path);
if (!$user) {
$this->setupRoot();
return;
@@ -349,17 +362,14 @@ class SetupManager {
return;
}
- // we perform a "cached" setup only after having done the full setup recently
- // this is also used to trigger a full setup after handling events that are likely
- // to change the available mounts
- $cachedSetup = $this->cache->get($user->getUID());
- if (!$cachedSetup) {
+ if ($this->fullSetupRequired($user)) {
$this->setupForUser($user);
+ return;
+ }
- $cacheDuration = $this->config->getSystemValueInt('fs_mount_cache_duration', 5 * 60);
- if ($cacheDuration > 0) {
- $this->cache->set($user->getUID(), true, $cacheDuration);
- }
+ // for the user's home folder, it's always the home mount
+ if (rtrim($path) === "/" . $user->getUID() . "/files" && !$includeChildren) {
+ $this->oneTimeUserSetup($user);
return;
}
@@ -381,7 +391,7 @@ class SetupManager {
$setupProviders[] = $cachedMount->getMountProvider();
$currentProviders[] = $cachedMount->getMountProvider();
if ($cachedMount->getMountProvider()) {
- $mounts = $this->mountProviderCollection->getUserMountsForProviderClass($user, $cachedMount->getMountProvider());
+ $mounts = $this->mountProviderCollection->getUserMountsForProviderClasses($user, [$cachedMount->getMountProvider()]);
} else {
$this->logger->debug("mount at " . $cachedMount->getMountPoint() . " has no provider set, performing full setup");
$this->setupForUser($user);
@@ -396,7 +406,7 @@ class SetupManager {
$setupProviders[] = $cachedMount->getMountProvider();
$currentProviders[] = $cachedMount->getMountProvider();
if ($cachedMount->getMountProvider()) {
- $mounts = array_merge($mounts, $this->mountProviderCollection->getUserMountsForProviderClass($user, $cachedMount->getMountProvider()));
+ $mounts = array_merge($mounts, $this->mountProviderCollection->getUserMountsForProviderClasses($user, [$cachedMount->getMountProvider()]));
} else {
$this->logger->debug("mount at " . $cachedMount->getMountPoint() . " has no provider set, performing full setup");
$this->setupForUser($user);
@@ -416,6 +426,60 @@ class SetupManager {
}
}
+ private function fullSetupRequired(IUser $user): bool {
+ // we perform a "cached" setup only after having done the full setup recently
+ // this is also used to trigger a full setup after handling events that are likely
+ // to change the available mounts
+ return !$this->cache->get($user->getUID());
+ }
+
+ /**
+ * @param string $path
+ * @param string[] $providers
+ */
+ public function setupForProvider(string $path, array $providers): void {
+ $user = $this->getUserForPath($path);
+ if (!$user) {
+ $this->setupRoot();
+ return;
+ }
+
+ if ($this->isSetupComplete($user)) {
+ return;
+ }
+
+ if ($this->fullSetupRequired($user)) {
+ $this->setupForUser($user);
+ return;
+ }
+
+ // home providers are always used
+ $providers = array_filter($providers, function (string $provider) {
+ return !is_subclass_of($provider, IHomeMountProvider::class);
+ });
+
+ if (in_array('', $providers)) {
+ $this->setupForUser($user);
+ }
+ $setupProviders = $this->setupUserMountProviders[$user->getUID()] ?? [];
+
+ $providers = array_diff($providers, $setupProviders);
+ if (count($providers) === 0) {
+ if (!$this->isSetupStarted($user)) {
+ $this->oneTimeUserSetup($user);
+ }
+ return;
+ } else {
+ $this->setupUserMountProviders[$user->getUID()] = array_merge($setupProviders, $providers);
+ $mounts = $this->mountProviderCollection->getUserMountsForProviderClasses($user, $providers);
+ }
+
+ $this->userMountCache->registerMounts($user, $mounts, $providers);
+ $this->setupForUserWith($user, function () use ($mounts) {
+ array_walk($mounts, [$this->mountManager, 'addMount']);
+ });
+ }
+
public function tearDown() {
$this->setupUsers = [];
$this->setupUsersComplete = [];
diff --git a/lib/private/Files/Utils/PathHelper.php b/lib/private/Files/Utils/PathHelper.php
new file mode 100644
index 00000000000..07985e884ce
--- /dev/null
+++ b/lib/private/Files/Utils/PathHelper.php
@@ -0,0 +1,71 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2022 Robin Appelman <robin@icewind.nl>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OC\Files\Utils;
+
+class PathHelper {
+ /**
+ * Make a path relative to a root path, or return null if the path is outside the root
+ *
+ * @param string $root
+ * @param string $path
+ * @return ?string
+ */
+ public static function getRelativePath(string $root, string $path) {
+ if ($root === '' or $root === '/') {
+ return self::normalizePath($path);
+ }
+ if ($path === $root) {
+ return '/';
+ } elseif (strpos($path, $root . '/') !== 0) {
+ return null;
+ } else {
+ $path = substr($path, strlen($root));
+ return self::normalizePath($path);
+ }
+ }
+
+ /**
+ * @param string $path
+ * @return string
+ */
+ public static function normalizePath(string $path): string {
+ if ($path === '' or $path === '/') {
+ return '/';
+ }
+ //no windows style slashes
+ $path = str_replace('\\', '/', $path);
+ //add leading slash
+ if ($path[0] !== '/') {
+ $path = '/' . $path;
+ }
+ //remove duplicate slashes
+ while (strpos($path, '//') !== false) {
+ $path = str_replace('//', '/', $path);
+ }
+ //remove trailing slash
+ $path = rtrim($path, '/');
+
+ return $path;
+ }
+}
diff --git a/lib/private/Files/View.php b/lib/private/Files/View.php
index 779e0611591..30dc5518be8 100644
--- a/lib/private/Files/View.php
+++ b/lib/private/Files/View.php
@@ -1099,6 +1099,7 @@ class View {
[Filesystem::signal_param_path => $this->getHookPath($path)]
);
}
+ /** @var Storage|null $storage */
[$storage, $internalPath] = Filesystem::resolvePath($absolutePath . $postFix);
if ($storage) {
return $storage->hash($type, $internalPath, $raw);
@@ -1179,7 +1180,7 @@ class View {
throw $e;
}
- if ($result && in_array('delete', $hooks) and $result) {
+ if ($result && in_array('delete', $hooks)) {
$this->removeUpdate($storage, $internalPath);
}
if ($result && in_array('write', $hooks, true) && $operation !== 'fopen' && $operation !== 'touch') {
@@ -1430,129 +1431,134 @@ class View {
* @param string $mimetype_filter limit returned content to this mimetype or mimepart
* @return FileInfo[]
*/
- public function getDirectoryContent($directory, $mimetype_filter = '') {
+ public function getDirectoryContent($directory, $mimetype_filter = '', \OCP\Files\FileInfo $directoryInfo = null) {
$this->assertPathLength($directory);
if (!Filesystem::isValidPath($directory)) {
return [];
}
+
$path = $this->getAbsolutePath($directory);
$path = Filesystem::normalizePath($path);
$mount = $this->getMount($directory);
- if (!$mount) {
- return [];
- }
$storage = $mount->getStorage();
$internalPath = $mount->getInternalPath($path);
- if ($storage) {
- $cache = $storage->getCache($internalPath);
- $user = \OC_User::getUser();
+ if (!$storage) {
+ return [];
+ }
- $data = $this->getCacheEntry($storage, $internalPath, $directory);
+ $cache = $storage->getCache($internalPath);
+ $user = \OC_User::getUser();
- if (!$data instanceof ICacheEntry || !isset($data['fileid']) || !($data->getPermissions() && Constants::PERMISSION_READ)) {
+ if (!$directoryInfo) {
+ $data = $this->getCacheEntry($storage, $internalPath, $directory);
+ if (!$data instanceof ICacheEntry || !isset($data['fileid'])) {
return [];
}
+ } else {
+ $data = $directoryInfo;
+ }
- $folderId = $data['fileid'];
- $contents = $cache->getFolderContentsById($folderId); //TODO: mimetype_filter
+ if (!($data->getPermissions() & Constants::PERMISSION_READ)) {
+ return [];
+ }
- $sharingDisabled = \OCP\Util::isSharingDisabledForUser();
+ $folderId = $data->getId();
+ $contents = $cache->getFolderContentsById($folderId); //TODO: mimetype_filter
- $fileNames = array_map(function (ICacheEntry $content) {
- return $content->getName();
- }, $contents);
- /**
- * @var \OC\Files\FileInfo[] $fileInfos
- */
- $fileInfos = array_map(function (ICacheEntry $content) use ($path, $storage, $mount, $sharingDisabled) {
- if ($sharingDisabled) {
- $content['permissions'] = $content['permissions'] & ~\OCP\Constants::PERMISSION_SHARE;
- }
- $owner = $this->getUserObjectForOwner($storage->getOwner($content['path']));
- return new FileInfo($path . '/' . $content['name'], $storage, $content['path'], $content, $mount, $owner);
- }, $contents);
- $files = array_combine($fileNames, $fileInfos);
-
- //add a folder for any mountpoint in this directory and add the sizes of other mountpoints to the folders
- $mounts = Filesystem::getMountManager()->findIn($path);
- $dirLength = strlen($path);
- foreach ($mounts as $mount) {
- $mountPoint = $mount->getMountPoint();
- $subStorage = $mount->getStorage();
- if ($subStorage) {
- $subCache = $subStorage->getCache('');
+ $sharingDisabled = \OCP\Util::isSharingDisabledForUser();
- $rootEntry = $subCache->get('');
- if (!$rootEntry) {
- $subScanner = $subStorage->getScanner();
- try {
- $subScanner->scanFile('');
- } catch (\OCP\Files\StorageNotAvailableException $e) {
- continue;
- } catch (\OCP\Files\StorageInvalidException $e) {
- continue;
- } catch (\Exception $e) {
- // sometimes when the storage is not available it can be any exception
- \OC::$server->getLogger()->logException($e, [
- 'message' => 'Exception while scanning storage "' . $subStorage->getId() . '"',
- 'level' => ILogger::ERROR,
- 'app' => 'lib',
- ]);
- continue;
- }
- $rootEntry = $subCache->get('');
+ $fileNames = array_map(function (ICacheEntry $content) {
+ return $content->getName();
+ }, $contents);
+ /**
+ * @var \OC\Files\FileInfo[] $fileInfos
+ */
+ $fileInfos = array_map(function (ICacheEntry $content) use ($path, $storage, $mount, $sharingDisabled) {
+ if ($sharingDisabled) {
+ $content['permissions'] = $content['permissions'] & ~\OCP\Constants::PERMISSION_SHARE;
+ }
+ $owner = $this->getUserObjectForOwner($storage->getOwner($content['path']));
+ return new FileInfo($path . '/' . $content['name'], $storage, $content['path'], $content, $mount, $owner);
+ }, $contents);
+ $files = array_combine($fileNames, $fileInfos);
+
+ //add a folder for any mountpoint in this directory and add the sizes of other mountpoints to the folders
+ $mounts = Filesystem::getMountManager()->findIn($path);
+ $dirLength = strlen($path);
+ foreach ($mounts as $mount) {
+ $mountPoint = $mount->getMountPoint();
+ $subStorage = $mount->getStorage();
+ if ($subStorage) {
+ $subCache = $subStorage->getCache('');
+
+ $rootEntry = $subCache->get('');
+ if (!$rootEntry) {
+ $subScanner = $subStorage->getScanner();
+ try {
+ $subScanner->scanFile('');
+ } catch (\OCP\Files\StorageNotAvailableException $e) {
+ continue;
+ } catch (\OCP\Files\StorageInvalidException $e) {
+ continue;
+ } catch (\Exception $e) {
+ // sometimes when the storage is not available it can be any exception
+ \OC::$server->getLogger()->logException($e, [
+ 'message' => 'Exception while scanning storage "' . $subStorage->getId() . '"',
+ 'level' => ILogger::ERROR,
+ 'app' => 'lib',
+ ]);
+ continue;
}
+ $rootEntry = $subCache->get('');
+ }
- if ($rootEntry && ($rootEntry->getPermissions() && Constants::PERMISSION_READ)) {
- $relativePath = trim(substr($mountPoint, $dirLength), '/');
- if ($pos = strpos($relativePath, '/')) {
- //mountpoint inside subfolder add size to the correct folder
- $entryName = substr($relativePath, 0, $pos);
- foreach ($files as &$entry) {
- if ($entry->getName() === $entryName) {
- $entry->addSubEntry($rootEntry, $mountPoint);
- }
- }
- } else { //mountpoint in this folder, add an entry for it
- $rootEntry['name'] = $relativePath;
- $rootEntry['type'] = $rootEntry['mimetype'] === 'httpd/unix-directory' ? 'dir' : 'file';
- $permissions = $rootEntry['permissions'];
- // do not allow renaming/deleting the mount point if they are not shared files/folders
- // for shared files/folders we use the permissions given by the owner
- if ($mount instanceof MoveableMount) {
- $rootEntry['permissions'] = $permissions | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE;
- } else {
- $rootEntry['permissions'] = $permissions & (\OCP\Constants::PERMISSION_ALL - (\OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE));
+ if ($rootEntry && ($rootEntry->getPermissions() & Constants::PERMISSION_READ)) {
+ $relativePath = trim(substr($mountPoint, $dirLength), '/');
+ if ($pos = strpos($relativePath, '/')) {
+ //mountpoint inside subfolder add size to the correct folder
+ $entryName = substr($relativePath, 0, $pos);
+ foreach ($files as &$entry) {
+ if ($entry->getName() === $entryName) {
+ $entry->addSubEntry($rootEntry, $mountPoint);
}
+ }
+ } else { //mountpoint in this folder, add an entry for it
+ $rootEntry['name'] = $relativePath;
+ $rootEntry['type'] = $rootEntry['mimetype'] === 'httpd/unix-directory' ? 'dir' : 'file';
+ $permissions = $rootEntry['permissions'];
+ // do not allow renaming/deleting the mount point if they are not shared files/folders
+ // for shared files/folders we use the permissions given by the owner
+ if ($mount instanceof MoveableMount) {
+ $rootEntry['permissions'] = $permissions | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE;
+ } else {
+ $rootEntry['permissions'] = $permissions & (\OCP\Constants::PERMISSION_ALL - (\OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE));
+ }
- $rootEntry['path'] = substr(Filesystem::normalizePath($path . '/' . $rootEntry['name']), strlen($user) + 2); // full path without /$user/
-
- // if sharing was disabled for the user we remove the share permissions
- if (\OCP\Util::isSharingDisabledForUser()) {
- $rootEntry['permissions'] = $rootEntry['permissions'] & ~\OCP\Constants::PERMISSION_SHARE;
- }
+ $rootEntry['path'] = substr(Filesystem::normalizePath($path . '/' . $rootEntry['name']), strlen($user) + 2); // full path without /$user/
- $owner = $this->getUserObjectForOwner($subStorage->getOwner(''));
- $files[$rootEntry->getName()] = new FileInfo($path . '/' . $rootEntry['name'], $subStorage, '', $rootEntry, $mount, $owner);
+ // if sharing was disabled for the user we remove the share permissions
+ if (\OCP\Util::isSharingDisabledForUser()) {
+ $rootEntry['permissions'] = $rootEntry['permissions'] & ~\OCP\Constants::PERMISSION_SHARE;
}
- }
- }
- }
- if ($mimetype_filter) {
- $files = array_filter($files, function (FileInfo $file) use ($mimetype_filter) {
- if (strpos($mimetype_filter, '/')) {
- return $file->getMimetype() === $mimetype_filter;
- } else {
- return $file->getMimePart() === $mimetype_filter;
+ $owner = $this->getUserObjectForOwner($subStorage->getOwner(''));
+ $files[$rootEntry->getName()] = new FileInfo($path . '/' . $rootEntry['name'], $subStorage, '', $rootEntry, $mount, $owner);
}
- });
+ }
}
+ }
- return array_values($files);
- } else {
- return [];
+ if ($mimetype_filter) {
+ $files = array_filter($files, function (FileInfo $file) use ($mimetype_filter) {
+ if (strpos($mimetype_filter, '/')) {
+ return $file->getMimetype() === $mimetype_filter;
+ } else {
+ return $file->getMimePart() === $mimetype_filter;
+ }
+ });
}
+
+ return array_values($files);
}
/**
diff --git a/lib/private/Group/Group.php b/lib/private/Group/Group.php
index 9a9996f7f60..2ef4d2ee23f 100644
--- a/lib/private/Group/Group.php
+++ b/lib/private/Group/Group.php
@@ -354,8 +354,7 @@ class Group implements IGroup {
}
foreach ($this->backends as $backend) {
if ($backend->implementsActions(\OC\Group\Backend::DELETE_GROUP)) {
- $result = true;
- $backend->deleteGroup($this->gid);
+ $result = $result || $backend->deleteGroup($this->gid);
}
}
if ($result) {
diff --git a/lib/private/Preview/Generator.php b/lib/private/Preview/Generator.php
index a770fe3b459..cef3fa4039a 100644
--- a/lib/private/Preview/Generator.php
+++ b/lib/private/Preview/Generator.php
@@ -137,9 +137,10 @@ class Generator {
$previewVersion = $file->getPreviewVersion() . '-';
}
+ // If imaginary is enabled, and we request a small thumbnail,
+ // let's not generate the max preview for performance reasons
if (count($specifications) === 1
- && (($specifications[0]['width'] === 250 && $specifications[0]['height'] === 250)
- || ($specifications[0]['width'] === 150 && $specifications[0]['height'] === 150))
+ && ($specifications[0]['width'] <= 256 || $specifications[0]['height'] <= 256)
&& preg_match(Imaginary::supportedMimeTypes(), $mimeType)
&& $this->config->getSystemValueString('preview_imaginary_url', 'invalid') !== 'invalid') {
$crop = $specifications[0]['crop'] ?? false;
@@ -221,6 +222,10 @@ class Generator {
return $preview;
}
+ /**
+ * Generate a small image straight away without generating a max preview first
+ * Preview generated is 256x256
+ */
private function getSmallImagePreview(ISimpleFolder $previewFolder, File $file, string $mimeType, string $prefix, bool $crop) {
$nodes = $previewFolder->getDirectoryListing();
diff --git a/lib/private/Preview/Imaginary.php b/lib/private/Preview/Imaginary.php
index 7e6ce86d4eb..4da88f1ab26 100644
--- a/lib/private/Preview/Imaginary.php
+++ b/lib/private/Preview/Imaginary.php
@@ -89,18 +89,26 @@ class Imaginary extends ProviderV2 {
$mimeType = 'jpeg';
}
- $parameters = [
- 'width' => $maxX,
- 'height' => $maxY,
- 'stripmeta' => 'true',
- 'type' => $mimeType,
+ $operations = [
+ [
+ 'operation' => 'autorotate',
+ ],
+ [
+ 'operation' => ($crop ? 'smartcrop' : 'fit'),
+ 'params' => [
+ 'width' => $maxX,
+ 'height' => $maxY,
+ 'stripmeta' => 'true',
+ 'type' => $mimeType,
+ 'norotation' => 'true',
+ ]
+ ]
];
-
try {
$response = $httpClient->post(
- $imaginaryUrl . ($crop ? '/smartcrop' : '/fit'), [
- 'query' => $parameters,
+ $imaginaryUrl . '/pipeline', [
+ 'query' => ['operations' => json_encode($operations)],
'stream' => true,
'content-type' => $file->getMimeType(),
'body' => $stream,
diff --git a/lib/private/Share20/Manager.php b/lib/private/Share20/Manager.php
index 2a76ddafb25..3fca9e3fe14 100644
--- a/lib/private/Share20/Manager.php
+++ b/lib/private/Share20/Manager.php
@@ -1783,9 +1783,21 @@ class Manager implements IManager {
/**
* Is password on public link requires
*
+ * @param bool Check group membership exclusion
* @return bool
*/
- public function shareApiLinkEnforcePassword() {
+ public function shareApiLinkEnforcePassword(bool $checkGroupMembership = true) {
+ $excludedGroups = $this->config->getAppValue('core', 'shareapi_enforce_links_password_excluded_groups', '');
+ if ($excludedGroups !== '' && $checkGroupMembership) {
+ $excludedGroups = json_decode($excludedGroups);
+ $user = $this->userSession->getUser();
+ if ($user) {
+ $userGroups = $this->groupManager->getUserGroupIds($user);
+ if ((bool)array_intersect($excludedGroups, $userGroups)) {
+ return false;
+ }
+ }
+ }
return $this->config->getAppValue('core', 'shareapi_enforce_links_password', 'no') === 'yes';
}
diff --git a/lib/private/Streamer.php b/lib/private/Streamer.php
index 176334b971d..80ab5a5524c 100644
--- a/lib/private/Streamer.php
+++ b/lib/private/Streamer.php
@@ -95,6 +95,7 @@ class Streamer {
* @param string $name
*/
public function sendHeaders($name) {
+ header('X-Accel-Buffering: no');
$extension = $this->streamerInstance instanceof ZipStreamer ? '.zip' : '.tar';
$fullName = $name . $extension;
$this->streamerInstance->sendHeaders($fullName);
diff --git a/lib/private/TemplateLayout.php b/lib/private/TemplateLayout.php
index cf06f021590..6d90d46ec67 100644
--- a/lib/private/TemplateLayout.php
+++ b/lib/private/TemplateLayout.php
@@ -47,7 +47,6 @@ use OC\Search\SearchQuery;
use OC\Template\JSCombiner;
use OC\Template\JSConfigHelper;
use OC\Template\SCSSCacher;
-use OCP\App\IAppManager;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\Defaults;
use OCP\IConfig;
@@ -55,7 +54,6 @@ use OCP\IInitialStateService;
use OCP\INavigationManager;
use OCP\IUserSession;
use OCP\Support\Subscription\IRegistry;
-use OCP\UserStatus\IManager as IUserStatusManager;
use OCP\Util;
use Psr\Log\LoggerInterface;
@@ -140,17 +138,6 @@ class TemplateLayout extends \OC_Template {
} else {
$this->assign('userAvatarSet', true);
$this->assign('userAvatarVersion', $this->config->getUserValue(\OC_User::getUser(), 'avatar', 'version', 0));
- if (\OC::$server->get(IAppManager::class)->isEnabledForUser('user_status')) {
- $userStatusManager = \OC::$server->get(IUserStatusManager::class);
- $userStatuses = $userStatusManager->getUserStatuses([$user->getUID()]);
- if (array_key_exists($user->getUID(), $userStatuses)) {
- $this->assign('userStatus', $userStatuses[$user->getUID()]);
- } else {
- $this->assign('userStatus', false);
- }
- } else {
- $this->assign('userStatus', false);
- }
}
// check if app menu icons should be inverted
diff --git a/lib/private/legacy/OC_App.php b/lib/private/legacy/OC_App.php
index ca01e91216b..f290b7a610c 100644
--- a/lib/private/legacy/OC_App.php
+++ b/lib/private/legacy/OC_App.php
@@ -629,11 +629,21 @@ class OC_App {
* @return string
*/
public static function getCurrentApp(): string {
+ if (\OC::$CLI) {
+ return '';
+ }
+
$request = \OC::$server->getRequest();
$script = substr($request->getScriptName(), strlen(OC::$WEBROOT) + 1);
$topFolder = substr($script, 0, strpos($script, '/') ?: 0);
if (empty($topFolder)) {
- $path_info = $request->getPathInfo();
+ try {
+ $path_info = $request->getPathInfo();
+ } catch (Exception $e) {
+ // Can happen from unit tests because the script name is `./vendor/bin/phpunit` or something a like then.
+ \OC::$server->get(LoggerInterface::class)->error('Failed to detect current app from script path', ['exception' => $e]);
+ return '';
+ }
if ($path_info) {
$topFolder = substr($path_info, 1, strpos($path_info, '/', 1) - 1);
}
diff --git a/lib/private/legacy/OC_Files.php b/lib/private/legacy/OC_Files.php
index d1af5b24bdd..41ac20577b2 100644
--- a/lib/private/legacy/OC_Files.php
+++ b/lib/private/legacy/OC_Files.php
@@ -98,6 +98,7 @@ class OC_Files {
}
}
header('Content-Type: '.$type, true);
+ header('X-Accel-Buffering: no');
}
/**
diff --git a/lib/private/legacy/OC_Helper.php b/lib/private/legacy/OC_Helper.php
index efb9252e346..547ffef8607 100644
--- a/lib/private/legacy/OC_Helper.php
+++ b/lib/private/legacy/OC_Helper.php
@@ -485,7 +485,7 @@ class OC_Helper {
* @return array
* @throws \OCP\Files\NotFoundException
*/
- public static function getStorageInfo($path, $rootInfo = null) {
+ public static function getStorageInfo($path, $rootInfo = null, $includeMountPoints = true) {
// return storage info without adding mount points
$includeExtStorage = \OC::$server->getSystemConfig()->getValue('quota_include_external_storage', false);
@@ -495,7 +495,7 @@ class OC_Helper {
if (!$rootInfo instanceof \OCP\Files\FileInfo) {
throw new \OCP\Files\NotFoundException();
}
- $used = $rootInfo->getSize();
+ $used = $rootInfo->getSize($includeMountPoints);
if ($used < 0) {
$used = 0;
}
diff --git a/lib/private/legacy/OC_Util.php b/lib/private/legacy/OC_Util.php
index ceed79bc9d5..edee23995f1 100644
--- a/lib/private/legacy/OC_Util.php
+++ b/lib/private/legacy/OC_Util.php
@@ -116,15 +116,16 @@ class OC_Util {
}
/**
- * check if a password is required for each public link
+ * Check if a password is required for each public link
*
+ * @param bool $checkGroupMembership Check group membership exclusion
* @return boolean
* @suppress PhanDeprecatedFunction
*/
- public static function isPublicLinkPasswordRequired() {
+ public static function isPublicLinkPasswordRequired(bool $checkGroupMembership = true) {
/** @var IManager $shareManager */
$shareManager = \OC::$server->get(IManager::class);
- return $shareManager->shareApiLinkEnforcePassword();
+ return $shareManager->shareApiLinkEnforcePassword($checkGroupMembership);
}
/**
diff --git a/lib/public/Files/Config/IMountProviderCollection.php b/lib/public/Files/Config/IMountProviderCollection.php
index 5894d06a388..2d42246b863 100644
--- a/lib/public/Files/Config/IMountProviderCollection.php
+++ b/lib/public/Files/Config/IMountProviderCollection.php
@@ -42,11 +42,11 @@ interface IMountProviderCollection {
* Get the configured mount points for the user from a specific mount provider
*
* @param \OCP\IUser $user
- * @param class-string<IMountProvider> $mountProviderClass
+ * @param class-string<IMountProvider>[] $mountProviderClasses
* @return \OCP\Files\Mount\IMountPoint[]
* @since 24.0.0
*/
- public function getUserMountsForProviderClass(IUser $user, string $mountProviderClass);
+ public function getUserMountsForProviderClasses(IUser $user, array $mountProviderClasses): array;
/**
* Get the configured home mount for this user
diff --git a/lib/public/Files/IRootFolder.php b/lib/public/Files/IRootFolder.php
index f89a0041146..7d007cb690c 100644
--- a/lib/public/Files/IRootFolder.php
+++ b/lib/public/Files/IRootFolder.php
@@ -38,11 +38,22 @@ interface IRootFolder extends Folder, Emitter {
* Returns a view to user's files folder
*
* @param string $userId user ID
- * @return \OCP\Files\Folder
+ * @return Folder
* @throws NoUserException
* @throws NotPermittedException
*
* @since 8.2.0
*/
public function getUserFolder($userId);
+
+ /**
+ * Get a file or folder by fileid, inside a parent path
+ *
+ * @param int $id
+ * @param string $path
+ * @return Node[]
+ *
+ * @since 24.0.0
+ */
+ public function getByIdInPath(int $id, string $path);
}
diff --git a/lib/public/Share/IManager.php b/lib/public/Share/IManager.php
index ff4b6af19e0..f6b74c4de4a 100644
--- a/lib/public/Share/IManager.php
+++ b/lib/public/Share/IManager.php
@@ -321,10 +321,12 @@ interface IManager {
/**
* Is password on public link requires
*
+ * @param bool $checkGroupMembership Check group membership exclusion
* @return bool
* @since 9.0.0
+ * @since 24.0.0 Added optional $checkGroupMembership parameter
*/
- public function shareApiLinkEnforcePassword();
+ public function shareApiLinkEnforcePassword(bool $checkGroupMembership = true);
/**
* Is default expire date enabled
diff --git a/lib/public/User/Events/UserLiveStatusEvent.php b/lib/public/User/Events/UserLiveStatusEvent.php
index dd90400cb3b..4bba71f95b9 100644
--- a/lib/public/User/Events/UserLiveStatusEvent.php
+++ b/lib/public/User/Events/UserLiveStatusEvent.php
@@ -27,6 +27,7 @@ namespace OCP\User\Events;
use OCP\EventDispatcher\Event;
use OCP\IUser;
+use OCP\UserStatus\IUserStatus;
/**
* @since 20.0.0
@@ -51,19 +52,12 @@ class UserLiveStatusEvent extends Event {
*/
public const STATUS_OFFLINE = 'offline';
- /** @var IUser */
- private $user;
-
- /** @var string */
- private $status;
-
- /** @var int */
- private $timestamp;
+ private IUser $user;
+ private string $status;
+ private int $timestamp;
+ private ?IUserStatus $userStatus = null;
/**
- * @param IUser $user
- * @param string $status
- * @param int $timestamp
* @since 20.0.0
*/
public function __construct(IUser $user,
@@ -98,4 +92,19 @@ class UserLiveStatusEvent extends Event {
public function getTimestamp(): int {
return $this->timestamp;
}
+
+ /**
+ * Get the user status that might be available after processing the event
+ * @since 24.0.0
+ */
+ public function getUserStatus(): ?IUserStatus {
+ return $this->userStatus;
+ }
+
+ /**
+ * @since 24.0.0
+ */
+ public function setUserStatus(IUserStatus $userStatus) {
+ $this->userStatus = $userStatus;
+ }
}
diff --git a/lib/public/Util.php b/lib/public/Util.php
index cd6f5f34a69..c8b55bb10e2 100644
--- a/lib/public/Util.php
+++ b/lib/public/Util.php
@@ -547,12 +547,14 @@ class Util {
}
/**
- * check if a password is required for each public link
+ * Check if a password is required for each public link
+ *
+ * @param bool $checkGroupMembership Check group membership exclusion
* @return boolean
* @since 7.0.0
*/
- public static function isPublicLinkPasswordRequired() {
- return \OC_Util::isPublicLinkPasswordRequired();
+ public static function isPublicLinkPasswordRequired(bool $checkGroupMembership = true) {
+ return \OC_Util::isPublicLinkPasswordRequired($checkGroupMembership);
}
/**