diff options
42 files changed, 350 insertions, 59 deletions
diff --git a/.github/workflows/s3-primary.yml b/.github/workflows/s3-primary.yml new file mode 100644 index 00000000000..3ea21b768ec --- /dev/null +++ b/.github/workflows/s3-primary.yml @@ -0,0 +1,70 @@ +name: S3 primary storage +on: + pull_request: + push: + branches: + - master + - stable* + + +jobs: + s3-primary-tests-minio: + runs-on: ubuntu-latest + + strategy: + # do not stop on another job's failure + fail-fast: false + matrix: + php-versions: ['8.0'] + key: ['objectstore', 'objectstore_multibucket'] + + name: php${{ matrix.php-versions }}-${{ matrix.key }}-minio + + services: + minio: + env: + MINIO_ACCESS_KEY: minio + MINIO_SECRET_KEY: minio123 + image: bitnami/minio:2021.10.6 + ports: + - "9000:9000" + + steps: + - name: Checkout server + uses: actions/checkout@v2 + with: + submodules: true + + - name: Set up php ${{ matrix.php-versions }} + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + tools: phpunit + extensions: mbstring, fileinfo, intl, sqlite, pdo_sqlite, zip, gd + + - name: Set up Nextcloud + run: | + mkdir data + echo '<?php $CONFIG=["${{ matrix.key }}" => ["class" => "OC\Files\ObjectStore\S3", "arguments" => ["bucket" => "nextcloud", "autocreate" => true, "key" => "minio", "secret" => "minio123", "hostname" => "localhost", "port" => 9000, "use_ssl" => false, "use_path_style" => true, "uploadPartSize" => 52428800]]];' > config/config.php + ./occ maintenance:install --verbose --database=sqlite --database-name=nextcloud --database-host=127.0.0.1 --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass password + php -f index.php + + - name: PHPUnit + working-directory: tests + run: phpunit --configuration phpunit-autotest.xml --group DB,SLOWDB + - name: S3 logs + if: always() + run: | + docker ps -a + docker logs $(docker ps -aq) + + + s3-primary-summary: + runs-on: ubuntu-latest + needs: [s3-primary-tests-minio] + + if: always() + + steps: + - name: Summary status + run: if ${{ needs.s3-primary-tests-minio.result != 'success' }}; then exit 1; fi diff --git a/apps/admin_audit/composer/composer/autoload_classmap.php b/apps/admin_audit/composer/composer/autoload_classmap.php index 2373bb90797..fc4be52ebbb 100644 --- a/apps/admin_audit/composer/composer/autoload_classmap.php +++ b/apps/admin_audit/composer/composer/autoload_classmap.php @@ -19,6 +19,8 @@ return array( 'OCA\\AdminAudit\\Actions\\UserManagement' => $baseDir . '/../lib/Actions/UserManagement.php', 'OCA\\AdminAudit\\Actions\\Versions' => $baseDir . '/../lib/Actions/Versions.php', 'OCA\\AdminAudit\\AppInfo\\Application' => $baseDir . '/../lib/AppInfo/Application.php', + 'OCA\\AdminAudit\\AuditLogger' => $baseDir . '/../lib/AuditLogger.php', 'OCA\\AdminAudit\\BackgroundJobs\\Rotate' => $baseDir . '/../lib/BackgroundJobs/Rotate.php', + 'OCA\\AdminAudit\\IAuditLogger' => $baseDir . '/../lib/IAuditLogger.php', 'OCA\\AdminAudit\\Listener\\CriticalActionPerformedEventListener' => $baseDir . '/../lib/Listener/CriticalActionPerformedEventListener.php', ); diff --git a/apps/admin_audit/composer/composer/autoload_static.php b/apps/admin_audit/composer/composer/autoload_static.php index 829bc2ab049..38518c8a9ba 100644 --- a/apps/admin_audit/composer/composer/autoload_static.php +++ b/apps/admin_audit/composer/composer/autoload_static.php @@ -34,7 +34,9 @@ class ComposerStaticInitAdminAudit 'OCA\\AdminAudit\\Actions\\UserManagement' => __DIR__ . '/..' . '/../lib/Actions/UserManagement.php', 'OCA\\AdminAudit\\Actions\\Versions' => __DIR__ . '/..' . '/../lib/Actions/Versions.php', 'OCA\\AdminAudit\\AppInfo\\Application' => __DIR__ . '/..' . '/../lib/AppInfo/Application.php', + 'OCA\\AdminAudit\\AuditLogger' => __DIR__ . '/..' . '/../lib/AuditLogger.php', 'OCA\\AdminAudit\\BackgroundJobs\\Rotate' => __DIR__ . '/..' . '/../lib/BackgroundJobs/Rotate.php', + 'OCA\\AdminAudit\\IAuditLogger' => __DIR__ . '/..' . '/../lib/IAuditLogger.php', 'OCA\\AdminAudit\\Listener\\CriticalActionPerformedEventListener' => __DIR__ . '/..' . '/../lib/Listener/CriticalActionPerformedEventListener.php', ); diff --git a/apps/admin_audit/lib/Actions/Action.php b/apps/admin_audit/lib/Actions/Action.php index 17be0fb8197..0eaf06b8c0f 100644 --- a/apps/admin_audit/lib/Actions/Action.php +++ b/apps/admin_audit/lib/Actions/Action.php @@ -28,13 +28,13 @@ declare(strict_types=1); */ namespace OCA\AdminAudit\Actions; -use Psr\Log\LoggerInterface; +use OCA\AdminAudit\IAuditLogger; class Action { - /** @var LoggerInterface */ + /** @var IAuditLogger */ private $logger; - public function __construct(LoggerInterface $logger) { + public function __construct(IAuditLogger $logger) { $this->logger = $logger; } diff --git a/apps/admin_audit/lib/AppInfo/Application.php b/apps/admin_audit/lib/AppInfo/Application.php index 594e1c7f2c4..1160d151710 100644 --- a/apps/admin_audit/lib/AppInfo/Application.php +++ b/apps/admin_audit/lib/AppInfo/Application.php @@ -48,6 +48,8 @@ use OCA\AdminAudit\Actions\Sharing; use OCA\AdminAudit\Actions\Trashbin; use OCA\AdminAudit\Actions\UserManagement; use OCA\AdminAudit\Actions\Versions; +use OCA\AdminAudit\AuditLogger; +use OCA\AdminAudit\IAuditLogger; use OCA\AdminAudit\Listener\CriticalActionPerformedEventListener; use OCP\App\ManagerEvent; use OCP\AppFramework\App; @@ -65,6 +67,7 @@ use OCP\Log\Audit\CriticalActionPerformedEvent; use OCP\Log\ILogFactory; use OCP\Share; use OCP\Util; +use Psr\Container\ContainerInterface; use Psr\Log\LoggerInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\GenericEvent; @@ -79,14 +82,16 @@ class Application extends App implements IBootstrap { } public function register(IRegistrationContext $context): void { + $context->registerService(IAuditLogger::class, function (ContainerInterface $c) { + return new AuditLogger($c->get(ILogFactory::class), $c->get(Iconfig::class)); + }); + $context->registerEventListener(CriticalActionPerformedEvent::class, CriticalActionPerformedEventListener::class); } public function boot(IBootContext $context): void { - /** @var LoggerInterface $logger */ - $logger = $context->injectFn( - Closure::fromCallable([$this, 'getLogger']) - ); + /** @var IAuditLogger $logger */ + $logger = $context->getAppContainer()->get(IAuditLogger::class); /* * TODO: once the hooks are migrated to lazy events, this should be done @@ -95,26 +100,10 @@ class Application extends App implements IBootstrap { $this->registerHooks($logger, $context->getServerContainer()); } - private function getLogger(IConfig $config, - ILogFactory $logFactory): LoggerInterface { - $auditType = $config->getSystemValueString('log_type_audit', 'file'); - $defaultTag = $config->getSystemValueString('syslog_tag', 'Nextcloud'); - $auditTag = $config->getSystemValueString('syslog_tag_audit', $defaultTag); - $logFile = $config->getSystemValueString('logfile_audit', ''); - - if ($auditType === 'file' && !$logFile) { - $default = $config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/audit.log'; - // Legacy way was appconfig, now it's paralleled with the normal log config - $logFile = $config->getAppValue('admin_audit', 'logfile', $default); - } - - return $logFactory->getCustomPsrLogger($logFile, $auditType, $auditTag); - } - /** * Register hooks in order to log them */ - private function registerHooks(LoggerInterface $logger, + private function registerHooks(IAuditLogger $logger, IServerContainer $serverContainer): void { $this->userManagementHooks($logger, $serverContainer->get(IUserSession::class)); $this->groupHooks($logger, $serverContainer->get(IGroupManager::class)); @@ -134,7 +123,7 @@ class Application extends App implements IBootstrap { $this->securityHooks($logger, $eventDispatcher); } - private function userManagementHooks(LoggerInterface $logger, + private function userManagementHooks(IAuditLogger $logger, IUserSession $userSession): void { $userActions = new UserManagement($logger); @@ -148,7 +137,7 @@ class Application extends App implements IBootstrap { $userSession->listen('\OC\User', 'postUnassignedUserId', [$userActions, 'unassign']); } - private function groupHooks(LoggerInterface $logger, + private function groupHooks(IAuditLogger $logger, IGroupManager $groupManager): void { $groupActions = new GroupManagement($logger); @@ -159,7 +148,7 @@ class Application extends App implements IBootstrap { $groupManager->listen('\OC\Group', 'postCreate', [$groupActions, 'createGroup']); } - private function sharingHooks(LoggerInterface $logger): void { + private function sharingHooks(IAuditLogger $logger): void { $shareActions = new Sharing($logger); Util::connectHook(Share::class, 'post_shared', $shareActions, 'shared'); @@ -171,7 +160,7 @@ class Application extends App implements IBootstrap { Util::connectHook(Share::class, 'share_link_access', $shareActions, 'shareAccessed'); } - private function authHooks(LoggerInterface $logger): void { + private function authHooks(IAuditLogger $logger): void { $authActions = new Auth($logger); Util::connectHook('OC_User', 'pre_login', $authActions, 'loginAttempt'); @@ -179,7 +168,7 @@ class Application extends App implements IBootstrap { Util::connectHook('OC_User', 'logout', $authActions, 'logout'); } - private function appHooks(LoggerInterface $logger, + private function appHooks(IAuditLogger $logger, EventDispatcherInterface $eventDispatcher): void { $eventDispatcher->addListener(ManagerEvent::EVENT_APP_ENABLE, function (ManagerEvent $event) use ($logger) { $appActions = new AppManagement($logger); @@ -195,7 +184,7 @@ class Application extends App implements IBootstrap { }); } - private function consoleHooks(LoggerInterface $logger, + private function consoleHooks(IAuditLogger $logger, EventDispatcherInterface $eventDispatcher): void { $eventDispatcher->addListener(ConsoleEvent::EVENT_RUN, function (ConsoleEvent $event) use ($logger) { $appActions = new Console($logger); @@ -203,7 +192,7 @@ class Application extends App implements IBootstrap { }); } - private function fileHooks(LoggerInterface $logger, + private function fileHooks(IAuditLogger $logger, EventDispatcherInterface $eventDispatcher): void { $fileActions = new Files($logger); $eventDispatcher->addListener( @@ -265,19 +254,19 @@ class Application extends App implements IBootstrap { ); } - private function versionsHooks(LoggerInterface $logger): void { + private function versionsHooks(IAuditLogger $logger): void { $versionsActions = new Versions($logger); Util::connectHook('\OCP\Versions', 'rollback', $versionsActions, 'rollback'); Util::connectHook('\OCP\Versions', 'delete', $versionsActions, 'delete'); } - private function trashbinHooks(LoggerInterface $logger): void { + private function trashbinHooks(IAuditLogger $logger): void { $trashActions = new Trashbin($logger); Util::connectHook('\OCP\Trashbin', 'preDelete', $trashActions, 'delete'); Util::connectHook('\OCA\Files_Trashbin\Trashbin', 'post_restore', $trashActions, 'restore'); } - private function securityHooks(LoggerInterface $logger, + private function securityHooks(IAuditLogger $logger, EventDispatcherInterface $eventDispatcher): void { $eventDispatcher->addListener(IProvider::EVENT_SUCCESS, function (GenericEvent $event) use ($logger) { $security = new Security($logger); diff --git a/apps/admin_audit/lib/AuditLogger.php b/apps/admin_audit/lib/AuditLogger.php new file mode 100644 index 00000000000..0a7a330a743 --- /dev/null +++ b/apps/admin_audit/lib/AuditLogger.php @@ -0,0 +1,88 @@ +<?php +/** + * @copyright Copyright (c) 2022 Carl Schwan <carl@carlschwan.eu> + * + * @author Carl Schwan <carl@carlschwan.eu> + * + * @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 OCA\AdminAudit; + +use OCP\IConfig; +use OCP\Log\ILogFactory; +use Psr\Log\LoggerInterface; + +/** + * Logger that logs in the audit log file instead of the normal log file + */ +class AuditLogger implements IAuditLogger { + + /** @var LoggerInterface */ + private $parentLogger; + + public function __construct(ILogFactory $logFactory, IConfig $config) { + $auditType = $config->getSystemValueString('log_type_audit', 'file'); + $defaultTag = $config->getSystemValueString('syslog_tag', 'Nextcloud'); + $auditTag = $config->getSystemValueString('syslog_tag_audit', $defaultTag); + $logFile = $config->getSystemValueString('logfile_audit', ''); + + if ($auditType === 'file' && !$logFile) { + $default = $config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/audit.log'; + // Legacy way was appconfig, now it's paralleled with the normal log config + $logFile = $config->getAppValue('admin_audit', 'logfile', $default); + } + + $this->parentLogger = $logFactory->getCustomPsrLogger($logFile, $auditType, $auditTag); + } + + public function emergency($message, array $context = array()) { + $this->parentLogger->emergency($message, $context); + } + + public function alert($message, array $context = array()) { + $this->parentLogger->alert($message, $context); + } + + public function critical($message, array $context = array()) { + $this->parentLogger->critical($message, $context); + } + + public function error($message, array $context = array()) { + $this->parentLogger->error($message, $context); + } + + public function warning($message, array $context = array()) { + $this->parentLogger->warning($message, $context); + } + + public function notice($message, array $context = array()) { + $this->parentLogger->notice($message, $context); + } + + public function info($message, array $context = array()) { + $this->parentLogger->info($message, $context); + } + + public function debug($message, array $context = array()) { + $this->parentLogger->debug($message, $context); + } + + public function log($level, $message, array $context = array()) { + $this->parentLogger->log($level, $message, $context); + } +} diff --git a/apps/admin_audit/lib/IAuditLogger.php b/apps/admin_audit/lib/IAuditLogger.php new file mode 100644 index 00000000000..b55d36b942d --- /dev/null +++ b/apps/admin_audit/lib/IAuditLogger.php @@ -0,0 +1,32 @@ +<?php +/** + * @copyright Copyright (c) 2022 Carl Schwan <carl@carlschwan.eu> + * + * @author Carl Schwan <carl@carlschwan.eu> + * + * @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 OCA\AdminAudit; + +use Psr\Log\LoggerInterface; + +/** + * Interface for a logger that logs in the audit log file instead of the normal log file + */ +interface IAuditLogger extends LoggerInterface { +} diff --git a/apps/admin_audit/tests/Actions/SecurityTest.php b/apps/admin_audit/tests/Actions/SecurityTest.php index aa9d9713768..604d2276fb2 100644 --- a/apps/admin_audit/tests/Actions/SecurityTest.php +++ b/apps/admin_audit/tests/Actions/SecurityTest.php @@ -44,7 +44,7 @@ class SecurityTest extends TestCase { protected function setUp(): void { parent::setUp(); - $this->logger = $this->createMock(LoggerInterface::class); + $this->logger = $this->createMock(AuditLogger::class); $this->security = new Security($this->logger); $this->user = $this->createMock(IUser::class); diff --git a/apps/dav/l10n/cs.js b/apps/dav/l10n/cs.js index 5b7ea906260..69d0de5f453 100644 --- a/apps/dav/l10n/cs.js +++ b/apps/dav/l10n/cs.js @@ -137,6 +137,7 @@ OC.L10N.register( "Due on %s by %s" : "Termín do %s od %s", "Due on %s" : "Termín do %s", "Migrated calendar (%1$s)" : "Přesunut kalendář (%1$s)", + "Calendars including events, details and attendees" : "Kalendáře včetně událostí, podrobností a účastníků", "Contacts and groups" : "Kontakty a skupiny", "WebDAV" : "WebDAV", "WebDAV endpoint" : "WebDAV endpoint", diff --git a/apps/dav/l10n/cs.json b/apps/dav/l10n/cs.json index 46d083fa035..c3e555519be 100644 --- a/apps/dav/l10n/cs.json +++ b/apps/dav/l10n/cs.json @@ -135,6 +135,7 @@ "Due on %s by %s" : "Termín do %s od %s", "Due on %s" : "Termín do %s", "Migrated calendar (%1$s)" : "Přesunut kalendář (%1$s)", + "Calendars including events, details and attendees" : "Kalendáře včetně událostí, podrobností a účastníků", "Contacts and groups" : "Kontakty a skupiny", "WebDAV" : "WebDAV", "WebDAV endpoint" : "WebDAV endpoint", diff --git a/apps/dav/l10n/pl.js b/apps/dav/l10n/pl.js index 03c14a1b50b..e9fd91268fc 100644 --- a/apps/dav/l10n/pl.js +++ b/apps/dav/l10n/pl.js @@ -137,6 +137,7 @@ OC.L10N.register( "Due on %s by %s" : "Na dzień %s w %s", "Due on %s" : "Na dzień %s", "Migrated calendar (%1$s)" : "Przeniesiony kalendarz (%1$s)", + "Calendars including events, details and attendees" : "Kalendarze zawierające wydarzenia, szczegóły i uczestników", "Contacts and groups" : "Kontakty i grupy", "WebDAV" : "WebDAV", "WebDAV endpoint" : "Adres WebDAV", diff --git a/apps/dav/l10n/pl.json b/apps/dav/l10n/pl.json index 147447993f1..4ea51401e82 100644 --- a/apps/dav/l10n/pl.json +++ b/apps/dav/l10n/pl.json @@ -135,6 +135,7 @@ "Due on %s by %s" : "Na dzień %s w %s", "Due on %s" : "Na dzień %s", "Migrated calendar (%1$s)" : "Przeniesiony kalendarz (%1$s)", + "Calendars including events, details and attendees" : "Kalendarze zawierające wydarzenia, szczegóły i uczestników", "Contacts and groups" : "Kontakty i grupy", "WebDAV" : "WebDAV", "WebDAV endpoint" : "Adres WebDAV", diff --git a/apps/dav/l10n/tr.js b/apps/dav/l10n/tr.js index 8f9f15d1de2..aa21694e64e 100644 --- a/apps/dav/l10n/tr.js +++ b/apps/dav/l10n/tr.js @@ -137,6 +137,8 @@ OC.L10N.register( "Due on %s by %s" : "%s tarihine kadar %s tarafından", "Due on %s" : "%s tarihine kadar", "Migrated calendar (%1$s)" : "Aktarılmış takvim (%1$s)", + "Calendars including events, details and attendees" : "Etkinlikler, bilgiler ve katılımcılar ile takvimler", + "Contacts and groups" : "Kişiler ve gruplar", "WebDAV" : "WebDAV", "WebDAV endpoint" : "WebDAV bağlantı noktası", "Availability" : "Kullanılabilirlik", diff --git a/apps/dav/l10n/tr.json b/apps/dav/l10n/tr.json index 7b640b0178b..b80217bfa8d 100644 --- a/apps/dav/l10n/tr.json +++ b/apps/dav/l10n/tr.json @@ -135,6 +135,8 @@ "Due on %s by %s" : "%s tarihine kadar %s tarafından", "Due on %s" : "%s tarihine kadar", "Migrated calendar (%1$s)" : "Aktarılmış takvim (%1$s)", + "Calendars including events, details and attendees" : "Etkinlikler, bilgiler ve katılımcılar ile takvimler", + "Contacts and groups" : "Kişiler ve gruplar", "WebDAV" : "WebDAV", "WebDAV endpoint" : "WebDAV bağlantı noktası", "Availability" : "Kullanılabilirlik", diff --git a/apps/dav/l10n/zh_TW.js b/apps/dav/l10n/zh_TW.js index 621c28b18bb..414eaad4c01 100644 --- a/apps/dav/l10n/zh_TW.js +++ b/apps/dav/l10n/zh_TW.js @@ -137,6 +137,7 @@ OC.L10N.register( "Due on %s by %s" : "到期於 %s 由 %s", "Due on %s" : "到期於 %s", "Migrated calendar (%1$s)" : "已導入的行事曆 (%1$s)", + "Calendars including events, details and attendees" : "行事曆,包含事件、詳細資訊及參與者", "Contacts and groups" : "聯絡人與群組", "WebDAV" : "WebDAV", "WebDAV endpoint" : "WebDAV 端點", diff --git a/apps/dav/l10n/zh_TW.json b/apps/dav/l10n/zh_TW.json index ff5ee940010..29dd4133fc5 100644 --- a/apps/dav/l10n/zh_TW.json +++ b/apps/dav/l10n/zh_TW.json @@ -135,6 +135,7 @@ "Due on %s by %s" : "到期於 %s 由 %s", "Due on %s" : "到期於 %s", "Migrated calendar (%1$s)" : "已導入的行事曆 (%1$s)", + "Calendars including events, details and attendees" : "行事曆,包含事件、詳細資訊及參與者", "Contacts and groups" : "聯絡人與群組", "WebDAV" : "WebDAV", "WebDAV endpoint" : "WebDAV 端點", diff --git a/apps/dav/lib/Connector/Sabre/File.php b/apps/dav/lib/Connector/Sabre/File.php index bf7e469a0c0..963a07a55fb 100644 --- a/apps/dav/lib/Connector/Sabre/File.php +++ b/apps/dav/lib/Connector/Sabre/File.php @@ -184,7 +184,23 @@ class File extends Node implements IFile { [$storage, $internalPath] = $this->fileView->resolvePath($this->path); try { if (!$needsPartFile) { - $this->changeLock(ILockingProvider::LOCK_EXCLUSIVE); + try { + $this->changeLock(ILockingProvider::LOCK_EXCLUSIVE); + } catch (LockedException $e) { + // during very large uploads, the shared lock we got at the start might have been expired + // meaning that the above lock can fail not just only because somebody else got a shared lock + // or because there is no existing shared lock to make exclusive + // + // Thus we try to get a new exclusive lock, if the original lock failed because of a different shared + // lock this will still fail, if our original shared lock expired the new lock will be successful and + // the entire operation will be safe + + try { + $this->acquireLock(ILockingProvider::LOCK_EXCLUSIVE); + } catch (LockedException $ex) { + throw new FileLocked($e->getMessage(), $e->getCode(), $e); + } + } } if (!is_resource($data)) { diff --git a/apps/files_sharing/tests/CacheTest.php b/apps/files_sharing/tests/CacheTest.php index 2623b4c34b5..f4f64bc6a32 100644 --- a/apps/files_sharing/tests/CacheTest.php +++ b/apps/files_sharing/tests/CacheTest.php @@ -243,6 +243,9 @@ class CacheTest extends TestCase { public function testGetFolderContentsInRoot() { $results = $this->user2View->getDirectoryContent('/'); + $results = (array_filter($results, function($file) { + return $file->getName() !== 'welcome.txt'; + })); // we should get the shared items "shareddir" and "shared single file.txt" // additional root will always contain the example file "welcome.txt", @@ -250,11 +253,6 @@ class CacheTest extends TestCase { $this->verifyFiles( [ [ - 'name' => 'welcome.txt', - 'path' => 'files/welcome.txt', - 'mimetype' => 'text/plain', - ], - [ 'name' => 'shareddir', 'path' => 'files/shareddir', 'mimetype' => 'httpd/unix-directory', diff --git a/apps/files_trashbin/l10n/tr.js b/apps/files_trashbin/l10n/tr.js index d2998f20d7b..f876ca47621 100644 --- a/apps/files_trashbin/l10n/tr.js +++ b/apps/files_trashbin/l10n/tr.js @@ -3,6 +3,7 @@ OC.L10N.register( { "Deleted files" : "Silinmiş dosyalar", "restored" : "geri yüklendi", + "Deleted files and folders in the trash bin" : "Çöp kutusundaki silinmiş dosya ve klasörler", "This application enables users to restore files that were deleted from the system." : "Bu uygulama kullanıcıların sistem üzerinde sildiği dosyaları geri yükleyebilmesini sağlar.", "This application enables users to restore files that were deleted from the system. It displays a list of deleted files in the web interface, and has options to restore those deleted files back to the users file directories or remove them permanently from the system. Restoring a file also restores related file versions, if the versions application is enabled. When a file is deleted from a share, it can be restored in the same manner, though it is no longer shared. By default, these files remain in the trash bin for 30 days.\nTo prevent a user from running out of disk space, the Deleted files app will not utilize more than 50% of the currently available free quota for deleted files. If the deleted files exceed this limit, the app deletes the oldest files until it gets below this limit. More information is available in the Deleted Files documentation." : "Bu uygulama kullanıcıların sistem üzerinde sildiği dosyaları geri yükleyebilmesini sağlar. Web arayüzünde silinmiş dosyaların listesini ve kullanıcı klasörlerine geri yükleme ya da kalıcı olarak silme seçeneklerini görüntüler. Sürümler uygulaması etkinleştirilmiş ise, geri yüklenen dosyaların önceki sürümleri de geri yüklenir. Paylaşım üzerinden silinen dosyalar da aynı şekilde ancak paylaşılmamış olarak geri yüklenebilir. Silinmiş dosyalar varsayılan olarak 30 gün boyunca çöp kutusunda tutulur.\nSilinmiş dosyalar uygulaması kullanıcıların disk alanının dolmasını engellemek için, kullanıcı depolama alanının en çok %50 oranındaki bölümünü kullanır. Silinmiş dosyaların boyutu bu sınırın üzerine çıkarsa, sınır değerine geri dönülene kadar en eski silinmiş dosyalar silinir. Ayrıntılı bilgi almak için Silinmiş Dosyalar uygulamasının belgelerine bakabilirsiniz.", "Restore" : "Geri yükle", diff --git a/apps/files_trashbin/l10n/tr.json b/apps/files_trashbin/l10n/tr.json index 6aa05e1e504..ab3847d0190 100644 --- a/apps/files_trashbin/l10n/tr.json +++ b/apps/files_trashbin/l10n/tr.json @@ -1,6 +1,7 @@ { "translations": { "Deleted files" : "Silinmiş dosyalar", "restored" : "geri yüklendi", + "Deleted files and folders in the trash bin" : "Çöp kutusundaki silinmiş dosya ve klasörler", "This application enables users to restore files that were deleted from the system." : "Bu uygulama kullanıcıların sistem üzerinde sildiği dosyaları geri yükleyebilmesini sağlar.", "This application enables users to restore files that were deleted from the system. It displays a list of deleted files in the web interface, and has options to restore those deleted files back to the users file directories or remove them permanently from the system. Restoring a file also restores related file versions, if the versions application is enabled. When a file is deleted from a share, it can be restored in the same manner, though it is no longer shared. By default, these files remain in the trash bin for 30 days.\nTo prevent a user from running out of disk space, the Deleted files app will not utilize more than 50% of the currently available free quota for deleted files. If the deleted files exceed this limit, the app deletes the oldest files until it gets below this limit. More information is available in the Deleted Files documentation." : "Bu uygulama kullanıcıların sistem üzerinde sildiği dosyaları geri yükleyebilmesini sağlar. Web arayüzünde silinmiş dosyaların listesini ve kullanıcı klasörlerine geri yükleme ya da kalıcı olarak silme seçeneklerini görüntüler. Sürümler uygulaması etkinleştirilmiş ise, geri yüklenen dosyaların önceki sürümleri de geri yüklenir. Paylaşım üzerinden silinen dosyalar da aynı şekilde ancak paylaşılmamış olarak geri yüklenebilir. Silinmiş dosyalar varsayılan olarak 30 gün boyunca çöp kutusunda tutulur.\nSilinmiş dosyalar uygulaması kullanıcıların disk alanının dolmasını engellemek için, kullanıcı depolama alanının en çok %50 oranındaki bölümünü kullanır. Silinmiş dosyaların boyutu bu sınırın üzerine çıkarsa, sınır değerine geri dönülene kadar en eski silinmiş dosyalar silinir. Ayrıntılı bilgi almak için Silinmiş Dosyalar uygulamasının belgelerine bakabilirsiniz.", "Restore" : "Geri yükle", diff --git a/apps/files_trashbin/tests/StorageTest.php b/apps/files_trashbin/tests/StorageTest.php index 448c3a6adb1..0a7a129ca28 100644 --- a/apps/files_trashbin/tests/StorageTest.php +++ b/apps/files_trashbin/tests/StorageTest.php @@ -33,6 +33,7 @@ namespace OCA\Files_Trashbin\Tests; use OC\Files\Filesystem; use OC\Files\Storage\Common; +use OC\Files\Storage\Local; use OC\Files\Storage\Temporary; use OCA\Files_Trashbin\AppInfo\Application; use OCA\Files_Trashbin\Events\MoveToTrashEvent; @@ -661,6 +662,9 @@ class StorageTest extends \Test\TestCase { } public function testMoveFromStoragePreserveFileId() { + if (!$this->userView->getMount('')->getStorage()->instanceOfStorage(Local::class)) { + $this->markTestSkipped("Skipping on non-local users storage"); + } $this->userView->file_put_contents('test.txt', 'foo'); $fileId = $this->userView->getFileInfo('test.txt')->getId(); diff --git a/apps/settings/l10n/tr.js b/apps/settings/l10n/tr.js index e2729142750..d4e49b47044 100644 --- a/apps/settings/l10n/tr.js +++ b/apps/settings/l10n/tr.js @@ -141,6 +141,8 @@ OC.L10N.register( "MariaDB version \"%s\" is used. Nextcloud 21 will no longer support this version and requires MariaDB 10.2 or higher." : "MariaDB \"%s\" sürümü kullanılıyor. Nextcloud 21 sürümünde bu sürüm desteklenmiyor. MariaDB 10.2 ya da üzerindeki bir sürüm kullanılmalıdır.", "MySQL version \"%s\" is used. Nextcloud 21 will no longer support this version and requires MySQL 8.0 or MariaDB 10.2 or higher." : "MySQL \"%s\" sürümü kullanılıyor. Nextcloud 21 sürümünde bu sürüm desteklenmiyor. MySQL 8 ya da MariaDB 10.2 üzerindeki bir sürüm kullanılmalıdır.", "PostgreSQL version \"%s\" is used. Nextcloud 21 will no longer support this version and requires PostgreSQL 9.6 or higher." : "PostgreSQL \"%s\" sürümü kullanılıyor. Nextcloud 21 sürümünde bu sürüm desteklenmiyor. PostgreSQL 9.6 ya da üzerindeki bir sürüm kullanılmalıdır.", + "Profile information" : "Profil bilgileri", + "Profile picture, full name, email, phone number, address, website, Twitter, organisation, role, headline, biography, and whether your profile is enabled" : "Profil görseli, tam ad, e-posta adresi, telefon numarası, adres, web sitesi, Twitter, kuruluş, rol, başlık, özgeçmi ve profilde etkinleştirilmiş diğer bilgiler", "Nextcloud settings" : "Nextcloud ayarları", "Administration privileges" : "Yönetim yetkileri", "Here you can decide which group can access certain sections of the administration settings." : "Hangi yönetici ayarlarına hangi grubun erişebileceğini bu bölümden belirleyebilirsiniz.", @@ -463,6 +465,8 @@ OC.L10N.register( "Allow username autocompletion to users within the same groups" : "Aynı gruplardaki kullanıcıların kullanıcı adları otomatik olarak tamamlanabilsin", "Allow username autocompletion to users based on phone number integration" : "Telefon numarası bütünleştirmesi eşleşmelerine göre kullanıcı adları otomatik olarak tamamlanabilsin", "If autocompletion \"same group\" and \"phone number integration\" are enabled a match in either is enough to show the user." : "\"Aynı grup\" ve \"telefon numarası bütünleştirmesi\" etkinleştirilmiş ise. Kullanıcının görüntülenmesi için ikisinden birinde eşleşme olması yeterlidir.", + "Allow autocompletion when entering the full name or email address (ignoring missing phonebook match and being in the same group)" : "Tam ad ya da e-posta adresi yazılırken otomatik olarak tamamlanabilsin (aynı grupta olma ya da telefon defteri eşleşmesi yok sayılarak)", + "Match username when restricting to full match" : "Tam eşleşme kısıtlamasında kullanıcı adı ile eşleşilsin", "Show disclaimer text on the public link upload page (only shown when the file list is hidden)" : "Herkese açık bağlantı yükleme sayfasındaki sorumluluk reddi bildirim metni (yalnız dosya listesi gizli iken görüntülenir)", "This text will be shown on the public link upload page when the file list is hidden." : "Dosya listesi gizli iken herkese açık bağlantı yükleme sayfasında görüntülenecek sorumluluk reddi bildirimi metni.", "Default share permissions" : "Varsayılan paylaşım izinleri", diff --git a/apps/settings/l10n/tr.json b/apps/settings/l10n/tr.json index 5505c89eb9b..8ab388a0520 100644 --- a/apps/settings/l10n/tr.json +++ b/apps/settings/l10n/tr.json @@ -139,6 +139,8 @@ "MariaDB version \"%s\" is used. Nextcloud 21 will no longer support this version and requires MariaDB 10.2 or higher." : "MariaDB \"%s\" sürümü kullanılıyor. Nextcloud 21 sürümünde bu sürüm desteklenmiyor. MariaDB 10.2 ya da üzerindeki bir sürüm kullanılmalıdır.", "MySQL version \"%s\" is used. Nextcloud 21 will no longer support this version and requires MySQL 8.0 or MariaDB 10.2 or higher." : "MySQL \"%s\" sürümü kullanılıyor. Nextcloud 21 sürümünde bu sürüm desteklenmiyor. MySQL 8 ya da MariaDB 10.2 üzerindeki bir sürüm kullanılmalıdır.", "PostgreSQL version \"%s\" is used. Nextcloud 21 will no longer support this version and requires PostgreSQL 9.6 or higher." : "PostgreSQL \"%s\" sürümü kullanılıyor. Nextcloud 21 sürümünde bu sürüm desteklenmiyor. PostgreSQL 9.6 ya da üzerindeki bir sürüm kullanılmalıdır.", + "Profile information" : "Profil bilgileri", + "Profile picture, full name, email, phone number, address, website, Twitter, organisation, role, headline, biography, and whether your profile is enabled" : "Profil görseli, tam ad, e-posta adresi, telefon numarası, adres, web sitesi, Twitter, kuruluş, rol, başlık, özgeçmi ve profilde etkinleştirilmiş diğer bilgiler", "Nextcloud settings" : "Nextcloud ayarları", "Administration privileges" : "Yönetim yetkileri", "Here you can decide which group can access certain sections of the administration settings." : "Hangi yönetici ayarlarına hangi grubun erişebileceğini bu bölümden belirleyebilirsiniz.", @@ -461,6 +463,8 @@ "Allow username autocompletion to users within the same groups" : "Aynı gruplardaki kullanıcıların kullanıcı adları otomatik olarak tamamlanabilsin", "Allow username autocompletion to users based on phone number integration" : "Telefon numarası bütünleştirmesi eşleşmelerine göre kullanıcı adları otomatik olarak tamamlanabilsin", "If autocompletion \"same group\" and \"phone number integration\" are enabled a match in either is enough to show the user." : "\"Aynı grup\" ve \"telefon numarası bütünleştirmesi\" etkinleştirilmiş ise. Kullanıcının görüntülenmesi için ikisinden birinde eşleşme olması yeterlidir.", + "Allow autocompletion when entering the full name or email address (ignoring missing phonebook match and being in the same group)" : "Tam ad ya da e-posta adresi yazılırken otomatik olarak tamamlanabilsin (aynı grupta olma ya da telefon defteri eşleşmesi yok sayılarak)", + "Match username when restricting to full match" : "Tam eşleşme kısıtlamasında kullanıcı adı ile eşleşilsin", "Show disclaimer text on the public link upload page (only shown when the file list is hidden)" : "Herkese açık bağlantı yükleme sayfasındaki sorumluluk reddi bildirim metni (yalnız dosya listesi gizli iken görüntülenir)", "This text will be shown on the public link upload page when the file list is hidden." : "Dosya listesi gizli iken herkese açık bağlantı yükleme sayfasında görüntülenecek sorumluluk reddi bildirimi metni.", "Default share permissions" : "Varsayılan paylaşım izinleri", diff --git a/apps/user_ldap/css/settings.css b/apps/user_ldap/css/settings.css index 54d0e2dbb0c..f99310118a9 100644 --- a/apps/user_ldap/css/settings.css +++ b/apps/user_ldap/css/settings.css @@ -9,7 +9,8 @@ } .tablerow { - display: table-row; + display: flex; + align-items: center; white-space: nowrap; text-align: left; } @@ -226,3 +227,8 @@ select[multiple=multiple] + button { #ldapSettings div.ui-accordion-content { background: white; } + +ul.ui-multiselect-checkboxes label { + display: flex; + align-items: center; +} diff --git a/apps/user_ldap/lib/Mapping/AbstractMapping.php b/apps/user_ldap/lib/Mapping/AbstractMapping.php index 16973f76ff4..1a747cc8bfd 100644 --- a/apps/user_ldap/lib/Mapping/AbstractMapping.php +++ b/apps/user_ldap/lib/Mapping/AbstractMapping.php @@ -438,14 +438,14 @@ abstract class AbstractMapping { $picker = $this->dbc->getQueryBuilder(); $picker->select('owncloud_name') ->from($this->getTableName()); - $cursor = $picker->execute(); + $cursor = $picker->executeQuery(); $result = true; - while ($id = $cursor->fetchOne()) { + while (($id = $cursor->fetchOne()) !== false) { $preCallback($id); if ($isUnmapped = $this->unmap($id)) { $postCallback($id); } - $result &= $isUnmapped; + $result = $result && $isUnmapped; } $cursor->closeCursor(); return $result; diff --git a/apps/user_ldap/lib/Migration/Version1130Date20211102154716.php b/apps/user_ldap/lib/Migration/Version1130Date20211102154716.php index 9e9554000d8..024c5801582 100644 --- a/apps/user_ldap/lib/Migration/Version1130Date20211102154716.php +++ b/apps/user_ldap/lib/Migration/Version1130Date20211102154716.php @@ -259,7 +259,7 @@ class Version1130Date20211102154716 extends SimpleMigrationStep { $result = $select->executeQuery(); $idList = []; - while ($id = $result->fetchOne()) { + while (($id = $result->fetchOne()) !== false) { $idList[] = $id; } $result->closeCursor(); @@ -278,7 +278,7 @@ class Version1130Date20211102154716 extends SimpleMigrationStep { ->having($select->expr()->gt($select->func()->count('owncloud_name'), $select->createNamedParameter(1))); $result = $select->executeQuery(); - while ($uuid = $result->fetchOne()) { + while (($uuid = $result->fetchOne()) !== false) { yield $uuid; } $result->closeCursor(); diff --git a/apps/workflowengine/lib/Manager.php b/apps/workflowengine/lib/Manager.php index 178bc87365b..34dbf507b91 100644 --- a/apps/workflowengine/lib/Manager.php +++ b/apps/workflowengine/lib/Manager.php @@ -351,7 +351,7 @@ class Manager implements IManager { $result = $qb->execute(); $this->operationsByScope[$scopeContext->getHash()] = []; - while ($opId = $result->fetchOne()) { + while (($opId = $result->fetchOne()) !== false) { $this->operationsByScope[$scopeContext->getHash()][] = (int)$opId; } $result->closeCursor(); diff --git a/apps/workflowengine/lib/Migration/PopulateNewlyIntroducedDatabaseFields.php b/apps/workflowengine/lib/Migration/PopulateNewlyIntroducedDatabaseFields.php index 019d3ae6bcc..974793e99b2 100644 --- a/apps/workflowengine/lib/Migration/PopulateNewlyIntroducedDatabaseFields.php +++ b/apps/workflowengine/lib/Migration/PopulateNewlyIntroducedDatabaseFields.php @@ -57,7 +57,7 @@ class PopulateNewlyIntroducedDatabaseFields implements IRepairStep { $qb = $this->dbc->getQueryBuilder(); $insertQuery = $qb->insert('flow_operations_scope'); - while ($id = $ids->fetchOne()) { + while (($id = $ids->fetchOne()) !== false) { $insertQuery->values(['operation_id' => $qb->createNamedParameter($id), 'type' => IManager::SCOPE_ADMIN]); $insertQuery->execute(); } diff --git a/core/l10n/cs.js b/core/l10n/cs.js index 6bd9dc6cd96..988af71ccd0 100644 --- a/core/l10n/cs.js +++ b/core/l10n/cs.js @@ -350,6 +350,10 @@ OC.L10N.register( "You can close this window." : "Toto okno je možné zavřít.", "This share is password-protected" : "Toto sdílení je chráněno heslem", "The password is wrong. Try again." : "Chybné heslo. Zkuste to znovu.", + "Email address" : "E-mailová adresa", + "Password sent!" : "Heslo zasláno!", + "You are not authorized to request a password for this share" : "Nejste oprávněni vyžádat si heslo pro toto sdílení", + "Request password" : "Vyžádat si heslo", "Go to %s" : "Jít na %s", "Two-factor authentication" : "Dvoufaktorové přihlášení", "Enhanced security is enabled for your account. Choose a second factor for authentication:" : "Bylo zapnuto vylepšené zabezpečení pro tento účet. Zvolte druhý faktor pro ověřování:", diff --git a/core/l10n/cs.json b/core/l10n/cs.json index d4767f03030..8370a426710 100644 --- a/core/l10n/cs.json +++ b/core/l10n/cs.json @@ -348,6 +348,10 @@ "You can close this window." : "Toto okno je možné zavřít.", "This share is password-protected" : "Toto sdílení je chráněno heslem", "The password is wrong. Try again." : "Chybné heslo. Zkuste to znovu.", + "Email address" : "E-mailová adresa", + "Password sent!" : "Heslo zasláno!", + "You are not authorized to request a password for this share" : "Nejste oprávněni vyžádat si heslo pro toto sdílení", + "Request password" : "Vyžádat si heslo", "Go to %s" : "Jít na %s", "Two-factor authentication" : "Dvoufaktorové přihlášení", "Enhanced security is enabled for your account. Choose a second factor for authentication:" : "Bylo zapnuto vylepšené zabezpečení pro tento účet. Zvolte druhý faktor pro ověřování:", diff --git a/core/l10n/hu.js b/core/l10n/hu.js index 0fd53c331b3..535885772db 100644 --- a/core/l10n/hu.js +++ b/core/l10n/hu.js @@ -350,6 +350,11 @@ OC.L10N.register( "You can close this window." : "Bezárhatja ezt az ablakot.", "This share is password-protected" : "Ez a megosztás jelszóval védett", "The password is wrong. Try again." : "A megadott jelszó hibás. Próbálja újra.", + "Please type in your email address to request a temporary password" : "Írja be az e-mail-címét, hogy ideiglenes jelszót kérjen", + "Email address" : "E-mail-cím", + "Password sent!" : "Jelszó elküldve.", + "You are not authorized to request a password for this share" : "Nincs jogosultsága, hogy jelszót kérjen ehhez a megosztáshoz", + "Request password" : "Jelszó kérése", "Go to %s" : "Ugrás ide: %s", "Two-factor authentication" : "Kétfaktoros hitelesítés", "Enhanced security is enabled for your account. Choose a second factor for authentication:" : "A fokozott biztonság engedélyezett a fiókja számára. Válasszon egy második faktort a hitelesítéshez.", diff --git a/core/l10n/hu.json b/core/l10n/hu.json index 077262166ed..9d2a213d28e 100644 --- a/core/l10n/hu.json +++ b/core/l10n/hu.json @@ -348,6 +348,11 @@ "You can close this window." : "Bezárhatja ezt az ablakot.", "This share is password-protected" : "Ez a megosztás jelszóval védett", "The password is wrong. Try again." : "A megadott jelszó hibás. Próbálja újra.", + "Please type in your email address to request a temporary password" : "Írja be az e-mail-címét, hogy ideiglenes jelszót kérjen", + "Email address" : "E-mail-cím", + "Password sent!" : "Jelszó elküldve.", + "You are not authorized to request a password for this share" : "Nincs jogosultsága, hogy jelszót kérjen ehhez a megosztáshoz", + "Request password" : "Jelszó kérése", "Go to %s" : "Ugrás ide: %s", "Two-factor authentication" : "Kétfaktoros hitelesítés", "Enhanced security is enabled for your account. Choose a second factor for authentication:" : "A fokozott biztonság engedélyezett a fiókja számára. Válasszon egy második faktort a hitelesítéshez.", diff --git a/core/l10n/pl.js b/core/l10n/pl.js index 9cec8fcb8c8..b4be46eb0f2 100644 --- a/core/l10n/pl.js +++ b/core/l10n/pl.js @@ -350,6 +350,11 @@ OC.L10N.register( "You can close this window." : "Możesz zamknąć to okno.", "This share is password-protected" : "Udostępnienie jest zabezpieczone hasłem", "The password is wrong. Try again." : "Hasło jest nieprawidłowe. Spróbuj ponownie.", + "Please type in your email address to request a temporary password" : "Wpisz swój adres e-mail, aby poprosić o tymczasowe hasło", + "Email address" : "Adres e-mail", + "Password sent!" : "Hasło wysłane!", + "You are not authorized to request a password for this share" : "Nie masz uprawnień do żądania hasła dla tego udostępnienia", + "Request password" : "Żądanie hasła", "Go to %s" : "Przejdź do %s", "Two-factor authentication" : "Uwierzytelnianie dwuskładnikowe", "Enhanced security is enabled for your account. Choose a second factor for authentication:" : "Dodatkowe zabezpieczenia są włączone dla Twojego konta. Wybierz drugą metodę uwierzytelniania:", diff --git a/core/l10n/pl.json b/core/l10n/pl.json index b0f75e0c05f..2b1a0b8b536 100644 --- a/core/l10n/pl.json +++ b/core/l10n/pl.json @@ -348,6 +348,11 @@ "You can close this window." : "Możesz zamknąć to okno.", "This share is password-protected" : "Udostępnienie jest zabezpieczone hasłem", "The password is wrong. Try again." : "Hasło jest nieprawidłowe. Spróbuj ponownie.", + "Please type in your email address to request a temporary password" : "Wpisz swój adres e-mail, aby poprosić o tymczasowe hasło", + "Email address" : "Adres e-mail", + "Password sent!" : "Hasło wysłane!", + "You are not authorized to request a password for this share" : "Nie masz uprawnień do żądania hasła dla tego udostępnienia", + "Request password" : "Żądanie hasła", "Go to %s" : "Przejdź do %s", "Two-factor authentication" : "Uwierzytelnianie dwuskładnikowe", "Enhanced security is enabled for your account. Choose a second factor for authentication:" : "Dodatkowe zabezpieczenia są włączone dla Twojego konta. Wybierz drugą metodę uwierzytelniania:", diff --git a/core/l10n/tr.js b/core/l10n/tr.js index 8749f9bcb58..05bfc7887c3 100644 --- a/core/l10n/tr.js +++ b/core/l10n/tr.js @@ -350,6 +350,11 @@ OC.L10N.register( "You can close this window." : "Bu pencereyi kapatabilirsiniz.", "This share is password-protected" : "Bu paylaşım parola korumalı", "The password is wrong. Try again." : "Parola yanlış. Yeniden deneyin.", + "Please type in your email address to request a temporary password" : "Lütfen geçici parola isteğinde bulunmak için e-posta adresinizi yazın", + "Email address" : "E-posta adresi", + "Password sent!" : "Parola gönderildi!", + "You are not authorized to request a password for this share" : "Bu paylaşım için parola isteğinde bulunma izniniz yok", + "Request password" : "Parola iste", "Go to %s" : "%s bölümüne git", "Two-factor authentication" : "İki aşamalı kimlik doğrulama", "Enhanced security is enabled for your account. Choose a second factor for authentication:" : "Hesabınız için gelişmiş güvenlik etkinleştirildi. Kimlik doğrulaması için bir ikinci aşama seçin:", diff --git a/core/l10n/tr.json b/core/l10n/tr.json index 12864d50d46..580f67a1775 100644 --- a/core/l10n/tr.json +++ b/core/l10n/tr.json @@ -348,6 +348,11 @@ "You can close this window." : "Bu pencereyi kapatabilirsiniz.", "This share is password-protected" : "Bu paylaşım parola korumalı", "The password is wrong. Try again." : "Parola yanlış. Yeniden deneyin.", + "Please type in your email address to request a temporary password" : "Lütfen geçici parola isteğinde bulunmak için e-posta adresinizi yazın", + "Email address" : "E-posta adresi", + "Password sent!" : "Parola gönderildi!", + "You are not authorized to request a password for this share" : "Bu paylaşım için parola isteğinde bulunma izniniz yok", + "Request password" : "Parola iste", "Go to %s" : "%s bölümüne git", "Two-factor authentication" : "İki aşamalı kimlik doğrulama", "Enhanced security is enabled for your account. Choose a second factor for authentication:" : "Hesabınız için gelişmiş güvenlik etkinleştirildi. Kimlik doğrulaması için bir ikinci aşama seçin:", diff --git a/core/l10n/zh_CN.js b/core/l10n/zh_CN.js index d372f24a39b..7ae95259504 100644 --- a/core/l10n/zh_CN.js +++ b/core/l10n/zh_CN.js @@ -42,10 +42,10 @@ OC.L10N.register( "Enter your subscription key to increase the user limit. For more information about Nextcloud Enterprise see our website." : "输入订阅密钥以增加用户限制。欲了解更多关于Nextcloud企业版的信息,请访问我们的网站。", "Preparing update" : "正在准备更新", "[%d / %d]: %s" : "[%d / %d]:%s", - "Repair step:" : "修复错误:", - "Repair info:" : "修复信息:", - "Repair warning:" : "修复警告:", - "Repair error:" : "修复错误:", + "Repair step:" : "修复日志 步骤:", + "Repair info:" : "修复 信息:", + "Repair warning:" : "修复 警告:", + "Repair error:" : "修复 错误:", "Please use the command line updater because automatic updating is disabled in the config.php." : "由于自动更新在 config.php 中已禁用,请使用命令行更新。", "[%d / %d]: Checking table %s" : "[%d / %d]:检查数据表 %s", "Turned on maintenance mode" : "启用维护模式", diff --git a/core/l10n/zh_CN.json b/core/l10n/zh_CN.json index ce98ba77cfe..583e3281760 100644 --- a/core/l10n/zh_CN.json +++ b/core/l10n/zh_CN.json @@ -40,10 +40,10 @@ "Enter your subscription key to increase the user limit. For more information about Nextcloud Enterprise see our website." : "输入订阅密钥以增加用户限制。欲了解更多关于Nextcloud企业版的信息,请访问我们的网站。", "Preparing update" : "正在准备更新", "[%d / %d]: %s" : "[%d / %d]:%s", - "Repair step:" : "修复错误:", - "Repair info:" : "修复信息:", - "Repair warning:" : "修复警告:", - "Repair error:" : "修复错误:", + "Repair step:" : "修复日志 步骤:", + "Repair info:" : "修复 信息:", + "Repair warning:" : "修复 警告:", + "Repair error:" : "修复 错误:", "Please use the command line updater because automatic updating is disabled in the config.php." : "由于自动更新在 config.php 中已禁用,请使用命令行更新。", "[%d / %d]: Checking table %s" : "[%d / %d]:检查数据表 %s", "Turned on maintenance mode" : "启用维护模式", diff --git a/core/l10n/zh_TW.js b/core/l10n/zh_TW.js index ba496bd0a0b..3ec54f66d81 100644 --- a/core/l10n/zh_TW.js +++ b/core/l10n/zh_TW.js @@ -350,6 +350,11 @@ OC.L10N.register( "You can close this window." : "可以關閉此視窗", "This share is password-protected" : "此分享受密碼保護", "The password is wrong. Try again." : "密碼錯誤,請重試", + "Please type in your email address to request a temporary password" : "請輸入您的電子郵件地址以申請臨時密碼", + "Email address" : "電子郵件地址", + "Password sent!" : "密碼已傳送!", + "You are not authorized to request a password for this share" : "您無權為此分享請求密碼", + "Request password" : "請求密碼", "Go to %s" : "前往 %s", "Two-factor authentication" : "雙因素驗證", "Enhanced security is enabled for your account. Choose a second factor for authentication:" : "您的帳號已啟用進階安全機制,請選擇一個雙因素驗證方法:", diff --git a/core/l10n/zh_TW.json b/core/l10n/zh_TW.json index b4efa671575..350b4043fc1 100644 --- a/core/l10n/zh_TW.json +++ b/core/l10n/zh_TW.json @@ -348,6 +348,11 @@ "You can close this window." : "可以關閉此視窗", "This share is password-protected" : "此分享受密碼保護", "The password is wrong. Try again." : "密碼錯誤,請重試", + "Please type in your email address to request a temporary password" : "請輸入您的電子郵件地址以申請臨時密碼", + "Email address" : "電子郵件地址", + "Password sent!" : "密碼已傳送!", + "You are not authorized to request a password for this share" : "您無權為此分享請求密碼", + "Request password" : "請求密碼", "Go to %s" : "前往 %s", "Two-factor authentication" : "雙因素驗證", "Enhanced security is enabled for your account. Choose a second factor for authentication:" : "您的帳號已啟用進階安全機制,請選擇一個雙因素驗證方法:", diff --git a/lib/private/Files/Cache/Storage.php b/lib/private/Files/Cache/Storage.php index 33785607ef7..359ce97507d 100644 --- a/lib/private/Files/Cache/Storage.php +++ b/lib/private/Files/Cache/Storage.php @@ -158,7 +158,7 @@ class Storage { } /** - * @return array|null [ available, last_checked ] + * @return array [ available, last_checked ] */ public function getAvailability() { if ($row = self::getStorageById($this->storageId)) { @@ -167,7 +167,10 @@ class Storage { 'last_checked' => $row['last_checked'] ]; } else { - return null; + return [ + 'available' => true, + 'last_checked' => time(), + ]; } } @@ -237,6 +240,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/tests/lib/User/UserTest.php b/tests/lib/User/UserTest.php index aa56959d3cb..7fab7ececca 100644 --- a/tests/lib/User/UserTest.php +++ b/tests/lib/User/UserTest.php @@ -10,9 +10,11 @@ namespace Test\User; use OC\AllConfig; +use OC\Files\Mount\ObjectHomeMountProvider; use OC\Hooks\PublicEmitter; use OC\User\User; use OCP\Comments\ICommentsManager; +use OCP\Files\Storage\IStorageFactory; use OCP\IConfig; use OCP\IUser; use OCP\Notification\IManager as INotificationManager; @@ -214,6 +216,16 @@ class UserTest extends TestCase { } public function testDeleteWithDifferentHome() { + + /** @var ObjectHomeMountProvider $homeProvider */ + $homeProvider = \OC::$server->get(ObjectHomeMountProvider::class); + $user = $this->createMock(IUser::class); + $user->method('getUID') + ->willReturn('foo'); + if ($homeProvider->getHomeMountForUser($user, $this->createMock(IStorageFactory::class)) !== null) { + $this->markTestSkipped("Skipping test for non local home storage"); + } + /** * @var Backend | \PHPUnit\Framework\MockObject\MockObject $backend */ |