aboutsummaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
Diffstat (limited to 'apps')
-rw-r--r--apps/dav/appinfo/v1/carddav.php1
-rw-r--r--apps/dav/lib/CardDAV/AddressBook.php4
-rw-r--r--apps/dav/lib/CardDAV/AddressBookImpl.php4
-rw-r--r--apps/dav/lib/CardDAV/CardDavBackend.php77
-rw-r--r--apps/dav/lib/CardDAV/SyncService.php61
-rw-r--r--apps/dav/lib/CardDAV/SystemAddressbook.php8
-rw-r--r--apps/dav/lib/Connector/Sabre/File.php12
-rw-r--r--apps/dav/lib/RootCollection.php3
-rw-r--r--apps/dav/tests/unit/CardDAV/AddressBookImplTest.php17
-rw-r--r--apps/dav/tests/unit/CardDAV/CardDavBackendTest.php21
-rw-r--r--apps/dav/tests/unit/CardDAV/SyncServiceTest.php8
-rw-r--r--apps/encryption/lib/Crypto/EncryptAll.php23
-rw-r--r--apps/encryption/tests/Crypto/EncryptAllTest.php33
-rw-r--r--apps/federation/lib/SyncFederationAddressBooks.php23
-rw-r--r--apps/federation/tests/SyncFederationAddressbooksTest.php4
-rw-r--r--apps/files/l10n/es.js41
-rw-r--r--apps/files/l10n/es.json41
-rw-r--r--apps/files/l10n/mk.js17
-rw-r--r--apps/files/l10n/mk.json17
-rw-r--r--apps/files/l10n/zh_CN.js8
-rw-r--r--apps/files/l10n/zh_CN.json8
-rw-r--r--apps/files/lib/AppInfo/Application.php2
-rw-r--r--apps/files/src/FilesApp.vue6
-rw-r--r--apps/files/src/composables/useHotKeys.spec.ts (renamed from apps/files/src/services/HotKeysService.spec.ts)73
-rw-r--r--apps/files/src/composables/useHotKeys.ts (renamed from apps/files/src/services/HotKeysService.ts)46
-rw-r--r--apps/files/src/main.ts4
-rw-r--r--apps/files_external/lib/Lib/Storage/SMB.php2
-rw-r--r--apps/files_sharing/src/views/SharingTab.vue3
-rw-r--r--apps/files_trashbin/l10n/pt_BR.js2
-rw-r--r--apps/files_trashbin/l10n/pt_BR.json2
-rw-r--r--apps/files_trashbin/lib/Sabre/TrashbinPlugin.php4
-rw-r--r--apps/files_versions/lib/Sabre/Plugin.php5
-rw-r--r--apps/provisioning_api/l10n/zh_CN.js1
-rw-r--r--apps/provisioning_api/l10n/zh_CN.json1
-rw-r--r--apps/settings/l10n/de.js1
-rw-r--r--apps/settings/l10n/de.json1
-rw-r--r--apps/settings/l10n/de_DE.js1
-rw-r--r--apps/settings/l10n/de_DE.json1
-rw-r--r--apps/settings/l10n/et_EE.js1
-rw-r--r--apps/settings/l10n/et_EE.json1
-rw-r--r--apps/settings/l10n/pt_BR.js1
-rw-r--r--apps/settings/l10n/pt_BR.json1
-rw-r--r--apps/settings/l10n/uk.js1
-rw-r--r--apps/settings/l10n/uk.json1
-rw-r--r--apps/settings/l10n/zh_CN.js9
-rw-r--r--apps/settings/l10n/zh_CN.json9
-rw-r--r--apps/settings/l10n/zh_TW.js1
-rw-r--r--apps/settings/l10n/zh_TW.json1
-rw-r--r--apps/settings/tests/Mailer/NewUserMailHelperTest.php126
-rw-r--r--apps/systemtags/src/components/SystemTagPicker.vue20
-rw-r--r--apps/user_status/l10n/zh_CN.js33
-rw-r--r--apps/user_status/l10n/zh_CN.json33
-rw-r--r--apps/user_status/lib/Capabilities.php3
-rw-r--r--apps/user_status/openapi.json6
-rw-r--r--apps/user_status/tests/Unit/CapabilitiesTest.php1
-rw-r--r--apps/workflowengine/l10n/de.js1
-rw-r--r--apps/workflowengine/l10n/de.json1
-rw-r--r--apps/workflowengine/l10n/de_DE.js1
-rw-r--r--apps/workflowengine/l10n/de_DE.json1
-rw-r--r--apps/workflowengine/l10n/en_GB.js1
-rw-r--r--apps/workflowengine/l10n/en_GB.json1
-rw-r--r--apps/workflowengine/l10n/et_EE.js1
-rw-r--r--apps/workflowengine/l10n/et_EE.json1
-rw-r--r--apps/workflowengine/l10n/ga.js1
-rw-r--r--apps/workflowengine/l10n/ga.json1
-rw-r--r--apps/workflowengine/l10n/pt_BR.js1
-rw-r--r--apps/workflowengine/l10n/pt_BR.json1
-rw-r--r--apps/workflowengine/l10n/ru.js1
-rw-r--r--apps/workflowengine/l10n/ru.json1
-rw-r--r--apps/workflowengine/l10n/uk.js1
-rw-r--r--apps/workflowengine/l10n/uk.json1
-rw-r--r--apps/workflowengine/l10n/zh_HK.js1
-rw-r--r--apps/workflowengine/l10n/zh_HK.json1
-rw-r--r--apps/workflowengine/l10n/zh_TW.js1
-rw-r--r--apps/workflowengine/l10n/zh_TW.json1
75 files changed, 559 insertions, 295 deletions
diff --git a/apps/dav/appinfo/v1/carddav.php b/apps/dav/appinfo/v1/carddav.php
index bcd66e47090..415a5c9634a 100644
--- a/apps/dav/appinfo/v1/carddav.php
+++ b/apps/dav/appinfo/v1/carddav.php
@@ -63,6 +63,7 @@ $cardDavBackend = new CardDavBackend(
Server::get(IUserManager::class),
Server::get(IEventDispatcher::class),
Server::get(\OCA\DAV\CardDAV\Sharing\Backend::class),
+ Server::get(IConfig::class),
);
$debugging = Server::get(IConfig::class)->getSystemValue('debug', false);
diff --git a/apps/dav/lib/CardDAV/AddressBook.php b/apps/dav/lib/CardDAV/AddressBook.php
index d2391880585..4d30d507a7d 100644
--- a/apps/dav/lib/CardDAV/AddressBook.php
+++ b/apps/dav/lib/CardDAV/AddressBook.php
@@ -8,7 +8,6 @@
namespace OCA\DAV\CardDAV;
use OCA\DAV\DAV\Sharing\IShareable;
-use OCA\DAV\Exception\UnsupportedLimitOnInitialSyncException;
use OCP\DB\Exception;
use OCP\IL10N;
use OCP\Server;
@@ -234,9 +233,6 @@ class AddressBook extends \Sabre\CardDAV\AddressBook implements IShareable, IMov
}
public function getChanges($syncToken, $syncLevel, $limit = null) {
- if (!$syncToken && $limit) {
- throw new UnsupportedLimitOnInitialSyncException();
- }
return parent::getChanges($syncToken, $syncLevel, $limit);
}
diff --git a/apps/dav/lib/CardDAV/AddressBookImpl.php b/apps/dav/lib/CardDAV/AddressBookImpl.php
index 6bb8e24f628..ae77498539b 100644
--- a/apps/dav/lib/CardDAV/AddressBookImpl.php
+++ b/apps/dav/lib/CardDAV/AddressBookImpl.php
@@ -152,6 +152,10 @@ class AddressBookImpl implements IAddressBookEnabled {
$permissions = $this->addressBook->getACL();
$result = 0;
foreach ($permissions as $permission) {
+ if ($this->addressBookInfo['principaluri'] !== $permission['principal']) {
+ continue;
+ }
+
switch ($permission['privilege']) {
case '{DAV:}read':
$result |= Constants::PERMISSION_READ;
diff --git a/apps/dav/lib/CardDAV/CardDavBackend.php b/apps/dav/lib/CardDAV/CardDavBackend.php
index 06bd8d8ee2c..a78686eb61d 100644
--- a/apps/dav/lib/CardDAV/CardDavBackend.php
+++ b/apps/dav/lib/CardDAV/CardDavBackend.php
@@ -23,6 +23,7 @@ use OCP\AppFramework\Db\TTransactional;
use OCP\DB\Exception;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\EventDispatcher\IEventDispatcher;
+use OCP\IConfig;
use OCP\IDBConnection;
use OCP\IUserManager;
use PDO;
@@ -59,6 +60,7 @@ class CardDavBackend implements BackendInterface, SyncSupport {
private IUserManager $userManager,
private IEventDispatcher $dispatcher,
private Sharing\Backend $sharingBackend,
+ private IConfig $config,
) {
}
@@ -851,6 +853,8 @@ class CardDavBackend implements BackendInterface, SyncSupport {
* @return array
*/
public function getChangesForAddressBook($addressBookId, $syncToken, $syncLevel, $limit = null) {
+ $maxLimit = $this->config->getSystemValueInt('carddav_sync_request_truncation', 2500);
+ $limit = ($limit === null) ? $maxLimit : min($limit, $maxLimit);
// Current synctoken
return $this->atomic(function () use ($addressBookId, $syncToken, $syncLevel, $limit) {
$qb = $this->db->getQueryBuilder();
@@ -873,10 +877,35 @@ class CardDavBackend implements BackendInterface, SyncSupport {
'modified' => [],
'deleted' => [],
];
-
- if ($syncToken) {
+ if (str_starts_with($syncToken, 'init_')) {
+ $syncValues = explode('_', $syncToken);
+ $lastID = $syncValues[1];
+ $initialSyncToken = $syncValues[2];
$qb = $this->db->getQueryBuilder();
- $qb->select('uri', 'operation')
+ $qb->select('id', 'uri')
+ ->from('cards')
+ ->where(
+ $qb->expr()->andX(
+ $qb->expr()->eq('addressbookid', $qb->createNamedParameter($addressBookId)),
+ $qb->expr()->gt('id', $qb->createNamedParameter($lastID)))
+ )->orderBy('id')
+ ->setMaxResults($limit);
+ $stmt = $qb->executeQuery();
+ $values = $stmt->fetchAll(\PDO::FETCH_ASSOC);
+ $stmt->closeCursor();
+ if (count($values) === 0) {
+ $result['syncToken'] = $initialSyncToken;
+ $result['result_truncated'] = false;
+ $result['added'] = [];
+ } else {
+ $lastID = $values[array_key_last($values)]['id'];
+ $result['added'] = array_column($values, 'uri');
+ $result['syncToken'] = count($result['added']) >= $limit ? "init_{$lastID}_$initialSyncToken" : $initialSyncToken ;
+ $result['result_truncated'] = count($result['added']) >= $limit;
+ }
+ } elseif ($syncToken) {
+ $qb = $this->db->getQueryBuilder();
+ $qb->select('uri', 'operation', 'synctoken')
->from('addressbookchanges')
->where(
$qb->expr()->andX(
@@ -886,22 +915,31 @@ class CardDavBackend implements BackendInterface, SyncSupport {
)
)->orderBy('synctoken');
- if (is_int($limit) && $limit > 0) {
+ if ($limit > 0) {
$qb->setMaxResults($limit);
}
// Fetching all changes
$stmt = $qb->executeQuery();
+ $rowCount = $stmt->rowCount();
$changes = [];
+ $highestSyncToken = 0;
// This loop ensures that any duplicates are overwritten, only the
// last change on a node is relevant.
while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
$changes[$row['uri']] = $row['operation'];
+ $highestSyncToken = $row['synctoken'];
}
+
$stmt->closeCursor();
+ // No changes found, use current token
+ if (empty($changes)) {
+ $result['syncToken'] = $currentToken;
+ }
+
foreach ($changes as $uri => $operation) {
switch ($operation) {
case 1:
@@ -915,16 +953,43 @@ class CardDavBackend implements BackendInterface, SyncSupport {
break;
}
}
+
+ /*
+ * The synctoken in oc_addressbooks is always the highest synctoken in oc_addressbookchanges for a given addressbook plus one (see addChange).
+ *
+ * For truncated results, it is expected that we return the highest token from the response, so the client can continue from the latest change.
+ *
+ * For non-truncated results, it is expected to return the currentToken. If we return the highest token, as with truncated results, the client will always think it is one change behind.
+ *
+ * Therefore, we differentiate between truncated and non-truncated results when returning the synctoken.
+ */
+ if ($rowCount === $limit && $highestSyncToken < $currentToken) {
+ $result['syncToken'] = $highestSyncToken;
+ $result['result_truncated'] = true;
+ }
} else {
$qb = $this->db->getQueryBuilder();
- $qb->select('uri')
+ $qb->select('id', 'uri')
->from('cards')
->where(
$qb->expr()->eq('addressbookid', $qb->createNamedParameter($addressBookId))
);
// No synctoken supplied, this is the initial sync.
+ $qb->setMaxResults($limit);
$stmt = $qb->executeQuery();
- $result['added'] = $stmt->fetchAll(\PDO::FETCH_COLUMN);
+ $values = $stmt->fetchAll(\PDO::FETCH_ASSOC);
+ if (empty($values)) {
+ $result['added'] = [];
+ return $result;
+ }
+ $lastID = $values[array_key_last($values)]['id'];
+ if (count($values) >= $limit) {
+ $result['syncToken'] = 'init_' . $lastID . '_' . $currentToken;
+ $result['result_truncated'] = true;
+ }
+
+ $result['added'] = array_column($values, 'uri');
+
$stmt->closeCursor();
}
return $result;
diff --git a/apps/dav/lib/CardDAV/SyncService.php b/apps/dav/lib/CardDAV/SyncService.php
index 4a75f8ced6c..e6da3ed5923 100644
--- a/apps/dav/lib/CardDAV/SyncService.php
+++ b/apps/dav/lib/CardDAV/SyncService.php
@@ -22,6 +22,7 @@ use Psr\Log\LoggerInterface;
use Sabre\DAV\Xml\Response\MultiStatus;
use Sabre\DAV\Xml\Service;
use Sabre\VObject\Reader;
+use Sabre\Xml\ParseException;
use function is_null;
class SyncService {
@@ -43,9 +44,10 @@ class SyncService {
}
/**
+ * @psalm-return list{0: ?string, 1: boolean}
* @throws \Exception
*/
- public function syncRemoteAddressBook(string $url, string $userName, string $addressBookUrl, string $sharedSecret, ?string $syncToken, string $targetBookHash, string $targetPrincipal, array $targetProperties): string {
+ public function syncRemoteAddressBook(string $url, string $userName, string $addressBookUrl, string $sharedSecret, ?string $syncToken, string $targetBookHash, string $targetPrincipal, array $targetProperties): array {
// 1. create addressbook
$book = $this->ensureSystemAddressBookExists($targetPrincipal, $targetBookHash, $targetProperties);
$addressBookId = $book['id'];
@@ -83,7 +85,10 @@ class SyncService {
}
}
- return $response['token'];
+ return [
+ $response['token'],
+ $response['truncated'],
+ ];
}
/**
@@ -127,7 +132,7 @@ class SyncService {
private function prepareUri(string $host, string $path): string {
/*
- * The trailing slash is important for merging the uris together.
+ * The trailing slash is important for merging the uris.
*
* $host is stored in oc_trusted_servers.url and usually without a trailing slash.
*
@@ -158,7 +163,9 @@ class SyncService {
}
/**
+ * @return array{response: array<string, array<array-key, mixed>>, token: ?string, truncated: bool}
* @throws ClientExceptionInterface
+ * @throws ParseException
*/
protected function requestSyncReport(string $url, string $userName, string $addressBookUrl, string $sharedSecret, ?string $syncToken): array {
$client = $this->clientService->newClient();
@@ -181,7 +188,7 @@ class SyncService {
$body = $response->getBody();
assert(is_string($body));
- return $this->parseMultiStatus($body);
+ return $this->parseMultiStatus($body, $addressBookUrl);
}
protected function download(string $url, string $userName, string $sharedSecret, string $resourcePath): string {
@@ -219,22 +226,50 @@ class SyncService {
}
/**
- * @param string $body
- * @return array
- * @throws \Sabre\Xml\ParseException
+ * @return array{response: array<string, array<array-key, mixed>>, token: ?string, truncated: bool}
+ * @throws ParseException
*/
- private function parseMultiStatus($body) {
- $xml = new Service();
-
+ private function parseMultiStatus(string $body, string $addressBookUrl): array {
/** @var MultiStatus $multiStatus */
- $multiStatus = $xml->expect('{DAV:}multistatus', $body);
+ $multiStatus = (new Service())->expect('{DAV:}multistatus', $body);
$result = [];
+ $truncated = false;
+
foreach ($multiStatus->getResponses() as $response) {
- $result[$response->getHref()] = $response->getResponseProperties();
+ $href = $response->getHref();
+ if ($response->getHttpStatus() === '507' && $this->isResponseForRequestUri($href, $addressBookUrl)) {
+ $truncated = true;
+ } else {
+ $result[$response->getHref()] = $response->getResponseProperties();
+ }
}
- return ['response' => $result, 'token' => $multiStatus->getSyncToken()];
+ return ['response' => $result, 'token' => $multiStatus->getSyncToken(), 'truncated' => $truncated];
+ }
+
+ /**
+ * Determines whether the provided response URI corresponds to the given request URI.
+ */
+ private function isResponseForRequestUri(string $responseUri, string $requestUri): bool {
+ /*
+ * Example response uri:
+ *
+ * /remote.php/dav/addressbooks/system/system/system/
+ * /cloud/remote.php/dav/addressbooks/system/system/system/ (when installed in a subdirectory)
+ *
+ * Example request uri:
+ *
+ * remote.php/dav/addressbooks/system/system/system
+ *
+ * References:
+ * https://github.com/nextcloud/3rdparty/blob/e0a509739b13820f0a62ff9cad5d0fede00e76ee/sabre/dav/lib/DAV/Sync/Plugin.php#L172-L174
+ * https://github.com/nextcloud/server/blob/b40acb34a39592070d8455eb91c5364c07928c50/apps/federation/lib/SyncFederationAddressBooks.php#L41
+ */
+ return str_ends_with(
+ rtrim($responseUri, '/'),
+ rtrim($requestUri, '/')
+ );
}
/**
diff --git a/apps/dav/lib/CardDAV/SystemAddressbook.php b/apps/dav/lib/CardDAV/SystemAddressbook.php
index e0032044e70..912a2f1dcee 100644
--- a/apps/dav/lib/CardDAV/SystemAddressbook.php
+++ b/apps/dav/lib/CardDAV/SystemAddressbook.php
@@ -8,7 +8,6 @@ declare(strict_types=1);
*/
namespace OCA\DAV\CardDAV;
-use OCA\DAV\Exception\UnsupportedLimitOnInitialSyncException;
use OCA\Federation\TrustedServers;
use OCP\Accounts\IAccountManager;
use OCP\IConfig;
@@ -212,14 +211,7 @@ class SystemAddressbook extends AddressBook {
}
return new Card($this->carddavBackend, $this->addressBookInfo, $obj);
}
-
- /**
- * @throws UnsupportedLimitOnInitialSyncException
- */
public function getChanges($syncToken, $syncLevel, $limit = null) {
- if (!$syncToken && $limit) {
- throw new UnsupportedLimitOnInitialSyncException();
- }
if (!$this->carddavBackend instanceof SyncSupport) {
return null;
diff --git a/apps/dav/lib/Connector/Sabre/File.php b/apps/dav/lib/Connector/Sabre/File.php
index 218d38e1c4b..d2a71eb3e7b 100644
--- a/apps/dav/lib/Connector/Sabre/File.php
+++ b/apps/dav/lib/Connector/Sabre/File.php
@@ -204,6 +204,9 @@ class File extends Node implements IFile {
}
}
+ $lengthHeader = $this->request->getHeader('content-length');
+ $expected = $lengthHeader !== '' ? (int)$lengthHeader : null;
+
if ($partStorage->instanceOfStorage(IWriteStreamStorage::class)) {
$isEOF = false;
$wrappedData = CallbackWrapper::wrap($data, null, null, null, null, function ($stream) use (&$isEOF): void {
@@ -215,7 +218,7 @@ class File extends Node implements IFile {
$count = -1;
try {
/** @var IWriteStreamStorage $partStorage */
- $count = $partStorage->writeStream($internalPartPath, $wrappedData);
+ $count = $partStorage->writeStream($internalPartPath, $wrappedData, $expected);
} catch (GenericFileException $e) {
$logger = Server::get(LoggerInterface::class);
$logger->error('Error while writing stream to storage: ' . $e->getMessage(), ['exception' => $e, 'app' => 'webdav']);
@@ -235,10 +238,7 @@ class File extends Node implements IFile {
[$count, $result] = Files::streamCopy($data, $target, true);
fclose($target);
}
-
- $lengthHeader = $this->request->getHeader('content-length');
- $expected = $lengthHeader !== '' ? (int)$lengthHeader : -1;
- if ($result === false && $expected >= 0) {
+ if ($result === false && $expected !== null) {
throw new Exception(
$this->l10n->t(
'Error while copying file to target location (copied: %1$s, expected filesize: %2$s)',
@@ -253,7 +253,7 @@ class File extends Node implements IFile {
// if content length is sent by client:
// double check if the file was fully received
// compare expected and actual size
- if ($expected >= 0
+ if ($expected !== null
&& $expected !== $count
&& $this->request->getMethod() === 'PUT'
) {
diff --git a/apps/dav/lib/RootCollection.php b/apps/dav/lib/RootCollection.php
index f1963c0ef01..870aa0d4540 100644
--- a/apps/dav/lib/RootCollection.php
+++ b/apps/dav/lib/RootCollection.php
@@ -132,6 +132,7 @@ class RootCollection extends SimpleCollection {
);
$contactsSharingBackend = Server::get(\OCA\DAV\CardDAV\Sharing\Backend::class);
+ $config = Server::get(IConfig::class);
$pluginManager = new PluginManager(\OC::$server, Server::get(IAppManager::class));
$usersCardDavBackend = new CardDavBackend(
@@ -140,6 +141,7 @@ class RootCollection extends SimpleCollection {
$userManager,
$dispatcher,
$contactsSharingBackend,
+ $config
);
$usersAddressBookRoot = new AddressBookRoot($userPrincipalBackend, $usersCardDavBackend, $pluginManager, $userSession->getUser(), $groupManager, 'principals/users');
$usersAddressBookRoot->disableListing = $disableListing;
@@ -150,6 +152,7 @@ class RootCollection extends SimpleCollection {
$userManager,
$dispatcher,
$contactsSharingBackend,
+ $config
);
$systemAddressBookRoot = new AddressBookRoot(new SystemPrincipalBackend(), $systemCardDavBackend, $pluginManager, $userSession->getUser(), $groupManager, 'principals/system');
$systemAddressBookRoot->disableListing = $disableListing;
diff --git a/apps/dav/tests/unit/CardDAV/AddressBookImplTest.php b/apps/dav/tests/unit/CardDAV/AddressBookImplTest.php
index f7daeb41cca..74699cf3925 100644
--- a/apps/dav/tests/unit/CardDAV/AddressBookImplTest.php
+++ b/apps/dav/tests/unit/CardDAV/AddressBookImplTest.php
@@ -241,14 +241,15 @@ class AddressBookImplTest extends TestCase {
public static function dataTestGetPermissions(): array {
return [
[[], 0],
- [[['privilege' => '{DAV:}read']], 1],
- [[['privilege' => '{DAV:}write']], 6],
- [[['privilege' => '{DAV:}all']], 31],
- [[['privilege' => '{DAV:}read'],['privilege' => '{DAV:}write']], 7],
- [[['privilege' => '{DAV:}read'],['privilege' => '{DAV:}all']], 31],
- [[['privilege' => '{DAV:}all'],['privilege' => '{DAV:}write']], 31],
- [[['privilege' => '{DAV:}read'],['privilege' => '{DAV:}write'],['privilege' => '{DAV:}all']], 31],
- [[['privilege' => '{DAV:}all'],['privilege' => '{DAV:}read'],['privilege' => '{DAV:}write']], 31],
+ [[['privilege' => '{DAV:}read', 'principal' => 'principals/system/system']], 1],
+ [[['privilege' => '{DAV:}read', 'principal' => 'principals/system/system'], ['privilege' => '{DAV:}write', 'principal' => 'principals/someone/else']], 1],
+ [[['privilege' => '{DAV:}write', 'principal' => 'principals/system/system']], 6],
+ [[['privilege' => '{DAV:}all', 'principal' => 'principals/system/system']], 31],
+ [[['privilege' => '{DAV:}read', 'principal' => 'principals/system/system'],['privilege' => '{DAV:}write', 'principal' => 'principals/system/system']], 7],
+ [[['privilege' => '{DAV:}read', 'principal' => 'principals/system/system'],['privilege' => '{DAV:}all', 'principal' => 'principals/system/system']], 31],
+ [[['privilege' => '{DAV:}all', 'principal' => 'principals/system/system'],['privilege' => '{DAV:}write', 'principal' => 'principals/system/system']], 31],
+ [[['privilege' => '{DAV:}read', 'principal' => 'principals/system/system'],['privilege' => '{DAV:}write', 'principal' => 'principals/system/system'],['privilege' => '{DAV:}all', 'principal' => 'principals/system/system']], 31],
+ [[['privilege' => '{DAV:}all', 'principal' => 'principals/system/system'],['privilege' => '{DAV:}read', 'principal' => 'principals/system/system'],['privilege' => '{DAV:}write', 'principal' => 'principals/system/system']], 31],
];
}
diff --git a/apps/dav/tests/unit/CardDAV/CardDavBackendTest.php b/apps/dav/tests/unit/CardDAV/CardDavBackendTest.php
index 1966a8d8c9a..c5eafa0764a 100644
--- a/apps/dav/tests/unit/CardDAV/CardDavBackendTest.php
+++ b/apps/dav/tests/unit/CardDAV/CardDavBackendTest.php
@@ -50,6 +50,7 @@ class CardDavBackendTest extends TestCase {
private IUserManager&MockObject $userManager;
private IGroupManager&MockObject $groupManager;
private IEventDispatcher&MockObject $dispatcher;
+ private IConfig&MockObject $config;
private Backend $sharingBackend;
private IDBConnection $db;
private CardDavBackend $backend;
@@ -96,6 +97,7 @@ class CardDavBackendTest extends TestCase {
$this->userManager = $this->createMock(IUserManager::class);
$this->groupManager = $this->createMock(IGroupManager::class);
+ $this->config = $this->createMock(IConfig::class);
$this->principal = $this->getMockBuilder(Principal::class)
->setConstructorArgs([
$this->userManager,
@@ -106,7 +108,7 @@ class CardDavBackendTest extends TestCase {
$this->createMock(IAppManager::class),
$this->createMock(ProxyMapper::class),
$this->createMock(KnownUserService::class),
- $this->createMock(IConfig::class),
+ $this->config,
$this->createMock(IFactory::class)
])
->onlyMethods(['getPrincipalByPath', 'getGroupMembership', 'findByUri'])
@@ -135,6 +137,7 @@ class CardDavBackendTest extends TestCase {
$this->userManager,
$this->dispatcher,
$this->sharingBackend,
+ $this->config,
);
// start every test with a empty cards_properties and cards table
$query = $this->db->getQueryBuilder();
@@ -231,7 +234,7 @@ class CardDavBackendTest extends TestCase {
public function testCardOperations(): void {
/** @var CardDavBackend&MockObject $backend */
$backend = $this->getMockBuilder(CardDavBackend::class)
- ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend])
+ ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend,$this->config])
->onlyMethods(['updateProperties', 'purgeProperties'])
->getMock();
@@ -291,7 +294,7 @@ class CardDavBackendTest extends TestCase {
public function testMultiCard(): void {
$this->backend = $this->getMockBuilder(CardDavBackend::class)
- ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend])
+ ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend,$this->config])
->onlyMethods(['updateProperties'])
->getMock();
@@ -345,7 +348,7 @@ class CardDavBackendTest extends TestCase {
public function testMultipleUIDOnDifferentAddressbooks(): void {
$this->backend = $this->getMockBuilder(CardDavBackend::class)
- ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend])
+ ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend,$this->config])
->onlyMethods(['updateProperties'])
->getMock();
@@ -368,7 +371,7 @@ class CardDavBackendTest extends TestCase {
public function testMultipleUIDDenied(): void {
$this->backend = $this->getMockBuilder(CardDavBackend::class)
- ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend])
+ ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend, $this->config])
->onlyMethods(['updateProperties'])
->getMock();
@@ -390,7 +393,7 @@ class CardDavBackendTest extends TestCase {
public function testNoValidUID(): void {
$this->backend = $this->getMockBuilder(CardDavBackend::class)
- ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend])
+ ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend, $this->config])
->onlyMethods(['updateProperties'])
->getMock();
@@ -408,7 +411,7 @@ class CardDavBackendTest extends TestCase {
public function testDeleteWithoutCard(): void {
$this->backend = $this->getMockBuilder(CardDavBackend::class)
- ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend])
+ ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend, $this->config])
->onlyMethods([
'getCardId',
'addChange',
@@ -453,7 +456,7 @@ class CardDavBackendTest extends TestCase {
public function testSyncSupport(): void {
$this->backend = $this->getMockBuilder(CardDavBackend::class)
- ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend])
+ ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend, $this->config])
->onlyMethods(['updateProperties'])
->getMock();
@@ -522,7 +525,7 @@ class CardDavBackendTest extends TestCase {
$cardId = 2;
$backend = $this->getMockBuilder(CardDavBackend::class)
- ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend])
+ ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend, $this->config])
->onlyMethods(['getCardId'])->getMock();
$backend->expects($this->any())->method('getCardId')->willReturn($cardId);
diff --git a/apps/dav/tests/unit/CardDAV/SyncServiceTest.php b/apps/dav/tests/unit/CardDAV/SyncServiceTest.php
index ea4886a67e6..77caed336f4 100644
--- a/apps/dav/tests/unit/CardDAV/SyncServiceTest.php
+++ b/apps/dav/tests/unit/CardDAV/SyncServiceTest.php
@@ -108,7 +108,7 @@ class SyncServiceTest extends TestCase {
'1',
'principals/system/system',
[]
- );
+ )[0];
$this->assertEquals('http://sabre.io/ns/sync/1', $token);
}
@@ -179,7 +179,7 @@ END:VCARD';
'1',
'principals/system/system',
[]
- );
+ )[0];
$this->assertEquals('http://sabre.io/ns/sync/2', $token);
}
@@ -250,7 +250,7 @@ END:VCARD';
'1',
'principals/system/system',
[]
- );
+ )[0];
$this->assertEquals('http://sabre.io/ns/sync/3', $token);
}
@@ -291,7 +291,7 @@ END:VCARD';
'1',
'principals/system/system',
[]
- );
+ )[0];
$this->assertEquals('http://sabre.io/ns/sync/4', $token);
}
diff --git a/apps/encryption/lib/Crypto/EncryptAll.php b/apps/encryption/lib/Crypto/EncryptAll.php
index d9db616e6f1..4ed75b85a93 100644
--- a/apps/encryption/lib/Crypto/EncryptAll.php
+++ b/apps/encryption/lib/Crypto/EncryptAll.php
@@ -12,6 +12,7 @@ use OC\Files\View;
use OCA\Encryption\KeyManager;
use OCA\Encryption\Users\Setup;
use OCA\Encryption\Util;
+use OCP\Files\FileInfo;
use OCP\IConfig;
use OCP\IL10N;
use OCP\IUser;
@@ -202,15 +203,19 @@ class EncryptAll {
while ($root = array_pop($directories)) {
$content = $this->rootView->getDirectoryContent($root);
foreach ($content as $file) {
- $path = $root . '/' . $file['name'];
- if ($this->rootView->is_dir($path)) {
+ $path = $root . '/' . $file->getName();
+ if ($file->isShared()) {
+ $progress->setMessage("Skip shared file/folder $path");
+ $progress->advance();
+ continue;
+ } elseif ($file->getType() === FileInfo::TYPE_FOLDER) {
$directories[] = $path;
continue;
} else {
$progress->setMessage("encrypt files for user $userCount: $path");
$progress->advance();
try {
- if ($this->encryptFile($path) === false) {
+ if ($this->encryptFile($file, $path) === false) {
$progress->setMessage("encrypt files for user $userCount: $path (already encrypted)");
$progress->advance();
}
@@ -231,17 +236,9 @@ class EncryptAll {
}
}
- /**
- * encrypt file
- *
- * @param string $path
- * @return bool
- */
- protected function encryptFile($path) {
-
+ protected function encryptFile(FileInfo $fileInfo, string $path): bool {
// skip already encrypted files
- $fileInfo = $this->rootView->getFileInfo($path);
- if ($fileInfo !== false && $fileInfo->isEncrypted()) {
+ if ($fileInfo->isEncrypted()) {
return true;
}
diff --git a/apps/encryption/tests/Crypto/EncryptAllTest.php b/apps/encryption/tests/Crypto/EncryptAllTest.php
index 9b39c62b650..c56e3375a73 100644
--- a/apps/encryption/tests/Crypto/EncryptAllTest.php
+++ b/apps/encryption/tests/Crypto/EncryptAllTest.php
@@ -82,7 +82,7 @@ class EncryptAllTest extends TestCase {
/**
* We need format method to return a string
- * @var OutputFormatterInterface|\PHPUnit\Framework\MockObject\MockObject
+ * @var OutputFormatterInterface&MockObject
*/
$outputFormatter = $this->createMock(OutputFormatterInterface::class);
$outputFormatter->method('isDecorated')->willReturn(false);
@@ -114,6 +114,13 @@ class EncryptAllTest extends TestCase {
);
}
+ protected function createFileInfoMock($type, string $name): FileInfo&MockObject {
+ $fileInfo = $this->createMock(FileInfo::class);
+ $fileInfo->method('getType')->willReturn($type);
+ $fileInfo->method('getName')->willReturn($name);
+ return $fileInfo;
+ }
+
public function testEncryptAll(): void {
/** @var EncryptAll&MockObject $encryptAll */
$encryptAll = $this->getMockBuilder(EncryptAll::class)
@@ -299,8 +306,8 @@ class EncryptAllTest extends TestCase {
'',
null,
[
- ['name' => 'foo', 'type' => 'dir'],
- ['name' => 'bar', 'type' => 'file'],
+ $this->createFileInfoMock(FileInfo::TYPE_FOLDER, 'foo'),
+ $this->createFileInfoMock(FileInfo::TYPE_FILE, 'bar'),
],
],
[
@@ -308,26 +315,17 @@ class EncryptAllTest extends TestCase {
'',
null,
[
- ['name' => 'subfile', 'type' => 'file']
+ $this->createFileInfoMock(FileInfo::TYPE_FILE, 'subfile'),
],
],
]);
- $this->view->expects($this->any())->method('is_dir')
- ->willReturnCallback(
- function ($path) {
- if ($path === '/user1/files/foo') {
- return true;
- }
- return false;
- }
- );
-
$encryptAllCalls = [];
$encryptAll->expects($this->exactly(2))
->method('encryptFile')
- ->willReturnCallback(function (string $path) use (&$encryptAllCalls): void {
+ ->willReturnCallback(function (FileInfo $file, string $path) use (&$encryptAllCalls): bool {
$encryptAllCalls[] = $path;
+ return true;
});
$outputFormatter = $this->createMock(OutputFormatterInterface::class);
@@ -362,8 +360,7 @@ class EncryptAllTest extends TestCase {
$fileInfo = $this->createMock(FileInfo::class);
$fileInfo->expects($this->any())->method('isEncrypted')
->willReturn($isEncrypted);
- $this->view->expects($this->any())->method('getFileInfo')
- ->willReturn($fileInfo);
+ $this->view->expects($this->never())->method('getFileInfo');
if ($isEncrypted) {
@@ -375,7 +372,7 @@ class EncryptAllTest extends TestCase {
}
$this->assertTrue(
- $this->invokePrivate($this->encryptAll, 'encryptFile', ['foo.txt'])
+ $this->invokePrivate($this->encryptAll, 'encryptFile', [$fileInfo, 'foo.txt'])
);
}
diff --git a/apps/federation/lib/SyncFederationAddressBooks.php b/apps/federation/lib/SyncFederationAddressBooks.php
index 05144b40879..d11f92b76ef 100644
--- a/apps/federation/lib/SyncFederationAddressBooks.php
+++ b/apps/federation/lib/SyncFederationAddressBooks.php
@@ -34,7 +34,7 @@ class SyncFederationAddressBooks {
$url = $trustedServer['url'];
$callback($url, null);
$sharedSecret = $trustedServer['shared_secret'];
- $syncToken = $trustedServer['sync_token'];
+ $oldSyncToken = $trustedServer['sync_token'];
$endPoints = $this->ocsDiscoveryService->discover($url, 'FEDERATED_SHARING');
$cardDavUser = $endPoints['carddav-user'] ?? 'system';
@@ -49,10 +49,25 @@ class SyncFederationAddressBooks {
$targetBookProperties = [
'{DAV:}displayname' => $url
];
+
try {
- $newToken = $this->syncService->syncRemoteAddressBook($url, $cardDavUser, $addressBookUrl, $sharedSecret, $syncToken, $targetBookId, $targetPrincipal, $targetBookProperties);
- if ($newToken !== $syncToken) {
- $this->dbHandler->setServerStatus($url, TrustedServers::STATUS_OK, $newToken);
+ $syncToken = $oldSyncToken;
+
+ do {
+ [$syncToken, $truncated] = $this->syncService->syncRemoteAddressBook(
+ $url,
+ $cardDavUser,
+ $addressBookUrl,
+ $sharedSecret,
+ $syncToken,
+ $targetBookId,
+ $targetPrincipal,
+ $targetBookProperties
+ );
+ } while ($truncated);
+
+ if ($syncToken !== $oldSyncToken) {
+ $this->dbHandler->setServerStatus($url, TrustedServers::STATUS_OK, $syncToken);
} else {
$this->logger->debug("Sync Token for $url unchanged from previous sync");
// The server status might have been changed to a failure status in previous runs.
diff --git a/apps/federation/tests/SyncFederationAddressbooksTest.php b/apps/federation/tests/SyncFederationAddressbooksTest.php
index 8b075204859..ff03f5cf442 100644
--- a/apps/federation/tests/SyncFederationAddressbooksTest.php
+++ b/apps/federation/tests/SyncFederationAddressbooksTest.php
@@ -45,7 +45,7 @@ class SyncFederationAddressbooksTest extends \Test\TestCase {
->with('https://cloud.example.org', 1, '1');
$syncService = $this->createMock(SyncService::class);
$syncService->expects($this->once())->method('syncRemoteAddressBook')
- ->willReturn('1');
+ ->willReturn(['1', false]);
/** @var SyncService $syncService */
$s = new SyncFederationAddressBooks($dbHandler, $syncService, $this->discoveryService, $this->logger);
@@ -96,7 +96,7 @@ class SyncFederationAddressbooksTest extends \Test\TestCase {
->with('https://cloud.example.org', 1);
$syncService = $this->createMock(SyncService::class);
$syncService->expects($this->once())->method('syncRemoteAddressBook')
- ->willReturn('0');
+ ->willReturn(['0', false]);
/** @var SyncService $syncService */
$s = new SyncFederationAddressBooks($dbHandler, $syncService, $this->discoveryService, $this->logger);
diff --git a/apps/files/l10n/es.js b/apps/files/l10n/es.js
index 6e32740a677..0d21bdef976 100644
--- a/apps/files/l10n/es.js
+++ b/apps/files/l10n/es.js
@@ -1,9 +1,9 @@
OC.L10N.register(
"files",
{
- "Added to favorites" : "Agregado a favoritos",
+ "Added to favorites" : "Añadido a favoritos",
"Removed from favorites" : "Quitado de favoritos",
- "You added {file} to your favorites" : "Has agregado {file} a tus favoritos",
+ "You added {file} to your favorites" : "Has añadido {file} a tus favoritos",
"You removed {file} from your favorites" : "Has quitado {file} de tus favoritos",
"Favorites" : "Favoritos",
"File changes" : "Cambios del archivo",
@@ -39,10 +39,10 @@ OC.L10N.register(
"{user} renamed {oldfile} to {newfile}" : "{user} ha renombrado {oldfile} a {newfile}",
"You moved {oldfile} to {newfile}" : "Has movido {oldfile} a {newfile}",
"{user} moved {oldfile} to {newfile}" : "{user} movió {oldfile} a {newfile}",
- "A file has been added to or removed from your <strong>favorites</strong>" : "Un archivo fue agregado o borrado de tus <strong>favoritos</strong>",
+ "A file has been added to or removed from your <strong>favorites</strong>" : "Un archivo se ha añadido o borrado de tus <strong>favoritos</strong>",
"Files" : "Archivos",
"A file or folder has been <strong>changed</strong>" : "Se ha <strong>modificado</strong> un archivo o carpeta",
- "A favorite file or folder has been <strong>changed</strong>" : "Un archivo o carpeta favorito ha sido <strong>cambiado</strong>",
+ "A favorite file or folder has been <strong>changed</strong>" : "Un archivo favorito o carpeta se ha <strong>cambiado</strong>",
"Failed to authorize" : "Fallo al autorizar",
"Invalid folder path" : "Ruta de carpeta inválida",
"Folder not found" : "Carpeta no encontrada",
@@ -129,6 +129,7 @@ OC.L10N.register(
"Search globally by filename …" : "Búsqueda global por nombre de archivo …",
"Search here by filename …" : "Buscar aquí por nombre de archivo …",
"Search scope options" : "Opciones de alcance de la búsqueda",
+ "Filter and search from this location" : "Filtrar y buscar en esta ubicación",
"Search globally" : "Buscar globalmente",
"{usedQuotaByte} used" : "{usedQuotaByte} utilizados",
"{used} of {quota} used" : "{used} usados de {quota}",
@@ -141,6 +142,7 @@ OC.L10N.register(
"Create new folder" : "Crear carpeta nueva",
"This name is already in use." : "Este nombre ya está en uso.",
"Create" : "Crear",
+ "Files starting with a dot are hidden by default" : "Los archivos que comienzan con un punto son ocultados por defecto",
"Fill template fields" : "Rellenar los campos de la plantilla",
"Submitting fields …" : "Enviando campos …",
"Submit" : "Enviar",
@@ -182,7 +184,7 @@ OC.L10N.register(
"Loading current folder" : "Cargando carpeta actual",
"Retry" : "Reintentar",
"No files in here" : "Aquí no hay archivos",
- "Upload some content or sync with your devices!" : "¡Suba contenidos o sincronice sus dispositivos!",
+ "Upload some content or sync with your devices!" : "¡Sube contenido o sincroniza tus dispositivos!",
"Go back" : "Ir atrás",
"Views" : "Vistas",
"Files settings" : "Configuración de archivos",
@@ -200,16 +202,21 @@ OC.L10N.register(
"Sort favorites first" : "Ordenar los favoritos primero",
"Sort folders before files" : "Ordenar carpetas antes que archivos",
"Enable folder tree" : "Habilitar el árbol de carpetas",
+ "Visual settings" : "Ajustes visuales",
"Show hidden files" : "Mostrar archivos ocultos",
"Show file type column" : "Mostrar la columna de tipo de archivo",
"Crop image previews" : "Recortar la previsualización de las imágenes",
+ "Show files extensions" : "Mostrar extensiones de archivos",
"Additional settings" : "Ajustes adicionales",
"WebDAV" : "WebDAV",
- "WebDAV URL" : "WebDAV URL",
+ "WebDAV URL" : "URL de WebDAV",
"Copy to clipboard" : "Copiar al portapapeles",
+ "Use this address to access your Files via WebDAV." : "Usa esta dirección para acceder a tus Archivos a través de WebDAV.",
+ "Two-Factor Authentication is enabled for your account, and therefore you need to use an app password to connect an external WebDAV client." : "La autenticación en dos pasos está habilitada para su cuenta y, por lo tanto, debe usar una contraseña de aplicación para conectarse a un cliente WebDAV externo.",
"Warnings" : "Advertencias",
"Prevent warning dialogs from open or reenable them." : "Evitar que se abran los diálogos de advertencia o volver a habilitarlos.",
"Show a warning dialog when changing a file extension." : "Mostrar un diálogo de advertencia cuando se cambia la extensión de un archivo.",
+ "Show a warning dialog when deleting files." : "Mostrar un cuadro de aviso cuando se borren archivos.",
"Keyboard shortcuts" : "Atajos de teclado",
"Speed up your Files experience with these quick shortcuts." : "Acelere su experiencia con Archivos con esos rápidos atajos de teclado.",
"Open the actions menu for a file" : "Abrir el menú de acciones para un archivo",
@@ -327,6 +334,7 @@ OC.L10N.register(
"Templates" : "Plantillas",
"New template folder" : "Nueva carpeta de plantillas",
"In folder" : "En carpeta",
+ "Search in all files" : "Buscar en todos los archivos",
"Search in folder: {folder}" : "Buscar en carpeta: {folder}",
"One of the dropped files could not be processed" : "Uno de los archivos arrastrados no puede ser procesado",
"Your browser does not support the Filesystem API. Directories will not be uploaded" : "Su navegador no soporta la API de Sistema de archivos. Los directorios no se subirán",
@@ -360,6 +368,7 @@ OC.L10N.register(
"No favorites yet" : "Aún no hay favoritos",
"Files and folders you mark as favorite will show up here" : "Aquí aparecerán los archivos y carpetas que has marcado como favoritos",
"List of your files and folders." : "Lista de sus archivos y carpetas.",
+ "Folder tree" : "Árbol de carpetas",
"List of your files and folders that are not shared." : "Lista de sus archivos y carpetas que no están compartidos.",
"No personal files found" : "No se encontraron archivos personales",
"Files that are not shared will show up here." : "Los archivos y carpetas que no ha compartido aparecerán aquí.",
@@ -372,12 +381,12 @@ OC.L10N.register(
"No entries found in this folder" : "No hay entradas en esta carpeta",
"Select all" : "Seleccionar todo",
"Upload too large" : "Subida demasido grande",
- "The files you are trying to upload exceed the maximum size for file uploads on this server." : "Los archivos que está intentando subir sobrepasan el tamaño máximo permitido en este servidor.",
+ "The files you are trying to upload exceed the maximum size for file uploads on this server." : "Los archivos que estás intentando subir sobrepasan el tamaño máximo permitido en este servidor.",
"File could not be found" : "El archivo no se ha encontrado",
"Show list view" : "Mostrar vista de lista",
"Show grid view" : "Mostrar vista de cuadrícula",
"Close" : "Cerrar",
- "Could not create folder \"{dir}\"" : "No se pudo crear la carpeta \"{dir}\"",
+ "Could not create folder \"{dir}\"" : "No se ha podido crear la carpeta \"{dir}\"",
"This will stop your current uploads." : "Esto detendrá las subidas en curso.",
"Upload cancelled." : "Subida cancelada.",
"Processing files …" : "Procesando archivos …",
@@ -385,7 +394,7 @@ OC.L10N.register(
"Unable to upload {filename} as it is a directory or has 0 bytes" : "No ha sido posible subir {filename} porque es un directorio o tiene 0 bytes",
"Not enough free space, you are uploading {size1} but only {size2} is left" : "No hay suficiente espacio libre. Quiere subir {size1} pero solo quedan {size2}",
"Target folder \"{dir}\" does not exist any more" : "La carpeta de destino \"{dir}\" ya no existe",
- "An unknown error has occurred" : "Ha ocurrido un error desconocido",
+ "An unknown error has occurred" : "Se ha producido un error desconocido",
"File could not be uploaded" : "No se ha podido subir el archivo",
"Uploading …" : "Subiendo …",
"{remainingTime} ({currentNumber}/{total})" : "{remainingTime} ({currentNumber}/{total})",
@@ -404,18 +413,18 @@ OC.L10N.register(
"Select directory \"{dirName}\"" : "Seleccione la carpeta \"{dirName}\"",
"Select file \"{fileName}\"" : "Seleccione el archivo \"{fileName}\"",
"Unable to determine date" : "No se ha podido determinar la fecha",
- "Could not move \"{file}\", target exists" : "No se pudo mover \"{file}\", ya existe",
- "Could not move \"{file}\"" : "No se pudo mover \"{file}\"",
+ "Could not move \"{file}\", target exists" : "No se ha podido mover \"{file}\", ya existe",
+ "Could not move \"{file}\"" : "No se ha podido mover \"{file}\"",
"copy" : "copiar",
"Could not copy \"{file}\", target exists" : "No se ha podido copiar \"{file}\", ya existe el destino",
"Could not copy \"{file}\"" : "No se ha podido copiar \"{file}\"",
"Copied {origin} inside {destination}" : "Se ha copiado {origin} dentro de {destination}",
"Copied {origin} and {nbfiles} other files inside {destination}" : "Se han copiado {origin} y {nbfiles} otros archivos dentro de {destination}",
"{newName} already exists" : "{newName} ya existe",
- "Could not create file \"{file}\"" : "No se pudo crear archivo \"{file}\"",
+ "Could not create file \"{file}\"" : "No se ha podido crear el archivo \"{file}\"",
"Could not create file \"{file}\" because it already exists" : "No se pudo crear archivo \"{file}\" porque ya existe",
"Could not create folder \"{dir}\" because it already exists" : "No se ha podido crear la carpeta \"{dir}\" porque ya existe",
- "Could not fetch file details \"{file}\"" : "No se pudieron obtener los detalles de \"{file}\"",
+ "Could not fetch file details \"{file}\"" : "No se han podido obtener los detalles de \"{file}\"",
"Error deleting file \"{fileName}\"." : "Error al borrar el archivo \"{fileName}\".",
"No search results in other folders for {tag}{filter}{endtag}" : "No hay resultados de búsqueda en otras carpetas para {tag}{filter}{endtag}",
"Enter more than two characters to search in other folders" : "Escriba más de dos caracteres para buscar en otras carpetas",
@@ -428,7 +437,7 @@ OC.L10N.register(
"Select file range" : "Seleccionar el rango de archivos",
"{used}%" : "{used}%",
"{used} used" : "{used} usados",
- "\"{name}\" is an invalid file name." : "\"{name}\" es un nombre de archivo inválido.",
+ "\"{name}\" is an invalid file name." : "\"{name}\" es un nombre de archivo no válido.",
"File name cannot be empty." : "El nombre de archivo no puede estar vacío.",
"\"/\" is not allowed inside a file name." : "\"/\" no se permite en un nombre de archivo.",
"\"{name}\" is not an allowed filetype" : "\"{name}\" no es un tipo de archivo permitido",
@@ -446,10 +455,10 @@ OC.L10N.register(
"_%n byte_::_%n bytes_" : ["%n byte","%n bytes","%n bytes"],
"Favored" : "Favorecido",
"Favor" : "Favorecer",
- "Copy direct link (only works for people who have access to this file/folder)" : "El enlace directo fue copiado (solo funciona para usuarios que tienen acceso a este archivo/carpeta)",
+ "Copy direct link (only works for people who have access to this file/folder)" : "Copiar enlace directo (solo funciona para usuarios que tienen acceso a este archivo/carpeta)",
"Upload file" : "Subir archivo",
"Not favored" : "No favorecido",
- "An error occurred while trying to update the tags" : "Se produjo un error al tratar de actualizar las etiquetas",
+ "An error occurred while trying to update the tags" : "Se ha producido un error al tratar de actualizar las etiquetas",
"Upload (max. %s)" : "Subida (máx. %s)",
"Submitting fields…" : "Enviando campos…",
"Filter filenames…" : "Filtrar nombres de archivo…",
diff --git a/apps/files/l10n/es.json b/apps/files/l10n/es.json
index c13b8161666..440e426013c 100644
--- a/apps/files/l10n/es.json
+++ b/apps/files/l10n/es.json
@@ -1,7 +1,7 @@
{ "translations": {
- "Added to favorites" : "Agregado a favoritos",
+ "Added to favorites" : "Añadido a favoritos",
"Removed from favorites" : "Quitado de favoritos",
- "You added {file} to your favorites" : "Has agregado {file} a tus favoritos",
+ "You added {file} to your favorites" : "Has añadido {file} a tus favoritos",
"You removed {file} from your favorites" : "Has quitado {file} de tus favoritos",
"Favorites" : "Favoritos",
"File changes" : "Cambios del archivo",
@@ -37,10 +37,10 @@
"{user} renamed {oldfile} to {newfile}" : "{user} ha renombrado {oldfile} a {newfile}",
"You moved {oldfile} to {newfile}" : "Has movido {oldfile} a {newfile}",
"{user} moved {oldfile} to {newfile}" : "{user} movió {oldfile} a {newfile}",
- "A file has been added to or removed from your <strong>favorites</strong>" : "Un archivo fue agregado o borrado de tus <strong>favoritos</strong>",
+ "A file has been added to or removed from your <strong>favorites</strong>" : "Un archivo se ha añadido o borrado de tus <strong>favoritos</strong>",
"Files" : "Archivos",
"A file or folder has been <strong>changed</strong>" : "Se ha <strong>modificado</strong> un archivo o carpeta",
- "A favorite file or folder has been <strong>changed</strong>" : "Un archivo o carpeta favorito ha sido <strong>cambiado</strong>",
+ "A favorite file or folder has been <strong>changed</strong>" : "Un archivo favorito o carpeta se ha <strong>cambiado</strong>",
"Failed to authorize" : "Fallo al autorizar",
"Invalid folder path" : "Ruta de carpeta inválida",
"Folder not found" : "Carpeta no encontrada",
@@ -127,6 +127,7 @@
"Search globally by filename …" : "Búsqueda global por nombre de archivo …",
"Search here by filename …" : "Buscar aquí por nombre de archivo …",
"Search scope options" : "Opciones de alcance de la búsqueda",
+ "Filter and search from this location" : "Filtrar y buscar en esta ubicación",
"Search globally" : "Buscar globalmente",
"{usedQuotaByte} used" : "{usedQuotaByte} utilizados",
"{used} of {quota} used" : "{used} usados de {quota}",
@@ -139,6 +140,7 @@
"Create new folder" : "Crear carpeta nueva",
"This name is already in use." : "Este nombre ya está en uso.",
"Create" : "Crear",
+ "Files starting with a dot are hidden by default" : "Los archivos que comienzan con un punto son ocultados por defecto",
"Fill template fields" : "Rellenar los campos de la plantilla",
"Submitting fields …" : "Enviando campos …",
"Submit" : "Enviar",
@@ -180,7 +182,7 @@
"Loading current folder" : "Cargando carpeta actual",
"Retry" : "Reintentar",
"No files in here" : "Aquí no hay archivos",
- "Upload some content or sync with your devices!" : "¡Suba contenidos o sincronice sus dispositivos!",
+ "Upload some content or sync with your devices!" : "¡Sube contenido o sincroniza tus dispositivos!",
"Go back" : "Ir atrás",
"Views" : "Vistas",
"Files settings" : "Configuración de archivos",
@@ -198,16 +200,21 @@
"Sort favorites first" : "Ordenar los favoritos primero",
"Sort folders before files" : "Ordenar carpetas antes que archivos",
"Enable folder tree" : "Habilitar el árbol de carpetas",
+ "Visual settings" : "Ajustes visuales",
"Show hidden files" : "Mostrar archivos ocultos",
"Show file type column" : "Mostrar la columna de tipo de archivo",
"Crop image previews" : "Recortar la previsualización de las imágenes",
+ "Show files extensions" : "Mostrar extensiones de archivos",
"Additional settings" : "Ajustes adicionales",
"WebDAV" : "WebDAV",
- "WebDAV URL" : "WebDAV URL",
+ "WebDAV URL" : "URL de WebDAV",
"Copy to clipboard" : "Copiar al portapapeles",
+ "Use this address to access your Files via WebDAV." : "Usa esta dirección para acceder a tus Archivos a través de WebDAV.",
+ "Two-Factor Authentication is enabled for your account, and therefore you need to use an app password to connect an external WebDAV client." : "La autenticación en dos pasos está habilitada para su cuenta y, por lo tanto, debe usar una contraseña de aplicación para conectarse a un cliente WebDAV externo.",
"Warnings" : "Advertencias",
"Prevent warning dialogs from open or reenable them." : "Evitar que se abran los diálogos de advertencia o volver a habilitarlos.",
"Show a warning dialog when changing a file extension." : "Mostrar un diálogo de advertencia cuando se cambia la extensión de un archivo.",
+ "Show a warning dialog when deleting files." : "Mostrar un cuadro de aviso cuando se borren archivos.",
"Keyboard shortcuts" : "Atajos de teclado",
"Speed up your Files experience with these quick shortcuts." : "Acelere su experiencia con Archivos con esos rápidos atajos de teclado.",
"Open the actions menu for a file" : "Abrir el menú de acciones para un archivo",
@@ -325,6 +332,7 @@
"Templates" : "Plantillas",
"New template folder" : "Nueva carpeta de plantillas",
"In folder" : "En carpeta",
+ "Search in all files" : "Buscar en todos los archivos",
"Search in folder: {folder}" : "Buscar en carpeta: {folder}",
"One of the dropped files could not be processed" : "Uno de los archivos arrastrados no puede ser procesado",
"Your browser does not support the Filesystem API. Directories will not be uploaded" : "Su navegador no soporta la API de Sistema de archivos. Los directorios no se subirán",
@@ -358,6 +366,7 @@
"No favorites yet" : "Aún no hay favoritos",
"Files and folders you mark as favorite will show up here" : "Aquí aparecerán los archivos y carpetas que has marcado como favoritos",
"List of your files and folders." : "Lista de sus archivos y carpetas.",
+ "Folder tree" : "Árbol de carpetas",
"List of your files and folders that are not shared." : "Lista de sus archivos y carpetas que no están compartidos.",
"No personal files found" : "No se encontraron archivos personales",
"Files that are not shared will show up here." : "Los archivos y carpetas que no ha compartido aparecerán aquí.",
@@ -370,12 +379,12 @@
"No entries found in this folder" : "No hay entradas en esta carpeta",
"Select all" : "Seleccionar todo",
"Upload too large" : "Subida demasido grande",
- "The files you are trying to upload exceed the maximum size for file uploads on this server." : "Los archivos que está intentando subir sobrepasan el tamaño máximo permitido en este servidor.",
+ "The files you are trying to upload exceed the maximum size for file uploads on this server." : "Los archivos que estás intentando subir sobrepasan el tamaño máximo permitido en este servidor.",
"File could not be found" : "El archivo no se ha encontrado",
"Show list view" : "Mostrar vista de lista",
"Show grid view" : "Mostrar vista de cuadrícula",
"Close" : "Cerrar",
- "Could not create folder \"{dir}\"" : "No se pudo crear la carpeta \"{dir}\"",
+ "Could not create folder \"{dir}\"" : "No se ha podido crear la carpeta \"{dir}\"",
"This will stop your current uploads." : "Esto detendrá las subidas en curso.",
"Upload cancelled." : "Subida cancelada.",
"Processing files …" : "Procesando archivos …",
@@ -383,7 +392,7 @@
"Unable to upload {filename} as it is a directory or has 0 bytes" : "No ha sido posible subir {filename} porque es un directorio o tiene 0 bytes",
"Not enough free space, you are uploading {size1} but only {size2} is left" : "No hay suficiente espacio libre. Quiere subir {size1} pero solo quedan {size2}",
"Target folder \"{dir}\" does not exist any more" : "La carpeta de destino \"{dir}\" ya no existe",
- "An unknown error has occurred" : "Ha ocurrido un error desconocido",
+ "An unknown error has occurred" : "Se ha producido un error desconocido",
"File could not be uploaded" : "No se ha podido subir el archivo",
"Uploading …" : "Subiendo …",
"{remainingTime} ({currentNumber}/{total})" : "{remainingTime} ({currentNumber}/{total})",
@@ -402,18 +411,18 @@
"Select directory \"{dirName}\"" : "Seleccione la carpeta \"{dirName}\"",
"Select file \"{fileName}\"" : "Seleccione el archivo \"{fileName}\"",
"Unable to determine date" : "No se ha podido determinar la fecha",
- "Could not move \"{file}\", target exists" : "No se pudo mover \"{file}\", ya existe",
- "Could not move \"{file}\"" : "No se pudo mover \"{file}\"",
+ "Could not move \"{file}\", target exists" : "No se ha podido mover \"{file}\", ya existe",
+ "Could not move \"{file}\"" : "No se ha podido mover \"{file}\"",
"copy" : "copiar",
"Could not copy \"{file}\", target exists" : "No se ha podido copiar \"{file}\", ya existe el destino",
"Could not copy \"{file}\"" : "No se ha podido copiar \"{file}\"",
"Copied {origin} inside {destination}" : "Se ha copiado {origin} dentro de {destination}",
"Copied {origin} and {nbfiles} other files inside {destination}" : "Se han copiado {origin} y {nbfiles} otros archivos dentro de {destination}",
"{newName} already exists" : "{newName} ya existe",
- "Could not create file \"{file}\"" : "No se pudo crear archivo \"{file}\"",
+ "Could not create file \"{file}\"" : "No se ha podido crear el archivo \"{file}\"",
"Could not create file \"{file}\" because it already exists" : "No se pudo crear archivo \"{file}\" porque ya existe",
"Could not create folder \"{dir}\" because it already exists" : "No se ha podido crear la carpeta \"{dir}\" porque ya existe",
- "Could not fetch file details \"{file}\"" : "No se pudieron obtener los detalles de \"{file}\"",
+ "Could not fetch file details \"{file}\"" : "No se han podido obtener los detalles de \"{file}\"",
"Error deleting file \"{fileName}\"." : "Error al borrar el archivo \"{fileName}\".",
"No search results in other folders for {tag}{filter}{endtag}" : "No hay resultados de búsqueda en otras carpetas para {tag}{filter}{endtag}",
"Enter more than two characters to search in other folders" : "Escriba más de dos caracteres para buscar en otras carpetas",
@@ -426,7 +435,7 @@
"Select file range" : "Seleccionar el rango de archivos",
"{used}%" : "{used}%",
"{used} used" : "{used} usados",
- "\"{name}\" is an invalid file name." : "\"{name}\" es un nombre de archivo inválido.",
+ "\"{name}\" is an invalid file name." : "\"{name}\" es un nombre de archivo no válido.",
"File name cannot be empty." : "El nombre de archivo no puede estar vacío.",
"\"/\" is not allowed inside a file name." : "\"/\" no se permite en un nombre de archivo.",
"\"{name}\" is not an allowed filetype" : "\"{name}\" no es un tipo de archivo permitido",
@@ -444,10 +453,10 @@
"_%n byte_::_%n bytes_" : ["%n byte","%n bytes","%n bytes"],
"Favored" : "Favorecido",
"Favor" : "Favorecer",
- "Copy direct link (only works for people who have access to this file/folder)" : "El enlace directo fue copiado (solo funciona para usuarios que tienen acceso a este archivo/carpeta)",
+ "Copy direct link (only works for people who have access to this file/folder)" : "Copiar enlace directo (solo funciona para usuarios que tienen acceso a este archivo/carpeta)",
"Upload file" : "Subir archivo",
"Not favored" : "No favorecido",
- "An error occurred while trying to update the tags" : "Se produjo un error al tratar de actualizar las etiquetas",
+ "An error occurred while trying to update the tags" : "Se ha producido un error al tratar de actualizar las etiquetas",
"Upload (max. %s)" : "Subida (máx. %s)",
"Submitting fields…" : "Enviando campos…",
"Filter filenames…" : "Filtrar nombres de archivo…",
diff --git a/apps/files/l10n/mk.js b/apps/files/l10n/mk.js
index 8d9883c4dd5..80f697aad04 100644
--- a/apps/files/l10n/mk.js
+++ b/apps/files/l10n/mk.js
@@ -277,15 +277,24 @@ OC.L10N.register(
"This year ({year})" : "Оваа година ({year})",
"Last year ({year})" : "Минатата година ({year})",
"Documents" : "Документи",
+ "Spreadsheets" : "Табели",
+ "Presentations" : "Презентации",
+ "PDFs" : "PDF-и",
"Folders" : "Папки",
"Audio" : "Аудио",
+ "Photos and images" : "Фотографии и слики",
"Videos" : "Видеа",
"Created new folder \"{name}\"" : "Креирана нова папка \"{name}\"",
"Unable to initialize the templates directory" : "Не може да се иницијализира папка за шаблони",
"Create templates folder" : "Креирај папка за шаблони",
"Templates" : "Шаблони",
"New template folder" : "Нова папка за шаблони",
+ "In folder" : "Во папка",
+ "Search in folder: {folder}" : "Барај во папка: {folder}",
"One of the dropped files could not be processed" : "Една од испуштените датотеки неможе да се процесоира",
+ "Unable to create the directory {directory}" : "Неможе да се креира папка {directory}",
+ "Some files could not be uploaded" : "Некој датотеки неможат да се прикачат",
+ "Files uploaded successfully" : "Успешно прикачени датотеки",
"Some files could not be moved" : "Некои датотеки не можат да се преместат",
"Could not rename \"{oldName}\", it does not exist any more" : "Неможе да се преименува \"{oldName}\", не постои повеќе",
"The name \"{newName}\" is already used in the folder \"{dir}\". Please choose a different name." : "Името \"{newName}\" веќе се користи во папката \"{dir}\". Ве молиме изберете друго име.",
@@ -293,9 +302,15 @@ OC.L10N.register(
"This operation is forbidden" : "Операцијата не е дозволена",
"This directory is unavailable, please check the logs or contact the administrator" : "Овој директориум е недостапен, ве молиме проверете ги логовите или контактирајте со администраторот",
"Storage is temporarily not available" : "Складиштето моментално не е достапно",
+ "Unexpected error: {error}" : "Неочекувана грешка: {error}",
"_%n file_::_%n files_" : ["%n датотека","%n датотеки"],
"_%n folder_::_%n folders_" : ["%n папка","%n папки"],
"Filename must not be empty." : "Името на датотеката не може да биде празно.",
+ "\"{char}\" is not allowed inside a filename." : "\"{char}\" не е дозволен во името на датотеката.",
+ "\"{segment}\" is a reserved name and not allowed for filenames." : "\"{segment}\" е резервирано име и не е дозволено во името на датотеката.",
+ "\"{extension}\" is not an allowed filetype." : "\"{extension}\" не е дозволен вид на датотека.",
+ "Filenames must not end with \"{extension}\"." : "Името неможе да завршува со \"{extension}\".",
+ "List of favorite files and folders." : "Листа на омилени датотеки и папки.",
"No favorites yet" : "Сеуште нема фаворити",
"Files and folders you mark as favorite will show up here" : "Датотеките и папките кои ќе ги означите како чести, ќе се појават тука",
"List of your files and folders." : "Листа на вашите датотеки и папки.",
@@ -397,6 +412,8 @@ OC.L10N.register(
"Personal Files" : "Лични датотеки",
"Text file" : "Текстуална датотека",
"New text file.txt" : "Нова текстуална датотека file.txt",
+ "%1$s (renamed)" : "%1$s (преименувано)",
+ "renamed file" : "преименувана датотека",
"Filter file names …" : "Филтрирај имиња на датотеки ..."
},
"nplurals=2; plural=(n % 10 == 1 && n % 100 != 11) ? 0 : 1;");
diff --git a/apps/files/l10n/mk.json b/apps/files/l10n/mk.json
index fca2b8d3b5c..bc76f3e5e86 100644
--- a/apps/files/l10n/mk.json
+++ b/apps/files/l10n/mk.json
@@ -275,15 +275,24 @@
"This year ({year})" : "Оваа година ({year})",
"Last year ({year})" : "Минатата година ({year})",
"Documents" : "Документи",
+ "Spreadsheets" : "Табели",
+ "Presentations" : "Презентации",
+ "PDFs" : "PDF-и",
"Folders" : "Папки",
"Audio" : "Аудио",
+ "Photos and images" : "Фотографии и слики",
"Videos" : "Видеа",
"Created new folder \"{name}\"" : "Креирана нова папка \"{name}\"",
"Unable to initialize the templates directory" : "Не може да се иницијализира папка за шаблони",
"Create templates folder" : "Креирај папка за шаблони",
"Templates" : "Шаблони",
"New template folder" : "Нова папка за шаблони",
+ "In folder" : "Во папка",
+ "Search in folder: {folder}" : "Барај во папка: {folder}",
"One of the dropped files could not be processed" : "Една од испуштените датотеки неможе да се процесоира",
+ "Unable to create the directory {directory}" : "Неможе да се креира папка {directory}",
+ "Some files could not be uploaded" : "Некој датотеки неможат да се прикачат",
+ "Files uploaded successfully" : "Успешно прикачени датотеки",
"Some files could not be moved" : "Некои датотеки не можат да се преместат",
"Could not rename \"{oldName}\", it does not exist any more" : "Неможе да се преименува \"{oldName}\", не постои повеќе",
"The name \"{newName}\" is already used in the folder \"{dir}\". Please choose a different name." : "Името \"{newName}\" веќе се користи во папката \"{dir}\". Ве молиме изберете друго име.",
@@ -291,9 +300,15 @@
"This operation is forbidden" : "Операцијата не е дозволена",
"This directory is unavailable, please check the logs or contact the administrator" : "Овој директориум е недостапен, ве молиме проверете ги логовите или контактирајте со администраторот",
"Storage is temporarily not available" : "Складиштето моментално не е достапно",
+ "Unexpected error: {error}" : "Неочекувана грешка: {error}",
"_%n file_::_%n files_" : ["%n датотека","%n датотеки"],
"_%n folder_::_%n folders_" : ["%n папка","%n папки"],
"Filename must not be empty." : "Името на датотеката не може да биде празно.",
+ "\"{char}\" is not allowed inside a filename." : "\"{char}\" не е дозволен во името на датотеката.",
+ "\"{segment}\" is a reserved name and not allowed for filenames." : "\"{segment}\" е резервирано име и не е дозволено во името на датотеката.",
+ "\"{extension}\" is not an allowed filetype." : "\"{extension}\" не е дозволен вид на датотека.",
+ "Filenames must not end with \"{extension}\"." : "Името неможе да завршува со \"{extension}\".",
+ "List of favorite files and folders." : "Листа на омилени датотеки и папки.",
"No favorites yet" : "Сеуште нема фаворити",
"Files and folders you mark as favorite will show up here" : "Датотеките и папките кои ќе ги означите како чести, ќе се појават тука",
"List of your files and folders." : "Листа на вашите датотеки и папки.",
@@ -395,6 +410,8 @@
"Personal Files" : "Лични датотеки",
"Text file" : "Текстуална датотека",
"New text file.txt" : "Нова текстуална датотека file.txt",
+ "%1$s (renamed)" : "%1$s (преименувано)",
+ "renamed file" : "преименувана датотека",
"Filter file names …" : "Филтрирај имиња на датотеки ..."
},"pluralForm" :"nplurals=2; plural=(n % 10 == 1 && n % 100 != 11) ? 0 : 1;"
} \ No newline at end of file
diff --git a/apps/files/l10n/zh_CN.js b/apps/files/l10n/zh_CN.js
index 2644bffadf7..4903a18b8f8 100644
--- a/apps/files/l10n/zh_CN.js
+++ b/apps/files/l10n/zh_CN.js
@@ -142,6 +142,7 @@ OC.L10N.register(
"Create new folder" : "创建新文件夹",
"This name is already in use." : "此名称已被使用。",
"Create" : "创建",
+ "Files starting with a dot are hidden by default" : "以点开头的文件默认是隐藏的",
"Fill template fields" : "填写模板字段",
"Submitting fields …" : "正在提交字段…",
"Submit" : "使用",
@@ -201,16 +202,21 @@ OC.L10N.register(
"Sort favorites first" : "收藏排序优先",
"Sort folders before files" : "将文件夹排在文件前面",
"Enable folder tree" : "启用文件夹树",
+ "Visual settings" : "视觉设置",
"Show hidden files" : "显示隐藏文件",
"Show file type column" : "显示文件类型列",
"Crop image previews" : "裁剪图片预览",
+ "Show files extensions" : "显示文件扩展名",
"Additional settings" : "其他设置",
"WebDAV" : "WebDAV",
"WebDAV URL" : "WebDAV URL",
"Copy to clipboard" : "复制到剪贴板",
+ "Use this address to access your Files via WebDAV." : "使用此地址通过 WebDAV 访问您的文件。",
+ "Two-Factor Authentication is enabled for your account, and therefore you need to use an app password to connect an external WebDAV client." : "您的账号已启用双因素身份验证,因此您需要使用应用密码来连接外部 WebDAV 客户端。",
"Warnings" : "警告",
"Prevent warning dialogs from open or reenable them." : "防止打开或重新启用警告对话框。",
"Show a warning dialog when changing a file extension." : "更改文件扩展名时显示警告对话框。",
+ "Show a warning dialog when deleting files." : "删除文件时显示警告对话框。",
"Keyboard shortcuts" : "键盘快捷键",
"Speed up your Files experience with these quick shortcuts." : "这些快捷键可以加快你的“文件”体验。",
"Open the actions menu for a file" : "打开文件的操作菜单",
@@ -328,6 +334,7 @@ OC.L10N.register(
"Templates" : "模板",
"New template folder" : "新建模板文件夹",
"In folder" : "文件夹内",
+ "Search in all files" : "在所有文件中搜索",
"Search in folder: {folder}" : "在文件夹内搜索:{folder}",
"One of the dropped files could not be processed" : "无法处理其中一个已删除的文件",
"Your browser does not support the Filesystem API. Directories will not be uploaded" : "你的浏览器不支持文件系统API。目录不会被上传",
@@ -361,6 +368,7 @@ OC.L10N.register(
"No favorites yet" : "暂无收藏",
"Files and folders you mark as favorite will show up here" : "收藏的文件和文件夹会在这里显示",
"List of your files and folders." : "您的文件与文件件列表。",
+ "Folder tree" : "文件夹树",
"List of your files and folders that are not shared." : "尚未分享的文件与文件夹",
"No personal files found" : "找不到个人文件",
"Files that are not shared will show up here." : "尚未分享的文件会显示在此处",
diff --git a/apps/files/l10n/zh_CN.json b/apps/files/l10n/zh_CN.json
index e519c6853c0..ef788c7c4e5 100644
--- a/apps/files/l10n/zh_CN.json
+++ b/apps/files/l10n/zh_CN.json
@@ -140,6 +140,7 @@
"Create new folder" : "创建新文件夹",
"This name is already in use." : "此名称已被使用。",
"Create" : "创建",
+ "Files starting with a dot are hidden by default" : "以点开头的文件默认是隐藏的",
"Fill template fields" : "填写模板字段",
"Submitting fields …" : "正在提交字段…",
"Submit" : "使用",
@@ -199,16 +200,21 @@
"Sort favorites first" : "收藏排序优先",
"Sort folders before files" : "将文件夹排在文件前面",
"Enable folder tree" : "启用文件夹树",
+ "Visual settings" : "视觉设置",
"Show hidden files" : "显示隐藏文件",
"Show file type column" : "显示文件类型列",
"Crop image previews" : "裁剪图片预览",
+ "Show files extensions" : "显示文件扩展名",
"Additional settings" : "其他设置",
"WebDAV" : "WebDAV",
"WebDAV URL" : "WebDAV URL",
"Copy to clipboard" : "复制到剪贴板",
+ "Use this address to access your Files via WebDAV." : "使用此地址通过 WebDAV 访问您的文件。",
+ "Two-Factor Authentication is enabled for your account, and therefore you need to use an app password to connect an external WebDAV client." : "您的账号已启用双因素身份验证,因此您需要使用应用密码来连接外部 WebDAV 客户端。",
"Warnings" : "警告",
"Prevent warning dialogs from open or reenable them." : "防止打开或重新启用警告对话框。",
"Show a warning dialog when changing a file extension." : "更改文件扩展名时显示警告对话框。",
+ "Show a warning dialog when deleting files." : "删除文件时显示警告对话框。",
"Keyboard shortcuts" : "键盘快捷键",
"Speed up your Files experience with these quick shortcuts." : "这些快捷键可以加快你的“文件”体验。",
"Open the actions menu for a file" : "打开文件的操作菜单",
@@ -326,6 +332,7 @@
"Templates" : "模板",
"New template folder" : "新建模板文件夹",
"In folder" : "文件夹内",
+ "Search in all files" : "在所有文件中搜索",
"Search in folder: {folder}" : "在文件夹内搜索:{folder}",
"One of the dropped files could not be processed" : "无法处理其中一个已删除的文件",
"Your browser does not support the Filesystem API. Directories will not be uploaded" : "你的浏览器不支持文件系统API。目录不会被上传",
@@ -359,6 +366,7 @@
"No favorites yet" : "暂无收藏",
"Files and folders you mark as favorite will show up here" : "收藏的文件和文件夹会在这里显示",
"List of your files and folders." : "您的文件与文件件列表。",
+ "Folder tree" : "文件夹树",
"List of your files and folders that are not shared." : "尚未分享的文件与文件夹",
"No personal files found" : "找不到个人文件",
"Files that are not shared will show up here." : "尚未分享的文件会显示在此处",
diff --git a/apps/files/lib/AppInfo/Application.php b/apps/files/lib/AppInfo/Application.php
index 32c072ef0f4..2761b44ecf9 100644
--- a/apps/files/lib/AppInfo/Application.php
+++ b/apps/files/lib/AppInfo/Application.php
@@ -117,7 +117,7 @@ class Application extends App implements IBootstrap {
$context->registerEventListener(RenderReferenceEvent::class, RenderReferenceEventListener::class);
$context->registerEventListener(BeforeNodeRenamedEvent::class, SyncLivePhotosListener::class);
$context->registerEventListener(BeforeNodeDeletedEvent::class, SyncLivePhotosListener::class);
- $context->registerEventListener(CacheEntryRemovedEvent::class, SyncLivePhotosListener::class);
+ $context->registerEventListener(CacheEntryRemovedEvent::class, SyncLivePhotosListener::class, 1); // Ensure this happen before the metadata are deleted.
$context->registerEventListener(BeforeNodeCopiedEvent::class, SyncLivePhotosListener::class);
$context->registerEventListener(NodeCopiedEvent::class, SyncLivePhotosListener::class);
$context->registerEventListener(LoadSearchPlugins::class, LoadSearchPluginsListener::class);
diff --git a/apps/files/src/FilesApp.vue b/apps/files/src/FilesApp.vue
index 54821a03457..6fc02113162 100644
--- a/apps/files/src/FilesApp.vue
+++ b/apps/files/src/FilesApp.vue
@@ -12,11 +12,10 @@
<script lang="ts">
import { isPublicShare } from '@nextcloud/sharing/public'
import { defineComponent } from 'vue'
-
import NcContent from '@nextcloud/vue/components/NcContent'
-
import Navigation from './views/Navigation.vue'
import FilesList from './views/FilesList.vue'
+import { useHotKeys } from './composables/useHotKeys'
export default defineComponent({
name: 'FilesApp',
@@ -28,6 +27,9 @@ export default defineComponent({
},
setup() {
+ // Register global hotkeys
+ useHotKeys()
+
const isPublic = isPublicShare()
return {
diff --git a/apps/files/src/services/HotKeysService.spec.ts b/apps/files/src/composables/useHotKeys.spec.ts
index 92430c8e6ad..9c001e8b5ff 100644
--- a/apps/files/src/services/HotKeysService.spec.ts
+++ b/apps/files/src/composables/useHotKeys.spec.ts
@@ -1,10 +1,14 @@
-/**
+/*
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
+
+import type { Location } from 'vue-router'
+
import { File, Folder, Permission, View } from '@nextcloud/files'
-import { describe, it, vi, expect, beforeEach, beforeAll, afterEach } from 'vitest'
-import { nextTick } from 'vue'
+import { enableAutoDestroy, mount } from '@vue/test-utils'
+import { describe, it, vi, expect, beforeEach, afterEach } from 'vitest'
+import { defineComponent, nextTick } from 'vue'
import axios from '@nextcloud/axios'
import { getPinia } from '../store/index.ts'
@@ -15,38 +19,64 @@ import { action as deleteAction } from '../actions/deleteAction.ts'
import { action as favoriteAction } from '../actions/favoriteAction.ts'
import { action as renameAction } from '../actions/renameAction.ts'
import { action as sidebarAction } from '../actions/sidebarAction.ts'
-import { registerHotkeys } from './HotKeysService.ts'
+import { useHotKeys } from './useHotKeys.ts'
import { useUserConfigStore } from '../store/userconfig.ts'
+// this is the mocked current route
+const route = vi.hoisted(() => ({
+ name: 'test',
+ params: {
+ fileId: 123,
+ },
+ query: {
+ openFile: 'false',
+ dir: '/parent/dir',
+ },
+}))
+
+// mocked router
+const router = vi.hoisted(() => ({
+ push: vi.fn<(route: Location) => void>(),
+}))
+
+vi.mock('../actions/sidebarAction.ts', { spy: true })
+vi.mock('../actions/deleteAction.ts', { spy: true })
+vi.mock('../actions/favoriteAction.ts', { spy: true })
+vi.mock('../actions/renameAction.ts', { spy: true })
+
+vi.mock('vue-router/composables', () => ({
+ useRoute: vi.fn(() => route),
+ useRouter: vi.fn(() => router),
+}))
+
let file: File
const view = {
id: 'files',
name: 'Files',
} as View
-vi.mock('../actions/sidebarAction.ts', { spy: true })
-vi.mock('../actions/deleteAction.ts', { spy: true })
-vi.mock('../actions/favoriteAction.ts', { spy: true })
-vi.mock('../actions/renameAction.ts', { spy: true })
+const TestComponent = defineComponent({
+ name: 'test',
+ setup() {
+ useHotKeys()
+ },
+ template: '<div />',
+})
describe('HotKeysService testing', () => {
const activeStore = useActiveStore(getPinia())
- const goToRouteMock = vi.fn()
-
let initialState: HTMLInputElement
+ enableAutoDestroy(afterEach)
+
afterEach(() => {
document.body.removeChild(initialState)
})
- beforeAll(() => {
- registerHotkeys()
- })
-
beforeEach(() => {
// Make sure the router is reset before each test
- goToRouteMock.mockClear()
+ router.push.mockClear()
// Make sure the file is reset before each test
file = new File({
@@ -66,9 +96,6 @@ describe('HotKeysService testing', () => {
activeStore.activeNode = file
window.OCA = { Files: { Sidebar: { open: () => {}, setActiveTab: () => {} } } }
- // We only mock what needed, we do not need Files.Router.goTo or Files.Navigation
- window.OCP = { Files: { Router: { goToRoute: goToRouteMock, params: {}, query: {} } } }
-
initialState = document.createElement('input')
initialState.setAttribute('type', 'hidden')
initialState.setAttribute('id', 'initial-state-files_trashbin-config')
@@ -76,6 +103,8 @@ describe('HotKeysService testing', () => {
allow_delete: true,
})))
document.body.appendChild(initialState)
+
+ mount(TestComponent)
})
it('Pressing d should open the sidebar once', () => {
@@ -135,13 +164,11 @@ describe('HotKeysService testing', () => {
})
it('Pressing alt+up should go to parent directory', () => {
- expect(goToRouteMock).toHaveBeenCalledTimes(0)
- window.OCP.Files.Router.query = { dir: '/foo/bar' }
-
+ expect(router.push).toHaveBeenCalledTimes(0)
dispatchEvent({ key: 'ArrowUp', code: 'ArrowUp', altKey: true })
- expect(goToRouteMock).toHaveBeenCalledOnce()
- expect(goToRouteMock.mock.calls[0][2].dir).toBe('/foo')
+ expect(router.push).toHaveBeenCalledOnce()
+ expect(router.push.mock.calls[0][0].query?.dir).toBe('/parent')
})
it('Pressing v should toggle grid view', async () => {
diff --git a/apps/files/src/services/HotKeysService.ts b/apps/files/src/composables/useHotKeys.ts
index 1ed369b061b..ff56627b2f9 100644
--- a/apps/files/src/services/HotKeysService.ts
+++ b/apps/files/src/composables/useHotKeys.ts
@@ -4,13 +4,15 @@
*/
import { useHotKey } from '@nextcloud/vue/composables/useHotKey'
import { dirname } from 'path'
+import { useRoute, useRouter } from 'vue-router/composables'
import { action as deleteAction } from '../actions/deleteAction.ts'
import { action as favoriteAction } from '../actions/favoriteAction.ts'
import { action as renameAction } from '../actions/renameAction.ts'
import { action as sidebarAction } from '../actions/sidebarAction.ts'
-import { executeAction } from '../utils/actionUtils.ts'
import { useUserConfigStore } from '../store/userconfig.ts'
+import { useRouteParameters } from './useRouteParameters.ts'
+import { executeAction } from '../utils/actionUtils.ts'
import logger from '../logger.ts'
/**
@@ -18,7 +20,12 @@ import logger from '../logger.ts'
* As much as possible, we try to have all the hotkeys in one place.
* Please make sure to add tests for the hotkeys after adding a new one.
*/
-export const registerHotkeys = function() {
+export function useHotKeys(): void {
+ const userConfigStore = useUserConfigStore()
+ const { directory } = useRouteParameters()
+ const router = useRouter()
+ const route = useRoute()
+
// d opens the sidebar
useHotKey('d', () => executeAction(sidebarAction), {
stop: true,
@@ -57,26 +64,23 @@ export const registerHotkeys = function() {
})
logger.debug('Hotkeys registered')
-}
-
-const goToParentDir = function() {
- const params = window.OCP.Files.Router?.params || {}
- const query = window.OCP.Files.Router?.query || {}
- const currentDir = (query?.dir || '/') as string
- const parentDir = dirname(currentDir)
+ /**
+ * Use the router to go to the parent directory
+ */
+ function goToParentDir() {
+ const dir = dirname(directory.value)
- logger.debug('Navigating to parent directory', { parentDir })
- window.OCP.Files.Router.goToRoute(
- null,
- { ...params },
- { ...query, dir: parentDir },
- )
-}
+ logger.debug('Navigating to parent directory', { dir })
+ router.push({ params: { ...route.params }, query: { ...route.query, dir } })
+ }
-const toggleGridView = function() {
- const userConfigStore = useUserConfigStore()
- const value = userConfigStore?.userConfig?.grid_view
- logger.debug('Toggling grid view', { old: value, new: !value })
- userConfigStore.update('grid_view', !value)
+ /**
+ * Toggle the grid view
+ */
+ function toggleGridView() {
+ const value = userConfigStore.userConfig.grid_view
+ logger.debug('Toggling grid view', { old: value, new: !value })
+ userConfigStore.update('grid_view', !value)
+ }
}
diff --git a/apps/files/src/main.ts b/apps/files/src/main.ts
index 4b8aca9efd4..463ecaf6239 100644
--- a/apps/files/src/main.ts
+++ b/apps/files/src/main.ts
@@ -8,7 +8,6 @@ import { PiniaVuePlugin } from 'pinia'
import Vue from 'vue'
import { getPinia } from './store/index.ts'
-import { registerHotkeys } from './services/HotKeysService.ts'
import FilesApp from './FilesApp.vue'
import router from './router/router'
import RouterService from './services/RouterService'
@@ -40,9 +39,6 @@ if (!window.OCP.Files.Router) {
// Init Pinia store
Vue.use(PiniaVuePlugin)
-// Init HotKeys AFTER pinia is set up
-registerHotkeys()
-
// Init Files App Settings Service
const Settings = new SettingsService()
Object.assign(window.OCA.Files, { Settings })
diff --git a/apps/files_external/lib/Lib/Storage/SMB.php b/apps/files_external/lib/Lib/Storage/SMB.php
index 0899d2ac093..8f8750864e1 100644
--- a/apps/files_external/lib/Lib/Storage/SMB.php
+++ b/apps/files_external/lib/Lib/Storage/SMB.php
@@ -336,7 +336,7 @@ class SMB extends Common implements INotifyStorage {
if ($retry) {
return $this->stat($path, false);
} else {
- throw $e;
+ throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
}
}
if ($this->remoteIsShare() && $this->isRootDir($path)) {
diff --git a/apps/files_sharing/src/views/SharingTab.vue b/apps/files_sharing/src/views/SharingTab.vue
index 262504aca01..dc200c61df4 100644
--- a/apps/files_sharing/src/views/SharingTab.vue
+++ b/apps/files_sharing/src/views/SharingTab.vue
@@ -243,8 +243,7 @@ export default {
* @return {boolean}
*/
isSharedWithMe() {
- return this.sharedWithMe !== null
- && this.sharedWithMe !== undefined
+ return !!this.sharedWithMe?.user
},
/**
diff --git a/apps/files_trashbin/l10n/pt_BR.js b/apps/files_trashbin/l10n/pt_BR.js
index f1e360f82c0..de9b08f8326 100644
--- a/apps/files_trashbin/l10n/pt_BR.js
+++ b/apps/files_trashbin/l10n/pt_BR.js
@@ -2,7 +2,7 @@ OC.L10N.register(
"files_trashbin",
{
"restored" : "restaurado",
- "Deleted files" : "Arquivos exluídos",
+ "Deleted files" : "Arquivos excluídos",
"Deleted files and folders in the trash bin (may expire during export if you are low on storage space)" : "Arquivos e pastas excluídos na lixeira (podem expirar durante a exportação se você estiver com pouco espaço de armazenamento)",
"This application enables people to restore files that were deleted from the system." : "Este aplicativo permite que as pessoas restaurem arquivos que foram excluídos do sistema.",
"This application enables people 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 people 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 an account 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." : "Este aplicativo permite que as pessoas restaurem arquivos que foram excluídos do sistema. Ele exibe uma lista de arquivos excluídos na interface da web e tem opções para restaurar esses arquivos excluídos de volta aos diretórios de arquivos de pessoas ou removê-los permanentemente do sistema. A restauração de um arquivo também restaura versões de arquivos relacionadas, se o aplicativo de versões estiver ativado. Quando um arquivo é excluído de um compartilhamento, ele pode ser restaurado da mesma maneira, embora não seja mais compartilhado. Por padrão, esses arquivos permanecem na lixeira por 30 dias. \nPara evitar que uma conta fique sem espaço em disco, o aplicativo Arquivos excluídos não utilizará mais de 50% da cota livre atualmente disponível para arquivos excluídos. Se os arquivos excluídos excederem esse limite, o aplicativo excluirá os arquivos mais antigos até ficarem abaixo desse limite. Mais informações estão disponíveis na documentação Arquivos Excluídos.",
diff --git a/apps/files_trashbin/l10n/pt_BR.json b/apps/files_trashbin/l10n/pt_BR.json
index da1f67d35ed..8e8c5a3ec3d 100644
--- a/apps/files_trashbin/l10n/pt_BR.json
+++ b/apps/files_trashbin/l10n/pt_BR.json
@@ -1,6 +1,6 @@
{ "translations": {
"restored" : "restaurado",
- "Deleted files" : "Arquivos exluídos",
+ "Deleted files" : "Arquivos excluídos",
"Deleted files and folders in the trash bin (may expire during export if you are low on storage space)" : "Arquivos e pastas excluídos na lixeira (podem expirar durante a exportação se você estiver com pouco espaço de armazenamento)",
"This application enables people to restore files that were deleted from the system." : "Este aplicativo permite que as pessoas restaurem arquivos que foram excluídos do sistema.",
"This application enables people 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 people 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 an account 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." : "Este aplicativo permite que as pessoas restaurem arquivos que foram excluídos do sistema. Ele exibe uma lista de arquivos excluídos na interface da web e tem opções para restaurar esses arquivos excluídos de volta aos diretórios de arquivos de pessoas ou removê-los permanentemente do sistema. A restauração de um arquivo também restaura versões de arquivos relacionadas, se o aplicativo de versões estiver ativado. Quando um arquivo é excluído de um compartilhamento, ele pode ser restaurado da mesma maneira, embora não seja mais compartilhado. Por padrão, esses arquivos permanecem na lixeira por 30 dias. \nPara evitar que uma conta fique sem espaço em disco, o aplicativo Arquivos excluídos não utilizará mais de 50% da cota livre atualmente disponível para arquivos excluídos. Se os arquivos excluídos excederem esse limite, o aplicativo excluirá os arquivos mais antigos até ficarem abaixo desse limite. Mais informações estão disponíveis na documentação Arquivos Excluídos.",
diff --git a/apps/files_trashbin/lib/Sabre/TrashbinPlugin.php b/apps/files_trashbin/lib/Sabre/TrashbinPlugin.php
index 36237ca080b..54bb1326966 100644
--- a/apps/files_trashbin/lib/Sabre/TrashbinPlugin.php
+++ b/apps/files_trashbin/lib/Sabre/TrashbinPlugin.php
@@ -104,8 +104,8 @@ class TrashbinPlugin extends ServerPlugin {
return $node->getFileId();
});
- $propFind->handle(FilesPlugin::HAS_PREVIEW_PROPERTYNAME, function () use ($node) {
- return $this->previewManager->isAvailable($node->getFileInfo());
+ $propFind->handle(FilesPlugin::HAS_PREVIEW_PROPERTYNAME, function () use ($node): string {
+ return $this->previewManager->isAvailable($node->getFileInfo()) ? 'true' : 'false';
});
$propFind->handle(FilesPlugin::MOUNT_TYPE_PROPERTYNAME, function () {
diff --git a/apps/files_versions/lib/Sabre/Plugin.php b/apps/files_versions/lib/Sabre/Plugin.php
index 4b4cb0638bf..984c4a36e5b 100644
--- a/apps/files_versions/lib/Sabre/Plugin.php
+++ b/apps/files_versions/lib/Sabre/Plugin.php
@@ -82,7 +82,10 @@ class Plugin extends ServerPlugin {
if ($node instanceof VersionFile) {
$propFind->handle(self::VERSION_LABEL, fn () => $node->getMetadataValue(self::LABEL));
$propFind->handle(self::VERSION_AUTHOR, fn () => $node->getMetadataValue(self::AUTHOR));
- $propFind->handle(FilesPlugin::HAS_PREVIEW_PROPERTYNAME, fn () => $this->previewManager->isMimeSupported($node->getContentType()));
+ $propFind->handle(
+ FilesPlugin::HAS_PREVIEW_PROPERTYNAME,
+ fn (): string => $this->previewManager->isMimeSupported($node->getContentType()) ? 'true' : 'false',
+ );
}
}
diff --git a/apps/provisioning_api/l10n/zh_CN.js b/apps/provisioning_api/l10n/zh_CN.js
index 2bbc7a444c9..2bb24cb01ae 100644
--- a/apps/provisioning_api/l10n/zh_CN.js
+++ b/apps/provisioning_api/l10n/zh_CN.js
@@ -13,6 +13,7 @@ OC.L10N.register(
"Invalid password value" : "密码值无效",
"An email address is required, to send a password link to the user." : "需要电子邮件地址,以将密码链接发送给用户。",
"Required email address was not provided" : "未提供所需的电子邮件地址",
+ "User creation failed" : "用户创建失败",
"Invalid quota value: %1$s" : "配额值无效:%1$s",
"Invalid quota value. %1$s is exceeding the maximum quota" : "配额值无效。%1$s 超过了最大配额",
"Unlimited quota is forbidden on this instance" : "此实例上禁止无限配额",
diff --git a/apps/provisioning_api/l10n/zh_CN.json b/apps/provisioning_api/l10n/zh_CN.json
index b38c49f3867..03efc200979 100644
--- a/apps/provisioning_api/l10n/zh_CN.json
+++ b/apps/provisioning_api/l10n/zh_CN.json
@@ -11,6 +11,7 @@
"Invalid password value" : "密码值无效",
"An email address is required, to send a password link to the user." : "需要电子邮件地址,以将密码链接发送给用户。",
"Required email address was not provided" : "未提供所需的电子邮件地址",
+ "User creation failed" : "用户创建失败",
"Invalid quota value: %1$s" : "配额值无效:%1$s",
"Invalid quota value. %1$s is exceeding the maximum quota" : "配额值无效。%1$s 超过了最大配额",
"Unlimited quota is forbidden on this instance" : "此实例上禁止无限配额",
diff --git a/apps/settings/l10n/de.js b/apps/settings/l10n/de.js
index 3b525970801..267617fce18 100644
--- a/apps/settings/l10n/de.js
+++ b/apps/settings/l10n/de.js
@@ -395,6 +395,7 @@ OC.L10N.register(
"Default expiration time of remote shares in days" : "Standardablaufzeit für Remote-Freigaben in Tagen",
"Expire remote shares after x days" : "Remote-Freigaben laufen nach x Tagen ab",
"Set default expiration date for shares via link or mail" : "Standardablaufzeit für Link- oder E-Mail-Freigaben festlegen",
+ "Enforce expiration date for link or mail shares" : "Ablaufdatum für Link- oder E-Mail-Freigaben erzwingen",
"Default expiration time of shares in days" : "Standardablaufzeit für Freigaben in Tagen",
"Privacy settings for sharing" : "Datenschutzeinstellungen bezüglich des Teilens",
"Allow account name autocompletion in share dialog and allow access to the system address book" : "Automatische Vervollständigung des Kontonamens im Freigabedialog und Zugriff auf das Systemadressbuch zulassen",
diff --git a/apps/settings/l10n/de.json b/apps/settings/l10n/de.json
index 0f484bf25f0..1c6ac9e8e14 100644
--- a/apps/settings/l10n/de.json
+++ b/apps/settings/l10n/de.json
@@ -393,6 +393,7 @@
"Default expiration time of remote shares in days" : "Standardablaufzeit für Remote-Freigaben in Tagen",
"Expire remote shares after x days" : "Remote-Freigaben laufen nach x Tagen ab",
"Set default expiration date for shares via link or mail" : "Standardablaufzeit für Link- oder E-Mail-Freigaben festlegen",
+ "Enforce expiration date for link or mail shares" : "Ablaufdatum für Link- oder E-Mail-Freigaben erzwingen",
"Default expiration time of shares in days" : "Standardablaufzeit für Freigaben in Tagen",
"Privacy settings for sharing" : "Datenschutzeinstellungen bezüglich des Teilens",
"Allow account name autocompletion in share dialog and allow access to the system address book" : "Automatische Vervollständigung des Kontonamens im Freigabedialog und Zugriff auf das Systemadressbuch zulassen",
diff --git a/apps/settings/l10n/de_DE.js b/apps/settings/l10n/de_DE.js
index 9d9d37ffdff..a9917558321 100644
--- a/apps/settings/l10n/de_DE.js
+++ b/apps/settings/l10n/de_DE.js
@@ -395,6 +395,7 @@ OC.L10N.register(
"Default expiration time of remote shares in days" : "Standardablaufzeit für Remote-Freigaben in Tagen",
"Expire remote shares after x days" : "Remote-Freigaben laufen nach x Tagen ab",
"Set default expiration date for shares via link or mail" : "Standardablaufzeit für Link- oder E-Mail-Freigaben festlegen",
+ "Enforce expiration date for link or mail shares" : "Ablaufdatum für Link- oder E-Mail-Freigaben erzwingen",
"Default expiration time of shares in days" : "Standardablaufzeit für Freigaben in Tagen",
"Privacy settings for sharing" : "Datenschutzeinstellungen bezüglich des Teilens",
"Allow account name autocompletion in share dialog and allow access to the system address book" : "Automatische Vervollständigung des Kontonamens im Freigabedialog und Zugriff auf das Systemadressbuch zulassen",
diff --git a/apps/settings/l10n/de_DE.json b/apps/settings/l10n/de_DE.json
index 5136fbbdf30..1a633f6f50a 100644
--- a/apps/settings/l10n/de_DE.json
+++ b/apps/settings/l10n/de_DE.json
@@ -393,6 +393,7 @@
"Default expiration time of remote shares in days" : "Standardablaufzeit für Remote-Freigaben in Tagen",
"Expire remote shares after x days" : "Remote-Freigaben laufen nach x Tagen ab",
"Set default expiration date for shares via link or mail" : "Standardablaufzeit für Link- oder E-Mail-Freigaben festlegen",
+ "Enforce expiration date for link or mail shares" : "Ablaufdatum für Link- oder E-Mail-Freigaben erzwingen",
"Default expiration time of shares in days" : "Standardablaufzeit für Freigaben in Tagen",
"Privacy settings for sharing" : "Datenschutzeinstellungen bezüglich des Teilens",
"Allow account name autocompletion in share dialog and allow access to the system address book" : "Automatische Vervollständigung des Kontonamens im Freigabedialog und Zugriff auf das Systemadressbuch zulassen",
diff --git a/apps/settings/l10n/et_EE.js b/apps/settings/l10n/et_EE.js
index dfff7899eb5..5f705138e2e 100644
--- a/apps/settings/l10n/et_EE.js
+++ b/apps/settings/l10n/et_EE.js
@@ -261,6 +261,7 @@ OC.L10N.register(
"Default expiration time of remote shares in days" : "Kaugserveris asuva jaosmeedia vaikimisi aegumine päevades",
"Expire remote shares after x days" : "Jaosmeedia aegub x päeva möödudes",
"Set default expiration date for shares via link or mail" : "Määra lingi või e-kirjaga jagatava jaosmeedia vaikimisi aegumiskuupäev",
+ "Enforce expiration date for link or mail shares" : "Jõusta lingiga või e-postiga jagamise aegumiskuupäev",
"Default expiration time of shares in days" : "Jaosmeedia vaikimisi aegumine päevades",
"Privacy settings for sharing" : "Jagamise privaatsusseadistused",
"Show disclaimer text on the public link upload page (only shown when the file list is hidden)" : "Kuva avaliku lingiga üleslaadimise lehel lahtiütluste tekst (vaid siis, kui failide loend on peidetud)",
diff --git a/apps/settings/l10n/et_EE.json b/apps/settings/l10n/et_EE.json
index e642931fd77..82c4767af7e 100644
--- a/apps/settings/l10n/et_EE.json
+++ b/apps/settings/l10n/et_EE.json
@@ -259,6 +259,7 @@
"Default expiration time of remote shares in days" : "Kaugserveris asuva jaosmeedia vaikimisi aegumine päevades",
"Expire remote shares after x days" : "Jaosmeedia aegub x päeva möödudes",
"Set default expiration date for shares via link or mail" : "Määra lingi või e-kirjaga jagatava jaosmeedia vaikimisi aegumiskuupäev",
+ "Enforce expiration date for link or mail shares" : "Jõusta lingiga või e-postiga jagamise aegumiskuupäev",
"Default expiration time of shares in days" : "Jaosmeedia vaikimisi aegumine päevades",
"Privacy settings for sharing" : "Jagamise privaatsusseadistused",
"Show disclaimer text on the public link upload page (only shown when the file list is hidden)" : "Kuva avaliku lingiga üleslaadimise lehel lahtiütluste tekst (vaid siis, kui failide loend on peidetud)",
diff --git a/apps/settings/l10n/pt_BR.js b/apps/settings/l10n/pt_BR.js
index 848734a4dbb..b676cd4e35b 100644
--- a/apps/settings/l10n/pt_BR.js
+++ b/apps/settings/l10n/pt_BR.js
@@ -395,6 +395,7 @@ OC.L10N.register(
"Default expiration time of remote shares in days" : "Tempo de expiração padrão de compartilhamentos remotos em dias",
"Expire remote shares after x days" : "Expiração de compartilhamentos remotos após x dias",
"Set default expiration date for shares via link or mail" : "Definir a data de expiração padrão para compartilhamentos via link ou e-mail",
+ "Enforce expiration date for link or mail shares" : "Impor data de expiração para compartilhamentos de link ou e-mail",
"Default expiration time of shares in days" : "Tempo de expiração padrão dos compartilhamentos em dias",
"Privacy settings for sharing" : "Configurações de privacidade para compartilhamento",
"Allow account name autocompletion in share dialog and allow access to the system address book" : "Permitir o preenchimento automático de nomes das contas na caixa de diálogo de compartilhamento e permitir o acesso ao catálogo de endereços do sistema",
diff --git a/apps/settings/l10n/pt_BR.json b/apps/settings/l10n/pt_BR.json
index 9ed68c369c2..891f8fa6d5f 100644
--- a/apps/settings/l10n/pt_BR.json
+++ b/apps/settings/l10n/pt_BR.json
@@ -393,6 +393,7 @@
"Default expiration time of remote shares in days" : "Tempo de expiração padrão de compartilhamentos remotos em dias",
"Expire remote shares after x days" : "Expiração de compartilhamentos remotos após x dias",
"Set default expiration date for shares via link or mail" : "Definir a data de expiração padrão para compartilhamentos via link ou e-mail",
+ "Enforce expiration date for link or mail shares" : "Impor data de expiração para compartilhamentos de link ou e-mail",
"Default expiration time of shares in days" : "Tempo de expiração padrão dos compartilhamentos em dias",
"Privacy settings for sharing" : "Configurações de privacidade para compartilhamento",
"Allow account name autocompletion in share dialog and allow access to the system address book" : "Permitir o preenchimento automático de nomes das contas na caixa de diálogo de compartilhamento e permitir o acesso ao catálogo de endereços do sistema",
diff --git a/apps/settings/l10n/uk.js b/apps/settings/l10n/uk.js
index 92fdadf224b..856aa098e8a 100644
--- a/apps/settings/l10n/uk.js
+++ b/apps/settings/l10n/uk.js
@@ -395,6 +395,7 @@ OC.L10N.register(
"Default expiration time of remote shares in days" : "Типовий термін дії спільних ресурсів у днях",
"Expire remote shares after x days" : "Термін дії спільних ресурсів завершується через x днів",
"Set default expiration date for shares via link or mail" : "Встановити типовий термін дії спільних ресурсів за посиланням або електронною поштою",
+ "Enforce expiration date for link or mail shares" : "Застосовувати термін дії для посилань або спільного доступу до пошти",
"Default expiration time of shares in days" : "Типовий термін дії спільних ресурсів у днях",
"Privacy settings for sharing" : "Налаштування конфіденційності для спільного доступу",
"Allow account name autocompletion in share dialog and allow access to the system address book" : "Дозволити автозаповнення імени користувача та доступ до системної адресної книги",
diff --git a/apps/settings/l10n/uk.json b/apps/settings/l10n/uk.json
index 6ff8ab9eeaa..b405793fb1e 100644
--- a/apps/settings/l10n/uk.json
+++ b/apps/settings/l10n/uk.json
@@ -393,6 +393,7 @@
"Default expiration time of remote shares in days" : "Типовий термін дії спільних ресурсів у днях",
"Expire remote shares after x days" : "Термін дії спільних ресурсів завершується через x днів",
"Set default expiration date for shares via link or mail" : "Встановити типовий термін дії спільних ресурсів за посиланням або електронною поштою",
+ "Enforce expiration date for link or mail shares" : "Застосовувати термін дії для посилань або спільного доступу до пошти",
"Default expiration time of shares in days" : "Типовий термін дії спільних ресурсів у днях",
"Privacy settings for sharing" : "Налаштування конфіденційності для спільного доступу",
"Allow account name autocompletion in share dialog and allow access to the system address book" : "Дозволити автозаповнення імени користувача та доступ до системної адресної книги",
diff --git a/apps/settings/l10n/zh_CN.js b/apps/settings/l10n/zh_CN.js
index 6b9f224f47f..ae8f90baf91 100644
--- a/apps/settings/l10n/zh_CN.js
+++ b/apps/settings/l10n/zh_CN.js
@@ -370,6 +370,8 @@ OC.L10N.register(
"Users will still be able to screenshot or record the screen. This does not provide any definitive protection." : "用户仍然可以屏幕截图或录制屏幕。这并不能提供任何明确的保护。",
"Allow users to share via link and emails" : "允许用户通过链接和电子邮件共享",
"Allow public uploads" : "允许公开上传",
+ "Allow public shares to be added to other clouds by federation." : "允许通过联邦方式将公开共享添加到其他云",
+ "This will add share permissions to all newly created link shares." : "这将为所有新创建的链接共享添加共享权限。",
"Always ask for a password" : "始终要求输入密码",
"Enforce password protection" : "强制密码保护",
"Exclude groups from password requirements" : "不对指定的组执行密码要求",
@@ -393,6 +395,7 @@ OC.L10N.register(
"Default expiration time of remote shares in days" : "远程共享的默认过期时间(天)",
"Expire remote shares after x days" : "远程共享在X天后过期",
"Set default expiration date for shares via link or mail" : "设置通过链接或电子邮件分享的默认过期时间",
+ "Enforce expiration date for link or mail shares" : "强制设置链接或邮件共享的到期日期",
"Default expiration time of shares in days" : "默认的共享过期时间(天)",
"Privacy settings for sharing" : "共享的隐私设置",
"Allow account name autocompletion in share dialog and allow access to the system address book" : "允许在共享对话框中自动完成帐户名称并允许访问系统地址簿",
@@ -667,8 +670,8 @@ OC.L10N.register(
"Unable to update {property}" : "无法更新 {property}",
"No {property} set" : "没有设置 {property}",
"Change scope level of {property}, current scope is {scope}" : "更改 {property} 的范围级别,当前范围为 {scope}",
- "Unable to update federation scope of the primary {property}" : "无法更新主要 {property} 的联盟范围",
- "Unable to update federation scope of additional {property}" : "无法更新额外 {property} 的联盟范围",
+ "Unable to update federation scope of the primary {property}" : "无法更新主 {property} 的联邦范围",
+ "Unable to update federation scope of additional {property}" : "无法更新附加 {property} 的联邦范围",
"Add additional email" : "添加额外的电子邮箱",
"Add" : "添加",
"Create" : "创建",
@@ -812,7 +815,7 @@ OC.L10N.register(
"Locale" : "地区语系",
"First day of week" : "每周的第一天",
"Not available as this property is required for core functionality including file sharing and calendar invitations" : "不可用,因为包括文件共享和日历邀请在内的核心功能需要此属性",
- "Not available as federation has been disabled for your account, contact your system administration if you have any questions" : "不可用,因为您的帐号已禁用联合,如果您有任何疑问,请联系您的系统管理员",
+ "Not available as federation has been disabled for your account, contact your system administration if you have any questions" : "由于您的账号已禁用联合,因此不可用,如有任何疑问,请联系您的系统管理员",
"Not available as publishing account specific data to the lookup server is not allowed, contact your system administration if you have any questions" : "不可用,因为不允许将帐户特定数据发布到查找服务器,如果您有任何疑问,请联系您的系统管理员",
"Discover" : "发现",
"Your apps" : "你的应用",
diff --git a/apps/settings/l10n/zh_CN.json b/apps/settings/l10n/zh_CN.json
index 997db288cc4..46b523ec1a6 100644
--- a/apps/settings/l10n/zh_CN.json
+++ b/apps/settings/l10n/zh_CN.json
@@ -368,6 +368,8 @@
"Users will still be able to screenshot or record the screen. This does not provide any definitive protection." : "用户仍然可以屏幕截图或录制屏幕。这并不能提供任何明确的保护。",
"Allow users to share via link and emails" : "允许用户通过链接和电子邮件共享",
"Allow public uploads" : "允许公开上传",
+ "Allow public shares to be added to other clouds by federation." : "允许通过联邦方式将公开共享添加到其他云",
+ "This will add share permissions to all newly created link shares." : "这将为所有新创建的链接共享添加共享权限。",
"Always ask for a password" : "始终要求输入密码",
"Enforce password protection" : "强制密码保护",
"Exclude groups from password requirements" : "不对指定的组执行密码要求",
@@ -391,6 +393,7 @@
"Default expiration time of remote shares in days" : "远程共享的默认过期时间(天)",
"Expire remote shares after x days" : "远程共享在X天后过期",
"Set default expiration date for shares via link or mail" : "设置通过链接或电子邮件分享的默认过期时间",
+ "Enforce expiration date for link or mail shares" : "强制设置链接或邮件共享的到期日期",
"Default expiration time of shares in days" : "默认的共享过期时间(天)",
"Privacy settings for sharing" : "共享的隐私设置",
"Allow account name autocompletion in share dialog and allow access to the system address book" : "允许在共享对话框中自动完成帐户名称并允许访问系统地址簿",
@@ -665,8 +668,8 @@
"Unable to update {property}" : "无法更新 {property}",
"No {property} set" : "没有设置 {property}",
"Change scope level of {property}, current scope is {scope}" : "更改 {property} 的范围级别,当前范围为 {scope}",
- "Unable to update federation scope of the primary {property}" : "无法更新主要 {property} 的联盟范围",
- "Unable to update federation scope of additional {property}" : "无法更新额外 {property} 的联盟范围",
+ "Unable to update federation scope of the primary {property}" : "无法更新主 {property} 的联邦范围",
+ "Unable to update federation scope of additional {property}" : "无法更新附加 {property} 的联邦范围",
"Add additional email" : "添加额外的电子邮箱",
"Add" : "添加",
"Create" : "创建",
@@ -810,7 +813,7 @@
"Locale" : "地区语系",
"First day of week" : "每周的第一天",
"Not available as this property is required for core functionality including file sharing and calendar invitations" : "不可用,因为包括文件共享和日历邀请在内的核心功能需要此属性",
- "Not available as federation has been disabled for your account, contact your system administration if you have any questions" : "不可用,因为您的帐号已禁用联合,如果您有任何疑问,请联系您的系统管理员",
+ "Not available as federation has been disabled for your account, contact your system administration if you have any questions" : "由于您的账号已禁用联合,因此不可用,如有任何疑问,请联系您的系统管理员",
"Not available as publishing account specific data to the lookup server is not allowed, contact your system administration if you have any questions" : "不可用,因为不允许将帐户特定数据发布到查找服务器,如果您有任何疑问,请联系您的系统管理员",
"Discover" : "发现",
"Your apps" : "你的应用",
diff --git a/apps/settings/l10n/zh_TW.js b/apps/settings/l10n/zh_TW.js
index 503ec672c1e..c9c99e8ab97 100644
--- a/apps/settings/l10n/zh_TW.js
+++ b/apps/settings/l10n/zh_TW.js
@@ -395,6 +395,7 @@ OC.L10N.register(
"Default expiration time of remote shares in days" : "遠端分享預設到期時間(天)",
"Expire remote shares after x days" : "x 天後遠端分享過期",
"Set default expiration date for shares via link or mail" : "設定透過連結或郵件分享的預設過期日",
+ "Enforce expiration date for link or mail shares" : "強制設定連結或郵件分享的到期日",
"Default expiration time of shares in days" : "分享預設到期時間(天)",
"Privacy settings for sharing" : "分享的隱私設定",
"Allow account name autocompletion in share dialog and allow access to the system address book" : "允許在分享對話方塊中自動補齊帳號名稱並允許存取系統通訊錄",
diff --git a/apps/settings/l10n/zh_TW.json b/apps/settings/l10n/zh_TW.json
index 23d6a3cc553..697673f8a55 100644
--- a/apps/settings/l10n/zh_TW.json
+++ b/apps/settings/l10n/zh_TW.json
@@ -393,6 +393,7 @@
"Default expiration time of remote shares in days" : "遠端分享預設到期時間(天)",
"Expire remote shares after x days" : "x 天後遠端分享過期",
"Set default expiration date for shares via link or mail" : "設定透過連結或郵件分享的預設過期日",
+ "Enforce expiration date for link or mail shares" : "強制設定連結或郵件分享的到期日",
"Default expiration time of shares in days" : "分享預設到期時間(天)",
"Privacy settings for sharing" : "分享的隱私設定",
"Allow account name autocompletion in share dialog and allow access to the system address book" : "允許在分享對話方塊中自動補齊帳號名稱並允許存取系統通訊錄",
diff --git a/apps/settings/tests/Mailer/NewUserMailHelperTest.php b/apps/settings/tests/Mailer/NewUserMailHelperTest.php
index 55520184fc9..f352a2b733d 100644
--- a/apps/settings/tests/Mailer/NewUserMailHelperTest.php
+++ b/apps/settings/tests/Mailer/NewUserMailHelperTest.php
@@ -256,32 +256,46 @@ class NewUserMailHelperTest extends TestCase {
<tr style="padding:0;text-align:left;vertical-align:top">
<th style="Margin:0;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0;text-align:left">
<center data-parsed="" style="min-width:490px;width:100%">
- <table class="button btn default primary float-center" style="Margin:0 0 30px 0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;margin:0 0 30px 0;margin-right:15px;border-radius:8px;max-width:300px;padding:0;text-align:center;vertical-align:top;width:auto;background:#00679e;background-color:#00679e;color:#fefefe;">
- <tr style="padding:0;text-align:left;vertical-align:top">
- <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
- <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%">
+ <!--[if (gte mso 9)|(IE)]>
+ <table>
+ <tr>
+ <td>
+ <![endif]-->
+ <table class="button btn default primary float-center" style="Margin:0 0 30px 0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;margin:0 0 30px 0;margin-right:15px;border-radius:8px;max-width:300px;padding:0;text-align:center;vertical-align:top;width:auto;background:#00679e;background-color:#00679e;color:#fefefe;">
<tr style="padding:0;text-align:left;vertical-align:top">
- <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border:0 solid #00679e;border-collapse:collapse!important;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
- <a href="https://example.com/resetPassword/MySuperLongSecureRandomToken" style="Margin:0;border:0 solid #00679e;color:#ffffff;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:regular;line-height:normal;margin:0;padding:8px;text-align:left;outline:1px solid #ffffff;text-decoration:none">Set your password</a>
+ <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
+ <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%">
+ <tr style="padding:0;text-align:left;vertical-align:top">
+ <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border:0 solid #00679e;border-collapse:collapse!important;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
+ <a href="https://example.com/resetPassword/MySuperLongSecureRandomToken" style="Margin:0;border:0 solid #00679e;color:#ffffff;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:regular;line-height:normal;margin:0;padding:8px;text-align:left;outline:1px solid #ffffff;text-decoration:none">Set your password</a>
+ </td>
+ </tr>
+ </table>
</td>
</tr>
</table>
+ <!--[if (gte mso 9)|(IE)]>
</td>
- </tr>
- </table>
- <table class="button btn default secondary float-center" style="Margin:0 0 30px 0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;background-color: #ccc;margin:0 0 30px 0;max-height:40px;max-width:300px;padding:1px;border-radius:8px;text-align:center;vertical-align:top;width:auto">
- <tr style="padding:0;text-align:left;vertical-align:top">
- <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
- <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%">
+ <td>
+ <![endif]-->
+ <table class="button btn default secondary float-center" style="Margin:0 0 30px 0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;background-color: #ccc;margin:0 0 30px 0;max-height:40px;max-width:300px;padding:1px;border-radius:8px;text-align:center;vertical-align:top;width:auto">
<tr style="padding:0;text-align:left;vertical-align:top">
- <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border:0 solid #777;border-collapse:collapse!important;color:#fefefe;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
- <a href="https://nextcloud.com/install/#install-clients" style="Margin:0;background-color:#fff;border:0 solid #777;color:#6C6C6C!important;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:regular;line-height:normal;margin:0;border-radius: 7px;padding:8px;text-align:left;text-decoration:none">Install Client</a>
+ <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
+ <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%">
+ <tr style="padding:0;text-align:left;vertical-align:top">
+ <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border:0 solid #777;border-collapse:collapse!important;color:#fefefe;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
+ <a href="https://nextcloud.com/install/#install-clients" style="Margin:0;background-color:#fff;border:0 solid #777;color:#6C6C6C!important;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:regular;line-height:normal;margin:0;border-radius: 7px;padding:8px;text-align:left;text-decoration:none">Install Client</a>
+ </td>
+ </tr>
+ </table>
</td>
</tr>
</table>
+ <!--[if (gte mso 9)|(IE)]>
</td>
</tr>
</table>
+ <![endif]-->
</center>
</th>
<th class="expander" style="Margin:0;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0!important;text-align:left;visibility:hidden;width:0"></th>
@@ -496,32 +510,46 @@ EOF;
<tr style="padding:0;text-align:left;vertical-align:top">
<th style="Margin:0;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0;text-align:left">
<center data-parsed="" style="min-width:490px;width:100%">
- <table class="button btn default primary float-center" style="Margin:0 0 30px 0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;margin:0 0 30px 0;margin-right:15px;border-radius:8px;max-width:300px;padding:0;text-align:center;vertical-align:top;width:auto;background:#00679e;background-color:#00679e;color:#fefefe;">
- <tr style="padding:0;text-align:left;vertical-align:top">
- <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
- <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%">
+ <!--[if (gte mso 9)|(IE)]>
+ <table>
+ <tr>
+ <td>
+ <![endif]-->
+ <table class="button btn default primary float-center" style="Margin:0 0 30px 0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;margin:0 0 30px 0;margin-right:15px;border-radius:8px;max-width:300px;padding:0;text-align:center;vertical-align:top;width:auto;background:#00679e;background-color:#00679e;color:#fefefe;">
<tr style="padding:0;text-align:left;vertical-align:top">
- <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border:0 solid #00679e;border-collapse:collapse!important;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
- <a href="https://example.com/" style="Margin:0;border:0 solid #00679e;color:#ffffff;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:regular;line-height:normal;margin:0;padding:8px;text-align:left;outline:1px solid #ffffff;text-decoration:none">Go to TestCloud</a>
+ <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
+ <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%">
+ <tr style="padding:0;text-align:left;vertical-align:top">
+ <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border:0 solid #00679e;border-collapse:collapse!important;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
+ <a href="https://example.com/" style="Margin:0;border:0 solid #00679e;color:#ffffff;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:regular;line-height:normal;margin:0;padding:8px;text-align:left;outline:1px solid #ffffff;text-decoration:none">Go to TestCloud</a>
+ </td>
+ </tr>
+ </table>
</td>
</tr>
</table>
+ <!--[if (gte mso 9)|(IE)]>
</td>
- </tr>
- </table>
- <table class="button btn default secondary float-center" style="Margin:0 0 30px 0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;background-color: #ccc;margin:0 0 30px 0;max-height:40px;max-width:300px;padding:1px;border-radius:8px;text-align:center;vertical-align:top;width:auto">
- <tr style="padding:0;text-align:left;vertical-align:top">
- <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
- <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%">
+ <td>
+ <![endif]-->
+ <table class="button btn default secondary float-center" style="Margin:0 0 30px 0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;background-color: #ccc;margin:0 0 30px 0;max-height:40px;max-width:300px;padding:1px;border-radius:8px;text-align:center;vertical-align:top;width:auto">
<tr style="padding:0;text-align:left;vertical-align:top">
- <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border:0 solid #777;border-collapse:collapse!important;color:#fefefe;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
- <a href="https://nextcloud.com/install/#install-clients" style="Margin:0;background-color:#fff;border:0 solid #777;color:#6C6C6C!important;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:regular;line-height:normal;margin:0;border-radius: 7px;padding:8px;text-align:left;text-decoration:none">Install Client</a>
+ <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
+ <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%">
+ <tr style="padding:0;text-align:left;vertical-align:top">
+ <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border:0 solid #777;border-collapse:collapse!important;color:#fefefe;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
+ <a href="https://nextcloud.com/install/#install-clients" style="Margin:0;background-color:#fff;border:0 solid #777;color:#6C6C6C!important;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:regular;line-height:normal;margin:0;border-radius: 7px;padding:8px;text-align:left;text-decoration:none">Install Client</a>
+ </td>
+ </tr>
+ </table>
</td>
</tr>
</table>
+ <!--[if (gte mso 9)|(IE)]>
</td>
</tr>
</table>
+ <![endif]-->
</center>
</th>
<th class="expander" style="Margin:0;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0!important;text-align:left;visibility:hidden;width:0"></th>
@@ -725,32 +753,46 @@ EOF;
<tr style="padding:0;text-align:left;vertical-align:top">
<th style="Margin:0;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0;text-align:left">
<center data-parsed="" style="min-width:490px;width:100%">
- <table class="button btn default primary float-center" style="Margin:0 0 30px 0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;margin:0 0 30px 0;margin-right:15px;border-radius:8px;max-width:300px;padding:0;text-align:center;vertical-align:top;width:auto;background:#00679e;background-color:#00679e;color:#fefefe;">
- <tr style="padding:0;text-align:left;vertical-align:top">
- <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
- <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%">
+ <!--[if (gte mso 9)|(IE)]>
+ <table>
+ <tr>
+ <td>
+ <![endif]-->
+ <table class="button btn default primary float-center" style="Margin:0 0 30px 0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;margin:0 0 30px 0;margin-right:15px;border-radius:8px;max-width:300px;padding:0;text-align:center;vertical-align:top;width:auto;background:#00679e;background-color:#00679e;color:#fefefe;">
<tr style="padding:0;text-align:left;vertical-align:top">
- <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border:0 solid #00679e;border-collapse:collapse!important;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
- <a href="https://example.com/" style="Margin:0;border:0 solid #00679e;color:#ffffff;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:regular;line-height:normal;margin:0;padding:8px;text-align:left;outline:1px solid #ffffff;text-decoration:none">Go to TestCloud</a>
+ <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
+ <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%">
+ <tr style="padding:0;text-align:left;vertical-align:top">
+ <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border:0 solid #00679e;border-collapse:collapse!important;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
+ <a href="https://example.com/" style="Margin:0;border:0 solid #00679e;color:#ffffff;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:regular;line-height:normal;margin:0;padding:8px;text-align:left;outline:1px solid #ffffff;text-decoration:none">Go to TestCloud</a>
+ </td>
+ </tr>
+ </table>
</td>
</tr>
</table>
+ <!--[if (gte mso 9)|(IE)]>
</td>
- </tr>
- </table>
- <table class="button btn default secondary float-center" style="Margin:0 0 30px 0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;background-color: #ccc;margin:0 0 30px 0;max-height:40px;max-width:300px;padding:1px;border-radius:8px;text-align:center;vertical-align:top;width:auto">
- <tr style="padding:0;text-align:left;vertical-align:top">
- <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
- <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%">
+ <td>
+ <![endif]-->
+ <table class="button btn default secondary float-center" style="Margin:0 0 30px 0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;background-color: #ccc;margin:0 0 30px 0;max-height:40px;max-width:300px;padding:1px;border-radius:8px;text-align:center;vertical-align:top;width:auto">
<tr style="padding:0;text-align:left;vertical-align:top">
- <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border:0 solid #777;border-collapse:collapse!important;color:#fefefe;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
- <a href="https://nextcloud.com/install/#install-clients" style="Margin:0;background-color:#fff;border:0 solid #777;color:#6C6C6C!important;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:regular;line-height:normal;margin:0;border-radius: 7px;padding:8px;text-align:left;text-decoration:none">Install Client</a>
+ <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
+ <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%">
+ <tr style="padding:0;text-align:left;vertical-align:top">
+ <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border:0 solid #777;border-collapse:collapse!important;color:#fefefe;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
+ <a href="https://nextcloud.com/install/#install-clients" style="Margin:0;background-color:#fff;border:0 solid #777;color:#6C6C6C!important;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:regular;line-height:normal;margin:0;border-radius: 7px;padding:8px;text-align:left;text-decoration:none">Install Client</a>
+ </td>
+ </tr>
+ </table>
</td>
</tr>
</table>
+ <!--[if (gte mso 9)|(IE)]>
</td>
</tr>
</table>
+ <![endif]-->
</center>
</th>
<th class="expander" style="Margin:0;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0!important;text-align:left;visibility:hidden;width:0"></th>
diff --git a/apps/systemtags/src/components/SystemTagPicker.vue b/apps/systemtags/src/components/SystemTagPicker.vue
index 377e76ed75f..966eedbc1fc 100644
--- a/apps/systemtags/src/components/SystemTagPicker.vue
+++ b/apps/systemtags/src/components/SystemTagPicker.vue
@@ -59,9 +59,15 @@
@submit="openedPicker = false">
<NcButton :aria-label="t('systemtags', 'Change tag color')" type="tertiary">
<template #icon>
- <CircleIcon v-if="tag.color" :size="24" fill-color="var(--color-circle-icon)" />
- <CircleOutlineIcon v-else :size="24" fill-color="var(--color-circle-icon)" />
- <PencilIcon />
+ <CircleIcon v-if="tag.color"
+ :size="24"
+ fill-color="var(--color-circle-icon)"
+ class="button-color-circle" />
+ <CircleOutlineIcon v-else
+ :size="24"
+ fill-color="var(--color-circle-icon)"
+ class="button-color-empty" />
+ <PencilIcon class="button-color-pencil" />
</template>
</NcButton>
</NcColorPicker>
@@ -622,7 +628,7 @@ export default defineComponent({
.systemtags-picker__tag-color button {
margin-inline-start: calc(var(--default-grid-baseline) * 2);
- span.pencil-icon {
+ .button-color-pencil {
display: none;
color: var(--color-main-text);
}
@@ -630,11 +636,11 @@ export default defineComponent({
&:focus,
&:hover,
&[aria-expanded='true'] {
- .pencil-icon {
+ .button-color-pencil {
display: block;
}
- .circle-icon,
- .circle-outline-icon {
+ .button-color-circle,
+ .button-color-empty {
display: none;
}
}
diff --git a/apps/user_status/l10n/zh_CN.js b/apps/user_status/l10n/zh_CN.js
index 1bf11fddfff..c36ad38c713 100644
--- a/apps/user_status/l10n/zh_CN.js
+++ b/apps/user_status/l10n/zh_CN.js
@@ -10,26 +10,27 @@ OC.L10N.register(
"Out of office" : "不在办公室",
"Working remotely" : "远程办公中",
"In a call" : "通话中",
+ "Be right back" : "马上回来",
"User status" : "用户状态",
"Clear status after" : "清除状态于",
- "Emoji for your status message" : "表示状态信息的表情符号",
- "What is your status?" : "你什么状态?",
- "Predefined statuses" : "预定义的状态",
- "Previously set" : "先前设定",
+ "Emoji for your status message" : "状态消息的表情符号",
+ "What is your status?" : "您的状态如何?",
+ "Predefined statuses" : "预定义状态",
+ "Previously set" : "先前设置",
"Reset status" : "重置状态",
- "Reset status to \"{icon} {message}\"" : "重置状态为 {icon} {message}",
- "Reset status to \"{message}\"" : "重置状态为 {message}",
- "Reset status to \"{icon}\"" : "重置状态为 {icon}",
+ "Reset status to \"{icon} {message}\"" : "将状态重置为“{icon} {message}”",
+ "Reset status to \"{message}\"" : "将状态重置为“{message}”",
+ "Reset status to \"{icon}\"" : "将状态重置为“{icon}”",
"There was an error saving the status" : "保存状态时出错",
"There was an error clearing the status" : "清除状态时出错",
- "There was an error reverting the status" : "还原状态时发生错误",
+ "There was an error reverting the status" : "恢复状态时出错",
"Online status" : "在线状态",
- "Status message" : "状态信息",
+ "Status message" : "状态消息",
"Set absence period" : "设置缺勤时段",
- "Set absence period and replacement" : "设置缺勤时段和替代人员",
- "Your status was set automatically" : "您的状态已自动设定",
- "Clear status message" : "清除状态信息",
- "Set status message" : "设定状态信息",
+ "Set absence period and replacement" : "设置缺勤时段和接替者",
+ "Your status was set automatically" : "您的状态已自动设置",
+ "Clear status message" : "清除状态消息",
+ "Set status message" : "设置状态消息",
"Don't clear" : "不要清除",
"Today" : "今天",
"This week" : "本周",
@@ -38,11 +39,11 @@ OC.L10N.register(
"Do not disturb" : "勿扰",
"Invisible" : "隐身",
"Offline" : "离线",
- "Set status" : "设定状态",
+ "Set status" : "设置状态",
"There was an error saving the new status" : "保存新状态时出错",
"30 minutes" : "30 分钟",
- "1 hour" : "1小时",
- "4 hours" : "4小时",
+ "1 hour" : "1 小时",
+ "4 hours" : "4 小时",
"Busy" : "忙碌",
"Mute all notifications" : "静音所有通知",
"Appear offline" : "显示为离线"
diff --git a/apps/user_status/l10n/zh_CN.json b/apps/user_status/l10n/zh_CN.json
index 7157edb0884..8546482d238 100644
--- a/apps/user_status/l10n/zh_CN.json
+++ b/apps/user_status/l10n/zh_CN.json
@@ -8,26 +8,27 @@
"Out of office" : "不在办公室",
"Working remotely" : "远程办公中",
"In a call" : "通话中",
+ "Be right back" : "马上回来",
"User status" : "用户状态",
"Clear status after" : "清除状态于",
- "Emoji for your status message" : "表示状态信息的表情符号",
- "What is your status?" : "你什么状态?",
- "Predefined statuses" : "预定义的状态",
- "Previously set" : "先前设定",
+ "Emoji for your status message" : "状态消息的表情符号",
+ "What is your status?" : "您的状态如何?",
+ "Predefined statuses" : "预定义状态",
+ "Previously set" : "先前设置",
"Reset status" : "重置状态",
- "Reset status to \"{icon} {message}\"" : "重置状态为 {icon} {message}",
- "Reset status to \"{message}\"" : "重置状态为 {message}",
- "Reset status to \"{icon}\"" : "重置状态为 {icon}",
+ "Reset status to \"{icon} {message}\"" : "将状态重置为“{icon} {message}”",
+ "Reset status to \"{message}\"" : "将状态重置为“{message}”",
+ "Reset status to \"{icon}\"" : "将状态重置为“{icon}”",
"There was an error saving the status" : "保存状态时出错",
"There was an error clearing the status" : "清除状态时出错",
- "There was an error reverting the status" : "还原状态时发生错误",
+ "There was an error reverting the status" : "恢复状态时出错",
"Online status" : "在线状态",
- "Status message" : "状态信息",
+ "Status message" : "状态消息",
"Set absence period" : "设置缺勤时段",
- "Set absence period and replacement" : "设置缺勤时段和替代人员",
- "Your status was set automatically" : "您的状态已自动设定",
- "Clear status message" : "清除状态信息",
- "Set status message" : "设定状态信息",
+ "Set absence period and replacement" : "设置缺勤时段和接替者",
+ "Your status was set automatically" : "您的状态已自动设置",
+ "Clear status message" : "清除状态消息",
+ "Set status message" : "设置状态消息",
"Don't clear" : "不要清除",
"Today" : "今天",
"This week" : "本周",
@@ -36,11 +37,11 @@
"Do not disturb" : "勿扰",
"Invisible" : "隐身",
"Offline" : "离线",
- "Set status" : "设定状态",
+ "Set status" : "设置状态",
"There was an error saving the new status" : "保存新状态时出错",
"30 minutes" : "30 分钟",
- "1 hour" : "1小时",
- "4 hours" : "4小时",
+ "1 hour" : "1 小时",
+ "4 hours" : "4 小时",
"Busy" : "忙碌",
"Mute all notifications" : "静音所有通知",
"Appear offline" : "显示为离线"
diff --git a/apps/user_status/lib/Capabilities.php b/apps/user_status/lib/Capabilities.php
index 0c5dc4e03d2..c3edbc032d6 100644
--- a/apps/user_status/lib/Capabilities.php
+++ b/apps/user_status/lib/Capabilities.php
@@ -23,7 +23,7 @@ class Capabilities implements ICapability {
}
/**
- * @return array{user_status: array{enabled: bool, restore: bool, supports_emoji: bool}}
+ * @return array{user_status: array{enabled: bool, restore: bool, supports_emoji: bool, supports_busy: bool}}
*/
public function getCapabilities() {
return [
@@ -31,6 +31,7 @@ class Capabilities implements ICapability {
'enabled' => true,
'restore' => true,
'supports_emoji' => $this->emojiHelper->doesPlatformSupportEmoji(),
+ 'supports_busy' => true,
],
];
}
diff --git a/apps/user_status/openapi.json b/apps/user_status/openapi.json
index d1018fa26e6..e48d4970b96 100644
--- a/apps/user_status/openapi.json
+++ b/apps/user_status/openapi.json
@@ -31,7 +31,8 @@
"required": [
"enabled",
"restore",
- "supports_emoji"
+ "supports_emoji",
+ "supports_busy"
],
"properties": {
"enabled": {
@@ -42,6 +43,9 @@
},
"supports_emoji": {
"type": "boolean"
+ },
+ "supports_busy": {
+ "type": "boolean"
}
}
}
diff --git a/apps/user_status/tests/Unit/CapabilitiesTest.php b/apps/user_status/tests/Unit/CapabilitiesTest.php
index f07892ff3fd..601fb207df4 100644
--- a/apps/user_status/tests/Unit/CapabilitiesTest.php
+++ b/apps/user_status/tests/Unit/CapabilitiesTest.php
@@ -35,6 +35,7 @@ class CapabilitiesTest extends TestCase {
'enabled' => true,
'restore' => true,
'supports_emoji' => $supportsEmojis,
+ 'supports_busy' => true,
]
], $this->capabilities->getCapabilities());
}
diff --git a/apps/workflowengine/l10n/de.js b/apps/workflowengine/l10n/de.js
index 8feec57e910..761beaa28d9 100644
--- a/apps/workflowengine/l10n/de.js
+++ b/apps/workflowengine/l10n/de.js
@@ -97,7 +97,6 @@ OC.L10N.register(
"is" : "ist",
"is not" : "ist nicht",
"File name" : "Dateiname",
- "Directory" : "Verzeichnis",
"File MIME type" : "Datei MIME-Typ",
"File size (upload)" : "Dateigröße (beim Hochladen)",
"less" : "weniger",
diff --git a/apps/workflowengine/l10n/de.json b/apps/workflowengine/l10n/de.json
index 9ded7338f81..eca78d35353 100644
--- a/apps/workflowengine/l10n/de.json
+++ b/apps/workflowengine/l10n/de.json
@@ -95,7 +95,6 @@
"is" : "ist",
"is not" : "ist nicht",
"File name" : "Dateiname",
- "Directory" : "Verzeichnis",
"File MIME type" : "Datei MIME-Typ",
"File size (upload)" : "Dateigröße (beim Hochladen)",
"less" : "weniger",
diff --git a/apps/workflowengine/l10n/de_DE.js b/apps/workflowengine/l10n/de_DE.js
index 8cf48150dad..882d975e932 100644
--- a/apps/workflowengine/l10n/de_DE.js
+++ b/apps/workflowengine/l10n/de_DE.js
@@ -97,7 +97,6 @@ OC.L10N.register(
"is" : "ist",
"is not" : "ist nicht",
"File name" : "Dateiname",
- "Directory" : "Verzeichnis",
"File MIME type" : "Datei MIME-Typ",
"File size (upload)" : "Dateigröße (beim Hochladen)",
"less" : "weniger",
diff --git a/apps/workflowengine/l10n/de_DE.json b/apps/workflowengine/l10n/de_DE.json
index 253ed3df8d2..26b952586ba 100644
--- a/apps/workflowengine/l10n/de_DE.json
+++ b/apps/workflowengine/l10n/de_DE.json
@@ -95,7 +95,6 @@
"is" : "ist",
"is not" : "ist nicht",
"File name" : "Dateiname",
- "Directory" : "Verzeichnis",
"File MIME type" : "Datei MIME-Typ",
"File size (upload)" : "Dateigröße (beim Hochladen)",
"less" : "weniger",
diff --git a/apps/workflowengine/l10n/en_GB.js b/apps/workflowengine/l10n/en_GB.js
index dc97bed2037..971aa8d6415 100644
--- a/apps/workflowengine/l10n/en_GB.js
+++ b/apps/workflowengine/l10n/en_GB.js
@@ -97,7 +97,6 @@ OC.L10N.register(
"is" : "is",
"is not" : "is not",
"File name" : "File name",
- "Directory" : "Directory",
"File MIME type" : "File MIME type",
"File size (upload)" : "File size (upload)",
"less" : "less",
diff --git a/apps/workflowengine/l10n/en_GB.json b/apps/workflowengine/l10n/en_GB.json
index f6597a1f36a..b4591846e96 100644
--- a/apps/workflowengine/l10n/en_GB.json
+++ b/apps/workflowengine/l10n/en_GB.json
@@ -95,7 +95,6 @@
"is" : "is",
"is not" : "is not",
"File name" : "File name",
- "Directory" : "Directory",
"File MIME type" : "File MIME type",
"File size (upload)" : "File size (upload)",
"less" : "less",
diff --git a/apps/workflowengine/l10n/et_EE.js b/apps/workflowengine/l10n/et_EE.js
index 89960752f17..a6dd9bd897e 100644
--- a/apps/workflowengine/l10n/et_EE.js
+++ b/apps/workflowengine/l10n/et_EE.js
@@ -97,7 +97,6 @@ OC.L10N.register(
"is" : "on",
"is not" : "ei ole",
"File name" : "Failinimi",
- "Directory" : "Kaust",
"File MIME type" : "Faili MIME-tüüp",
"File size (upload)" : "Faili suurus (üleslaadimine)",
"less" : "väiksem",
diff --git a/apps/workflowengine/l10n/et_EE.json b/apps/workflowengine/l10n/et_EE.json
index 5a86411bdb3..3da17c5756d 100644
--- a/apps/workflowengine/l10n/et_EE.json
+++ b/apps/workflowengine/l10n/et_EE.json
@@ -95,7 +95,6 @@
"is" : "on",
"is not" : "ei ole",
"File name" : "Failinimi",
- "Directory" : "Kaust",
"File MIME type" : "Faili MIME-tüüp",
"File size (upload)" : "Faili suurus (üleslaadimine)",
"less" : "väiksem",
diff --git a/apps/workflowengine/l10n/ga.js b/apps/workflowengine/l10n/ga.js
index c6f9b29a57d..0c61ee9b09d 100644
--- a/apps/workflowengine/l10n/ga.js
+++ b/apps/workflowengine/l10n/ga.js
@@ -97,7 +97,6 @@ OC.L10N.register(
"is" : "tá",
"is not" : "níl",
"File name" : "Ainm comhaid",
- "Directory" : "Eolaire",
"File MIME type" : "Cineál comhaid MIME",
"File size (upload)" : "Méid comhaid (uaslódáil)",
"less" : "níos lú",
diff --git a/apps/workflowengine/l10n/ga.json b/apps/workflowengine/l10n/ga.json
index d5f47ae7f97..46ee7bd06a9 100644
--- a/apps/workflowengine/l10n/ga.json
+++ b/apps/workflowengine/l10n/ga.json
@@ -95,7 +95,6 @@
"is" : "tá",
"is not" : "níl",
"File name" : "Ainm comhaid",
- "Directory" : "Eolaire",
"File MIME type" : "Cineál comhaid MIME",
"File size (upload)" : "Méid comhaid (uaslódáil)",
"less" : "níos lú",
diff --git a/apps/workflowengine/l10n/pt_BR.js b/apps/workflowengine/l10n/pt_BR.js
index 1404b310f47..8d40914252d 100644
--- a/apps/workflowengine/l10n/pt_BR.js
+++ b/apps/workflowengine/l10n/pt_BR.js
@@ -97,7 +97,6 @@ OC.L10N.register(
"is" : "é",
"is not" : "não é",
"File name" : "Nome do arquivo",
- "Directory" : "Diretório",
"File MIME type" : "Tipo de arquivo MIME",
"File size (upload)" : "Tamanho do arquivo (upload)",
"less" : "menor que",
diff --git a/apps/workflowengine/l10n/pt_BR.json b/apps/workflowengine/l10n/pt_BR.json
index 1604f631e11..2cbde62dc24 100644
--- a/apps/workflowengine/l10n/pt_BR.json
+++ b/apps/workflowengine/l10n/pt_BR.json
@@ -95,7 +95,6 @@
"is" : "é",
"is not" : "não é",
"File name" : "Nome do arquivo",
- "Directory" : "Diretório",
"File MIME type" : "Tipo de arquivo MIME",
"File size (upload)" : "Tamanho do arquivo (upload)",
"less" : "menor que",
diff --git a/apps/workflowengine/l10n/ru.js b/apps/workflowengine/l10n/ru.js
index c00da93134c..6d24bbea78e 100644
--- a/apps/workflowengine/l10n/ru.js
+++ b/apps/workflowengine/l10n/ru.js
@@ -97,7 +97,6 @@ OC.L10N.register(
"is" : "равняется",
"is not" : "не равняется",
"File name" : "Имя файла",
- "Directory" : "Директория",
"File MIME type" : "MIME-тип файла",
"File size (upload)" : "Размер передаваемого на сервер файла",
"less" : "меньше",
diff --git a/apps/workflowengine/l10n/ru.json b/apps/workflowengine/l10n/ru.json
index c23b9d020b0..228ad6a71ce 100644
--- a/apps/workflowengine/l10n/ru.json
+++ b/apps/workflowengine/l10n/ru.json
@@ -95,7 +95,6 @@
"is" : "равняется",
"is not" : "не равняется",
"File name" : "Имя файла",
- "Directory" : "Директория",
"File MIME type" : "MIME-тип файла",
"File size (upload)" : "Размер передаваемого на сервер файла",
"less" : "меньше",
diff --git a/apps/workflowengine/l10n/uk.js b/apps/workflowengine/l10n/uk.js
index f335db33985..a9583382441 100644
--- a/apps/workflowengine/l10n/uk.js
+++ b/apps/workflowengine/l10n/uk.js
@@ -97,7 +97,6 @@ OC.L10N.register(
"is" : "є",
"is not" : "не",
"File name" : "Ім'я файлу",
- "Directory" : "Каталог",
"File MIME type" : "Тип MIME файлу",
"File size (upload)" : "Розмір файлу (завантаження)",
"less" : "менше",
diff --git a/apps/workflowengine/l10n/uk.json b/apps/workflowengine/l10n/uk.json
index 13674a3b207..93c18f047c3 100644
--- a/apps/workflowengine/l10n/uk.json
+++ b/apps/workflowengine/l10n/uk.json
@@ -95,7 +95,6 @@
"is" : "є",
"is not" : "не",
"File name" : "Ім'я файлу",
- "Directory" : "Каталог",
"File MIME type" : "Тип MIME файлу",
"File size (upload)" : "Розмір файлу (завантаження)",
"less" : "менше",
diff --git a/apps/workflowengine/l10n/zh_HK.js b/apps/workflowengine/l10n/zh_HK.js
index bca42dd024f..edd352c1880 100644
--- a/apps/workflowengine/l10n/zh_HK.js
+++ b/apps/workflowengine/l10n/zh_HK.js
@@ -97,7 +97,6 @@ OC.L10N.register(
"is" : "是",
"is not" : "不是",
"File name" : "檔案名稱",
- "Directory" : "目錄",
"File MIME type" : "檔案MIME類型",
"File size (upload)" : "檔案大小(上傳)",
"less" : "更少",
diff --git a/apps/workflowengine/l10n/zh_HK.json b/apps/workflowengine/l10n/zh_HK.json
index d606da3c02e..9b249cd1af4 100644
--- a/apps/workflowengine/l10n/zh_HK.json
+++ b/apps/workflowengine/l10n/zh_HK.json
@@ -95,7 +95,6 @@
"is" : "是",
"is not" : "不是",
"File name" : "檔案名稱",
- "Directory" : "目錄",
"File MIME type" : "檔案MIME類型",
"File size (upload)" : "檔案大小(上傳)",
"less" : "更少",
diff --git a/apps/workflowengine/l10n/zh_TW.js b/apps/workflowengine/l10n/zh_TW.js
index a1e54ca3ba9..8cbe56564d3 100644
--- a/apps/workflowengine/l10n/zh_TW.js
+++ b/apps/workflowengine/l10n/zh_TW.js
@@ -97,7 +97,6 @@ OC.L10N.register(
"is" : "是",
"is not" : "不是",
"File name" : "檔案名稱",
- "Directory" : "目錄",
"File MIME type" : "檔案 MIME 類型",
"File size (upload)" : "檔案大小(上傳)",
"less" : "小於",
diff --git a/apps/workflowengine/l10n/zh_TW.json b/apps/workflowengine/l10n/zh_TW.json
index 3d0c883ca7f..0bc8fdf63a6 100644
--- a/apps/workflowengine/l10n/zh_TW.json
+++ b/apps/workflowengine/l10n/zh_TW.json
@@ -95,7 +95,6 @@
"is" : "是",
"is not" : "不是",
"File name" : "檔案名稱",
- "Directory" : "目錄",
"File MIME type" : "檔案 MIME 類型",
"File size (upload)" : "檔案大小(上傳)",
"less" : "小於",