diff options
121 files changed, 1234 insertions, 331 deletions
diff --git a/apps/dav/appinfo/info.xml b/apps/dav/appinfo/info.xml index ac8886555f6..e0f4608e8c2 100644 --- a/apps/dav/appinfo/info.xml +++ b/apps/dav/appinfo/info.xml @@ -55,6 +55,7 @@ </repair-steps> <commands> + <command>OCA\DAV\Command\ClearCalendarUnshares</command> <command>OCA\DAV\Command\CreateAddressBook</command> <command>OCA\DAV\Command\CreateCalendar</command> <command>OCA\DAV\Command\CreateSubscription</command> @@ -63,6 +64,7 @@ <command>OCA\DAV\Command\ExportCalendar</command> <command>OCA\DAV\Command\FixCalendarSyncCommand</command> <command>OCA\DAV\Command\ListAddressbooks</command> + <command>OCA\DAV\Command\ListCalendarShares</command> <command>OCA\DAV\Command\ListCalendars</command> <command>OCA\DAV\Command\ListSubscriptions</command> <command>OCA\DAV\Command\MoveCalendar</command> diff --git a/apps/dav/composer/composer/autoload_classmap.php b/apps/dav/composer/composer/autoload_classmap.php index 466ef98d433..913f5b46d98 100644 --- a/apps/dav/composer/composer/autoload_classmap.php +++ b/apps/dav/composer/composer/autoload_classmap.php @@ -155,6 +155,7 @@ return array( 'OCA\\DAV\\CardDAV\\UserAddressBooks' => $baseDir . '/../lib/CardDAV/UserAddressBooks.php', 'OCA\\DAV\\CardDAV\\Validation\\CardDavValidatePlugin' => $baseDir . '/../lib/CardDAV/Validation/CardDavValidatePlugin.php', 'OCA\\DAV\\CardDAV\\Xml\\Groups' => $baseDir . '/../lib/CardDAV/Xml/Groups.php', + 'OCA\\DAV\\Command\\ClearCalendarUnshares' => $baseDir . '/../lib/Command/ClearCalendarUnshares.php', 'OCA\\DAV\\Command\\CreateAddressBook' => $baseDir . '/../lib/Command/CreateAddressBook.php', 'OCA\\DAV\\Command\\CreateCalendar' => $baseDir . '/../lib/Command/CreateCalendar.php', 'OCA\\DAV\\Command\\CreateSubscription' => $baseDir . '/../lib/Command/CreateSubscription.php', @@ -163,6 +164,7 @@ return array( 'OCA\\DAV\\Command\\ExportCalendar' => $baseDir . '/../lib/Command/ExportCalendar.php', 'OCA\\DAV\\Command\\FixCalendarSyncCommand' => $baseDir . '/../lib/Command/FixCalendarSyncCommand.php', 'OCA\\DAV\\Command\\ListAddressbooks' => $baseDir . '/../lib/Command/ListAddressbooks.php', + 'OCA\\DAV\\Command\\ListCalendarShares' => $baseDir . '/../lib/Command/ListCalendarShares.php', 'OCA\\DAV\\Command\\ListCalendars' => $baseDir . '/../lib/Command/ListCalendars.php', 'OCA\\DAV\\Command\\ListSubscriptions' => $baseDir . '/../lib/Command/ListSubscriptions.php', 'OCA\\DAV\\Command\\MoveCalendar' => $baseDir . '/../lib/Command/MoveCalendar.php', diff --git a/apps/dav/composer/composer/autoload_static.php b/apps/dav/composer/composer/autoload_static.php index 995d27adc2d..6369511343e 100644 --- a/apps/dav/composer/composer/autoload_static.php +++ b/apps/dav/composer/composer/autoload_static.php @@ -170,6 +170,7 @@ class ComposerStaticInitDAV 'OCA\\DAV\\CardDAV\\UserAddressBooks' => __DIR__ . '/..' . '/../lib/CardDAV/UserAddressBooks.php', 'OCA\\DAV\\CardDAV\\Validation\\CardDavValidatePlugin' => __DIR__ . '/..' . '/../lib/CardDAV/Validation/CardDavValidatePlugin.php', 'OCA\\DAV\\CardDAV\\Xml\\Groups' => __DIR__ . '/..' . '/../lib/CardDAV/Xml/Groups.php', + 'OCA\\DAV\\Command\\ClearCalendarUnshares' => __DIR__ . '/..' . '/../lib/Command/ClearCalendarUnshares.php', 'OCA\\DAV\\Command\\CreateAddressBook' => __DIR__ . '/..' . '/../lib/Command/CreateAddressBook.php', 'OCA\\DAV\\Command\\CreateCalendar' => __DIR__ . '/..' . '/../lib/Command/CreateCalendar.php', 'OCA\\DAV\\Command\\CreateSubscription' => __DIR__ . '/..' . '/../lib/Command/CreateSubscription.php', @@ -178,6 +179,7 @@ class ComposerStaticInitDAV 'OCA\\DAV\\Command\\ExportCalendar' => __DIR__ . '/..' . '/../lib/Command/ExportCalendar.php', 'OCA\\DAV\\Command\\FixCalendarSyncCommand' => __DIR__ . '/..' . '/../lib/Command/FixCalendarSyncCommand.php', 'OCA\\DAV\\Command\\ListAddressbooks' => __DIR__ . '/..' . '/../lib/Command/ListAddressbooks.php', + 'OCA\\DAV\\Command\\ListCalendarShares' => __DIR__ . '/..' . '/../lib/Command/ListCalendarShares.php', 'OCA\\DAV\\Command\\ListCalendars' => __DIR__ . '/..' . '/../lib/Command/ListCalendars.php', 'OCA\\DAV\\Command\\ListSubscriptions' => __DIR__ . '/..' . '/../lib/Command/ListSubscriptions.php', 'OCA\\DAV\\Command\\MoveCalendar' => __DIR__ . '/..' . '/../lib/Command/MoveCalendar.php', diff --git a/apps/dav/l10n/de.js b/apps/dav/l10n/de.js index cf6e45c577d..d6569cdc4f9 100644 --- a/apps/dav/l10n/de.js +++ b/apps/dav/l10n/de.js @@ -274,10 +274,10 @@ OC.L10N.register( "Name of the replacement" : "Name der Vertretung", "No results." : "Keine Ergebnisse", "Start typing." : "Mit dem Schreiben beginnen.", - "Short absence status" : "Kurze Abwesenheits Meldung", - "Long absence Message" : "Lange Abwesenheits Meldung", + "Short absence status" : "Kurzer Abwesenheitsstatus", + "Long absence Message" : "Lange Abwesenheitsnachricht", "Save" : "Speichern", - "Disable absence" : "Abwesenheitmeldungen deaktivieren", + "Disable absence" : "Abwesenheit deaktivieren", "Failed to load availability" : "Verfügbarkeit konnte nicht geladen werden", "Saved availability" : "Verfügbarkeit gespeichert", "Failed to save availability" : "Verfügbarkeit konnte nicht gespeichert werden", @@ -291,7 +291,7 @@ OC.L10N.register( "Pick a end time for {dayName}" : "Eine Endezeit für {dayName} wählen", "Automatically set user status to \"Do not disturb\" outside of availability to mute all notifications." : "Setze den Benutzerstatus außerhalb deiner Verfügbarkeit automatisch auf \"Nicht stören\", um alle Benachrichtigungen stumm zu schalten.", "Availability" : "Verfügbarkeit", - "If you configure your working hours, other people will see when you are out of office when they book a meeting." : "Wenn du deine Arbeitszeiten konfigurierst, können andere Benutzer sehen, wann du nicht im Büro bist, wenn sie eine Besprechung buchen.", + "If you configure your working hours, other people will see when you are out of office when they book a meeting." : "Wenn du deine Arbeitszeiten angibst, können andere beim Buchen einer Besprechung sehen, wann du nicht im Büro bist.", "Absence" : "Abwesenheit", "Configure your next absence period." : "Richte deinen nächsten Abwesenheitszeitraum ein.", "Also install the {calendarappstoreopen}Calendar app{linkclose}, or {calendardocopen}connect your desktop & mobile for syncing ↗{linkclose}." : "Installiere außerdem die {calendarappstoreopen}Kalender-App{linkclose} oder {calendardocopen}verbinde deinen Desktop & Mobilgerät zur Synchronisierung ↗{linkclose}.", diff --git a/apps/dav/l10n/de.json b/apps/dav/l10n/de.json index 6fceefcf9a7..e3a6f980ba1 100644 --- a/apps/dav/l10n/de.json +++ b/apps/dav/l10n/de.json @@ -272,10 +272,10 @@ "Name of the replacement" : "Name der Vertretung", "No results." : "Keine Ergebnisse", "Start typing." : "Mit dem Schreiben beginnen.", - "Short absence status" : "Kurze Abwesenheits Meldung", - "Long absence Message" : "Lange Abwesenheits Meldung", + "Short absence status" : "Kurzer Abwesenheitsstatus", + "Long absence Message" : "Lange Abwesenheitsnachricht", "Save" : "Speichern", - "Disable absence" : "Abwesenheitmeldungen deaktivieren", + "Disable absence" : "Abwesenheit deaktivieren", "Failed to load availability" : "Verfügbarkeit konnte nicht geladen werden", "Saved availability" : "Verfügbarkeit gespeichert", "Failed to save availability" : "Verfügbarkeit konnte nicht gespeichert werden", @@ -289,7 +289,7 @@ "Pick a end time for {dayName}" : "Eine Endezeit für {dayName} wählen", "Automatically set user status to \"Do not disturb\" outside of availability to mute all notifications." : "Setze den Benutzerstatus außerhalb deiner Verfügbarkeit automatisch auf \"Nicht stören\", um alle Benachrichtigungen stumm zu schalten.", "Availability" : "Verfügbarkeit", - "If you configure your working hours, other people will see when you are out of office when they book a meeting." : "Wenn du deine Arbeitszeiten konfigurierst, können andere Benutzer sehen, wann du nicht im Büro bist, wenn sie eine Besprechung buchen.", + "If you configure your working hours, other people will see when you are out of office when they book a meeting." : "Wenn du deine Arbeitszeiten angibst, können andere beim Buchen einer Besprechung sehen, wann du nicht im Büro bist.", "Absence" : "Abwesenheit", "Configure your next absence period." : "Richte deinen nächsten Abwesenheitszeitraum ein.", "Also install the {calendarappstoreopen}Calendar app{linkclose}, or {calendardocopen}connect your desktop & mobile for syncing ↗{linkclose}." : "Installiere außerdem die {calendarappstoreopen}Kalender-App{linkclose} oder {calendardocopen}verbinde deinen Desktop & Mobilgerät zur Synchronisierung ↗{linkclose}.", diff --git a/apps/dav/l10n/de_DE.js b/apps/dav/l10n/de_DE.js index f513d35cdc9..1072d8f9ff2 100644 --- a/apps/dav/l10n/de_DE.js +++ b/apps/dav/l10n/de_DE.js @@ -274,7 +274,7 @@ OC.L10N.register( "Name of the replacement" : "Name der Vertretung", "No results." : "Keine Ergebnisse.", "Start typing." : "Anfangen zu tippen.", - "Short absence status" : "Kurzer Abwesenheitsstaus", + "Short absence status" : "Kurzer Abwesenheitsstatus", "Long absence Message" : "Lange Abwesenheitsnachricht", "Save" : "Speichern", "Disable absence" : "Abwesenheit deaktivieren", @@ -284,16 +284,16 @@ OC.L10N.register( "Time zone:" : "Zeitzone:", "to" : "an", "Delete slot" : "Zeitfenster löschen", - "No working hours set" : "Arbeitsfreie Stunden gesetzt", + "No working hours set" : "Keine Arbeitszeiten konfiguriert", "Add slot" : "Zeitfenster hinzufügen", "Weekdays" : "Wochentage", "Pick a start time for {dayName}" : "Eine Startzeit für {dayName} wählen", "Pick a end time for {dayName}" : "Eine Endezeit für {dayName} wählen", "Automatically set user status to \"Do not disturb\" outside of availability to mute all notifications." : "Setzen Sie den Benutzerstatus außerhalb Ihrer Verfügbarkeit automatisch auf \"Nicht stören\", um alle Benachrichtigungen stumm zu schalten.", "Availability" : "Verfügbarkeit", - "If you configure your working hours, other people will see when you are out of office when they book a meeting." : "Wenn Sie Ihre Arbeitszeiten angeben, können Andere beim Buchen einer Besprechung sehen, wann Sie nicht im Büro sind.", + "If you configure your working hours, other people will see when you are out of office when they book a meeting." : "Wenn Sie Ihre Arbeitszeiten angeben, können andere beim Buchen einer Besprechung sehen, wann Sie nicht im Büro sind.", "Absence" : "Abwesenheit", - "Configure your next absence period." : "Richten Sie ihren nächsten Abwesenheitszeitraum ein", + "Configure your next absence period." : "Richten Sie ihren nächsten Abwesenheitszeitraum ein.", "Also install the {calendarappstoreopen}Calendar app{linkclose}, or {calendardocopen}connect your desktop & mobile for syncing ↗{linkclose}." : "Installieren Sie außerdem die {calendarappstoreopen}Kalender-App{linkclose} oder {calendardocopen}verbinden Sie Ihren Desktop & Mobilgerät zur Synchronisierung ↗{linkclose}.", "Please make sure to properly set up {emailopen}the email server{linkclose}." : "Bitte stellen Sie sicher, dass Sie {emailopen}den E-Mail Server{linkclose} ordnungsgemäß eingerichtet haben.", "Calendar server" : "Kalender-Server", diff --git a/apps/dav/l10n/de_DE.json b/apps/dav/l10n/de_DE.json index e27822fbaa6..721cee8c91a 100644 --- a/apps/dav/l10n/de_DE.json +++ b/apps/dav/l10n/de_DE.json @@ -272,7 +272,7 @@ "Name of the replacement" : "Name der Vertretung", "No results." : "Keine Ergebnisse.", "Start typing." : "Anfangen zu tippen.", - "Short absence status" : "Kurzer Abwesenheitsstaus", + "Short absence status" : "Kurzer Abwesenheitsstatus", "Long absence Message" : "Lange Abwesenheitsnachricht", "Save" : "Speichern", "Disable absence" : "Abwesenheit deaktivieren", @@ -282,16 +282,16 @@ "Time zone:" : "Zeitzone:", "to" : "an", "Delete slot" : "Zeitfenster löschen", - "No working hours set" : "Arbeitsfreie Stunden gesetzt", + "No working hours set" : "Keine Arbeitszeiten konfiguriert", "Add slot" : "Zeitfenster hinzufügen", "Weekdays" : "Wochentage", "Pick a start time for {dayName}" : "Eine Startzeit für {dayName} wählen", "Pick a end time for {dayName}" : "Eine Endezeit für {dayName} wählen", "Automatically set user status to \"Do not disturb\" outside of availability to mute all notifications." : "Setzen Sie den Benutzerstatus außerhalb Ihrer Verfügbarkeit automatisch auf \"Nicht stören\", um alle Benachrichtigungen stumm zu schalten.", "Availability" : "Verfügbarkeit", - "If you configure your working hours, other people will see when you are out of office when they book a meeting." : "Wenn Sie Ihre Arbeitszeiten angeben, können Andere beim Buchen einer Besprechung sehen, wann Sie nicht im Büro sind.", + "If you configure your working hours, other people will see when you are out of office when they book a meeting." : "Wenn Sie Ihre Arbeitszeiten angeben, können andere beim Buchen einer Besprechung sehen, wann Sie nicht im Büro sind.", "Absence" : "Abwesenheit", - "Configure your next absence period." : "Richten Sie ihren nächsten Abwesenheitszeitraum ein", + "Configure your next absence period." : "Richten Sie ihren nächsten Abwesenheitszeitraum ein.", "Also install the {calendarappstoreopen}Calendar app{linkclose}, or {calendardocopen}connect your desktop & mobile for syncing ↗{linkclose}." : "Installieren Sie außerdem die {calendarappstoreopen}Kalender-App{linkclose} oder {calendardocopen}verbinden Sie Ihren Desktop & Mobilgerät zur Synchronisierung ↗{linkclose}.", "Please make sure to properly set up {emailopen}the email server{linkclose}." : "Bitte stellen Sie sicher, dass Sie {emailopen}den E-Mail Server{linkclose} ordnungsgemäß eingerichtet haben.", "Calendar server" : "Kalender-Server", diff --git a/apps/dav/lib/CalDAV/CalDavBackend.php b/apps/dav/lib/CalDAV/CalDavBackend.php index e69fe9ed3f0..5643e89d797 100644 --- a/apps/dav/lib/CalDAV/CalDavBackend.php +++ b/apps/dav/lib/CalDAV/CalDavBackend.php @@ -90,6 +90,19 @@ use function time; * Code is heavily inspired by https://github.com/fruux/sabre-dav/blob/master/lib/CalDAV/Backend/PDO.php * * @package OCA\DAV\CalDAV + * + * @psalm-type CalendarInfo = array{ + * id: int, + * uri: string, + * principaluri: string, + * '{http://calendarserver.org/ns/}getctag': string, + * '{http://sabredav.org/ns}sync-token': int, + * '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set': \Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet, + * '{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp': \Sabre\CalDAV\Xml\Property\ScheduleCalendarTransp, + * '{DAV:}displayname': string, + * '{urn:ietf:params:xml:ns:caldav}calendar-timezone': ?string, + * '{http://nextcloud.com/ns}owner-displayname': string, + * } */ class CalDavBackend extends AbstractBackend implements SyncSupport, SubscriptionSupport, SchedulingSupport { use TTransactional; @@ -374,7 +387,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription $subSelect->select('resourceid') ->from('dav_shares', 'd') ->where($subSelect->expr()->eq('d.access', $select->createNamedParameter(Backend::ACCESS_UNSHARED, IQueryBuilder::PARAM_INT), IQueryBuilder::PARAM_INT)) - ->andWhere($subSelect->expr()->in('d.principaluri', $select->createNamedParameter($principals, IQueryBuilder::PARAM_STR_ARRAY), IQueryBuilder::PARAM_STR_ARRAY)); + ->andWhere($subSelect->expr()->eq('d.principaluri', $select->createNamedParameter($principalUri, IQueryBuilder::PARAM_STR), IQueryBuilder::PARAM_STR)); $select->select($fields) ->from('dav_shares', 's') @@ -651,7 +664,8 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription } /** - * @return array{id: int, uri: string, '{http://calendarserver.org/ns/}getctag': string, '{http://sabredav.org/ns}sync-token': int, '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set': SupportedCalendarComponentSet, '{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp': ScheduleCalendarTransp, '{urn:ietf:params:xml:ns:caldav}calendar-timezone': ?string }|null + * @psalm-return CalendarInfo|null + * @return array|null */ public function getCalendarById(int $calendarId): ?array { $fields = array_column($this->propertyMap, 0); @@ -3671,4 +3685,26 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription ->where($cmd->expr()->eq('uid', $cmd->createNamedParameter($eventId, IQueryBuilder::PARAM_STR), IQueryBuilder::PARAM_STR)); $cmd->executeStatement(); } + + public function unshare(IShareable $shareable, string $principal): void { + $this->atomic(function () use ($shareable, $principal): void { + $calendarData = $this->getCalendarById($shareable->getResourceId()); + if ($calendarData === null) { + throw new \RuntimeException('Trying to update shares for non-existing calendar: ' . $shareable->getResourceId()); + } + + $oldShares = $this->getShares($shareable->getResourceId()); + $unshare = $this->calendarSharingBackend->unshare($shareable, $principal); + + if ($unshare) { + $this->dispatcher->dispatchTyped(new CalendarShareUpdatedEvent( + $shareable->getResourceId(), + $calendarData, + $oldShares, + [], + [$principal] + )); + } + }, $this->db); + } } diff --git a/apps/dav/lib/CalDAV/Calendar.php b/apps/dav/lib/CalDAV/Calendar.php index 789fe4b55c0..1d88d04a5e3 100644 --- a/apps/dav/lib/CalDAV/Calendar.php +++ b/apps/dav/lib/CalDAV/Calendar.php @@ -214,12 +214,8 @@ class Calendar extends \Sabre\CalDAV\Calendar implements IRestorable, IShareable } public function delete() { - if (isset($this->calendarInfo['{http://owncloud.org/ns}owner-principal']) && - $this->calendarInfo['{http://owncloud.org/ns}owner-principal'] !== $this->calendarInfo['principaluri']) { - $principal = 'principal:' . parent::getOwner(); - $this->caldavBackend->updateShares($this, [], [ - $principal - ]); + if ($this->isShared()) { + $this->caldavBackend->unshare($this, 'principal:' . $this->getPrincipalURI()); return; } diff --git a/apps/dav/lib/CardDAV/CardDavBackend.php b/apps/dav/lib/CardDAV/CardDavBackend.php index b15ed607076..de209754ae4 100644 --- a/apps/dav/lib/CardDAV/CardDavBackend.php +++ b/apps/dav/lib/CardDAV/CardDavBackend.php @@ -137,7 +137,7 @@ class CardDavBackend implements BackendInterface, SyncSupport { $subSelect->select('id') ->from('dav_shares', 'd') ->where($subSelect->expr()->eq('d.access', $select->createNamedParameter(\OCA\DAV\CardDAV\Sharing\Backend::ACCESS_UNSHARED, IQueryBuilder::PARAM_INT), IQueryBuilder::PARAM_INT)) - ->andWhere($subSelect->expr()->in('d.principaluri', $select->createNamedParameter($principals, IQueryBuilder::PARAM_STR_ARRAY), IQueryBuilder::PARAM_STR_ARRAY)); + ->andWhere($subSelect->expr()->eq('d.principaluri', $select->createNamedParameter($principalUri, IQueryBuilder::PARAM_STR), IQueryBuilder::PARAM_STR)); $select->select(['a.id', 'a.uri', 'a.displayname', 'a.principaluri', 'a.description', 'a.synctoken', 's.access']) diff --git a/apps/dav/lib/Command/ClearCalendarUnshares.php b/apps/dav/lib/Command/ClearCalendarUnshares.php new file mode 100644 index 00000000000..bb367a9cd0f --- /dev/null +++ b/apps/dav/lib/Command/ClearCalendarUnshares.php @@ -0,0 +1,114 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OCA\DAV\Command; + +use OCA\DAV\CalDAV\CalDavBackend; +use OCA\DAV\CalDAV\Sharing\Backend; +use OCA\DAV\CalDAV\Sharing\Service; +use OCA\DAV\Connector\Sabre\Principal; +use OCA\DAV\DAV\Sharing\Backend as BackendAlias; +use OCA\DAV\DAV\Sharing\SharingMapper; +use OCP\IAppConfig; +use OCP\IUserManager; +use Symfony\Component\Console\Attribute\AsCommand; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\QuestionHelper; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\ConfirmationQuestion; + +#[AsCommand( + name: 'dav:clear-calendar-unshares', + description: 'Clear calendar unshares for a user', + hidden: false, +)] +class ClearCalendarUnshares extends Command { + public function __construct( + private IUserManager $userManager, + private IAppConfig $appConfig, + private Principal $principal, + private CalDavBackend $caldav, + private Backend $sharingBackend, + private Service $sharingService, + private SharingMapper $mapper, + ) { + parent::__construct(); + } + + protected function configure(): void { + $this->addArgument( + 'uid', + InputArgument::REQUIRED, + 'User whose unshares to clear' + ); + } + + protected function execute(InputInterface $input, OutputInterface $output): int { + $user = (string)$input->getArgument('uid'); + if (!$this->userManager->userExists($user)) { + throw new \InvalidArgumentException("User $user is unknown"); + } + + $principal = $this->principal->getPrincipalByPath('principals/users/' . $user); + if ($principal === null) { + throw new \InvalidArgumentException("Unable to fetch principal for user $user "); + } + + $shares = $this->mapper->getSharesByPrincipals([$principal['uri']], 'calendar'); + $unshares = array_filter($shares, static fn ($share) => $share['access'] === BackendAlias::ACCESS_UNSHARED); + + if (count($unshares) === 0) { + $output->writeln("User $user has no calendar unshares"); + return self::SUCCESS; + } + + $rows = array_map(fn ($share) => $this->formatCalendarUnshare($share), $shares); + + $table = new Table($output); + $table + ->setHeaders(['Share Id', 'Calendar Id', 'Calendar URI', 'Calendar Name']) + ->setRows($rows) + ->render(); + + $output->writeln(''); + + /** @var QuestionHelper $helper */ + $helper = $this->getHelper('question'); + $question = new ConfirmationQuestion('Please confirm to delete the above calendar unshare entries [y/n]', false); + + if ($helper->ask($input, $output, $question)) { + $this->mapper->deleteUnsharesByPrincipal($principal['uri'], 'calendar'); + $output->writeln("Calendar unshares for user $user deleted"); + } + + return self::SUCCESS; + } + + private function formatCalendarUnshare(array $share): array { + $calendarInfo = $this->caldav->getCalendarById($share['resourceid']); + + $resourceUri = 'Resource not found'; + $resourceName = ''; + + if ($calendarInfo !== null) { + $resourceUri = $calendarInfo['uri']; + $resourceName = $calendarInfo['{DAV:}displayname']; + } + + return [ + $share['id'], + $share['resourceid'], + $resourceUri, + $resourceName, + ]; + } +} diff --git a/apps/dav/lib/Command/ListCalendarShares.php b/apps/dav/lib/Command/ListCalendarShares.php new file mode 100644 index 00000000000..2729bc80530 --- /dev/null +++ b/apps/dav/lib/Command/ListCalendarShares.php @@ -0,0 +1,131 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OCA\DAV\Command; + +use OCA\DAV\CalDAV\CalDavBackend; +use OCA\DAV\CalDAV\Sharing\Backend; +use OCA\DAV\Connector\Sabre\Principal; +use OCA\DAV\DAV\Sharing\SharingMapper; +use OCP\IUserManager; +use Symfony\Component\Console\Attribute\AsCommand; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +#[AsCommand( + name: 'dav:list-calendar-shares', + description: 'List all calendar shares for a user', + hidden: false, +)] +class ListCalendarShares extends Command { + public function __construct( + private IUserManager $userManager, + private Principal $principal, + private CalDavBackend $caldav, + private SharingMapper $mapper, + ) { + parent::__construct(); + } + + protected function configure(): void { + $this->addArgument( + 'uid', + InputArgument::REQUIRED, + 'User whose calendar shares will be listed' + ); + $this->addOption( + 'calendar-id', + '', + InputOption::VALUE_REQUIRED, + 'List only shares for the given calendar id id', + null, + ); + } + + protected function execute(InputInterface $input, OutputInterface $output): int { + $user = (string)$input->getArgument('uid'); + if (!$this->userManager->userExists($user)) { + throw new \InvalidArgumentException("User $user is unknown"); + } + + $principal = $this->principal->getPrincipalByPath('principals/users/' . $user); + if ($principal === null) { + throw new \InvalidArgumentException("Unable to fetch principal for user $user"); + } + + $memberships = array_merge( + [$principal['uri']], + $this->principal->getGroupMembership($principal['uri']), + $this->principal->getCircleMembership($principal['uri']), + ); + + $shares = $this->mapper->getSharesByPrincipals($memberships, 'calendar'); + + $calendarId = $input->getOption('calendar-id'); + if ($calendarId !== null) { + $shares = array_filter($shares, fn ($share) => $share['resourceid'] === (int)$calendarId); + } + + $rows = array_map(fn ($share) => $this->formatCalendarShare($share), $shares); + + if (count($rows) > 0) { + $table = new Table($output); + $table + ->setHeaders(['Share Id', 'Calendar Id', 'Calendar URI', 'Calendar Name', 'Calendar Owner', 'Access By', 'Permissions']) + ->setRows($rows) + ->render(); + } else { + $output->writeln("User $user has no calendar shares"); + } + + return self::SUCCESS; + } + + private function formatCalendarShare(array $share): array { + $calendarInfo = $this->caldav->getCalendarById($share['resourceid']); + + $calendarUri = 'Resource not found'; + $calendarName = ''; + $calendarOwner = ''; + + if ($calendarInfo !== null) { + $calendarUri = $calendarInfo['uri']; + $calendarName = $calendarInfo['{DAV:}displayname']; + $calendarOwner = $calendarInfo['{http://nextcloud.com/ns}owner-displayname'] . ' (' . $calendarInfo['principaluri'] . ')'; + } + + $accessBy = match (true) { + str_starts_with($share['principaluri'], 'principals/users/') => 'Individual', + str_starts_with($share['principaluri'], 'principals/groups/') => 'Group (' . $share['principaluri'] . ')', + str_starts_with($share['principaluri'], 'principals/circles/') => 'Team (' . $share['principaluri'] . ')', + default => $share['principaluri'], + }; + + $permissions = match ($share['access']) { + Backend::ACCESS_READ => 'Read', + Backend::ACCESS_READ_WRITE => 'Read/Write', + Backend::ACCESS_UNSHARED => 'Unshare', + default => $share['access'], + }; + + return [ + $share['id'], + $share['resourceid'], + $calendarUri, + $calendarName, + $calendarOwner, + $accessBy, + $permissions, + ]; + } +} diff --git a/apps/dav/lib/DAV/Sharing/Backend.php b/apps/dav/lib/DAV/Sharing/Backend.php index 06a082628d3..de0d6891b7c 100644 --- a/apps/dav/lib/DAV/Sharing/Backend.php +++ b/apps/dav/lib/DAV/Sharing/Backend.php @@ -90,14 +90,6 @@ abstract class Backend { // Delete any possible direct shares (since the frontend does not separate between them) $this->service->deleteShare($shareable->getResourceId(), $principal); - - // Check if a user has a groupshare that they're trying to free themselves from - // If so we need to add a self::ACCESS_UNSHARED row - if (!str_contains($principal, 'group') - && $this->service->hasGroupShare($oldShares) - ) { - $this->service->unshare($shareable->getResourceId(), $principal); - } } } @@ -204,4 +196,45 @@ abstract class Backend { } return $acl; } + + public function unshare(IShareable $shareable, string $principalUri): bool { + $this->shareCache->clear(); + + $principal = $this->principalBackend->findByUri($principalUri, ''); + if (empty($principal)) { + return false; + } + + if ($shareable->getOwner() === $principal) { + return false; + } + + // Delete any possible direct shares (since the frontend does not separate between them) + $this->service->deleteShare($shareable->getResourceId(), $principal); + + $needsUnshare = $this->hasAccessByGroupOrCirclesMembership( + $shareable->getResourceId(), + $principal + ); + + if ($needsUnshare) { + $this->service->unshare($shareable->getResourceId(), $principal); + } + + return true; + } + + private function hasAccessByGroupOrCirclesMembership(int $resourceId, string $principal) { + $memberships = array_merge( + $this->principalBackend->getGroupMembership($principal, true), + $this->principalBackend->getCircleMembership($principal) + ); + + $shares = array_column( + $this->service->getShares($resourceId), + 'principaluri' + ); + + return count(array_intersect($memberships, $shares)) > 0; + } } diff --git a/apps/dav/lib/DAV/Sharing/SharingMapper.php b/apps/dav/lib/DAV/Sharing/SharingMapper.php index 0aec5b7fe81..e4722208189 100644 --- a/apps/dav/lib/DAV/Sharing/SharingMapper.php +++ b/apps/dav/lib/DAV/Sharing/SharingMapper.php @@ -110,4 +110,28 @@ class SharingMapper { ->andWhere($query->expr()->eq('type', $query->createNamedParameter($resourceType))) ->executeStatement(); } + + public function getSharesByPrincipals(array $principals, string $resourceType): array { + $query = $this->db->getQueryBuilder(); + $result = $query->select(['id', 'principaluri', 'type', 'access', 'resourceid']) + ->from('dav_shares') + ->where($query->expr()->in('principaluri', $query->createNamedParameter($principals, IQueryBuilder::PARAM_STR_ARRAY), IQueryBuilder::PARAM_STR_ARRAY)) + ->andWhere($query->expr()->eq('type', $query->createNamedParameter($resourceType))) + ->orderBy('id') + ->executeQuery(); + + $rows = $result->fetchAll(); + $result->closeCursor(); + + return $rows; + } + + public function deleteUnsharesByPrincipal(string $principal, string $resourceType): void { + $query = $this->db->getQueryBuilder(); + $query->delete('dav_shares') + ->where($query->expr()->eq('principaluri', $query->createNamedParameter($principal))) + ->andWhere($query->expr()->eq('type', $query->createNamedParameter($resourceType))) + ->andWhere($query->expr()->eq('access', $query->createNamedParameter(Backend::ACCESS_UNSHARED, IQueryBuilder::PARAM_INT))) + ->executeStatement(); + } } diff --git a/apps/dav/lib/DAV/Sharing/SharingService.php b/apps/dav/lib/DAV/Sharing/SharingService.php index b9ac36ea066..11459e12d74 100644 --- a/apps/dav/lib/DAV/Sharing/SharingService.php +++ b/apps/dav/lib/DAV/Sharing/SharingService.php @@ -50,14 +50,4 @@ abstract class SharingService { public function getSharesForIds(array $resourceIds): array { return $this->mapper->getSharesForIds($resourceIds, $this->getResourceType()); } - - /** - * @param array $oldShares - * @return bool - */ - public function hasGroupShare(array $oldShares): bool { - return !empty(array_filter($oldShares, function (array $share) { - return $share['{http://owncloud.org/ns}group-share'] === true; - })); - } } diff --git a/apps/dav/lib/Events/CalendarShareUpdatedEvent.php b/apps/dav/lib/Events/CalendarShareUpdatedEvent.php index 762307b202f..2e49df956ec 100644 --- a/apps/dav/lib/Events/CalendarShareUpdatedEvent.php +++ b/apps/dav/lib/Events/CalendarShareUpdatedEvent.php @@ -9,21 +9,22 @@ declare(strict_types=1); namespace OCA\DAV\Events; use OCP\EventDispatcher\Event; -use Sabre\CalDAV\Xml\Property\ScheduleCalendarTransp; -use Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet; /** * Class CalendarShareUpdatedEvent * * @package OCA\DAV\Events * @since 20.0.0 + * + * @psalm-import-type CalendarInfo from \OCA\DAV\CalDAV\CalDavBackend */ class CalendarShareUpdatedEvent extends Event { /** * CalendarShareUpdatedEvent constructor. * * @param int $calendarId - * @param array{id: int, uri: string, '{http://calendarserver.org/ns/}getctag': string, '{http://sabredav.org/ns}sync-token': int, '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set': SupportedCalendarComponentSet, '{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp': ScheduleCalendarTransp, '{urn:ietf:params:xml:ns:caldav}calendar-timezone': ?string } $calendarData + * @psalm-param CalendarInfo $calendarData + * @param array $calendarData * @param list<array{href: string, commonName: string, status: int, readOnly: bool, '{http://owncloud.org/ns}principal': string, '{http://owncloud.org/ns}group-share': bool}> $oldShares * @param list<array{href: string, commonName: string, readOnly: bool}> $added * @param list<string> $removed @@ -47,7 +48,8 @@ class CalendarShareUpdatedEvent extends Event { } /** - * @return array{id: int, uri: string, '{http://calendarserver.org/ns/}getctag': string, '{http://sabredav.org/ns}sync-token': int, '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set': SupportedCalendarComponentSet, '{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp': ScheduleCalendarTransp, '{urn:ietf:params:xml:ns:caldav}calendar-timezone': ?string } + * @psalm-return CalendarInfo + * @return array * @since 20.0.0 */ public function getCalendarData(): array { diff --git a/apps/dav/tests/integration/DAV/Sharing/CalDavSharingBackendTest.php b/apps/dav/tests/integration/DAV/Sharing/CalDavSharingBackendTest.php new file mode 100644 index 00000000000..407d9f9ba41 --- /dev/null +++ b/apps/dav/tests/integration/DAV/Sharing/CalDavSharingBackendTest.php @@ -0,0 +1,249 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OCA\DAV\Tests\integration\DAV\Sharing; + +use OCA\DAV\CalDAV\Calendar; +use OCA\DAV\Connector\Sabre\Principal; +use OCA\DAV\DAV\Sharing\Backend; +use OCA\DAV\DAV\Sharing\SharingMapper; +use OCA\DAV\DAV\Sharing\SharingService; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\ICacheFactory; +use OCP\IDBConnection; +use OCP\IGroupManager; +use OCP\IUserManager; +use OCP\Server; +use Psr\Log\LoggerInterface; +use Test\TestCase; + +/** + * @group DB + */ +class CalDavSharingBackendTest extends TestCase { + + private IDBConnection $db; + private IUserManager $userManager; + private IGroupManager $groupManager; + private Principal $principalBackend; + private ICacheFactory $cacheFactory; + private LoggerInterface $logger; + private SharingMapper $sharingMapper; + private SharingService $sharingService; + private Backend $sharingBackend; + + private $resourceIds = [10001]; + + protected function setUp(): void { + parent::setUp(); + + $this->db = Server::get(IDBConnection::class); + + $this->userManager = $this->createMock(IUserManager::class); + $this->groupManager = $this->createMock(IGroupManager::class); + $this->principalBackend = $this->createMock(Principal::class); + $this->cacheFactory = $this->createMock(ICacheFactory::class); + $this->cacheFactory->method('createInMemory') + ->willReturn(new \OC\Memcache\NullCache()); + $this->logger = new \Psr\Log\NullLogger(); + + $this->sharingMapper = new SharingMapper($this->db); + $this->sharingService = new \OCA\DAV\CalDAV\Sharing\Service($this->sharingMapper); + + $this->sharingBackend = new \OCA\DAV\CalDAV\Sharing\Backend( + $this->userManager, + $this->groupManager, + $this->principalBackend, + $this->cacheFactory, + $this->sharingService, + $this->logger + ); + + $this->removeFixtures(); + } + + protected function tearDown(): void { + $this->removeFixtures(); + } + + protected function removeFixtures(): void { + $qb = $this->db->getQueryBuilder(); + $qb->delete('dav_shares') + ->where($qb->expr()->in('resourceid', $qb->createNamedParameter($this->resourceIds, IQueryBuilder::PARAM_INT_ARRAY))); + $qb->executeStatement(); + } + + public function testShareCalendarWithGroup(): void { + $calendar = $this->createMock(Calendar::class); + $calendar->method('getResourceId') + ->willReturn(10001); + $calendar->method('getOwner') + ->willReturn('principals/users/admin'); + + $this->principalBackend->method('findByUri') + ->willReturn('principals/groups/alice_bob'); + + $this->groupManager->method('groupExists') + ->willReturn(true); + + $this->sharingBackend->updateShares( + $calendar, + [['href' => 'principals/groups/alice_bob']], + [], + [] + ); + + $this->assertCount(1, $this->sharingService->getShares(10001)); + } + + public function testUnshareCalendarFromGroup(): void { + $calendar = $this->createMock(Calendar::class); + $calendar->method('getResourceId') + ->willReturn(10001); + $calendar->method('getOwner') + ->willReturn('principals/users/admin'); + + $this->principalBackend->method('findByUri') + ->willReturn('principals/groups/alice_bob'); + + $this->groupManager->method('groupExists') + ->willReturn(true); + + $this->sharingBackend->updateShares( + shareable: $calendar, + add: [['href' => 'principals/groups/alice_bob']], + remove: [], + ); + + $this->assertCount(1, $this->sharingService->getShares(10001)); + + $this->sharingBackend->updateShares( + shareable: $calendar, + add: [], + remove: ['principals/groups/alice_bob'], + ); + + $this->assertCount(0, $this->sharingService->getShares(10001)); + } + + public function testShareCalendarWithGroupAndUnshareAsUser(): void { + $calendar = $this->createMock(Calendar::class); + $calendar->method('getResourceId') + ->willReturn(10001); + $calendar->method('getOwner') + ->willReturn('principals/users/admin'); + + $this->principalBackend->method('findByUri') + ->willReturnMap([ + ['principals/groups/alice_bob', '', 'principals/groups/alice_bob'], + ['principals/users/bob', '', 'principals/users/bob'], + ]); + $this->principalBackend->method('getGroupMembership') + ->willReturn([ + 'principals/groups/alice_bob', + ]); + $this->principalBackend->method('getCircleMembership') + ->willReturn([]); + + $this->groupManager->method('groupExists') + ->willReturn(true); + + /* + * Owner is sharing the calendar with a group. + */ + $this->sharingBackend->updateShares( + shareable: $calendar, + add: [['href' => 'principals/groups/alice_bob']], + remove: [], + ); + + $this->assertCount(1, $this->sharingService->getShares(10001)); + + /* + * Member of the group unshares the calendar. + */ + $this->sharingBackend->unshare( + shareable: $calendar, + principalUri: 'principals/users/bob' + ); + + $this->assertCount(1, $this->sharingService->getShares(10001)); + $this->assertCount(1, $this->sharingService->getUnshares(10001)); + } + + /** + * Tests the functionality of sharing a calendar with a user, then with a group (that includes the shared user), + * and subsequently unsharing it from the individual user. Verifies that the unshare operation correctly removes the specific user share + * without creating an additional unshare entry. + */ + public function testShareCalendarWithUserThenGroupThenUnshareUser(): void { + $calendar = $this->createMock(Calendar::class); + $calendar->method('getResourceId') + ->willReturn(10001); + $calendar->method('getOwner') + ->willReturn('principals/users/admin'); + + $this->principalBackend->method('findByUri') + ->willReturnMap([ + ['principals/groups/alice_bob', '', 'principals/groups/alice_bob'], + ['principals/users/bob', '', 'principals/users/bob'], + ]); + $this->principalBackend->method('getGroupMembership') + ->willReturn([ + 'principals/groups/alice_bob', + ]); + $this->principalBackend->method('getCircleMembership') + ->willReturn([]); + + $this->userManager->method('userExists') + ->willReturn(true); + $this->groupManager->method('groupExists') + ->willReturn(true); + + /* + * Step 1) The owner shares the calendar with a user. + */ + $this->sharingBackend->updateShares( + shareable: $calendar, + add: [['href' => 'principals/users/bob']], + remove: [], + ); + + $this->assertCount(1, $this->sharingService->getShares(10001)); + + /* + * Step 2) The owner shares the calendar with a group that includes the + * user from step 1 as a member. + */ + $this->sharingBackend->updateShares( + shareable: $calendar, + add: [['href' => 'principals/groups/alice_bob']], + remove: [], + ); + + $this->assertCount(2, $this->sharingService->getShares(10001)); + + /* + * Step 3) Unshare the calendar from user as owner. + */ + $this->sharingBackend->updateShares( + shareable: $calendar, + add: [], + remove: ['principals/users/bob'], + ); + + /* + * The purpose of this test is to ensure that removing a user from a share, as the owner, does not result in an "unshare" row being added. + * Instead, the actual user share should be removed. + */ + $this->assertCount(1, $this->sharingService->getShares(10001)); + $this->assertCount(0, $this->sharingService->getUnshares(10001)); + } + +} diff --git a/apps/dav/tests/integration/DAV/Sharing/SharingMapperTest.php b/apps/dav/tests/integration/DAV/Sharing/SharingMapperTest.php index bde6b1060c8..bcf84254034 100644 --- a/apps/dav/tests/integration/DAV/Sharing/SharingMapperTest.php +++ b/apps/dav/tests/integration/DAV/Sharing/SharingMapperTest.php @@ -7,6 +7,8 @@ declare(strict_types=1); * SPDX-License-Identifier: AGPL-3.0-or-later */ +namespace OCA\DAV\Tests\integration\DAV\Sharing; + use OCA\DAV\DAV\Sharing\SharingMapper; use OCP\IDBConnection; use OCP\Server; diff --git a/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php b/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php index 61c03c9e4c1..825d798e7e1 100644 --- a/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php +++ b/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php @@ -17,6 +17,7 @@ use OCA\DAV\DAV\Sharing\Plugin as SharingPlugin; use OCA\DAV\Events\CalendarDeletedEvent; use OCP\IConfig; use OCP\IL10N; +use Psr\Log\NullLogger; use Sabre\DAV\Exception\NotFound; use Sabre\DAV\PropPatch; use Sabre\DAV\Xml\Property\Href; @@ -519,7 +520,7 @@ EOD; sort($stateLive['deleted']); // test live state $this->assertEquals($stateTest, $stateLive, 'Failed test delta sync state with events in calendar'); - + /** modify/delete events in calendar */ $this->deleteEvent($calendarId, $event1); $this->modifyEvent($calendarId, $event2, '20250701T140000Z', '20250701T150000Z'); @@ -1847,4 +1848,46 @@ EOD; $this->assertEquals('Missing DTSTART 1', $results[2]['objects'][0]['SUMMARY'][0]); $this->assertEquals('Missing DTSTART 2', $results[3]['objects'][0]['SUMMARY'][0]); } + + public function testUnshare(): void { + $principalGroup = 'principal:' . self::UNIT_TEST_GROUP; + $principalUser = 'principal:' . self::UNIT_TEST_USER; + + $l10n = $this->createMock(IL10N::class); + $l10n->method('t') + ->willReturnCallback(fn ($text, $parameters = []) => vsprintf($text, $parameters)); + $config = $this->createMock(IConfig::class); + $logger = new NullLogger(); + + $this->principal->expects($this->exactly(2)) + ->method('findByUri') + ->willReturnMap([ + [$principalGroup, '', self::UNIT_TEST_GROUP], + [$principalUser, '', self::UNIT_TEST_USER], + ]); + $this->groupManager->expects($this->once()) + ->method('groupExists') + ->willReturn(true); + $this->dispatcher->expects($this->exactly(2)) + ->method('dispatchTyped'); + + $calendarId = $this->createTestCalendar(); + $calendarInfo = $this->backend->getCalendarById($calendarId); + + $calendar = new Calendar($this->backend, $calendarInfo, $l10n, $config, $logger); + + $this->backend->updateShares( + shareable: $calendar, + add: [ + ['href' => $principalGroup, 'readOnly' => false] + ], + remove: [] + ); + + $this->backend->unshare( + shareable: $calendar, + principal: $principalUser + ); + + } } diff --git a/apps/dav/tests/unit/CalDAV/CalendarTest.php b/apps/dav/tests/unit/CalDAV/CalendarTest.php index 6433af8c340..7f2d0052162 100644 --- a/apps/dav/tests/unit/CalDAV/CalendarTest.php +++ b/apps/dav/tests/unit/CalDAV/CalendarTest.php @@ -43,12 +43,13 @@ class CalendarTest extends TestCase { } public function testDelete(): void { - /** @var MockObject | CalDavBackend $backend */ - $backend = $this->getMockBuilder(CalDavBackend::class)->disableOriginalConstructor()->getMock(); - $backend->expects($this->once())->method('updateShares'); - $backend->expects($this->any())->method('getShares')->willReturn([ - ['href' => 'principal:user2'] - ]); + /** @var CalDavBackend&MockObject $backend */ + $backend = $this->createMock(CalDavBackend::class); + $backend->expects($this->never()) + ->method('updateShares'); + $backend->expects($this->once()) + ->method('unshare'); + $calendarInfo = [ '{http://owncloud.org/ns}owner-principal' => 'user1', 'principaluri' => 'user2', @@ -61,12 +62,13 @@ class CalendarTest extends TestCase { public function testDeleteFromGroup(): void { - /** @var MockObject | CalDavBackend $backend */ - $backend = $this->getMockBuilder(CalDavBackend::class)->disableOriginalConstructor()->getMock(); - $backend->expects($this->once())->method('updateShares'); - $backend->expects($this->any())->method('getShares')->willReturn([ - ['href' => 'principal:group2'] - ]); + /** @var CalDavBackend&MockObject $backend */ + $backend = $this->createMock(CalDavBackend::class); + $backend->expects($this->never()) + ->method('updateShares'); + $backend->expects($this->once()) + ->method('unshare'); + $calendarInfo = [ '{http://owncloud.org/ns}owner-principal' => 'user1', 'principaluri' => 'user2', diff --git a/apps/dav/tests/unit/Command/ListCalendarSharesTest.php b/apps/dav/tests/unit/Command/ListCalendarSharesTest.php new file mode 100644 index 00000000000..3b3f3a1519e --- /dev/null +++ b/apps/dav/tests/unit/Command/ListCalendarSharesTest.php @@ -0,0 +1,172 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OCA\DAV\Tests\Command; + +use OCA\DAV\CalDAV\CalDavBackend; +use OCA\DAV\Command\ListCalendarShares; +use OCA\DAV\Connector\Sabre\Principal; +use OCA\DAV\DAV\Sharing\SharingMapper; +use OCP\IUserManager; +use PHPUnit\Framework\MockObject\MockObject; +use Symfony\Component\Console\Tester\CommandTester; +use Test\TestCase; + +class ListCalendarSharesTest extends TestCase { + + private IUserManager&MockObject $userManager; + private Principal&MockObject $principal; + private CalDavBackend&MockObject $caldav; + private SharingMapper $sharingMapper; + private ListCalendarShares $command; + + protected function setUp(): void { + parent::setUp(); + + $this->userManager = $this->createMock(IUserManager::class); + $this->principal = $this->createMock(Principal::class); + $this->caldav = $this->createMock(CalDavBackend::class); + $this->sharingMapper = $this->createMock(SharingMapper::class); + + $this->command = new ListCalendarShares( + $this->userManager, + $this->principal, + $this->caldav, + $this->sharingMapper, + ); + } + + public function testUserUnknown(): void { + $user = 'bob'; + + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage("User $user is unknown"); + + $this->userManager->expects($this->once()) + ->method('userExists') + ->with($user) + ->willReturn(false); + + $commandTester = new CommandTester($this->command); + $commandTester->execute([ + 'uid' => $user, + ]); + } + + public function testPrincipalNotFound(): void { + $user = 'bob'; + + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage("Unable to fetch principal for user $user"); + + $this->userManager->expects($this->once()) + ->method('userExists') + ->with($user) + ->willReturn(true); + + $this->principal->expects($this->once()) + ->method('getPrincipalByPath') + ->with('principals/users/' . $user) + ->willReturn(null); + + $commandTester = new CommandTester($this->command); + $commandTester->execute([ + 'uid' => $user, + ]); + } + + public function testNoCalendarShares(): void { + $user = 'bob'; + + $this->userManager->expects($this->once()) + ->method('userExists') + ->with($user) + ->willReturn(true); + + $this->principal->expects($this->once()) + ->method('getPrincipalByPath') + ->with('principals/users/' . $user) + ->willReturn([ + 'uri' => 'principals/users/' . $user, + ]); + + $this->principal->expects($this->once()) + ->method('getGroupMembership') + ->willReturn([]); + $this->principal->expects($this->once()) + ->method('getCircleMembership') + ->willReturn([]); + + $this->sharingMapper->expects($this->once()) + ->method('getSharesByPrincipals') + ->willReturn([]); + + $commandTester = new CommandTester($this->command); + $commandTester->execute([ + 'uid' => $user, + ]); + + $this->assertStringContainsString( + "User $user has no calendar shares", + $commandTester->getDisplay() + ); + } + + public function testFilterByCalendarId(): void { + $user = 'bob'; + + $this->userManager->expects($this->once()) + ->method('userExists') + ->with($user) + ->willReturn(true); + + $this->principal->expects($this->once()) + ->method('getPrincipalByPath') + ->with('principals/users/' . $user) + ->willReturn([ + 'uri' => 'principals/users/' . $user, + ]); + + $this->principal->expects($this->once()) + ->method('getGroupMembership') + ->willReturn([]); + $this->principal->expects($this->once()) + ->method('getCircleMembership') + ->willReturn([]); + + $this->sharingMapper->expects($this->once()) + ->method('getSharesByPrincipals') + ->willReturn([ + [ + 'id' => 1000, + 'principaluri' => 'principals/users/bob', + 'type' => 'calendar', + 'access' => 2, + 'resourceid' => 10 + ], + [ + 'id' => 1001, + 'principaluri' => 'principals/users/bob', + 'type' => 'calendar', + 'access' => 3, + 'resourceid' => 11 + ], + ]); + + $commandTester = new CommandTester($this->command); + $commandTester->execute([ + 'uid' => $user, + '--calendar-id' => 10, + ]); + + $this->assertStringNotContainsString( + '1001', + $commandTester->getDisplay() + ); + } +} diff --git a/apps/dav/tests/unit/DAV/Sharing/BackendTest.php b/apps/dav/tests/unit/DAV/Sharing/BackendTest.php index 344d57d1808..dd2681d149f 100644 --- a/apps/dav/tests/unit/DAV/Sharing/BackendTest.php +++ b/apps/dav/tests/unit/DAV/Sharing/BackendTest.php @@ -214,10 +214,7 @@ class BackendTest extends TestCase { 'getResourceId' => 42, ]); $remove = [ - [ - 'href' => 'principal:principals/users/bob', - 'readOnly' => true, - ] + 'principal:principals/users/bob', ]; $principal = 'principals/users/bob'; @@ -229,9 +226,6 @@ class BackendTest extends TestCase { $this->calendarService->expects(self::once()) ->method('deleteShare') ->with($shareable->getResourceId(), $principal); - $this->calendarService->expects(self::once()) - ->method('hasGroupShare') - ->willReturn(false); $this->calendarService->expects(self::never()) ->method('unshare'); @@ -244,10 +238,7 @@ class BackendTest extends TestCase { 'getResourceId' => 42, ]); $remove = [ - [ - 'href' => 'principal:principals/users/bob', - 'readOnly' => true, - ] + 'principal:principals/users/bob', ]; $oldShares = [ [ @@ -269,13 +260,8 @@ class BackendTest extends TestCase { $this->calendarService->expects(self::once()) ->method('deleteShare') ->with($shareable->getResourceId(), 'principals/users/bob'); - $this->calendarService->expects(self::once()) - ->method('hasGroupShare') - ->with($oldShares) - ->willReturn(true); - $this->calendarService->expects(self::once()) - ->method('unshare') - ->with($shareable->getResourceId(), 'principals/users/bob'); + $this->calendarService->expects(self::never()) + ->method('unshare'); $this->backend->updateShares($shareable, [], $remove, $oldShares); } diff --git a/apps/dav/tests/unit/DAV/Sharing/SharingServiceTest.php b/apps/dav/tests/unit/DAV/Sharing/SharingServiceTest.php deleted file mode 100644 index fad077eeda3..00000000000 --- a/apps/dav/tests/unit/DAV/Sharing/SharingServiceTest.php +++ /dev/null @@ -1,58 +0,0 @@ -<?php - -declare(strict_types=1); -/** - * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors - * SPDX-License-Identifier: AGPL-3.0-or-later - */ -namespace OCA\DAV\Tests\unit\DAV\Sharing; - -use OCA\DAV\CalDAV\Sharing\Service; -use OCA\DAV\DAV\Sharing\SharingMapper; -use OCA\DAV\DAV\Sharing\SharingService; -use Test\TestCase; - -class SharingServiceTest extends TestCase { - - private SharingService $service; - - protected function setUp(): void { - parent::setUp(); - $this->service = new Service($this->createMock(SharingMapper::class)); - } - - public function testHasGroupShare(): void { - $oldShares = [ - [ - 'href' => 'principal:principals/groups/bob', - 'commonName' => 'bob', - 'status' => 1, - 'readOnly' => true, - '{http://owncloud.org/ns}principal' => 'principals/groups/bob', - '{http://owncloud.org/ns}group-share' => true, - ], - [ - 'href' => 'principal:principals/users/bob', - 'commonName' => 'bob', - 'status' => 1, - 'readOnly' => true, - '{http://owncloud.org/ns}principal' => 'principals/users/bob', - '{http://owncloud.org/ns}group-share' => false, - ] - ]; - - $this->assertTrue($this->service->hasGroupShare($oldShares)); - - $oldShares = [ - [ - 'href' => 'principal:principals/users/bob', - 'commonName' => 'bob', - 'status' => 1, - 'readOnly' => true, - '{http://owncloud.org/ns}principal' => 'principals/users/bob', - '{http://owncloud.org/ns}group-share' => false, - ] - ]; - $this->assertFalse($this->service->hasGroupShare($oldShares)); - } -} diff --git a/apps/files/l10n/cs.js b/apps/files/l10n/cs.js index acf20a0af79..b6c83a08aee 100644 --- a/apps/files/l10n/cs.js +++ b/apps/files/l10n/cs.js @@ -43,8 +43,6 @@ OC.L10N.register( "Files" : "Soubory", "A file or folder has been <strong>changed</strong>" : "Soubor nebo složka byla <strong>změněna</strong>", "A favorite file or folder has been <strong>changed</strong>" : "Oblíbený soubor nebo složka byla <strong>změněna</strong>", - "%1$s (renamed)" : "%1$s (přejmenované)", - "renamed file" : "přejmenovaný soubor", "Failed to authorize" : "Nepodařilo se získat pověření", "Invalid folder path" : "Neplatný popis umístění složky", "Folder not found" : "Složka nenalezena", diff --git a/apps/files/l10n/cs.json b/apps/files/l10n/cs.json index fec4ee94d64..1ee66b0e9c8 100644 --- a/apps/files/l10n/cs.json +++ b/apps/files/l10n/cs.json @@ -41,8 +41,6 @@ "Files" : "Soubory", "A file or folder has been <strong>changed</strong>" : "Soubor nebo složka byla <strong>změněna</strong>", "A favorite file or folder has been <strong>changed</strong>" : "Oblíbený soubor nebo složka byla <strong>změněna</strong>", - "%1$s (renamed)" : "%1$s (přejmenované)", - "renamed file" : "přejmenovaný soubor", "Failed to authorize" : "Nepodařilo se získat pověření", "Invalid folder path" : "Neplatný popis umístění složky", "Folder not found" : "Složka nenalezena", diff --git a/apps/files/l10n/de.js b/apps/files/l10n/de.js index 10df3dbd1cb..084372cc5be 100644 --- a/apps/files/l10n/de.js +++ b/apps/files/l10n/de.js @@ -43,8 +43,6 @@ OC.L10N.register( "Files" : "Dateien", "A file or folder has been <strong>changed</strong>" : "Eine Datei oder ein Ordner wurde <strong>geändert</strong>", "A favorite file or folder has been <strong>changed</strong>" : "Eine favorisierte Datei oder ein Ordner wurde <strong>geändert</strong>", - "%1$s (renamed)" : "%1$s (umbenannt)", - "renamed file" : "Umbenannte Datei", "Failed to authorize" : "Autorisierung fehlgeschlagen", "Invalid folder path" : "Ungültiger Order-Pfad", "Folder not found" : "Ordner nicht gefunden", @@ -123,7 +121,7 @@ OC.L10N.register( "{count} selected" : "{count} ausgewählt", "{usedQuotaByte} used" : "{usedQuotaByte} verwendet", "{used} of {quota} used" : "{used} von {quota} verwendet", - "{relative}% used" : "{relative}% verwendet", + "{relative}% used" : "{relative} % verwendet", "Could not refresh storage stats" : "Die Speicherstatistik konnte nicht aktualisiert werden", "Your storage is full, files can not be updated or synced anymore!" : "Der Speicher ist voll, daher können keine Dateien mehr aktualisiert oder synchronisiert werden!", "Storage information" : "Speicherinformation", @@ -161,7 +159,7 @@ OC.L10N.register( "Shared by link" : "Über einen Link geteilt", "Shared" : "Geteilt", "Switch to list view" : "Zur Listenansicht wechseln", - "Switch to grid view" : "Zur Rasteransicht wechseln", + "Switch to grid view" : "Zur Kachelansicht wechseln", "The file could not be found" : "Die Datei konnte nicht gefunden werden", "Upload was cancelled by user" : "Das Hochladen wurde vom Benutzer abgebrochen.", "Not enough free space" : "Nicht genügend freier Speicherplatz", @@ -188,11 +186,11 @@ OC.L10N.register( "Sort folders before files" : "Ordner vor Dateien sortieren", "Show hidden files" : "Versteckte Dateien anzeigen", "Crop image previews" : "Bildvorschauen zuschneiden", - "Enable the grid view" : "Rasteransicht aktivieren", - "Enable folder tree" : "Baumstruktur für Ordner aktivieren", + "Enable the grid view" : "Kachelansicht aktivieren", + "Enable folder tree" : "Ordnerstruktur aktivieren", "Additional settings" : "Zusätzliche Einstellungen", "WebDAV" : "WebDAV", - "WebDAV URL" : "WebDAV URL", + "WebDAV URL" : "WebDAV-URL", "Copy to clipboard" : "In die Zwischenablage kopieren", "Use this address to access your Files via WebDAV" : "Diese Adresse benutzen, um über WebDAV auf deine Dateien zuzugreifen", "If you have enabled 2FA, you must create and use a new app password by clicking here." : "Wenn du 2FA aktiviert hast, musst du ein neues App-Passwort erstellen und verwenden, indem du hier klickst.", @@ -215,8 +213,8 @@ OC.L10N.register( "Navigate to the parent folder" : "In den übergeordneten Ordner navigieren", "Navigate to the file above" : "Zur obigen Datei navigieren", "Navigate to the file below" : "Zur unteren Datei navigieren", - "Navigate to the file on the left (in grid mode)" : "Zur Datei links navigieren (In der Kachelansicht)", - "Navigate to the file on the right (in grid mode)" : "Zur Datei rechts navigieren (In der Kachelansicht)", + "Navigate to the file on the left (in grid mode)" : "Zur Datei links navigieren (in der Kachelansicht)", + "Navigate to the file on the right (in grid mode)" : "Zur Datei rechts navigieren (in der Kachelansicht)", "View" : "Ansehen", "Toggle the grid view" : "Kachelansicht umschalten", "Open the sidebar for a file" : "Seitenleiste für eine Datei öffnen", @@ -359,7 +357,7 @@ OC.L10N.register( "File could not be found" : "Datei konnte nicht gefunden werden", "Download" : "Herunterladen", "Show list view" : "Listenansicht anzeigen", - "Show grid view" : "Rasteransicht anzeigen", + "Show grid view" : "Kachelansicht anzeigen", "Close" : "Schließen", "Could not create folder \"{dir}\"" : "Der Ordner konnte nicht erstellt werden \"{dir}\"", "This will stop your current uploads." : "Hiermit werden die aktuellen Uploads angehalten.", @@ -413,7 +411,7 @@ OC.L10N.register( "New" : "Neu", "New file/folder menu" : "Menü für neue Datei/Ordner", "Select file range" : "Dateibereich auswählen", - "{used}%" : "{used}%", + "{used}%" : "{used} %", "{used} used" : "{used} verwendet", "\"{name}\" is an invalid file name." : "\"{name}\" ist kein gültiger Dateiname.", "File name cannot be empty." : "Der Dateiname darf nicht leer sein.", diff --git a/apps/files/l10n/de.json b/apps/files/l10n/de.json index bb2e08cd416..e77105bba5b 100644 --- a/apps/files/l10n/de.json +++ b/apps/files/l10n/de.json @@ -41,8 +41,6 @@ "Files" : "Dateien", "A file or folder has been <strong>changed</strong>" : "Eine Datei oder ein Ordner wurde <strong>geändert</strong>", "A favorite file or folder has been <strong>changed</strong>" : "Eine favorisierte Datei oder ein Ordner wurde <strong>geändert</strong>", - "%1$s (renamed)" : "%1$s (umbenannt)", - "renamed file" : "Umbenannte Datei", "Failed to authorize" : "Autorisierung fehlgeschlagen", "Invalid folder path" : "Ungültiger Order-Pfad", "Folder not found" : "Ordner nicht gefunden", @@ -121,7 +119,7 @@ "{count} selected" : "{count} ausgewählt", "{usedQuotaByte} used" : "{usedQuotaByte} verwendet", "{used} of {quota} used" : "{used} von {quota} verwendet", - "{relative}% used" : "{relative}% verwendet", + "{relative}% used" : "{relative} % verwendet", "Could not refresh storage stats" : "Die Speicherstatistik konnte nicht aktualisiert werden", "Your storage is full, files can not be updated or synced anymore!" : "Der Speicher ist voll, daher können keine Dateien mehr aktualisiert oder synchronisiert werden!", "Storage information" : "Speicherinformation", @@ -159,7 +157,7 @@ "Shared by link" : "Über einen Link geteilt", "Shared" : "Geteilt", "Switch to list view" : "Zur Listenansicht wechseln", - "Switch to grid view" : "Zur Rasteransicht wechseln", + "Switch to grid view" : "Zur Kachelansicht wechseln", "The file could not be found" : "Die Datei konnte nicht gefunden werden", "Upload was cancelled by user" : "Das Hochladen wurde vom Benutzer abgebrochen.", "Not enough free space" : "Nicht genügend freier Speicherplatz", @@ -186,11 +184,11 @@ "Sort folders before files" : "Ordner vor Dateien sortieren", "Show hidden files" : "Versteckte Dateien anzeigen", "Crop image previews" : "Bildvorschauen zuschneiden", - "Enable the grid view" : "Rasteransicht aktivieren", - "Enable folder tree" : "Baumstruktur für Ordner aktivieren", + "Enable the grid view" : "Kachelansicht aktivieren", + "Enable folder tree" : "Ordnerstruktur aktivieren", "Additional settings" : "Zusätzliche Einstellungen", "WebDAV" : "WebDAV", - "WebDAV URL" : "WebDAV URL", + "WebDAV URL" : "WebDAV-URL", "Copy to clipboard" : "In die Zwischenablage kopieren", "Use this address to access your Files via WebDAV" : "Diese Adresse benutzen, um über WebDAV auf deine Dateien zuzugreifen", "If you have enabled 2FA, you must create and use a new app password by clicking here." : "Wenn du 2FA aktiviert hast, musst du ein neues App-Passwort erstellen und verwenden, indem du hier klickst.", @@ -213,8 +211,8 @@ "Navigate to the parent folder" : "In den übergeordneten Ordner navigieren", "Navigate to the file above" : "Zur obigen Datei navigieren", "Navigate to the file below" : "Zur unteren Datei navigieren", - "Navigate to the file on the left (in grid mode)" : "Zur Datei links navigieren (In der Kachelansicht)", - "Navigate to the file on the right (in grid mode)" : "Zur Datei rechts navigieren (In der Kachelansicht)", + "Navigate to the file on the left (in grid mode)" : "Zur Datei links navigieren (in der Kachelansicht)", + "Navigate to the file on the right (in grid mode)" : "Zur Datei rechts navigieren (in der Kachelansicht)", "View" : "Ansehen", "Toggle the grid view" : "Kachelansicht umschalten", "Open the sidebar for a file" : "Seitenleiste für eine Datei öffnen", @@ -357,7 +355,7 @@ "File could not be found" : "Datei konnte nicht gefunden werden", "Download" : "Herunterladen", "Show list view" : "Listenansicht anzeigen", - "Show grid view" : "Rasteransicht anzeigen", + "Show grid view" : "Kachelansicht anzeigen", "Close" : "Schließen", "Could not create folder \"{dir}\"" : "Der Ordner konnte nicht erstellt werden \"{dir}\"", "This will stop your current uploads." : "Hiermit werden die aktuellen Uploads angehalten.", @@ -411,7 +409,7 @@ "New" : "Neu", "New file/folder menu" : "Menü für neue Datei/Ordner", "Select file range" : "Dateibereich auswählen", - "{used}%" : "{used}%", + "{used}%" : "{used} %", "{used} used" : "{used} verwendet", "\"{name}\" is an invalid file name." : "\"{name}\" ist kein gültiger Dateiname.", "File name cannot be empty." : "Der Dateiname darf nicht leer sein.", diff --git a/apps/files/l10n/de_DE.js b/apps/files/l10n/de_DE.js index 894db3c84d3..b814941c851 100644 --- a/apps/files/l10n/de_DE.js +++ b/apps/files/l10n/de_DE.js @@ -43,8 +43,6 @@ OC.L10N.register( "Files" : "Dateien", "A file or folder has been <strong>changed</strong>" : "Eine Datei oder ein Ordner wurde <strong>geändert</strong>", "A favorite file or folder has been <strong>changed</strong>" : "Eine favorisierte Datei oder ein Ordner wurde <strong>geändert</strong>", - "%1$s (renamed)" : "%1$s (umbenannt)", - "renamed file" : "Umbenannte Datei", "Failed to authorize" : "Autorisierung fehlgeschlagen", "Invalid folder path" : "Ungültiger Order-Pfad", "Folder not found" : "Ordner nicht gefunden", @@ -123,7 +121,7 @@ OC.L10N.register( "{count} selected" : "{count} ausgewählt", "{usedQuotaByte} used" : "{usedQuotaByte} verwendet", "{used} of {quota} used" : "{used} von {quota} verwendet", - "{relative}% used" : "{relative}% verwendet", + "{relative}% used" : "{relative} % verwendet", "Could not refresh storage stats" : "Die Speicherstatistik konnte nicht aktualisiert werden", "Your storage is full, files can not be updated or synced anymore!" : "Ihr Speicher ist voll, daher können keine Dateien mehr aktualisiert oder synchronisiert werden!", "Storage information" : "Speicherinformation", @@ -215,8 +213,8 @@ OC.L10N.register( "Navigate to the parent folder" : "In den übergeordneten Ordner navigieren", "Navigate to the file above" : "Zur obigen Datei navigieren", "Navigate to the file below" : "Zur unteren Datei navigieren", - "Navigate to the file on the left (in grid mode)" : "Zur Datei links navigieren (In der Kachelansicht)", - "Navigate to the file on the right (in grid mode)" : "Zur Datei rechts navigieren (In der Kachelansicht)", + "Navigate to the file on the left (in grid mode)" : "Zur Datei links navigieren (in der Kachelansicht)", + "Navigate to the file on the right (in grid mode)" : "Zur Datei rechts navigieren (in der Kachelansicht)", "View" : "Ansicht", "Toggle the grid view" : "Kachelansicht umschalten", "Open the sidebar for a file" : "Seitenleiste für eine Datei öffnen", @@ -413,7 +411,7 @@ OC.L10N.register( "New" : "Neu", "New file/folder menu" : "Menü für neue Datei/Ordner", "Select file range" : "Dateibereich auswählen", - "{used}%" : "{used}%", + "{used}%" : "{used} %", "{used} used" : "{used} verwendet", "\"{name}\" is an invalid file name." : "\"{name}\" ist kein gültiger Dateiname.", "File name cannot be empty." : "Der Dateiname darf nicht leer sein.", diff --git a/apps/files/l10n/de_DE.json b/apps/files/l10n/de_DE.json index b67179d0c0d..5825a32fa85 100644 --- a/apps/files/l10n/de_DE.json +++ b/apps/files/l10n/de_DE.json @@ -41,8 +41,6 @@ "Files" : "Dateien", "A file or folder has been <strong>changed</strong>" : "Eine Datei oder ein Ordner wurde <strong>geändert</strong>", "A favorite file or folder has been <strong>changed</strong>" : "Eine favorisierte Datei oder ein Ordner wurde <strong>geändert</strong>", - "%1$s (renamed)" : "%1$s (umbenannt)", - "renamed file" : "Umbenannte Datei", "Failed to authorize" : "Autorisierung fehlgeschlagen", "Invalid folder path" : "Ungültiger Order-Pfad", "Folder not found" : "Ordner nicht gefunden", @@ -121,7 +119,7 @@ "{count} selected" : "{count} ausgewählt", "{usedQuotaByte} used" : "{usedQuotaByte} verwendet", "{used} of {quota} used" : "{used} von {quota} verwendet", - "{relative}% used" : "{relative}% verwendet", + "{relative}% used" : "{relative} % verwendet", "Could not refresh storage stats" : "Die Speicherstatistik konnte nicht aktualisiert werden", "Your storage is full, files can not be updated or synced anymore!" : "Ihr Speicher ist voll, daher können keine Dateien mehr aktualisiert oder synchronisiert werden!", "Storage information" : "Speicherinformation", @@ -213,8 +211,8 @@ "Navigate to the parent folder" : "In den übergeordneten Ordner navigieren", "Navigate to the file above" : "Zur obigen Datei navigieren", "Navigate to the file below" : "Zur unteren Datei navigieren", - "Navigate to the file on the left (in grid mode)" : "Zur Datei links navigieren (In der Kachelansicht)", - "Navigate to the file on the right (in grid mode)" : "Zur Datei rechts navigieren (In der Kachelansicht)", + "Navigate to the file on the left (in grid mode)" : "Zur Datei links navigieren (in der Kachelansicht)", + "Navigate to the file on the right (in grid mode)" : "Zur Datei rechts navigieren (in der Kachelansicht)", "View" : "Ansicht", "Toggle the grid view" : "Kachelansicht umschalten", "Open the sidebar for a file" : "Seitenleiste für eine Datei öffnen", @@ -411,7 +409,7 @@ "New" : "Neu", "New file/folder menu" : "Menü für neue Datei/Ordner", "Select file range" : "Dateibereich auswählen", - "{used}%" : "{used}%", + "{used}%" : "{used} %", "{used} used" : "{used} verwendet", "\"{name}\" is an invalid file name." : "\"{name}\" ist kein gültiger Dateiname.", "File name cannot be empty." : "Der Dateiname darf nicht leer sein.", diff --git a/apps/files/l10n/en_GB.js b/apps/files/l10n/en_GB.js index 2b4acc4cb16..f27fbcb8657 100644 --- a/apps/files/l10n/en_GB.js +++ b/apps/files/l10n/en_GB.js @@ -43,8 +43,6 @@ OC.L10N.register( "Files" : "Files", "A file or folder has been <strong>changed</strong>" : "A file or folder has been <strong>changed</strong>", "A favorite file or folder has been <strong>changed</strong>" : "A favourite file or folder has been <strong>changed</strong>", - "%1$s (renamed)" : "%1$s (renamed)", - "renamed file" : "renamed file", "Failed to authorize" : "Failed to authorize", "Invalid folder path" : "Invalid folder path", "Folder not found" : "Folder not found", diff --git a/apps/files/l10n/en_GB.json b/apps/files/l10n/en_GB.json index 1a34f016d41..b693b54b6b3 100644 --- a/apps/files/l10n/en_GB.json +++ b/apps/files/l10n/en_GB.json @@ -41,8 +41,6 @@ "Files" : "Files", "A file or folder has been <strong>changed</strong>" : "A file or folder has been <strong>changed</strong>", "A favorite file or folder has been <strong>changed</strong>" : "A favourite file or folder has been <strong>changed</strong>", - "%1$s (renamed)" : "%1$s (renamed)", - "renamed file" : "renamed file", "Failed to authorize" : "Failed to authorize", "Invalid folder path" : "Invalid folder path", "Folder not found" : "Folder not found", diff --git a/apps/files/l10n/es.js b/apps/files/l10n/es.js index edb4acfd4f5..73f0c8d5303 100644 --- a/apps/files/l10n/es.js +++ b/apps/files/l10n/es.js @@ -43,8 +43,6 @@ OC.L10N.register( "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>", - "%1$s (renamed)" : "%1$s (renombrado)", - "renamed file" : "archivo renombrado", "Failed to authorize" : "Fallo al autorizar", "Invalid folder path" : "Ruta de carpeta inválida", "Folder not found" : "Carpeta no encontrada", diff --git a/apps/files/l10n/es.json b/apps/files/l10n/es.json index d9940c3968d..9c2d523d549 100644 --- a/apps/files/l10n/es.json +++ b/apps/files/l10n/es.json @@ -41,8 +41,6 @@ "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>", - "%1$s (renamed)" : "%1$s (renombrado)", - "renamed file" : "archivo renombrado", "Failed to authorize" : "Fallo al autorizar", "Invalid folder path" : "Ruta de carpeta inválida", "Folder not found" : "Carpeta no encontrada", diff --git a/apps/files/l10n/et_EE.js b/apps/files/l10n/et_EE.js index 1b29b50ecac..f6654cd8427 100644 --- a/apps/files/l10n/et_EE.js +++ b/apps/files/l10n/et_EE.js @@ -43,8 +43,6 @@ OC.L10N.register( "Files" : "Failid", "A file or folder has been <strong>changed</strong>" : "Fail või kaust on <strong>muudetud</strong>", "A favorite file or folder has been <strong>changed</strong>" : "Lemmikuks märgitud faili või kausta on <strong>muudetud</strong>", - "%1$s (renamed)" : "%1$s (nimi on muudetud)", - "renamed file" : "muudetud nimega fail", "Failed to authorize" : "Autoriseerimine ei õnnestunud", "Invalid folder path" : "Kausta vigane asukoht", "Folder not found" : "Kausta ei leidu", diff --git a/apps/files/l10n/et_EE.json b/apps/files/l10n/et_EE.json index 58cb0b8a894..ef1613f551c 100644 --- a/apps/files/l10n/et_EE.json +++ b/apps/files/l10n/et_EE.json @@ -41,8 +41,6 @@ "Files" : "Failid", "A file or folder has been <strong>changed</strong>" : "Fail või kaust on <strong>muudetud</strong>", "A favorite file or folder has been <strong>changed</strong>" : "Lemmikuks märgitud faili või kausta on <strong>muudetud</strong>", - "%1$s (renamed)" : "%1$s (nimi on muudetud)", - "renamed file" : "muudetud nimega fail", "Failed to authorize" : "Autoriseerimine ei õnnestunud", "Invalid folder path" : "Kausta vigane asukoht", "Folder not found" : "Kausta ei leidu", diff --git a/apps/files/l10n/ga.js b/apps/files/l10n/ga.js index abcbbb78d80..dd1ec3586da 100644 --- a/apps/files/l10n/ga.js +++ b/apps/files/l10n/ga.js @@ -71,6 +71,8 @@ OC.L10N.register( "Transferred from %1$s on %2$s" : "Aistrithe ó %1$s ar %2$s", "Files compatibility" : "Comhoiriúnacht comhaid", "Allow to restrict filenames to ensure files can be synced with all clients. By default all filenames valid on POSIX (e.g. Linux or macOS) are allowed." : "Ceadaigh comhaid a shrianadh lena chinntiú gur féidir comhaid a shioncronú le gach cliant. De réir réamhshocraithe ceadaítear gach comhadainm atá bailí ar POSIX (m.sh. Linux nó macOS).", + "After enabling the Windows compatible filenames, existing files cannot be modified anymore but can be renamed to valid new names by their owner." : "Tar éis na hainmneacha comhad atá comhoiriúnach le Windows a chumasú, ní féidir comhaid atá ann cheana a mhodhnú a thuilleadh ach is féidir lena n-úinéir iad a athainmniú go hainmneacha nua bailí.", + "It is also possible to migrate files automatically after enabling this setting, please refer to the documentation about the occ command." : "Is féidir comhaid a aistriú go huathoibríoch freisin tar éis an socrú seo a chumasú, féach ar an doiciméadú faoin ordú occ.", "Enforce Windows compatibility" : "Comhoiriúnacht Windows a fhorfheidhmiú", "This will block filenames not valid on Windows systems, like using reserved names or special characters. But this will not enforce compatibility of case sensitivity." : "Cuirfidh sé seo bac ar ainmneacha comhaid nach bhfuil bailí ar chórais Windows, ar nós ainmneacha forchoimeádta nó carachtair speisialta a úsáid. Ach ní chuirfidh sé seo i bhfeidhm comhoiriúnacht íogaireacht cáis.", "File Management" : "Bainistíocht Comhad", diff --git a/apps/files/l10n/ga.json b/apps/files/l10n/ga.json index 9122bb3f7b4..0286dd29b1a 100644 --- a/apps/files/l10n/ga.json +++ b/apps/files/l10n/ga.json @@ -69,6 +69,8 @@ "Transferred from %1$s on %2$s" : "Aistrithe ó %1$s ar %2$s", "Files compatibility" : "Comhoiriúnacht comhaid", "Allow to restrict filenames to ensure files can be synced with all clients. By default all filenames valid on POSIX (e.g. Linux or macOS) are allowed." : "Ceadaigh comhaid a shrianadh lena chinntiú gur féidir comhaid a shioncronú le gach cliant. De réir réamhshocraithe ceadaítear gach comhadainm atá bailí ar POSIX (m.sh. Linux nó macOS).", + "After enabling the Windows compatible filenames, existing files cannot be modified anymore but can be renamed to valid new names by their owner." : "Tar éis na hainmneacha comhad atá comhoiriúnach le Windows a chumasú, ní féidir comhaid atá ann cheana a mhodhnú a thuilleadh ach is féidir lena n-úinéir iad a athainmniú go hainmneacha nua bailí.", + "It is also possible to migrate files automatically after enabling this setting, please refer to the documentation about the occ command." : "Is féidir comhaid a aistriú go huathoibríoch freisin tar éis an socrú seo a chumasú, féach ar an doiciméadú faoin ordú occ.", "Enforce Windows compatibility" : "Comhoiriúnacht Windows a fhorfheidhmiú", "This will block filenames not valid on Windows systems, like using reserved names or special characters. But this will not enforce compatibility of case sensitivity." : "Cuirfidh sé seo bac ar ainmneacha comhaid nach bhfuil bailí ar chórais Windows, ar nós ainmneacha forchoimeádta nó carachtair speisialta a úsáid. Ach ní chuirfidh sé seo i bhfeidhm comhoiriúnacht íogaireacht cáis.", "File Management" : "Bainistíocht Comhad", diff --git a/apps/files/l10n/it.js b/apps/files/l10n/it.js index 4277802cb80..c633cc522e2 100644 --- a/apps/files/l10n/it.js +++ b/apps/files/l10n/it.js @@ -43,8 +43,6 @@ OC.L10N.register( "Files" : "File", "A file or folder has been <strong>changed</strong>" : "Un file o una cartella è stato <strong>modificato</strong>", "A favorite file or folder has been <strong>changed</strong>" : "Un file preferito o una cartella è stato <strong>modificato</strong>", - "%1$s (renamed)" : "%1$s (rinominato)", - "renamed file" : "file rinominato", "Failed to authorize" : "Impossibile dare l'autorizzazione", "Invalid folder path" : "Percorso della cartella non valido", "Folder not found" : "Cartella non trovata", diff --git a/apps/files/l10n/it.json b/apps/files/l10n/it.json index 80c8044307b..266f1a7ad55 100644 --- a/apps/files/l10n/it.json +++ b/apps/files/l10n/it.json @@ -41,8 +41,6 @@ "Files" : "File", "A file or folder has been <strong>changed</strong>" : "Un file o una cartella è stato <strong>modificato</strong>", "A favorite file or folder has been <strong>changed</strong>" : "Un file preferito o una cartella è stato <strong>modificato</strong>", - "%1$s (renamed)" : "%1$s (rinominato)", - "renamed file" : "file rinominato", "Failed to authorize" : "Impossibile dare l'autorizzazione", "Invalid folder path" : "Percorso della cartella non valido", "Folder not found" : "Cartella non trovata", diff --git a/apps/files/l10n/pl.js b/apps/files/l10n/pl.js index 88f77ae9d76..068e760f738 100644 --- a/apps/files/l10n/pl.js +++ b/apps/files/l10n/pl.js @@ -43,8 +43,6 @@ OC.L10N.register( "Files" : "Pliki", "A file or folder has been <strong>changed</strong>" : "Plik lub katalog został <strong>zmieniony</strong>", "A favorite file or folder has been <strong>changed</strong>" : "Ulubiony plik lub katalog został <strong>zmieniony</strong>", - "%1$s (renamed)" : "%1$s (zmieniona nazwa)", - "renamed file" : "zmieniona nazwa pliku", "Failed to authorize" : "Błąd autoryzacji", "Invalid folder path" : "Nieprawidłowa ścieżka katalogu", "Folder not found" : "Nie znaleziono katalogu", diff --git a/apps/files/l10n/pl.json b/apps/files/l10n/pl.json index 0fc336bd1bb..b23b7341ceb 100644 --- a/apps/files/l10n/pl.json +++ b/apps/files/l10n/pl.json @@ -41,8 +41,6 @@ "Files" : "Pliki", "A file or folder has been <strong>changed</strong>" : "Plik lub katalog został <strong>zmieniony</strong>", "A favorite file or folder has been <strong>changed</strong>" : "Ulubiony plik lub katalog został <strong>zmieniony</strong>", - "%1$s (renamed)" : "%1$s (zmieniona nazwa)", - "renamed file" : "zmieniona nazwa pliku", "Failed to authorize" : "Błąd autoryzacji", "Invalid folder path" : "Nieprawidłowa ścieżka katalogu", "Folder not found" : "Nie znaleziono katalogu", diff --git a/apps/files/l10n/pt_BR.js b/apps/files/l10n/pt_BR.js index 27571901b39..f01729f16d7 100644 --- a/apps/files/l10n/pt_BR.js +++ b/apps/files/l10n/pt_BR.js @@ -43,8 +43,6 @@ OC.L10N.register( "Files" : "Arquivos", "A file or folder has been <strong>changed</strong>" : "Um arquivo ou pasta foi <strong>modificado</strong>", "A favorite file or folder has been <strong>changed</strong>" : "Um arquivo ou pasta favorita foi <strong>modificado</strong>", - "%1$s (renamed)" : "%1$s (renomeado)", - "renamed file" : "arquivo renomeado", "Failed to authorize" : "Falha ao autorizar", "Invalid folder path" : "Caminho de pasta inválido", "Folder not found" : "Pasta não encontrada", @@ -185,7 +183,7 @@ OC.L10N.register( "Clipboard is not available" : "A área de transferência não está disponível", "WebDAV URL copied to clipboard" : "URL de WebDAV copiado para a área de transferência", "Sort favorites first" : "Ordenar favoritos primeiro", - "Sort folders before files" : "Classifique as pastas antes dos arquivos", + "Sort folders before files" : "Ordenar pastas antes de arquivos", "Show hidden files" : "Mostrar arquivos ocultos", "Crop image previews" : "Cortar visualizações de imagem", "Enable the grid view" : "Ativar a visualização em grade", @@ -324,7 +322,7 @@ OC.L10N.register( "Some files could not be moved" : "Alguns arquivos não puderam ser movidos", "Files copied successfully" : "Arquivos copiados com sucesso", "Files moved successfully" : "Arquivos movidos com sucesso", - "Conflicts resolution skipped" : "Resolução de conflitos ignorada", + "Conflicts resolution skipped" : "Resolução de conflitos pulada", "Upload cancelled" : "Upload cancelado", "This operation is forbidden" : "Esta operação é proibida", "This directory is unavailable, please check the logs or contact the administrator" : "Este diretório não está disponível, por favor verifique os logs ou contacte o administrador", @@ -458,7 +456,7 @@ OC.L10N.register( "Favor" : "Favor", "Not favored" : "Não favoritado", "Submitting fields…" : "Enviando campos…", - "Filter filenames…" : "Filtrar nomes de arquivos...", + "Filter filenames…" : "Filtrar nomes de arquivos…", "{count} files could not be converted" : "{count} arquivos não puderam ser convertidos", "{count} files successfully converted" : "{count} arquivos convertidos com sucesso" }, diff --git a/apps/files/l10n/pt_BR.json b/apps/files/l10n/pt_BR.json index fcf3d2d7425..2f4353ef24d 100644 --- a/apps/files/l10n/pt_BR.json +++ b/apps/files/l10n/pt_BR.json @@ -41,8 +41,6 @@ "Files" : "Arquivos", "A file or folder has been <strong>changed</strong>" : "Um arquivo ou pasta foi <strong>modificado</strong>", "A favorite file or folder has been <strong>changed</strong>" : "Um arquivo ou pasta favorita foi <strong>modificado</strong>", - "%1$s (renamed)" : "%1$s (renomeado)", - "renamed file" : "arquivo renomeado", "Failed to authorize" : "Falha ao autorizar", "Invalid folder path" : "Caminho de pasta inválido", "Folder not found" : "Pasta não encontrada", @@ -183,7 +181,7 @@ "Clipboard is not available" : "A área de transferência não está disponível", "WebDAV URL copied to clipboard" : "URL de WebDAV copiado para a área de transferência", "Sort favorites first" : "Ordenar favoritos primeiro", - "Sort folders before files" : "Classifique as pastas antes dos arquivos", + "Sort folders before files" : "Ordenar pastas antes de arquivos", "Show hidden files" : "Mostrar arquivos ocultos", "Crop image previews" : "Cortar visualizações de imagem", "Enable the grid view" : "Ativar a visualização em grade", @@ -322,7 +320,7 @@ "Some files could not be moved" : "Alguns arquivos não puderam ser movidos", "Files copied successfully" : "Arquivos copiados com sucesso", "Files moved successfully" : "Arquivos movidos com sucesso", - "Conflicts resolution skipped" : "Resolução de conflitos ignorada", + "Conflicts resolution skipped" : "Resolução de conflitos pulada", "Upload cancelled" : "Upload cancelado", "This operation is forbidden" : "Esta operação é proibida", "This directory is unavailable, please check the logs or contact the administrator" : "Este diretório não está disponível, por favor verifique os logs ou contacte o administrador", @@ -456,7 +454,7 @@ "Favor" : "Favor", "Not favored" : "Não favoritado", "Submitting fields…" : "Enviando campos…", - "Filter filenames…" : "Filtrar nomes de arquivos...", + "Filter filenames…" : "Filtrar nomes de arquivos…", "{count} files could not be converted" : "{count} arquivos não puderam ser convertidos", "{count} files successfully converted" : "{count} arquivos convertidos com sucesso" },"pluralForm" :"nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;" diff --git a/apps/files/l10n/sr.js b/apps/files/l10n/sr.js index 5be69357590..1d332b4f0e3 100644 --- a/apps/files/l10n/sr.js +++ b/apps/files/l10n/sr.js @@ -43,8 +43,6 @@ OC.L10N.register( "Files" : "Фајлови", "A file or folder has been <strong>changed</strong>" : "Фајл или фасцикла су <strong>измењени</strong>", "A favorite file or folder has been <strong>changed</strong>" : "Омиљени фајл или фасцикла су <strong>измењени</strong>", - "%1$s (renamed)" : "%1$s (преименован)", - "renamed file" : "преименован фајл", "Failed to authorize" : "Није успело добијање дозволе", "Invalid folder path" : "Неисправна путања фолдера", "Folder not found" : "Није пронађен фолдер", diff --git a/apps/files/l10n/sr.json b/apps/files/l10n/sr.json index 52c41a7612e..50619b151ef 100644 --- a/apps/files/l10n/sr.json +++ b/apps/files/l10n/sr.json @@ -41,8 +41,6 @@ "Files" : "Фајлови", "A file or folder has been <strong>changed</strong>" : "Фајл или фасцикла су <strong>измењени</strong>", "A favorite file or folder has been <strong>changed</strong>" : "Омиљени фајл или фасцикла су <strong>измењени</strong>", - "%1$s (renamed)" : "%1$s (преименован)", - "renamed file" : "преименован фајл", "Failed to authorize" : "Није успело добијање дозволе", "Invalid folder path" : "Неисправна путања фолдера", "Folder not found" : "Није пронађен фолдер", diff --git a/apps/files/l10n/sv.js b/apps/files/l10n/sv.js index d7c920ed474..3dfba5c05b8 100644 --- a/apps/files/l10n/sv.js +++ b/apps/files/l10n/sv.js @@ -43,8 +43,6 @@ OC.L10N.register( "Files" : "Filer", "A file or folder has been <strong>changed</strong>" : "En ny fil eller mapp har blivit <strong>ändrad</strong>", "A favorite file or folder has been <strong>changed</strong>" : "En favorit-fil eller mapp har blivit <strong>ändrad</strong>", - "%1$s (renamed)" : "%1$s (omdöpt)", - "renamed file" : "omdöpt fil", "Failed to authorize" : "Det gick inte att auktorisera", "Invalid folder path" : "Ogiltig mappsökväg", "Folder not found" : "Mappen hittades inte", diff --git a/apps/files/l10n/sv.json b/apps/files/l10n/sv.json index 79cf7d6f062..868705b2f7f 100644 --- a/apps/files/l10n/sv.json +++ b/apps/files/l10n/sv.json @@ -41,8 +41,6 @@ "Files" : "Filer", "A file or folder has been <strong>changed</strong>" : "En ny fil eller mapp har blivit <strong>ändrad</strong>", "A favorite file or folder has been <strong>changed</strong>" : "En favorit-fil eller mapp har blivit <strong>ändrad</strong>", - "%1$s (renamed)" : "%1$s (omdöpt)", - "renamed file" : "omdöpt fil", "Failed to authorize" : "Det gick inte att auktorisera", "Invalid folder path" : "Ogiltig mappsökväg", "Folder not found" : "Mappen hittades inte", diff --git a/apps/files/l10n/zh_CN.js b/apps/files/l10n/zh_CN.js index 003a12b2320..d8cc70d4ce2 100644 --- a/apps/files/l10n/zh_CN.js +++ b/apps/files/l10n/zh_CN.js @@ -43,8 +43,6 @@ OC.L10N.register( "Files" : "文件", "A file or folder has been <strong>changed</strong>" : "文件或文件夹已经被<strong>修改</strong>", "A favorite file or folder has been <strong>changed</strong>" : "一个收藏的文件或文件夹已经被<strong>修改</strong>", - "%1$s (renamed)" : "%1$s(已重命名)", - "renamed file" : "已重命名文件", "Failed to authorize" : "授权失败", "Invalid folder path" : "无效文件夹路径", "Folder not found" : "未找到文件夹", diff --git a/apps/files/l10n/zh_CN.json b/apps/files/l10n/zh_CN.json index aab018cfab8..e49e6b9f37e 100644 --- a/apps/files/l10n/zh_CN.json +++ b/apps/files/l10n/zh_CN.json @@ -41,8 +41,6 @@ "Files" : "文件", "A file or folder has been <strong>changed</strong>" : "文件或文件夹已经被<strong>修改</strong>", "A favorite file or folder has been <strong>changed</strong>" : "一个收藏的文件或文件夹已经被<strong>修改</strong>", - "%1$s (renamed)" : "%1$s(已重命名)", - "renamed file" : "已重命名文件", "Failed to authorize" : "授权失败", "Invalid folder path" : "无效文件夹路径", "Folder not found" : "未找到文件夹", diff --git a/apps/files/l10n/zh_HK.js b/apps/files/l10n/zh_HK.js index 85ce0a40cc1..b4c540e5ace 100644 --- a/apps/files/l10n/zh_HK.js +++ b/apps/files/l10n/zh_HK.js @@ -43,8 +43,6 @@ OC.L10N.register( "Files" : "檔案", "A file or folder has been <strong>changed</strong>" : "檔案或資料夾有所<strong>更改</strong>", "A favorite file or folder has been <strong>changed</strong>" : "收藏的檔案或資料夾有所<strong>更改</strong>", - "%1$s (renamed)" : "%1$s(已重新命名)", - "renamed file" : "已重新命名的檔案", "Failed to authorize" : "無法授權", "Invalid folder path" : "無效的資料夾路徑", "Folder not found" : "找不到資料夾", diff --git a/apps/files/l10n/zh_HK.json b/apps/files/l10n/zh_HK.json index b65a2360b35..a3be5020acb 100644 --- a/apps/files/l10n/zh_HK.json +++ b/apps/files/l10n/zh_HK.json @@ -41,8 +41,6 @@ "Files" : "檔案", "A file or folder has been <strong>changed</strong>" : "檔案或資料夾有所<strong>更改</strong>", "A favorite file or folder has been <strong>changed</strong>" : "收藏的檔案或資料夾有所<strong>更改</strong>", - "%1$s (renamed)" : "%1$s(已重新命名)", - "renamed file" : "已重新命名的檔案", "Failed to authorize" : "無法授權", "Invalid folder path" : "無效的資料夾路徑", "Folder not found" : "找不到資料夾", diff --git a/apps/files/l10n/zh_TW.js b/apps/files/l10n/zh_TW.js index 40cdfe0c710..1f1bb560d91 100644 --- a/apps/files/l10n/zh_TW.js +++ b/apps/files/l10n/zh_TW.js @@ -43,8 +43,6 @@ OC.L10N.register( "Files" : "檔案", "A file or folder has been <strong>changed</strong>" : "檔案或資料夾已被<strong>變更</strong>", "A favorite file or folder has been <strong>changed</strong>" : "一個喜愛的檔案或資料夾已<strong>變更</strong>", - "%1$s (renamed)" : "%1$s(已重新命名)", - "renamed file" : "已重新命名檔案", "Failed to authorize" : "授權失敗", "Invalid folder path" : "無效的資料夾路徑", "Folder not found" : "找不到資料夾", diff --git a/apps/files/l10n/zh_TW.json b/apps/files/l10n/zh_TW.json index 543e4cc4e00..4fc43ecd2ff 100644 --- a/apps/files/l10n/zh_TW.json +++ b/apps/files/l10n/zh_TW.json @@ -41,8 +41,6 @@ "Files" : "檔案", "A file or folder has been <strong>changed</strong>" : "檔案或資料夾已被<strong>變更</strong>", "A favorite file or folder has been <strong>changed</strong>" : "一個喜愛的檔案或資料夾已<strong>變更</strong>", - "%1$s (renamed)" : "%1$s(已重新命名)", - "renamed file" : "已重新命名檔案", "Failed to authorize" : "授權失敗", "Invalid folder path" : "無效的資料夾路徑", "Folder not found" : "找不到資料夾", diff --git a/apps/files/lib/Command/TransferOwnership.php b/apps/files/lib/Command/TransferOwnership.php index edc73e62c38..104a8fb4985 100644 --- a/apps/files/lib/Command/TransferOwnership.php +++ b/apps/files/lib/Command/TransferOwnership.php @@ -11,20 +11,26 @@ namespace OCA\Files\Command; use OCA\Files\Exception\TransferOwnershipException; use OCA\Files\Service\OwnershipTransferService; +use OCA\Files_External\Config\ConfigAdapter; +use OCP\Files\Mount\IMountManager; +use OCP\Files\Mount\IMountPoint; use OCP\IConfig; use OCP\IUser; use OCP\IUserManager; use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\QuestionHelper; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\ConfirmationQuestion; class TransferOwnership extends Command { public function __construct( private IUserManager $userManager, private OwnershipTransferService $transferService, private IConfig $config, + private IMountManager $mountManager, ) { parent::__construct(); } @@ -60,6 +66,16 @@ class TransferOwnership extends Command { InputOption::VALUE_OPTIONAL, 'transfer incoming user file shares to destination user. Usage: --transfer-incoming-shares=1 (value required)', '2' + )->addOption( + 'include-external-storage', + null, + InputOption::VALUE_NONE, + 'include files on external storages, this will _not_ setup an external storage for the target user, but instead moves all the files from the external storages into the target users home directory', + )->addOption( + 'force-include-external-storage', + null, + InputOption::VALUE_NONE, + 'don\'t ask for confirmation for transferring external storages', ); } @@ -87,6 +103,31 @@ class TransferOwnership extends Command { return self::FAILURE; } + $path = ltrim($input->getOption('path'), '/'); + $includeExternalStorage = $input->getOption('include-external-storage'); + if ($includeExternalStorage) { + $mounts = $this->mountManager->findIn('/' . rtrim($sourceUserObject->getUID() . '/files/' . $path, '/')); + /** @var IMountPoint[] $mounts */ + $mounts = array_filter($mounts, fn ($mount) => $mount->getMountProvider() === ConfigAdapter::class); + if (count($mounts) > 0) { + $output->writeln(count($mounts) . ' external storages will be transferred:'); + foreach ($mounts as $mount) { + $output->writeln(' - <info>' . $mount->getMountPoint() . '</info>'); + } + $output->writeln(''); + $output->writeln('<comment>Any other users with access to these external storages will lose access to the files.</comment>'); + $output->writeln(''); + if (!$input->getOption('force-include-external-storage')) { + /** @var QuestionHelper $helper */ + $helper = $this->getHelper('question'); + $question = new ConfirmationQuestion('Are you sure you want to transfer external storages? (y/N) ', false); + if (!$helper->ask($input, $output, $question)) { + return self::FAILURE; + } + } + } + } + try { $includeIncomingArgument = $input->getOption('transfer-incoming-shares'); @@ -112,11 +153,12 @@ class TransferOwnership extends Command { $this->transferService->transfer( $sourceUserObject, $destinationUserObject, - ltrim($input->getOption('path'), '/'), + $path, $output, $input->getOption('move') === true, false, - $includeIncoming + $includeIncoming, + $includeExternalStorage, ); } catch (TransferOwnershipException $e) { $output->writeln('<error>' . $e->getMessage() . '</error>'); diff --git a/apps/files/lib/Service/OwnershipTransferService.php b/apps/files/lib/Service/OwnershipTransferService.php index 7f6681a9b89..b3a36ee13e5 100644 --- a/apps/files/lib/Service/OwnershipTransferService.php +++ b/apps/files/lib/Service/OwnershipTransferService.php @@ -15,10 +15,11 @@ use OC\Files\View; use OC\User\NoUserException; use OCA\Encryption\Util; use OCA\Files\Exception\TransferOwnershipException; +use OCA\Files_External\Config\ConfigAdapter; use OCP\Encryption\IManager as IEncryptionManager; +use OCP\Files\Config\IHomeMountProvider; use OCP\Files\Config\IUserMountCache; use OCP\Files\FileInfo; -use OCP\Files\IHomeStorage; use OCP\Files\InvalidPathException; use OCP\Files\IRootFolder; use OCP\Files\Mount\IMountManager; @@ -70,6 +71,7 @@ class OwnershipTransferService { bool $move = false, bool $firstLogin = false, bool $transferIncomingShares = false, + bool $includeExternalStorage = false, ): void { $output = $output ?? new NullOutput(); $sourceUid = $sourceUser->getUID(); @@ -149,7 +151,8 @@ class OwnershipTransferService { $sourcePath, $finalTarget, $view, - $output + $output, + $includeExternalStorage, ); $sizeDifference = $sourceSize - $view->getFileInfo($finalTarget)->getSize(); @@ -215,11 +218,14 @@ class OwnershipTransferService { * * @throws TransferOwnershipException */ - protected function analyse(string $sourceUid, + protected function analyse( + string $sourceUid, string $destinationUid, string $sourcePath, View $view, - OutputInterface $output): void { + OutputInterface $output, + bool $includeExternalStorage = false, + ): void { $output->writeln('Validating quota'); $sourceFileInfo = $view->getFileInfo($sourcePath, false); if ($sourceFileInfo === false) { @@ -247,17 +253,22 @@ class OwnershipTransferService { $encryptedFiles[] = $sourceFileInfo; } else { $this->walkFiles($view, $sourcePath, - function (FileInfo $fileInfo) use ($progress, $masterKeyEnabled, &$encryptedFiles) { + function (FileInfo $fileInfo) use ($progress, $masterKeyEnabled, &$encryptedFiles, $includeExternalStorage) { if ($fileInfo->getType() === FileInfo::TYPE_FOLDER) { + $mount = $fileInfo->getMountPoint(); // only analyze into folders from main storage, - if (!$fileInfo->getStorage()->instanceOfStorage(IHomeStorage::class)) { + if ( + $mount->getMountProvider() instanceof IHomeMountProvider || + ($includeExternalStorage && $mount->getMountProvider() instanceof ConfigAdapter) + ) { + if ($fileInfo->isEncrypted()) { + /* Encrypted folder means e2ee encrypted, we cannot transfer it */ + $encryptedFiles[] = $fileInfo; + } + return true; + } else { return false; } - if ($fileInfo->isEncrypted()) { - /* Encrypted folder means e2ee encrypted, we cannot transfer it */ - $encryptedFiles[] = $fileInfo; - } - return true; } $progress->advance(); if ($fileInfo->isEncrypted() && !$masterKeyEnabled) { @@ -399,11 +410,14 @@ class OwnershipTransferService { /** * @throws TransferOwnershipException */ - protected function transferFiles(string $sourceUid, + protected function transferFiles( + string $sourceUid, string $sourcePath, string $finalTarget, View $view, - OutputInterface $output): void { + OutputInterface $output, + bool $includeExternalStorage, + ): void { $output->writeln("Transferring files to $finalTarget ..."); // This change will help user to transfer the folder specified using --path option. @@ -412,15 +426,50 @@ class OwnershipTransferService { $view->mkdir($finalTarget); $finalTarget = $finalTarget . '/' . basename($sourcePath); } - if ($view->rename($sourcePath, $finalTarget, ['checkSubMounts' => false]) === false) { - throw new TransferOwnershipException('Could not transfer files.', 1); + $sourceInfo = $view->getFileInfo($sourcePath); + + /// handle the external storages mounted at the root, or the admin specifying an external storage with --path + if ($sourceInfo->getInternalPath() === '' && $includeExternalStorage) { + $this->moveMountContents($view, $sourcePath, $finalTarget); + } else { + if ($view->rename($sourcePath, $finalTarget, ['checkSubMounts' => false]) === false) { + throw new TransferOwnershipException('Could not transfer files.', 1); + } + } + + if ($includeExternalStorage) { + $nestedMounts = $this->mountManager->findIn($sourcePath); + foreach ($nestedMounts as $mount) { + if ($mount->getMountProvider() === ConfigAdapter::class) { + $relativePath = substr(trim($mount->getMountPoint(), '/'), strlen($sourcePath)); + $this->moveMountContents($view, $mount->getMountPoint(), $finalTarget . $relativePath); + } + } } + if (!is_dir("$sourceUid/files")) { // because the files folder is moved away we need to recreate it $view->mkdir("$sourceUid/files"); } } + private function moveMountContents(View $rootView, string $source, string $target) { + if ($rootView->copy($source, $target)) { + // just doing `rmdir` on the mountpoint would cause it to try and unmount the storage + // we need to empty the contents instead + $content = $rootView->getDirectoryContent($source); + foreach ($content as $item) { + if ($item->getType() === FileInfo::TYPE_FOLDER) { + $rootView->rmdir($item->getPath()); + } else { + $rootView->unlink($item->getPath()); + } + } + } else { + throw new TransferOwnershipException("Could not transfer $source to $target"); + } + } + /** * @param string $targetLocation New location of the transfered node * @param array<array{share: IShare, suffix: string}> $shares previously collected share information diff --git a/apps/files_external/l10n/de.js b/apps/files_external/l10n/de.js index 719065c6e6a..1ae634dfb0e 100644 --- a/apps/files_external/l10n/de.js +++ b/apps/files_external/l10n/de.js @@ -33,7 +33,7 @@ OC.L10N.register( "Client ID" : "Client-ID", "Client secret" : "Geheime Zeichenkette des Clients", "OpenStack v2" : "OpenStack v2", - "Login" : "Anmelden", + "Login" : "Anmeldename", "Password" : "Passwort", "Tenant name" : "Name des Mieters", "Identity endpoint URL" : "Identität Endpunkt-URL", @@ -117,7 +117,7 @@ OC.L10N.register( "External mount error" : "Fehler beim Einbinden des externen Speichers", "List of external storage." : "Liste der externen Speicher.", "There is no external storage configured. You can configure them in your Personal settings." : "Es ist kein externer Speicher eingerichtet. Du kannst diese in deinen persönlichen Einstellungen konfigurieren.", - "There is no external storage configured and you don't have the permission to configure them." : "Es ist kein externer Speicher eingerichtet und du hast nicht die Berechtigung, einen solchen einzurichten.", + "There is no external storage configured and you don't have the permission to configure them." : "Es ist kein externer Speicher eingerichtet oder du hast keine Berechtigung, einen solchen einzurichten.", "No external storage" : "Kein externer Speicher", "Storage type" : "Speichertyp", "Unknown" : "Unbekannt", @@ -146,7 +146,7 @@ OC.L10N.register( "Save" : "Speichern", "Failed to save global credentials" : "Globale Anmeldeinformationen konnten nicht gespeichert werden", "Failed to save global credentials: {message}" : "Globale Anmeldeinformationen konnten nicht gespeichert werden: {message}", - "No external storage configured or you don't have the permission to configure them" : "Es ist kein externer Speicher eingerichtet oder du hast keine Berechtigung dies zu tun.", + "No external storage configured or you don't have the permission to configure them" : "Es ist kein externer Speicher eingerichtet oder du hast keine Berechtigung, diesen einzurichten.", "Open documentation" : "Dokumentation öffnen", "External storage enables you to mount external storage services and devices as secondary Nextcloud storage devices. You may also allow people to mount their own external storage services." : "Externer Speicher erlaubt es, externe Speicherdienste und Geräte als Zweitspeicher-Geräte in Nextcloud einzubinden. Es kann Personen erlaubt werden eigene externe Speicherdienste einzubinden.", "Folder name" : "Ordnername", @@ -157,6 +157,6 @@ OC.L10N.register( "All people" : "Alle Personen", "Advanced settings" : "Erweiterte Einstellungen", "Allow people to mount external storage" : "Personen erlauben, externen Speicher einzubinden", - "Global credentials can be used to authenticate with multiple external storages that have the same credentials." : "Globale Anmeldeinformationen können zur Anmeldung bei mehreren externen Speichern mit denselben Anmeldeinformationen verwendet werden." + "Global credentials can be used to authenticate with multiple external storages that have the same credentials." : "Globale Anmeldeinformationen können zur Authentifizierung bei mehreren externen Speichern verwendet werden, die dieselben Anmeldeinformationen haben." }, "nplurals=2; plural=(n != 1);"); diff --git a/apps/files_external/l10n/de.json b/apps/files_external/l10n/de.json index d6448798ae4..86d08fb5c73 100644 --- a/apps/files_external/l10n/de.json +++ b/apps/files_external/l10n/de.json @@ -31,7 +31,7 @@ "Client ID" : "Client-ID", "Client secret" : "Geheime Zeichenkette des Clients", "OpenStack v2" : "OpenStack v2", - "Login" : "Anmelden", + "Login" : "Anmeldename", "Password" : "Passwort", "Tenant name" : "Name des Mieters", "Identity endpoint URL" : "Identität Endpunkt-URL", @@ -115,7 +115,7 @@ "External mount error" : "Fehler beim Einbinden des externen Speichers", "List of external storage." : "Liste der externen Speicher.", "There is no external storage configured. You can configure them in your Personal settings." : "Es ist kein externer Speicher eingerichtet. Du kannst diese in deinen persönlichen Einstellungen konfigurieren.", - "There is no external storage configured and you don't have the permission to configure them." : "Es ist kein externer Speicher eingerichtet und du hast nicht die Berechtigung, einen solchen einzurichten.", + "There is no external storage configured and you don't have the permission to configure them." : "Es ist kein externer Speicher eingerichtet oder du hast keine Berechtigung, einen solchen einzurichten.", "No external storage" : "Kein externer Speicher", "Storage type" : "Speichertyp", "Unknown" : "Unbekannt", @@ -144,7 +144,7 @@ "Save" : "Speichern", "Failed to save global credentials" : "Globale Anmeldeinformationen konnten nicht gespeichert werden", "Failed to save global credentials: {message}" : "Globale Anmeldeinformationen konnten nicht gespeichert werden: {message}", - "No external storage configured or you don't have the permission to configure them" : "Es ist kein externer Speicher eingerichtet oder du hast keine Berechtigung dies zu tun.", + "No external storage configured or you don't have the permission to configure them" : "Es ist kein externer Speicher eingerichtet oder du hast keine Berechtigung, diesen einzurichten.", "Open documentation" : "Dokumentation öffnen", "External storage enables you to mount external storage services and devices as secondary Nextcloud storage devices. You may also allow people to mount their own external storage services." : "Externer Speicher erlaubt es, externe Speicherdienste und Geräte als Zweitspeicher-Geräte in Nextcloud einzubinden. Es kann Personen erlaubt werden eigene externe Speicherdienste einzubinden.", "Folder name" : "Ordnername", @@ -155,6 +155,6 @@ "All people" : "Alle Personen", "Advanced settings" : "Erweiterte Einstellungen", "Allow people to mount external storage" : "Personen erlauben, externen Speicher einzubinden", - "Global credentials can be used to authenticate with multiple external storages that have the same credentials." : "Globale Anmeldeinformationen können zur Anmeldung bei mehreren externen Speichern mit denselben Anmeldeinformationen verwendet werden." + "Global credentials can be used to authenticate with multiple external storages that have the same credentials." : "Globale Anmeldeinformationen können zur Authentifizierung bei mehreren externen Speichern verwendet werden, die dieselben Anmeldeinformationen haben." },"pluralForm" :"nplurals=2; plural=(n != 1);" }
\ No newline at end of file diff --git a/apps/files_external/l10n/de_DE.js b/apps/files_external/l10n/de_DE.js index d0541618efe..dfe6f0bc387 100644 --- a/apps/files_external/l10n/de_DE.js +++ b/apps/files_external/l10n/de_DE.js @@ -33,7 +33,7 @@ OC.L10N.register( "Client ID" : "Client-ID", "Client secret" : "Geheime Zeichenkette des Clients", "OpenStack v2" : "OpenStack v2", - "Login" : "Anmelden", + "Login" : "Anmeldename", "Password" : "Passwort", "Tenant name" : "Name des Mieters", "Identity endpoint URL" : "Identität Endpunkt-URL", @@ -117,7 +117,7 @@ OC.L10N.register( "External mount error" : "Fehler beim Einbinden des externen Speichers", "List of external storage." : "Liste der externen Speicher.", "There is no external storage configured. You can configure them in your Personal settings." : "Es ist kein externer Speicher eingerichtet. Sie können diese in Ihren persönlichen Einstellungen konfigurieren.", - "There is no external storage configured and you don't have the permission to configure them." : "Es ist kein externer Speicher eingerichtet und Sie haben nicht die Berechtigung, einen solchen einzurichten.", + "There is no external storage configured and you don't have the permission to configure them." : "Es ist kein externer Speicher eingerichtet und Sie haben keine Berechtigung, einen solchen einzurichten.", "No external storage" : "Kein externer Speicher", "Storage type" : "Speichertyp", "Unknown" : "Unbekannt", @@ -146,7 +146,7 @@ OC.L10N.register( "Save" : "Speichern", "Failed to save global credentials" : "Globale Anmeldeinformationen konnten nicht gespeichert werden", "Failed to save global credentials: {message}" : "Globale Anmeldeinformationen konnten nicht gespeichert werden: {message}", - "No external storage configured or you don't have the permission to configure them" : "Es ist kein externer Speicher eingerichtet oder Sie haben keine Berechtigung diesen einzurichten", + "No external storage configured or you don't have the permission to configure them" : "Es ist kein externer Speicher eingerichtet oder Sie haben keine Berechtigung, diesen einzurichten.", "Open documentation" : "Dokumentation öffnen", "External storage enables you to mount external storage services and devices as secondary Nextcloud storage devices. You may also allow people to mount their own external storage services." : "Externer Speicher erlaubt es, externe Speicherdienste und Geräte als Zweitspeicher-Geräte in Nextcloud einzubinden. Es kann Personen erlaubt werden eigene externe Speicherdienste einzubinden.", "Folder name" : "Ordnername", @@ -157,6 +157,6 @@ OC.L10N.register( "All people" : "Alle Personen", "Advanced settings" : "Erweiterte Einstellungen", "Allow people to mount external storage" : "Personen erlauben, externen Speicher einzubinden", - "Global credentials can be used to authenticate with multiple external storages that have the same credentials." : "Globale Zugangsdaten können für die Authentifizierung für mehrere externe Speicher verwendet werden, solange sie identische Zugangsdaten benötigen." + "Global credentials can be used to authenticate with multiple external storages that have the same credentials." : "Globale Anmeldeinformationen können zur Authentifizierung bei mehreren externen Speichern verwendet werden, die dieselben Anmeldeinformationen haben." }, "nplurals=2; plural=(n != 1);"); diff --git a/apps/files_external/l10n/de_DE.json b/apps/files_external/l10n/de_DE.json index e5fc8adb012..e51c610c829 100644 --- a/apps/files_external/l10n/de_DE.json +++ b/apps/files_external/l10n/de_DE.json @@ -31,7 +31,7 @@ "Client ID" : "Client-ID", "Client secret" : "Geheime Zeichenkette des Clients", "OpenStack v2" : "OpenStack v2", - "Login" : "Anmelden", + "Login" : "Anmeldename", "Password" : "Passwort", "Tenant name" : "Name des Mieters", "Identity endpoint URL" : "Identität Endpunkt-URL", @@ -115,7 +115,7 @@ "External mount error" : "Fehler beim Einbinden des externen Speichers", "List of external storage." : "Liste der externen Speicher.", "There is no external storage configured. You can configure them in your Personal settings." : "Es ist kein externer Speicher eingerichtet. Sie können diese in Ihren persönlichen Einstellungen konfigurieren.", - "There is no external storage configured and you don't have the permission to configure them." : "Es ist kein externer Speicher eingerichtet und Sie haben nicht die Berechtigung, einen solchen einzurichten.", + "There is no external storage configured and you don't have the permission to configure them." : "Es ist kein externer Speicher eingerichtet und Sie haben keine Berechtigung, einen solchen einzurichten.", "No external storage" : "Kein externer Speicher", "Storage type" : "Speichertyp", "Unknown" : "Unbekannt", @@ -144,7 +144,7 @@ "Save" : "Speichern", "Failed to save global credentials" : "Globale Anmeldeinformationen konnten nicht gespeichert werden", "Failed to save global credentials: {message}" : "Globale Anmeldeinformationen konnten nicht gespeichert werden: {message}", - "No external storage configured or you don't have the permission to configure them" : "Es ist kein externer Speicher eingerichtet oder Sie haben keine Berechtigung diesen einzurichten", + "No external storage configured or you don't have the permission to configure them" : "Es ist kein externer Speicher eingerichtet oder Sie haben keine Berechtigung, diesen einzurichten.", "Open documentation" : "Dokumentation öffnen", "External storage enables you to mount external storage services and devices as secondary Nextcloud storage devices. You may also allow people to mount their own external storage services." : "Externer Speicher erlaubt es, externe Speicherdienste und Geräte als Zweitspeicher-Geräte in Nextcloud einzubinden. Es kann Personen erlaubt werden eigene externe Speicherdienste einzubinden.", "Folder name" : "Ordnername", @@ -155,6 +155,6 @@ "All people" : "Alle Personen", "Advanced settings" : "Erweiterte Einstellungen", "Allow people to mount external storage" : "Personen erlauben, externen Speicher einzubinden", - "Global credentials can be used to authenticate with multiple external storages that have the same credentials." : "Globale Zugangsdaten können für die Authentifizierung für mehrere externe Speicher verwendet werden, solange sie identische Zugangsdaten benötigen." + "Global credentials can be used to authenticate with multiple external storages that have the same credentials." : "Globale Anmeldeinformationen können zur Authentifizierung bei mehreren externen Speichern verwendet werden, die dieselben Anmeldeinformationen haben." },"pluralForm" :"nplurals=2; plural=(n != 1);" }
\ No newline at end of file diff --git a/apps/files_sharing/l10n/de.js b/apps/files_sharing/l10n/de.js index f547ac5d350..f6aa0685ab2 100644 --- a/apps/files_sharing/l10n/de.js +++ b/apps/files_sharing/l10n/de.js @@ -54,7 +54,7 @@ OC.L10N.register( "{actor} removed you from the share named {file}" : "{actor} hat dich aus der Freigabe von {file} entfernt", "Share for file {file} with {user} expired" : "Freigabe für die Datei {file} mit {user} abgelaufen", "Share for file {file} expired" : "Freigabe für die Datei {file} abgelaufen", - "A file or folder shared by mail or by public link was <strong>downloaded</strong>" : "Öffentlich oder per E-Mail geteilte Datei oder Ordner wurde <strong>heruntergeladen</strong>", + "A file or folder shared by mail or by public link was <strong>downloaded</strong>" : "Öffentlich oder per E-Mail geteilte Datei oder geteilter Ordner wurde <strong>heruntergeladen</strong>", "Files have been <strong>uploaded</strong> to a folder shared by mail or by public link" : "Dateien wurden in einen per E-Mail oder über einen öffentlichen Link freigegebenen Ordner <strong>hochgeladen</strong>", "A file or folder was shared from <strong>another server</strong>" : "Eine Datei oder ein Ordner wurde von <strong>einem anderen Server</strong> geteilt", "Sharing" : "Teilen", @@ -356,10 +356,10 @@ OC.L10N.register( "List of files that you shared with others." : "Liste der Dateien, die du mit anderen geteilt hast", "Nothing shared yet" : "Noch nichts geteilt", "Files and folders you shared will show up here" : "Von dir geteilte Dateien und Ordner werden hier angezeigt", - "Shared by link" : "Geteilt über einen Link", + "Shared by link" : "Über einen Link geteilt", "List of files that are shared by link." : "Liste der Dateien, die mittels Link geteilt wurden.", "No shared links" : "Keine geteilten Links", - "Files and folders you shared by link will show up here" : "Von dir per Link geteilte Dateien werden hier angezeigt", + "Files and folders you shared by link will show up here" : "Von dir mittels Link geteilte Dateien und Ordner werden hier angezeigt werden", "File requests" : "Dateianfragen", "List of file requests." : "Liste der Dateianfragen.", "No file requests" : "Keine Dateianfragen", diff --git a/apps/files_sharing/l10n/de.json b/apps/files_sharing/l10n/de.json index ec1399b28fa..52b17773dbd 100644 --- a/apps/files_sharing/l10n/de.json +++ b/apps/files_sharing/l10n/de.json @@ -52,7 +52,7 @@ "{actor} removed you from the share named {file}" : "{actor} hat dich aus der Freigabe von {file} entfernt", "Share for file {file} with {user} expired" : "Freigabe für die Datei {file} mit {user} abgelaufen", "Share for file {file} expired" : "Freigabe für die Datei {file} abgelaufen", - "A file or folder shared by mail or by public link was <strong>downloaded</strong>" : "Öffentlich oder per E-Mail geteilte Datei oder Ordner wurde <strong>heruntergeladen</strong>", + "A file or folder shared by mail or by public link was <strong>downloaded</strong>" : "Öffentlich oder per E-Mail geteilte Datei oder geteilter Ordner wurde <strong>heruntergeladen</strong>", "Files have been <strong>uploaded</strong> to a folder shared by mail or by public link" : "Dateien wurden in einen per E-Mail oder über einen öffentlichen Link freigegebenen Ordner <strong>hochgeladen</strong>", "A file or folder was shared from <strong>another server</strong>" : "Eine Datei oder ein Ordner wurde von <strong>einem anderen Server</strong> geteilt", "Sharing" : "Teilen", @@ -354,10 +354,10 @@ "List of files that you shared with others." : "Liste der Dateien, die du mit anderen geteilt hast", "Nothing shared yet" : "Noch nichts geteilt", "Files and folders you shared will show up here" : "Von dir geteilte Dateien und Ordner werden hier angezeigt", - "Shared by link" : "Geteilt über einen Link", + "Shared by link" : "Über einen Link geteilt", "List of files that are shared by link." : "Liste der Dateien, die mittels Link geteilt wurden.", "No shared links" : "Keine geteilten Links", - "Files and folders you shared by link will show up here" : "Von dir per Link geteilte Dateien werden hier angezeigt", + "Files and folders you shared by link will show up here" : "Von dir mittels Link geteilte Dateien und Ordner werden hier angezeigt werden", "File requests" : "Dateianfragen", "List of file requests." : "Liste der Dateianfragen.", "No file requests" : "Keine Dateianfragen", diff --git a/apps/files_sharing/l10n/de_DE.js b/apps/files_sharing/l10n/de_DE.js index b8928cb36b9..1269c6a0cfb 100644 --- a/apps/files_sharing/l10n/de_DE.js +++ b/apps/files_sharing/l10n/de_DE.js @@ -54,7 +54,7 @@ OC.L10N.register( "{actor} removed you from the share named {file}" : "{actor} hat Sie aus der Freigabe von {file} entfernt", "Share for file {file} with {user} expired" : "Freigabe der Datei {file} für {user} abgelaufen", "Share for file {file} expired" : "Freigabe für die Datei {file} abgelaufen", - "A file or folder shared by mail or by public link was <strong>downloaded</strong>" : "Öffentlich oder per E-Mail geteilte Datei / Ordner wurde <strong>heruntergeladen</strong>", + "A file or folder shared by mail or by public link was <strong>downloaded</strong>" : "Öffentlich oder per E-Mail geteilte Datei oder geteilter Ordner wurde <strong>heruntergeladen</strong>", "Files have been <strong>uploaded</strong> to a folder shared by mail or by public link" : "Dateien wurden in einen per E-Mail oder über einen öffentlichen Link freigegebenen Ordner <strong>hochgeladen</strong>", "A file or folder was shared from <strong>another server</strong>" : "Eine Datei oder ein Ordner wurde von <strong>einem anderen Server</strong> geteilt", "Sharing" : "Teilen", @@ -356,7 +356,7 @@ OC.L10N.register( "List of files that you shared with others." : "Liste der Dateien, die Sie mit anderen geteilt haben.", "Nothing shared yet" : "Noch nichts geteilt", "Files and folders you shared will show up here" : "Von Ihnen geteilte Dateien und Ordner werden hier angezeigt werden", - "Shared by link" : "Geteilt über einen Link", + "Shared by link" : "Über einen Link geteilt", "List of files that are shared by link." : "Liste der Dateien, die mittels Link geteilt wurden.", "No shared links" : "Keine geteilten Links", "Files and folders you shared by link will show up here" : "Von Ihnen mittels Link geteilte Dateien und Ordner werden hier angezeigt werden", diff --git a/apps/files_sharing/l10n/de_DE.json b/apps/files_sharing/l10n/de_DE.json index 1a2c0380c70..0a61a725d3e 100644 --- a/apps/files_sharing/l10n/de_DE.json +++ b/apps/files_sharing/l10n/de_DE.json @@ -52,7 +52,7 @@ "{actor} removed you from the share named {file}" : "{actor} hat Sie aus der Freigabe von {file} entfernt", "Share for file {file} with {user} expired" : "Freigabe der Datei {file} für {user} abgelaufen", "Share for file {file} expired" : "Freigabe für die Datei {file} abgelaufen", - "A file or folder shared by mail or by public link was <strong>downloaded</strong>" : "Öffentlich oder per E-Mail geteilte Datei / Ordner wurde <strong>heruntergeladen</strong>", + "A file or folder shared by mail or by public link was <strong>downloaded</strong>" : "Öffentlich oder per E-Mail geteilte Datei oder geteilter Ordner wurde <strong>heruntergeladen</strong>", "Files have been <strong>uploaded</strong> to a folder shared by mail or by public link" : "Dateien wurden in einen per E-Mail oder über einen öffentlichen Link freigegebenen Ordner <strong>hochgeladen</strong>", "A file or folder was shared from <strong>another server</strong>" : "Eine Datei oder ein Ordner wurde von <strong>einem anderen Server</strong> geteilt", "Sharing" : "Teilen", @@ -354,7 +354,7 @@ "List of files that you shared with others." : "Liste der Dateien, die Sie mit anderen geteilt haben.", "Nothing shared yet" : "Noch nichts geteilt", "Files and folders you shared will show up here" : "Von Ihnen geteilte Dateien und Ordner werden hier angezeigt werden", - "Shared by link" : "Geteilt über einen Link", + "Shared by link" : "Über einen Link geteilt", "List of files that are shared by link." : "Liste der Dateien, die mittels Link geteilt wurden.", "No shared links" : "Keine geteilten Links", "Files and folders you shared by link will show up here" : "Von Ihnen mittels Link geteilte Dateien und Ordner werden hier angezeigt werden", diff --git a/apps/files_sharing/l10n/ga.js b/apps/files_sharing/l10n/ga.js index 0d41d4d1235..f8ddae9a8ea 100644 --- a/apps/files_sharing/l10n/ga.js +++ b/apps/files_sharing/l10n/ga.js @@ -313,7 +313,9 @@ OC.L10N.register( "Use this method to share files with individuals or teams within your organization. If the recipient already has access to the share but cannot locate it, you can send them the internal share link for easy access." : "Bain úsáid as an modh seo chun comhaid a roinnt le daoine aonair nó le foirne laistigh de d'eagraíocht. Má tá rochtain ag an bhfaighteoir ar an sciar cheana féin ach nach féidir leis í a aimsiú, is féidir leat an nasc scaire inmheánach a sheoladh chucu le go mbeidh rochtain éasca air.", "Use this method to share files with individuals or organizations outside your organization. Files and folders can be shared via public share links and email addresses. You can also share to other Nextcloud accounts hosted on different instances using their federated cloud ID." : "Bain úsáid as an modh seo chun comhaid a roinnt le daoine aonair nó le heagraíochtaí lasmuigh de d'eagraíocht. Is féidir comhaid agus fillteáin a roinnt trí naisc scaireanna poiblí agus seoltaí ríomhphoist. Is féidir leat a roinnt freisin le cuntais Nextcloud eile arna óstáil ar chásanna éagsúla ag baint úsáide as a n-ID néil cónasctha.", "Shares that are not part of the internal or external shares. This can be shares from apps or other sources." : "Scaireanna nach cuid de na scaireanna inmheánacha nó seachtracha iad. Is féidir gur scaireanna iad seo ó aipeanna nó ó fhoinsí eile.", + "Share with accounts, teams, federated cloud IDs" : "Comhroinn le cuntais, foirne, agus aitheantóirí scamall cónaidhme", "Share with accounts and teams" : "Roinn le cuntais agus foirne", + "Email, federated cloud ID" : "Ríomhphost, ID scamall cónaidhme", "Unable to load the shares list" : "Ní féidir an liosta scaireanna a lódáil", "Expires {relativetime}" : "In éag {relativetime}", "this share just expired." : "tá an sciar seo díreach imithe in éag.", @@ -422,6 +424,7 @@ OC.L10N.register( "You are not allowed to edit link shares that you don't own" : "Níl cead agat scaireanna naisc nach leatsa a chur in eagar", "_1 email address already added_::_{count} email addresses already added_" : ["1 seoladh ríomhphoist curtha leis cheana féin","{count} seoladh ríomhphoist curtha leis cheana","{count} seoladh ríomhphoist curtha leis cheana","{count} seoladh ríomhphoist curtha leis cheana","{count} seoladh ríomhphoist curtha leis cheana"], "_1 email address added_::_{count} email addresses added_" : ["Cuireadh 1 seoladh ríomhphoist leis","{count} seoladh ríomhphoist curtha leis","{count} seoladh ríomhphoist curtha leis","{count} seoladh ríomhphoist curtha leis","{count} seoladh ríomhphoist curtha leis"], + "Share with accounts, teams, federated cloud id" : "Comhroinn le cuntais, foirne, aitheantas scamall cónaidhme", "Email, federated cloud id" : "Ríomhphost, aitheantas scamall cónaidhme" }, "nplurals=5; plural=(n==1 ? 0 : n==2 ? 1 : n<7 ? 2 : n<11 ? 3 : 4);"); diff --git a/apps/files_sharing/l10n/ga.json b/apps/files_sharing/l10n/ga.json index 418aa49ccc3..f556e802f52 100644 --- a/apps/files_sharing/l10n/ga.json +++ b/apps/files_sharing/l10n/ga.json @@ -311,7 +311,9 @@ "Use this method to share files with individuals or teams within your organization. If the recipient already has access to the share but cannot locate it, you can send them the internal share link for easy access." : "Bain úsáid as an modh seo chun comhaid a roinnt le daoine aonair nó le foirne laistigh de d'eagraíocht. Má tá rochtain ag an bhfaighteoir ar an sciar cheana féin ach nach féidir leis í a aimsiú, is féidir leat an nasc scaire inmheánach a sheoladh chucu le go mbeidh rochtain éasca air.", "Use this method to share files with individuals or organizations outside your organization. Files and folders can be shared via public share links and email addresses. You can also share to other Nextcloud accounts hosted on different instances using their federated cloud ID." : "Bain úsáid as an modh seo chun comhaid a roinnt le daoine aonair nó le heagraíochtaí lasmuigh de d'eagraíocht. Is féidir comhaid agus fillteáin a roinnt trí naisc scaireanna poiblí agus seoltaí ríomhphoist. Is féidir leat a roinnt freisin le cuntais Nextcloud eile arna óstáil ar chásanna éagsúla ag baint úsáide as a n-ID néil cónasctha.", "Shares that are not part of the internal or external shares. This can be shares from apps or other sources." : "Scaireanna nach cuid de na scaireanna inmheánacha nó seachtracha iad. Is féidir gur scaireanna iad seo ó aipeanna nó ó fhoinsí eile.", + "Share with accounts, teams, federated cloud IDs" : "Comhroinn le cuntais, foirne, agus aitheantóirí scamall cónaidhme", "Share with accounts and teams" : "Roinn le cuntais agus foirne", + "Email, federated cloud ID" : "Ríomhphost, ID scamall cónaidhme", "Unable to load the shares list" : "Ní féidir an liosta scaireanna a lódáil", "Expires {relativetime}" : "In éag {relativetime}", "this share just expired." : "tá an sciar seo díreach imithe in éag.", @@ -420,6 +422,7 @@ "You are not allowed to edit link shares that you don't own" : "Níl cead agat scaireanna naisc nach leatsa a chur in eagar", "_1 email address already added_::_{count} email addresses already added_" : ["1 seoladh ríomhphoist curtha leis cheana féin","{count} seoladh ríomhphoist curtha leis cheana","{count} seoladh ríomhphoist curtha leis cheana","{count} seoladh ríomhphoist curtha leis cheana","{count} seoladh ríomhphoist curtha leis cheana"], "_1 email address added_::_{count} email addresses added_" : ["Cuireadh 1 seoladh ríomhphoist leis","{count} seoladh ríomhphoist curtha leis","{count} seoladh ríomhphoist curtha leis","{count} seoladh ríomhphoist curtha leis","{count} seoladh ríomhphoist curtha leis"], + "Share with accounts, teams, federated cloud id" : "Comhroinn le cuntais, foirne, aitheantas scamall cónaidhme", "Email, federated cloud id" : "Ríomhphost, aitheantas scamall cónaidhme" },"pluralForm" :"nplurals=5; plural=(n==1 ? 0 : n==2 ? 1 : n<7 ? 2 : n<11 ? 3 : 4);" }
\ No newline at end of file diff --git a/apps/files_sharing/lib/Controller/PublicPreviewController.php b/apps/files_sharing/lib/Controller/PublicPreviewController.php index 91dead57e80..16886e930fa 100644 --- a/apps/files_sharing/lib/Controller/PublicPreviewController.php +++ b/apps/files_sharing/lib/Controller/PublicPreviewController.php @@ -11,6 +11,7 @@ use OCP\AppFramework\Http\Attribute\OpenAPI; use OCP\AppFramework\Http\Attribute\PublicPage; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\FileDisplayResponse; +use OCP\AppFramework\Http\RedirectResponse; use OCP\AppFramework\PublicShareController; use OCP\Constants; use OCP\Files\Folder; @@ -18,6 +19,7 @@ use OCP\Files\NotFoundException; use OCP\IPreview; use OCP\IRequest; use OCP\ISession; +use OCP\Preview\IMimeIconProvider; use OCP\Share\Exceptions\ShareNotFound; use OCP\Share\IManager as ShareManager; use OCP\Share\IShare; @@ -33,6 +35,7 @@ class PublicPreviewController extends PublicShareController { private ShareManager $shareManager, ISession $session, private IPreview $previewManager, + private IMimeIconProvider $mimeIconProvider, ) { parent::__construct($appName, $request, $session); } @@ -63,9 +66,11 @@ class PublicPreviewController extends PublicShareController { * @param int $x Width of the preview * @param int $y Height of the preview * @param bool $a Whether to not crop the preview - * @return FileDisplayResponse<Http::STATUS_OK, array{Content-Type: string}>|DataResponse<Http::STATUS_BAD_REQUEST|Http::STATUS_FORBIDDEN|Http::STATUS_NOT_FOUND, list<empty>, array{}> + * @param bool $mimeFallback Whether to fallback to the mime icon if no preview is available + * @return FileDisplayResponse<Http::STATUS_OK, array{Content-Type: string}>|DataResponse<Http::STATUS_BAD_REQUEST|Http::STATUS_FORBIDDEN|Http::STATUS_NOT_FOUND, list<empty>, array{}>|RedirectResponse<Http::STATUS_SEE_OTHER, array{}> * * 200: Preview returned + * 303: Redirect to the mime icon url if mimeFallback is true * 400: Getting preview is not possible * 403: Getting preview is not allowed * 404: Share or preview not found @@ -79,6 +84,7 @@ class PublicPreviewController extends PublicShareController { int $x = 32, int $y = 32, $a = false, + bool $mimeFallback = false, ) { $cacheForSeconds = 60 * 60 * 24; // 1 day @@ -124,6 +130,12 @@ class PublicPreviewController extends PublicShareController { $response->cacheFor($cacheForSeconds); return $response; } catch (NotFoundException $e) { + // If we have no preview enabled, we can redirect to the mime icon if any + if ($mimeFallback) { + if ($url = $this->mimeIconProvider->getMimeIconUrl($file->getMimeType())) { + return new RedirectResponse($url); + } + } return new DataResponse([], Http::STATUS_NOT_FOUND); } catch (\InvalidArgumentException $e) { return new DataResponse([], Http::STATUS_BAD_REQUEST); diff --git a/apps/files_sharing/openapi.json b/apps/files_sharing/openapi.json index e70764a59f2..68420dde80e 100644 --- a/apps/files_sharing/openapi.json +++ b/apps/files_sharing/openapi.json @@ -1477,6 +1477,19 @@ 1 ] } + }, + { + "name": "mimeFallback", + "in": "query", + "description": "Whether to fallback to the mime icon if no preview is available", + "schema": { + "type": "integer", + "default": 0, + "enum": [ + 0, + 1 + ] + } } ], "responses": { @@ -1514,6 +1527,16 @@ "schema": {} } } + }, + "303": { + "description": "Redirect to the mime icon url if mimeFallback is true", + "headers": { + "Location": { + "schema": { + "type": "string" + } + } + } } } } diff --git a/apps/files_sharing/tests/Controller/PublicPreviewControllerTest.php b/apps/files_sharing/tests/Controller/PublicPreviewControllerTest.php index bd64cfc60b4..9750274ff62 100644 --- a/apps/files_sharing/tests/Controller/PublicPreviewControllerTest.php +++ b/apps/files_sharing/tests/Controller/PublicPreviewControllerTest.php @@ -18,6 +18,7 @@ use OCP\Files\SimpleFS\ISimpleFile; use OCP\IPreview; use OCP\IRequest; use OCP\ISession; +use OCP\Preview\IMimeIconProvider; use OCP\Share\Exceptions\ShareNotFound; use OCP\Share\IAttributes; use OCP\Share\IManager; @@ -52,7 +53,8 @@ class PublicPreviewControllerTest extends TestCase { $this->request, $this->shareManager, $this->createMock(ISession::class), - $this->previewManager + $this->previewManager, + $this->createMock(IMimeIconProvider::class), ); } diff --git a/apps/files_trashbin/l10n/de.js b/apps/files_trashbin/l10n/de.js index e5fc521180e..30f503f52d9 100644 --- a/apps/files_trashbin/l10n/de.js +++ b/apps/files_trashbin/l10n/de.js @@ -3,22 +3,22 @@ OC.L10N.register( { "restored" : "Wiederhergestellt", "Deleted files" : "Gelöschte Dateien", - "Deleted files and folders in the trash bin (may expire during export if you are low on storage space)" : "Gelöschte Dateien und Ordner im Papierkorb (können während des Exports gelöscht werden, wenn der Speicherplatz knapp wird)", - "This application enables people to restore files that were deleted from the system." : "Diese Anwendung ermöglicht es Benutzern, Dateien die vom System gelöscht wurden wieder herzustellen.", - "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." : "Diese Anwendung ermöglicht es Personen, Dateien wiederherzustellen, die aus dem System gelöscht wurden. Es zeigt eine Liste der gelöschten Dateien in der Web-Oberfläche an und bietet die Möglichkeit, diese gelöschten Dateien in die Verzeichnisse der Personen wiederherzustellen oder sie dauerhaft aus dem System zu entfernen. Beim Wiederherstellen einer Datei werden auch die zugehörigen Dateiversionen wiederhergestellt, sofern die Versionsanwendung aktiviert ist. Wenn eine Datei aus einer Freigabe gelöscht wird, kann sie auf dem gleichen Weg wiederhergestellt werden, ist jedoch nicht mehr geteilt. Standardmäßig verbleiben gelöschte Dateien 30 Tage lang im Papierkorb.\nUm zu verhindern, dass einem Konto der Speicherplatz ausgeht, nutzt die Anwendung für gelöschte Dateien maximal 50% des aktuell verfügbaren freien Kontingents. Wenn die gelöschten Dateien dieses Limit überschreiten, löscht die Anwendung die ältesten Dateien, bis sie unter dieses Limit fällt. Weitere Informationen findest du in der Dokumentation zu gelöschten Dateien.", + "Deleted files and folders in the trash bin (may expire during export if you are low on storage space)" : "Gelöschte Dateien und Ordner im Papierkorb (können während des Exports ablaufen, wenn der Speicherplatz knapp wird)", + "This application enables people to restore files that were deleted from the system." : "Diese Anwendung ermöglicht es Personen, Dateien wiederherzustellen, die vom System gelöscht wurden.", + "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." : "Diese Anwendung ermöglicht es Personen, Dateien wiederherzustellen, die aus dem System gelöscht wurden. Es zeigt eine Liste der gelöschten Dateien in der Web-Oberfläche an und bietet die Möglichkeit, diese gelöschten Dateien in die Verzeichnisse der Personen wiederherzustellen oder sie dauerhaft aus dem System zu entfernen. Beim Wiederherstellen einer Datei werden auch die zugehörigen Dateiversionen wiederhergestellt, sofern die Versionsanwendung aktiviert ist. Wenn eine Datei aus einer Freigabe gelöscht wird, kann sie auf dem gleichen Weg wiederhergestellt werden, ist jedoch nicht mehr geteilt. Standardmäßig verbleiben gelöschte Dateien 30 Tage lang im Papierkorb.\nUm zu verhindern, dass einem Konto der Speicherplatz ausgeht, nutzt die Anwendung für gelöschte Dateien maximal 50 % des aktuell verfügbaren freien Kontingents. Wenn die gelöschten Dateien dieses Limit überschreiten, löscht die Anwendung die ältesten Dateien, bis sie unter dieses Limit fällt. Weitere Informationen findest du in der Dokumentation zu gelöschten Dateien.", "Restore" : "Wiederherstellen", "Empty deleted files" : "Gelöschte Dateien leeren", "Confirm permanent deletion" : "Endgültiges Löschen bestätigen", - "Are you sure you want to permanently delete all files and folders in the trash? This cannot be undone." : "Möchtest Du wirklich alle Dateien und Ordner im Papierkorb endgültig löschen? Dies kann nicht rückgängig gemacht werden.", + "Are you sure you want to permanently delete all files and folders in the trash? This cannot be undone." : "Möchtest du wirklich alle Dateien und Ordner im Papierkorb endgültig löschen? Dies kann nicht rückgängig gemacht werden.", "Cancel" : "Abbrechen", "Deletion cancelled" : "Löschen abgebrochen", "Original location" : "Ursprünglicher Ort", "Deleted by" : "Gelöscht von", - "Deleted" : "gelöscht", + "Deleted" : "Gelöscht", "A long time ago" : "Vor langer Zeit", "Unknown" : "Unbekannt", "All files" : "Alle Dateien", - "You" : "Du", + "You" : "Dir", "List of files that have been deleted." : "Liste der gelöschten Dateien.", "No deleted files" : "Keine gelöschten Dateien", "Files and folders you have deleted will show up here" : "Die von dir gelöschten Dateien und Ordner werden hier angezeigt" diff --git a/apps/files_trashbin/l10n/de.json b/apps/files_trashbin/l10n/de.json index bfa65bb5d0d..9017b23b0ca 100644 --- a/apps/files_trashbin/l10n/de.json +++ b/apps/files_trashbin/l10n/de.json @@ -1,22 +1,22 @@ { "translations": { "restored" : "Wiederhergestellt", "Deleted files" : "Gelöschte Dateien", - "Deleted files and folders in the trash bin (may expire during export if you are low on storage space)" : "Gelöschte Dateien und Ordner im Papierkorb (können während des Exports gelöscht werden, wenn der Speicherplatz knapp wird)", - "This application enables people to restore files that were deleted from the system." : "Diese Anwendung ermöglicht es Benutzern, Dateien die vom System gelöscht wurden wieder herzustellen.", - "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." : "Diese Anwendung ermöglicht es Personen, Dateien wiederherzustellen, die aus dem System gelöscht wurden. Es zeigt eine Liste der gelöschten Dateien in der Web-Oberfläche an und bietet die Möglichkeit, diese gelöschten Dateien in die Verzeichnisse der Personen wiederherzustellen oder sie dauerhaft aus dem System zu entfernen. Beim Wiederherstellen einer Datei werden auch die zugehörigen Dateiversionen wiederhergestellt, sofern die Versionsanwendung aktiviert ist. Wenn eine Datei aus einer Freigabe gelöscht wird, kann sie auf dem gleichen Weg wiederhergestellt werden, ist jedoch nicht mehr geteilt. Standardmäßig verbleiben gelöschte Dateien 30 Tage lang im Papierkorb.\nUm zu verhindern, dass einem Konto der Speicherplatz ausgeht, nutzt die Anwendung für gelöschte Dateien maximal 50% des aktuell verfügbaren freien Kontingents. Wenn die gelöschten Dateien dieses Limit überschreiten, löscht die Anwendung die ältesten Dateien, bis sie unter dieses Limit fällt. Weitere Informationen findest du in der Dokumentation zu gelöschten Dateien.", + "Deleted files and folders in the trash bin (may expire during export if you are low on storage space)" : "Gelöschte Dateien und Ordner im Papierkorb (können während des Exports ablaufen, wenn der Speicherplatz knapp wird)", + "This application enables people to restore files that were deleted from the system." : "Diese Anwendung ermöglicht es Personen, Dateien wiederherzustellen, die vom System gelöscht wurden.", + "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." : "Diese Anwendung ermöglicht es Personen, Dateien wiederherzustellen, die aus dem System gelöscht wurden. Es zeigt eine Liste der gelöschten Dateien in der Web-Oberfläche an und bietet die Möglichkeit, diese gelöschten Dateien in die Verzeichnisse der Personen wiederherzustellen oder sie dauerhaft aus dem System zu entfernen. Beim Wiederherstellen einer Datei werden auch die zugehörigen Dateiversionen wiederhergestellt, sofern die Versionsanwendung aktiviert ist. Wenn eine Datei aus einer Freigabe gelöscht wird, kann sie auf dem gleichen Weg wiederhergestellt werden, ist jedoch nicht mehr geteilt. Standardmäßig verbleiben gelöschte Dateien 30 Tage lang im Papierkorb.\nUm zu verhindern, dass einem Konto der Speicherplatz ausgeht, nutzt die Anwendung für gelöschte Dateien maximal 50 % des aktuell verfügbaren freien Kontingents. Wenn die gelöschten Dateien dieses Limit überschreiten, löscht die Anwendung die ältesten Dateien, bis sie unter dieses Limit fällt. Weitere Informationen findest du in der Dokumentation zu gelöschten Dateien.", "Restore" : "Wiederherstellen", "Empty deleted files" : "Gelöschte Dateien leeren", "Confirm permanent deletion" : "Endgültiges Löschen bestätigen", - "Are you sure you want to permanently delete all files and folders in the trash? This cannot be undone." : "Möchtest Du wirklich alle Dateien und Ordner im Papierkorb endgültig löschen? Dies kann nicht rückgängig gemacht werden.", + "Are you sure you want to permanently delete all files and folders in the trash? This cannot be undone." : "Möchtest du wirklich alle Dateien und Ordner im Papierkorb endgültig löschen? Dies kann nicht rückgängig gemacht werden.", "Cancel" : "Abbrechen", "Deletion cancelled" : "Löschen abgebrochen", "Original location" : "Ursprünglicher Ort", "Deleted by" : "Gelöscht von", - "Deleted" : "gelöscht", + "Deleted" : "Gelöscht", "A long time ago" : "Vor langer Zeit", "Unknown" : "Unbekannt", "All files" : "Alle Dateien", - "You" : "Du", + "You" : "Dir", "List of files that have been deleted." : "Liste der gelöschten Dateien.", "No deleted files" : "Keine gelöschten Dateien", "Files and folders you have deleted will show up here" : "Die von dir gelöschten Dateien und Ordner werden hier angezeigt" diff --git a/apps/files_trashbin/l10n/de_DE.js b/apps/files_trashbin/l10n/de_DE.js index f790c296f4a..291f3733b02 100644 --- a/apps/files_trashbin/l10n/de_DE.js +++ b/apps/files_trashbin/l10n/de_DE.js @@ -4,8 +4,8 @@ OC.L10N.register( "restored" : "Wiederhergestellt", "Deleted files" : "Gelöschte Dateien", "Deleted files and folders in the trash bin (may expire during export if you are low on storage space)" : "Gelöschte Dateien und Ordner im Papierkorb (können während des Exports ablaufen, wenn der Speicherplatz knapp wird)", - "This application enables people to restore files that were deleted from the system." : "Diese Anwendung ermöglicht Personen, Dateien wiederherzustellen, die vom System gelöscht wurden.", - "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." : "Diese Anwendung ermöglicht es Personen, Dateien wiederherzustellen, die aus dem System gelöscht wurden. Es zeigt eine Liste der gelöschten Dateien in der Web-Oberfläche an und bietet die Möglichkeit, diese gelöschten Dateien in die Verzeichnisse der Personen wiederherzustellen oder sie dauerhaft aus dem System zu entfernen. Beim Wiederherstellen einer Datei werden auch die zugehörigen Dateiversionen wiederhergestellt, sofern die Versionsanwendung aktiviert ist. Wenn eine Datei aus einer Freigabe gelöscht wird, kann sie auf dem gleichen Weg wiederhergestellt werden, ist jedoch nicht mehr geteilt. Standardmäßig verbleiben gelöschte Dateien 30 Tage lang im Papierkorb.\nUm zu verhindern, dass einem Konto der Speicherplatz ausgeht, nutzt die Anwendung für gelöschte Dateien maximal 50% des aktuell verfügbaren freien Kontingents. Wenn die gelöschten Dateien dieses Limit überschreiten, löscht die Anwendung die ältesten Dateien, bis sie unter dieses Limit fällt. Weitere Informationen finden Sie in der Dokumentation zu gelöschten Dateien.", + "This application enables people to restore files that were deleted from the system." : "Diese Anwendung ermöglicht es Personen, Dateien wiederherzustellen, die vom System gelöscht wurden.", + "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." : "Diese Anwendung ermöglicht es Personen, Dateien wiederherzustellen, die aus dem System gelöscht wurden. Es zeigt eine Liste der gelöschten Dateien in der Web-Oberfläche an und bietet die Möglichkeit, diese gelöschten Dateien in die Verzeichnisse der Personen wiederherzustellen oder sie dauerhaft aus dem System zu entfernen. Beim Wiederherstellen einer Datei werden auch die zugehörigen Dateiversionen wiederhergestellt, sofern die Versionsanwendung aktiviert ist. Wenn eine Datei aus einer Freigabe gelöscht wird, kann sie auf dem gleichen Weg wiederhergestellt werden, ist jedoch nicht mehr geteilt. Standardmäßig verbleiben gelöschte Dateien 30 Tage lang im Papierkorb.\nUm zu verhindern, dass einem Konto der Speicherplatz ausgeht, nutzt die Anwendung für gelöschte Dateien maximal 50 % des aktuell verfügbaren freien Kontingents. Wenn die gelöschten Dateien dieses Limit überschreiten, löscht die Anwendung die ältesten Dateien, bis sie unter dieses Limit fällt. Weitere Informationen finden Sie in der Dokumentation zu gelöschten Dateien.", "Restore" : "Wiederherstellen", "Empty deleted files" : "Gelöschte Dateien leeren", "Confirm permanent deletion" : "Endgültiges Löschen bestätigen", @@ -18,7 +18,7 @@ OC.L10N.register( "A long time ago" : "Vor langer Zeit", "Unknown" : "Unbekannt", "All files" : "Alle Dateien", - "You" : "Sie", + "You" : "Ihnen", "List of files that have been deleted." : "Liste der gelöschten Dateien.", "No deleted files" : "Keine gelöschten Dateien", "Files and folders you have deleted will show up here" : "Die von Ihnen gelöschten Dateien und Ordner werden hier angezeigt" diff --git a/apps/files_trashbin/l10n/de_DE.json b/apps/files_trashbin/l10n/de_DE.json index 74b1b262e57..645f7a146a0 100644 --- a/apps/files_trashbin/l10n/de_DE.json +++ b/apps/files_trashbin/l10n/de_DE.json @@ -2,8 +2,8 @@ "restored" : "Wiederhergestellt", "Deleted files" : "Gelöschte Dateien", "Deleted files and folders in the trash bin (may expire during export if you are low on storage space)" : "Gelöschte Dateien und Ordner im Papierkorb (können während des Exports ablaufen, wenn der Speicherplatz knapp wird)", - "This application enables people to restore files that were deleted from the system." : "Diese Anwendung ermöglicht Personen, Dateien wiederherzustellen, die vom System gelöscht wurden.", - "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." : "Diese Anwendung ermöglicht es Personen, Dateien wiederherzustellen, die aus dem System gelöscht wurden. Es zeigt eine Liste der gelöschten Dateien in der Web-Oberfläche an und bietet die Möglichkeit, diese gelöschten Dateien in die Verzeichnisse der Personen wiederherzustellen oder sie dauerhaft aus dem System zu entfernen. Beim Wiederherstellen einer Datei werden auch die zugehörigen Dateiversionen wiederhergestellt, sofern die Versionsanwendung aktiviert ist. Wenn eine Datei aus einer Freigabe gelöscht wird, kann sie auf dem gleichen Weg wiederhergestellt werden, ist jedoch nicht mehr geteilt. Standardmäßig verbleiben gelöschte Dateien 30 Tage lang im Papierkorb.\nUm zu verhindern, dass einem Konto der Speicherplatz ausgeht, nutzt die Anwendung für gelöschte Dateien maximal 50% des aktuell verfügbaren freien Kontingents. Wenn die gelöschten Dateien dieses Limit überschreiten, löscht die Anwendung die ältesten Dateien, bis sie unter dieses Limit fällt. Weitere Informationen finden Sie in der Dokumentation zu gelöschten Dateien.", + "This application enables people to restore files that were deleted from the system." : "Diese Anwendung ermöglicht es Personen, Dateien wiederherzustellen, die vom System gelöscht wurden.", + "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." : "Diese Anwendung ermöglicht es Personen, Dateien wiederherzustellen, die aus dem System gelöscht wurden. Es zeigt eine Liste der gelöschten Dateien in der Web-Oberfläche an und bietet die Möglichkeit, diese gelöschten Dateien in die Verzeichnisse der Personen wiederherzustellen oder sie dauerhaft aus dem System zu entfernen. Beim Wiederherstellen einer Datei werden auch die zugehörigen Dateiversionen wiederhergestellt, sofern die Versionsanwendung aktiviert ist. Wenn eine Datei aus einer Freigabe gelöscht wird, kann sie auf dem gleichen Weg wiederhergestellt werden, ist jedoch nicht mehr geteilt. Standardmäßig verbleiben gelöschte Dateien 30 Tage lang im Papierkorb.\nUm zu verhindern, dass einem Konto der Speicherplatz ausgeht, nutzt die Anwendung für gelöschte Dateien maximal 50 % des aktuell verfügbaren freien Kontingents. Wenn die gelöschten Dateien dieses Limit überschreiten, löscht die Anwendung die ältesten Dateien, bis sie unter dieses Limit fällt. Weitere Informationen finden Sie in der Dokumentation zu gelöschten Dateien.", "Restore" : "Wiederherstellen", "Empty deleted files" : "Gelöschte Dateien leeren", "Confirm permanent deletion" : "Endgültiges Löschen bestätigen", @@ -16,7 +16,7 @@ "A long time ago" : "Vor langer Zeit", "Unknown" : "Unbekannt", "All files" : "Alle Dateien", - "You" : "Sie", + "You" : "Ihnen", "List of files that have been deleted." : "Liste der gelöschten Dateien.", "No deleted files" : "Keine gelöschten Dateien", "Files and folders you have deleted will show up here" : "Die von Ihnen gelöschten Dateien und Ordner werden hier angezeigt" diff --git a/apps/files_versions/lib/Versions/LegacyVersionsBackend.php b/apps/files_versions/lib/Versions/LegacyVersionsBackend.php index 92b326b6cd3..59a10a0775d 100644 --- a/apps/files_versions/lib/Versions/LegacyVersionsBackend.php +++ b/apps/files_versions/lib/Versions/LegacyVersionsBackend.php @@ -367,16 +367,20 @@ class LegacyVersionsBackend implements IVersionBackend, IDeletableVersionBackend * @inheritdoc */ public function clearVersionsForFile(IUser $user, Node $source, Node $target): void { - $userFolder = $this->rootFolder->getUserFolder($user->getUID()); + $userId = $user->getUID(); + $userFolder = $this->rootFolder->getUserFolder($userId); $relativePath = $userFolder->getRelativePath($source->getPath()); if ($relativePath === null) { throw new Exception('Relative path not found for node with path: ' . $source->getPath()); } - $versions = Storage::getVersions($user->getUID(), $relativePath); - /** @var Folder versionFolder */ - $versionFolder = $this->rootFolder->get('admin/files_versions'); + $versionFolder = $this->rootFolder->get($userId . '/files_versions'); + if (!$versionFolder instanceof Folder) { + throw new Exception('User versions folder does not exist'); + } + + $versions = Storage::getVersions($userId, $relativePath); foreach ($versions as $version) { $versionFolder->get($version['path'] . '.v' . (int)$version['version'])->delete(); } diff --git a/apps/settings/l10n/de.js b/apps/settings/l10n/de.js index 37a1ff21e33..55913b90b3c 100644 --- a/apps/settings/l10n/de.js +++ b/apps/settings/l10n/de.js @@ -647,14 +647,14 @@ OC.L10N.register( "Edit your Profile visibility" : "Sichtbarkeit des Profils anpassen", "Unable to update profile enabled state" : "Der Status „Profil aktiviert“ kann nicht aktualisiert werden.", "Enable profile" : "Profil aktivieren", - "The more restrictive setting of either visibility or scope is respected on your Profile. For example, if visibility is set to \"Show to everyone\" and scope is set to \"Private\", \"Private\" is respected." : "Die restriktivere Einstellung von Sichtbarkeit oder Bereich wird in deinem Profil berücksichtigt. Wenn beispielsweise die Sichtbarkeit auf \"Für jeden sichtbar\" und der Bereich auf \"Privat\" festgelegt ist, wird \"Privat\" respektiert.", + "The more restrictive setting of either visibility or scope is respected on your Profile. For example, if visibility is set to \"Show to everyone\" and scope is set to \"Private\", \"Private\" is respected." : "Die restriktivere Einstellung von Sichtbarkeit oder Bereich wird in deinem Profil berücksichtigt. Wenn beispielsweise die Sichtbarkeit auf \"Für alle sichtbar\" und der Bereich auf \"Privat\" festgelegt ist, wird \"Privat\" respektiert.", "Unable to update visibility of {displayId}" : "Sichtbarkeit von {displayId} kann nicht aktualisiert werden", "she/her" : "sie/ihr", "he/him" : "er/sein", "they/them" : "sie/sie", "Your role" : "Deine Funktion", "Your X (formerly Twitter) handle" : "Dein X (früher Twitter)-Handle", - "Your website" : "Deine Internetseite", + "Your website" : "Deine Webseite", "Invalid value" : "Ungültiger Wert", "Unable to update {property}" : "{property} konnte nicht aktualisiert werden.", "No {property} set" : "Keine {property} eingestellt", @@ -804,8 +804,8 @@ OC.L10N.register( "Locale" : "Gebietsschema", "First day of week" : "Erster Tag der Woche", "Not available as this property is required for core functionality including file sharing and calendar invitations" : "Nicht verfügbar, da diese Eigenschaft für Kernfunktionen wie Dateifreigabe und Kalendereinladungen erforderlich ist.", - "Not available as federation has been disabled for your account, contact your system administration if you have any questions" : "Nicht verfügbar, da Federation für dein Konto deaktiviert ist. Wende dich an deine Administration, wenn du Fragen hast.", - "Not available as publishing account specific data to the lookup server is not allowed, contact your system administration if you have any questions" : "Nicht verfügbar, da die Veröffentlichung benutzerspezifischer Daten auf dem Lookup-Server nicht zulässig ist. Bei Fragen bitte an die Systemadministration wenden..", + "Not available as federation has been disabled for your account, contact your system administration if you have any questions" : "Nicht verfügbar, da Federation für dein Konto deaktiviert ist. Wende dich bei Fragen an deine Systemadministration.", + "Not available as publishing account specific data to the lookup server is not allowed, contact your system administration if you have any questions" : "Nicht verfügbar, da die Veröffentlichung kontospezifischer Daten auf dem Lookup-Server nicht zulässig ist. Wende dich bei Fragen an deine Systemadministration.", "Discover" : "Entdecken", "Your apps" : "Deine Apps", "Active apps" : "Aktive Apps", @@ -814,7 +814,7 @@ OC.L10N.register( "App bundles" : "App-Pakete", "Featured apps" : "Vorgestellte Apps", "Supported apps" : "Unterstützte Apps", - "Show to everyone" : "Für jeden sichtbar", + "Show to everyone" : "Für alle sichtbar", "Show to logged in accounts only" : "Nur angemeldeten Konten anzeigen", "Hide" : "Ausblenden", "Manually installed apps cannot be updated" : "Manuell installierte Apps können nicht aktualisiert werden", diff --git a/apps/settings/l10n/de.json b/apps/settings/l10n/de.json index 2b43ac861a5..a02362567d6 100644 --- a/apps/settings/l10n/de.json +++ b/apps/settings/l10n/de.json @@ -645,14 +645,14 @@ "Edit your Profile visibility" : "Sichtbarkeit des Profils anpassen", "Unable to update profile enabled state" : "Der Status „Profil aktiviert“ kann nicht aktualisiert werden.", "Enable profile" : "Profil aktivieren", - "The more restrictive setting of either visibility or scope is respected on your Profile. For example, if visibility is set to \"Show to everyone\" and scope is set to \"Private\", \"Private\" is respected." : "Die restriktivere Einstellung von Sichtbarkeit oder Bereich wird in deinem Profil berücksichtigt. Wenn beispielsweise die Sichtbarkeit auf \"Für jeden sichtbar\" und der Bereich auf \"Privat\" festgelegt ist, wird \"Privat\" respektiert.", + "The more restrictive setting of either visibility or scope is respected on your Profile. For example, if visibility is set to \"Show to everyone\" and scope is set to \"Private\", \"Private\" is respected." : "Die restriktivere Einstellung von Sichtbarkeit oder Bereich wird in deinem Profil berücksichtigt. Wenn beispielsweise die Sichtbarkeit auf \"Für alle sichtbar\" und der Bereich auf \"Privat\" festgelegt ist, wird \"Privat\" respektiert.", "Unable to update visibility of {displayId}" : "Sichtbarkeit von {displayId} kann nicht aktualisiert werden", "she/her" : "sie/ihr", "he/him" : "er/sein", "they/them" : "sie/sie", "Your role" : "Deine Funktion", "Your X (formerly Twitter) handle" : "Dein X (früher Twitter)-Handle", - "Your website" : "Deine Internetseite", + "Your website" : "Deine Webseite", "Invalid value" : "Ungültiger Wert", "Unable to update {property}" : "{property} konnte nicht aktualisiert werden.", "No {property} set" : "Keine {property} eingestellt", @@ -802,8 +802,8 @@ "Locale" : "Gebietsschema", "First day of week" : "Erster Tag der Woche", "Not available as this property is required for core functionality including file sharing and calendar invitations" : "Nicht verfügbar, da diese Eigenschaft für Kernfunktionen wie Dateifreigabe und Kalendereinladungen erforderlich ist.", - "Not available as federation has been disabled for your account, contact your system administration if you have any questions" : "Nicht verfügbar, da Federation für dein Konto deaktiviert ist. Wende dich an deine Administration, wenn du Fragen hast.", - "Not available as publishing account specific data to the lookup server is not allowed, contact your system administration if you have any questions" : "Nicht verfügbar, da die Veröffentlichung benutzerspezifischer Daten auf dem Lookup-Server nicht zulässig ist. Bei Fragen bitte an die Systemadministration wenden..", + "Not available as federation has been disabled for your account, contact your system administration if you have any questions" : "Nicht verfügbar, da Federation für dein Konto deaktiviert ist. Wende dich bei Fragen an deine Systemadministration.", + "Not available as publishing account specific data to the lookup server is not allowed, contact your system administration if you have any questions" : "Nicht verfügbar, da die Veröffentlichung kontospezifischer Daten auf dem Lookup-Server nicht zulässig ist. Wende dich bei Fragen an deine Systemadministration.", "Discover" : "Entdecken", "Your apps" : "Deine Apps", "Active apps" : "Aktive Apps", @@ -812,7 +812,7 @@ "App bundles" : "App-Pakete", "Featured apps" : "Vorgestellte Apps", "Supported apps" : "Unterstützte Apps", - "Show to everyone" : "Für jeden sichtbar", + "Show to everyone" : "Für alle sichtbar", "Show to logged in accounts only" : "Nur angemeldeten Konten anzeigen", "Hide" : "Ausblenden", "Manually installed apps cannot be updated" : "Manuell installierte Apps können nicht aktualisiert werden", diff --git a/apps/settings/l10n/de_DE.js b/apps/settings/l10n/de_DE.js index 49c20c8e722..5388ff52529 100644 --- a/apps/settings/l10n/de_DE.js +++ b/apps/settings/l10n/de_DE.js @@ -2,7 +2,7 @@ OC.L10N.register( "settings", { "Private" : "Privat", - "Only visible to people matched via phone number integration through Talk on mobile" : "Nur sichtbar für Personen, die über Rufnummernintegration von Talk on mobile abgeglichen wurden.", + "Only visible to people matched via phone number integration through Talk on mobile" : "Nur sichtbar für Personen, die über die Rufnummernintegration von Talk auf dem Smartphone abgeglichen wurden.", "Local" : "Lokal", "Only visible to people on this instance and guests" : "Nur für Personen dieser Instanz und Gästen sichtbar", "Federated" : "Federated", @@ -626,7 +626,7 @@ OC.L10N.register( "Unable to add additional email address" : "Zusätzliche E-Mail-Adresse kann nicht hinzugefügt werden", "Unable to update additional email address" : "Zusätzliche E-Mail-Adresse konnte nicht aktualisiert werden", "Unable to delete additional email address" : "Zusätzliche E-Mail-Adresse konnte nicht gelöscht werden", - "Primary email for password reset and notifications" : "Primäre E-Mail-Adresse für Benachrichtigungen und Passwort-Zurücksetzen", + "Primary email for password reset and notifications" : "Primäre E-Mail-Adresse für Benachrichtigungen und zum Zurücksetzen des Passworts", "No email address set" : "Keine E-Mail-Adresse angegeben", "Your handle" : "Dein Online-Name", "Derived from your locale ({weekDayName})" : "Abgeleitet von Ihrem Gebietsschema ({weekDayName})", @@ -644,10 +644,10 @@ OC.L10N.register( "Your city" : "Ihre Stadt", "Your organisation" : "Ihre Organisation", "Your phone number" : "Ihre Telefonnummer", - "Edit your Profile visibility" : "Bearbeiten Sie die Sichtbarkeit Ihres Profils", + "Edit your Profile visibility" : "Sichtbarkeit des Profils anpassen", "Unable to update profile enabled state" : "Der Status für aktiviertes Profil konnte nicht aktualisiert werden", "Enable profile" : "Profil aktivieren", - "The more restrictive setting of either visibility or scope is respected on your Profile. For example, if visibility is set to \"Show to everyone\" and scope is set to \"Private\", \"Private\" is respected." : "Die restriktivere Einstellung von Sichtbarkeit oder Bereich wird in Ihrem Profil berücksichtigt. Wenn beispielsweise die Sichtbarkeit auf \"Für alle anzeigen\" und der Bereich auf \"Privat\" festgelegt ist, wird \"Privat\" respektiert.", + "The more restrictive setting of either visibility or scope is respected on your Profile. For example, if visibility is set to \"Show to everyone\" and scope is set to \"Private\", \"Private\" is respected." : "Die restriktivere Einstellung von Sichtbarkeit oder Bereich wird in Ihrem Profil berücksichtigt. Wenn beispielsweise die Sichtbarkeit auf \"Für alle sichtbar\" und der Bereich auf \"Privat\" festgelegt ist, wird \"Privat\" respektiert.", "Unable to update visibility of {displayId}" : "Sichtbarkeit von {displayId} kann nicht aktualisiert werden", "she/her" : "sie/ihr", "he/him" : "er/sein", @@ -684,7 +684,7 @@ OC.L10N.register( "Either password or email is required" : "Passwort oder E-Mail-Adresse ist erforderlich", "Password (required)" : "Passwort (erforderlich)", "Email (required)" : "E-Mail-Adresse (erforderlich)", - "Email" : "E-Mail", + "Email" : "E-Mail-Adresse", "Member of the following groups (required)" : "Mitglied der folgenden Gruppen (erforderlich)", "Member of the following groups" : "Mitglied der folgenden Gruppen", "Set account groups" : "Kontengruppen setzen", @@ -804,7 +804,7 @@ OC.L10N.register( "Locale" : "Gebietsschema", "First day of week" : "Erster Tag der Woche", "Not available as this property is required for core functionality including file sharing and calendar invitations" : "Nicht verfügbar, da diese Eigenschaft für Kernfunktionen wie Dateifreigabe und Kalendereinladungen erforderlich ist.", - "Not available as federation has been disabled for your account, contact your system administration if you have any questions" : "Nicht verfügbar, da Federation für Ihr Konto deaktiviert ist. Bitte wenden Sie sich an Ihre Administration, wenn Sie Fragen haben", + "Not available as federation has been disabled for your account, contact your system administration if you have any questions" : "Nicht verfügbar, da Federation für Ihr Konto deaktiviert ist. Wenden Sie sich bei Fragen an Ihre Systemadministration.", "Not available as publishing account specific data to the lookup server is not allowed, contact your system administration if you have any questions" : "Nicht verfügbar, da die Veröffentlichung kontospezifischer Daten auf dem Lookup-Server nicht zulässig ist. Wenden Sie sich bei Fragen an Ihre Systemadministration.", "Discover" : "Entdecken", "Your apps" : "Ihre Apps", @@ -814,7 +814,7 @@ OC.L10N.register( "App bundles" : "App-Pakete", "Featured apps" : "Vorgestellte Apps", "Supported apps" : "Unterstützte Apps", - "Show to everyone" : "Jedem anzeigen", + "Show to everyone" : "Für alle sichtbar", "Show to logged in accounts only" : "Nur angemeldeten Konten anzeigen", "Hide" : "Ausblenden", "Manually installed apps cannot be updated" : "Manuell installierte Apps können nicht aktualisiert werden", diff --git a/apps/settings/l10n/de_DE.json b/apps/settings/l10n/de_DE.json index 85ed8fe4a63..0f35dc26f55 100644 --- a/apps/settings/l10n/de_DE.json +++ b/apps/settings/l10n/de_DE.json @@ -1,6 +1,6 @@ { "translations": { "Private" : "Privat", - "Only visible to people matched via phone number integration through Talk on mobile" : "Nur sichtbar für Personen, die über Rufnummernintegration von Talk on mobile abgeglichen wurden.", + "Only visible to people matched via phone number integration through Talk on mobile" : "Nur sichtbar für Personen, die über die Rufnummernintegration von Talk auf dem Smartphone abgeglichen wurden.", "Local" : "Lokal", "Only visible to people on this instance and guests" : "Nur für Personen dieser Instanz und Gästen sichtbar", "Federated" : "Federated", @@ -624,7 +624,7 @@ "Unable to add additional email address" : "Zusätzliche E-Mail-Adresse kann nicht hinzugefügt werden", "Unable to update additional email address" : "Zusätzliche E-Mail-Adresse konnte nicht aktualisiert werden", "Unable to delete additional email address" : "Zusätzliche E-Mail-Adresse konnte nicht gelöscht werden", - "Primary email for password reset and notifications" : "Primäre E-Mail-Adresse für Benachrichtigungen und Passwort-Zurücksetzen", + "Primary email for password reset and notifications" : "Primäre E-Mail-Adresse für Benachrichtigungen und zum Zurücksetzen des Passworts", "No email address set" : "Keine E-Mail-Adresse angegeben", "Your handle" : "Dein Online-Name", "Derived from your locale ({weekDayName})" : "Abgeleitet von Ihrem Gebietsschema ({weekDayName})", @@ -642,10 +642,10 @@ "Your city" : "Ihre Stadt", "Your organisation" : "Ihre Organisation", "Your phone number" : "Ihre Telefonnummer", - "Edit your Profile visibility" : "Bearbeiten Sie die Sichtbarkeit Ihres Profils", + "Edit your Profile visibility" : "Sichtbarkeit des Profils anpassen", "Unable to update profile enabled state" : "Der Status für aktiviertes Profil konnte nicht aktualisiert werden", "Enable profile" : "Profil aktivieren", - "The more restrictive setting of either visibility or scope is respected on your Profile. For example, if visibility is set to \"Show to everyone\" and scope is set to \"Private\", \"Private\" is respected." : "Die restriktivere Einstellung von Sichtbarkeit oder Bereich wird in Ihrem Profil berücksichtigt. Wenn beispielsweise die Sichtbarkeit auf \"Für alle anzeigen\" und der Bereich auf \"Privat\" festgelegt ist, wird \"Privat\" respektiert.", + "The more restrictive setting of either visibility or scope is respected on your Profile. For example, if visibility is set to \"Show to everyone\" and scope is set to \"Private\", \"Private\" is respected." : "Die restriktivere Einstellung von Sichtbarkeit oder Bereich wird in Ihrem Profil berücksichtigt. Wenn beispielsweise die Sichtbarkeit auf \"Für alle sichtbar\" und der Bereich auf \"Privat\" festgelegt ist, wird \"Privat\" respektiert.", "Unable to update visibility of {displayId}" : "Sichtbarkeit von {displayId} kann nicht aktualisiert werden", "she/her" : "sie/ihr", "he/him" : "er/sein", @@ -682,7 +682,7 @@ "Either password or email is required" : "Passwort oder E-Mail-Adresse ist erforderlich", "Password (required)" : "Passwort (erforderlich)", "Email (required)" : "E-Mail-Adresse (erforderlich)", - "Email" : "E-Mail", + "Email" : "E-Mail-Adresse", "Member of the following groups (required)" : "Mitglied der folgenden Gruppen (erforderlich)", "Member of the following groups" : "Mitglied der folgenden Gruppen", "Set account groups" : "Kontengruppen setzen", @@ -802,7 +802,7 @@ "Locale" : "Gebietsschema", "First day of week" : "Erster Tag der Woche", "Not available as this property is required for core functionality including file sharing and calendar invitations" : "Nicht verfügbar, da diese Eigenschaft für Kernfunktionen wie Dateifreigabe und Kalendereinladungen erforderlich ist.", - "Not available as federation has been disabled for your account, contact your system administration if you have any questions" : "Nicht verfügbar, da Federation für Ihr Konto deaktiviert ist. Bitte wenden Sie sich an Ihre Administration, wenn Sie Fragen haben", + "Not available as federation has been disabled for your account, contact your system administration if you have any questions" : "Nicht verfügbar, da Federation für Ihr Konto deaktiviert ist. Wenden Sie sich bei Fragen an Ihre Systemadministration.", "Not available as publishing account specific data to the lookup server is not allowed, contact your system administration if you have any questions" : "Nicht verfügbar, da die Veröffentlichung kontospezifischer Daten auf dem Lookup-Server nicht zulässig ist. Wenden Sie sich bei Fragen an Ihre Systemadministration.", "Discover" : "Entdecken", "Your apps" : "Ihre Apps", @@ -812,7 +812,7 @@ "App bundles" : "App-Pakete", "Featured apps" : "Vorgestellte Apps", "Supported apps" : "Unterstützte Apps", - "Show to everyone" : "Jedem anzeigen", + "Show to everyone" : "Für alle sichtbar", "Show to logged in accounts only" : "Nur angemeldeten Konten anzeigen", "Hide" : "Ausblenden", "Manually installed apps cannot be updated" : "Manuell installierte Apps können nicht aktualisiert werden", diff --git a/apps/settings/l10n/ru.js b/apps/settings/l10n/ru.js index f6a2ba501b8..58f1e7bcb9c 100644 --- a/apps/settings/l10n/ru.js +++ b/apps/settings/l10n/ru.js @@ -562,6 +562,7 @@ OC.L10N.register( "Profile" : "Профиль", "Enable or disable profile by default for new accounts." : "Включите или отключите профиль по умолчанию для новых учетных записей.", "Failed to save setting" : "Не удалось сохранить параметры", + "{app}'s declarative setting field: {name}" : "{app} поле декларативной настройки: {name}", "Unable to update server side encryption config" : "Не удалось обновить параметры шифрования на стороне сервера", "Server-side encryption" : "Шифрование на стороне сервера", "Server-side encryption makes it possible to encrypt files which are uploaded to this server. This comes with limitations like a performance penalty, so enable this only if needed." : "Шифрование на стороне сервера позволяет шифровать файлы, которые загружаются на этот сервер. Это связано с ограничениями, такими как снижение производительности, поэтому включите его только в случае необходимости.", diff --git a/apps/settings/l10n/ru.json b/apps/settings/l10n/ru.json index 0fecb762e22..48fea024a39 100644 --- a/apps/settings/l10n/ru.json +++ b/apps/settings/l10n/ru.json @@ -560,6 +560,7 @@ "Profile" : "Профиль", "Enable or disable profile by default for new accounts." : "Включите или отключите профиль по умолчанию для новых учетных записей.", "Failed to save setting" : "Не удалось сохранить параметры", + "{app}'s declarative setting field: {name}" : "{app} поле декларативной настройки: {name}", "Unable to update server side encryption config" : "Не удалось обновить параметры шифрования на стороне сервера", "Server-side encryption" : "Шифрование на стороне сервера", "Server-side encryption makes it possible to encrypt files which are uploaded to this server. This comes with limitations like a performance penalty, so enable this only if needed." : "Шифрование на стороне сервера позволяет шифровать файлы, которые загружаются на этот сервер. Это связано с ограничениями, такими как снижение производительности, поэтому включите его только в случае необходимости.", diff --git a/apps/systemtags/l10n/de.js b/apps/systemtags/l10n/de.js index bae01f498d9..06cc0e154ea 100644 --- a/apps/systemtags/l10n/de.js +++ b/apps/systemtags/l10n/de.js @@ -42,7 +42,7 @@ OC.L10N.register( "Tags" : "Tags", "All tagged %s …" : "Alle Schlagworte %s hinzugefügt …", "tagged %s" : "Schlagwort %s hinzugefügt", - "Collaborative tags" : "Kollaborative Tags", + "Collaborative tags" : "Kollaborative Schlagworte", "Collaborative tagging functionality which shares tags among people." : "Gemeinschaftliche Schlagwort-Funktionalität, welche Schlagworte unter den Benutzern teilt.", "Collaborative tagging functionality which shares tags among people. Great for teams.\n\t(If you are a provider with a multi-tenancy installation, it is advised to deactivate this app as tags are shared.)" : "Schlagwort-Funktionalität für die Zusammenarbeit, die das Teilen von Schlagworten unter Benutzern ermöglicht. Sehr gut für Gruppen geeignet.\n\t(In eine Mehrkundeninstallation sollte diese App deaktiviert werden, da sonst die Schlagworte mit allen Kunden geteilt werden.)", "Public" : "Öffentlich", diff --git a/apps/systemtags/l10n/de.json b/apps/systemtags/l10n/de.json index 83fc4b6daf6..8c2f25aa302 100644 --- a/apps/systemtags/l10n/de.json +++ b/apps/systemtags/l10n/de.json @@ -40,7 +40,7 @@ "Tags" : "Tags", "All tagged %s …" : "Alle Schlagworte %s hinzugefügt …", "tagged %s" : "Schlagwort %s hinzugefügt", - "Collaborative tags" : "Kollaborative Tags", + "Collaborative tags" : "Kollaborative Schlagworte", "Collaborative tagging functionality which shares tags among people." : "Gemeinschaftliche Schlagwort-Funktionalität, welche Schlagworte unter den Benutzern teilt.", "Collaborative tagging functionality which shares tags among people. Great for teams.\n\t(If you are a provider with a multi-tenancy installation, it is advised to deactivate this app as tags are shared.)" : "Schlagwort-Funktionalität für die Zusammenarbeit, die das Teilen von Schlagworten unter Benutzern ermöglicht. Sehr gut für Gruppen geeignet.\n\t(In eine Mehrkundeninstallation sollte diese App deaktiviert werden, da sonst die Schlagworte mit allen Kunden geteilt werden.)", "Public" : "Öffentlich", diff --git a/apps/systemtags/l10n/de_DE.js b/apps/systemtags/l10n/de_DE.js index 447cb344bce..531023ade74 100644 --- a/apps/systemtags/l10n/de_DE.js +++ b/apps/systemtags/l10n/de_DE.js @@ -89,7 +89,7 @@ OC.L10N.register( "Failed to load selected tags" : "Ausgewählte Schlagworte konnten nicht geladen werden", "Failed to select tag" : "Schlagwort konnte nicht ausgewählt werden", "System admin disabled tag creation. You can only use existing ones." : "Die Systemadministration hat die Erstellung von Schlagworten deaktiviert. Sie können nur vorhandene Schlagworte verwenden.", - "Loading collaborative tags …" : "Lade kollaborative Schlagworte …", + "Loading collaborative tags …" : "Kollaborative Schlagworte laden…", "Search or create collaborative tags" : "Suchen oder erstellen von kollaborativen Schlagworten", "No tags to select, type to create a new tag" : "Keine Schlagworte zur Auswahl, geben Sie ein, um ein neues Schlagwort zu erstellen", "Unable to update setting" : "Einstellung konnte nicht aktualisiert werden", diff --git a/apps/systemtags/l10n/de_DE.json b/apps/systemtags/l10n/de_DE.json index cc905e232d5..adaff5ffc70 100644 --- a/apps/systemtags/l10n/de_DE.json +++ b/apps/systemtags/l10n/de_DE.json @@ -87,7 +87,7 @@ "Failed to load selected tags" : "Ausgewählte Schlagworte konnten nicht geladen werden", "Failed to select tag" : "Schlagwort konnte nicht ausgewählt werden", "System admin disabled tag creation. You can only use existing ones." : "Die Systemadministration hat die Erstellung von Schlagworten deaktiviert. Sie können nur vorhandene Schlagworte verwenden.", - "Loading collaborative tags …" : "Lade kollaborative Schlagworte …", + "Loading collaborative tags …" : "Kollaborative Schlagworte laden…", "Search or create collaborative tags" : "Suchen oder erstellen von kollaborativen Schlagworten", "No tags to select, type to create a new tag" : "Keine Schlagworte zur Auswahl, geben Sie ein, um ein neues Schlagwort zu erstellen", "Unable to update setting" : "Einstellung konnte nicht aktualisiert werden", diff --git a/apps/theming/l10n/de.js b/apps/theming/l10n/de.js index 702a47c76ec..3ecc474c96a 100644 --- a/apps/theming/l10n/de.js +++ b/apps/theming/l10n/de.js @@ -82,7 +82,7 @@ OC.L10N.register( "Misc accessibility options" : "Verschiedene Barrierefreiheitsoptionen", "Enable blur background filter (may increase GPU load)" : "Filter für unscharfes Hintergrundbild aktivieren (kann die GPU-Auslastung erhöhen)", "Customization has been disabled by your administrator" : "Diese Anpassungsmöglichkeit wurde von deiner Administration deaktiviert", - "Set a primary color to highlight important elements. The color used for elements such as primary buttons might differ a bit as it gets adjusted to fulfill accessibility requirements." : "Legen eine Primärfarbe fest, um wichtige Elemente hervorzuheben. Die für Elemente wie Primärschaltflächen verwendete Farbe kann etwas abweichen, da sie angepasst wird, um die Zugänglichkeitsanforderungen zu erfüllen.", + "Set a primary color to highlight important elements. The color used for elements such as primary buttons might differ a bit as it gets adjusted to fulfill accessibility requirements." : "Lege eine Primärfarbe fest, um wichtige Elemente hervorzuheben. Die für Elemente wie Primärschaltflächen verwendete Farbe kann etwas abweichen, da sie angepasst wird, um die Zugänglichkeitsanforderungen zu erfüllen.", "Background and color" : "Hintergrund und Farbe", "The background can be set to an image from the default set, a custom uploaded image, or a plain color." : "Als Hintergrund kann ein Bild aus dem Standardsatz, ein benutzerdefiniertes hochgeladenes Bild oder eine einfache Farbe ausgewählt werden.", "Keyboard shortcuts" : "Tastaturkürzel", diff --git a/apps/theming/l10n/de.json b/apps/theming/l10n/de.json index 97051ce93c9..1d2b5fb9803 100644 --- a/apps/theming/l10n/de.json +++ b/apps/theming/l10n/de.json @@ -80,7 +80,7 @@ "Misc accessibility options" : "Verschiedene Barrierefreiheitsoptionen", "Enable blur background filter (may increase GPU load)" : "Filter für unscharfes Hintergrundbild aktivieren (kann die GPU-Auslastung erhöhen)", "Customization has been disabled by your administrator" : "Diese Anpassungsmöglichkeit wurde von deiner Administration deaktiviert", - "Set a primary color to highlight important elements. The color used for elements such as primary buttons might differ a bit as it gets adjusted to fulfill accessibility requirements." : "Legen eine Primärfarbe fest, um wichtige Elemente hervorzuheben. Die für Elemente wie Primärschaltflächen verwendete Farbe kann etwas abweichen, da sie angepasst wird, um die Zugänglichkeitsanforderungen zu erfüllen.", + "Set a primary color to highlight important elements. The color used for elements such as primary buttons might differ a bit as it gets adjusted to fulfill accessibility requirements." : "Lege eine Primärfarbe fest, um wichtige Elemente hervorzuheben. Die für Elemente wie Primärschaltflächen verwendete Farbe kann etwas abweichen, da sie angepasst wird, um die Zugänglichkeitsanforderungen zu erfüllen.", "Background and color" : "Hintergrund und Farbe", "The background can be set to an image from the default set, a custom uploaded image, or a plain color." : "Als Hintergrund kann ein Bild aus dem Standardsatz, ein benutzerdefiniertes hochgeladenes Bild oder eine einfache Farbe ausgewählt werden.", "Keyboard shortcuts" : "Tastaturkürzel", diff --git a/apps/theming/l10n/de_DE.js b/apps/theming/l10n/de_DE.js index c122e73ec49..b2c84dce2dc 100644 --- a/apps/theming/l10n/de_DE.js +++ b/apps/theming/l10n/de_DE.js @@ -50,7 +50,7 @@ OC.L10N.register( "The default light appearance." : "Das helle Standarddesign.", "Legal notice" : "Impressum", "Privacy policy" : "Datenschutzerklärung", - "Adjust the Nextcloud theme" : "Passe das Design von Nextcloud an", + "Adjust the Nextcloud theme" : "Passen Sie das Design von Nextcloud an", "Name" : "Name", "Web link" : "Internet-Link", "a safe home for all your data" : "Ein sicheres Zuhause für Ihre Daten", diff --git a/apps/theming/l10n/de_DE.json b/apps/theming/l10n/de_DE.json index d62d9039ab4..d3064661462 100644 --- a/apps/theming/l10n/de_DE.json +++ b/apps/theming/l10n/de_DE.json @@ -48,7 +48,7 @@ "The default light appearance." : "Das helle Standarddesign.", "Legal notice" : "Impressum", "Privacy policy" : "Datenschutzerklärung", - "Adjust the Nextcloud theme" : "Passe das Design von Nextcloud an", + "Adjust the Nextcloud theme" : "Passen Sie das Design von Nextcloud an", "Name" : "Name", "Web link" : "Internet-Link", "a safe home for all your data" : "Ein sicheres Zuhause für Ihre Daten", diff --git a/core/l10n/de.js b/core/l10n/de.js index edebc5587d8..4b4aa28781d 100644 --- a/core/l10n/de.js +++ b/core/l10n/de.js @@ -293,7 +293,7 @@ OC.L10N.register( "Invisible" : "Unsichtbar", "Delete" : "Löschen", "Rename" : "Umbenennen", - "Collaborative tags" : "Kollaborative Tags", + "Collaborative tags" : "Kollaborative Schlagworte", "No tags found" : "Keine Tags gefunden", "Clipboard not available, please copy manually" : "Zwischenablage nicht verfügbar, bitte manuell kopieren", "Personal" : "Persönlich", diff --git a/core/l10n/de.json b/core/l10n/de.json index 00acc96d9ab..931662a848c 100644 --- a/core/l10n/de.json +++ b/core/l10n/de.json @@ -291,7 +291,7 @@ "Invisible" : "Unsichtbar", "Delete" : "Löschen", "Rename" : "Umbenennen", - "Collaborative tags" : "Kollaborative Tags", + "Collaborative tags" : "Kollaborative Schlagworte", "No tags found" : "Keine Tags gefunden", "Clipboard not available, please copy manually" : "Zwischenablage nicht verfügbar, bitte manuell kopieren", "Personal" : "Persönlich", diff --git a/lib/l10n/cs.js b/lib/l10n/cs.js index 2233f31f686..b4304fb14ab 100644 --- a/lib/l10n/cs.js +++ b/lib/l10n/cs.js @@ -93,6 +93,8 @@ OC.L10N.register( "Destination does not exist" : "Cíl neexistuje", "Destination is not creatable" : "Cíl není možné vytvořit", "Dot files are not allowed" : "Názvy souborů, začínající na tečku nejsou dovolené", + "%1$s (renamed)" : "%1$s (přejmenované)", + "renamed file" : "přejmenovaný soubor", "\"%1$s\" is a forbidden file or folder name." : "„%1$s“ je zakázáno v názvu souboru nebo složky.", "\"%1$s\" is a forbidden prefix for file or folder names." : "„%1$s“ je zakázaná předpona pro názvy souborů či složek.", "\"%1$s\" is not allowed inside a file or folder name." : "„%1$s“ není povoleno v rámci názvu souboru či složky.", diff --git a/lib/l10n/cs.json b/lib/l10n/cs.json index 087e2244eeb..f6cb00467cb 100644 --- a/lib/l10n/cs.json +++ b/lib/l10n/cs.json @@ -91,6 +91,8 @@ "Destination does not exist" : "Cíl neexistuje", "Destination is not creatable" : "Cíl není možné vytvořit", "Dot files are not allowed" : "Názvy souborů, začínající na tečku nejsou dovolené", + "%1$s (renamed)" : "%1$s (přejmenované)", + "renamed file" : "přejmenovaný soubor", "\"%1$s\" is a forbidden file or folder name." : "„%1$s“ je zakázáno v názvu souboru nebo složky.", "\"%1$s\" is a forbidden prefix for file or folder names." : "„%1$s“ je zakázaná předpona pro názvy souborů či složek.", "\"%1$s\" is not allowed inside a file or folder name." : "„%1$s“ není povoleno v rámci názvu souboru či složky.", diff --git a/lib/l10n/de.js b/lib/l10n/de.js index 672c6ca4f25..e44f2b89e2b 100644 --- a/lib/l10n/de.js +++ b/lib/l10n/de.js @@ -93,6 +93,8 @@ OC.L10N.register( "Destination does not exist" : "Ziel existiert nicht", "Destination is not creatable" : "Ziel kann nicht erstellt werden", "Dot files are not allowed" : "Dateinamen mit einem Punkt am Anfang sind nicht erlaubt", + "%1$s (renamed)" : "%1$s (umbenannt)", + "renamed file" : "Umbenannte Datei", "\"%1$s\" is a forbidden file or folder name." : "\"%1$s\" ist ein unzulässiger Datei- oder Ordnername.", "\"%1$s\" is a forbidden prefix for file or folder names." : "\"%1$s\" ist ein unzulässiges Präfix für Datei- oder Ordnernamen.", "\"%1$s\" is not allowed inside a file or folder name." : "\"%1$s\" ist in einem Datei- oder Ordnernamen unzulässig.", @@ -120,7 +122,7 @@ OC.L10N.register( "__language_name__" : "Deutsch (Persönlich: Du)", "This is an automatically sent email, please do not reply." : "Dies ist eine automatisch gesendete E-Mail. Bitte antworte nicht auf diese E-Mail.", "Help & privacy" : "Hilfe & Datenschutz", - "Appearance and accessibility" : "Erscheinungsbild und Barrierefreiheit", + "Appearance and accessibility" : "Aussehen und Barrierefreiheit", "Apps" : "Apps", "Personal settings" : "Persönliche Einstellungen", "Administration settings" : "Verwaltungseinstellungen", diff --git a/lib/l10n/de.json b/lib/l10n/de.json index 17ce3b96f49..7a05dfc101f 100644 --- a/lib/l10n/de.json +++ b/lib/l10n/de.json @@ -91,6 +91,8 @@ "Destination does not exist" : "Ziel existiert nicht", "Destination is not creatable" : "Ziel kann nicht erstellt werden", "Dot files are not allowed" : "Dateinamen mit einem Punkt am Anfang sind nicht erlaubt", + "%1$s (renamed)" : "%1$s (umbenannt)", + "renamed file" : "Umbenannte Datei", "\"%1$s\" is a forbidden file or folder name." : "\"%1$s\" ist ein unzulässiger Datei- oder Ordnername.", "\"%1$s\" is a forbidden prefix for file or folder names." : "\"%1$s\" ist ein unzulässiges Präfix für Datei- oder Ordnernamen.", "\"%1$s\" is not allowed inside a file or folder name." : "\"%1$s\" ist in einem Datei- oder Ordnernamen unzulässig.", @@ -118,7 +120,7 @@ "__language_name__" : "Deutsch (Persönlich: Du)", "This is an automatically sent email, please do not reply." : "Dies ist eine automatisch gesendete E-Mail. Bitte antworte nicht auf diese E-Mail.", "Help & privacy" : "Hilfe & Datenschutz", - "Appearance and accessibility" : "Erscheinungsbild und Barrierefreiheit", + "Appearance and accessibility" : "Aussehen und Barrierefreiheit", "Apps" : "Apps", "Personal settings" : "Persönliche Einstellungen", "Administration settings" : "Verwaltungseinstellungen", diff --git a/lib/l10n/de_DE.js b/lib/l10n/de_DE.js index aa926c96d4f..4bfd5faa7f2 100644 --- a/lib/l10n/de_DE.js +++ b/lib/l10n/de_DE.js @@ -93,6 +93,8 @@ OC.L10N.register( "Destination does not exist" : "Ziel existiert nicht", "Destination is not creatable" : "Ziel kann nicht erstellt werden", "Dot files are not allowed" : "Dateinamen mit einem Punkt am Anfang sind nicht erlaubt", + "%1$s (renamed)" : "%1$s (umbenannt)", + "renamed file" : "Umbenannte Datei", "\"%1$s\" is a forbidden file or folder name." : "\"%1$s\" ist ein verbotener Datei- oder Ordnername.", "\"%1$s\" is a forbidden prefix for file or folder names." : "\"%1$s\" ist ein verbotenes Präfix für Datei- oder Ordnernamen.", "\"%1$s\" is not allowed inside a file or folder name." : "\"%1$s\" ist in einem Datei- oder Ordnernamen unzulässig.", diff --git a/lib/l10n/de_DE.json b/lib/l10n/de_DE.json index f23493df570..6faebb2c6c0 100644 --- a/lib/l10n/de_DE.json +++ b/lib/l10n/de_DE.json @@ -91,6 +91,8 @@ "Destination does not exist" : "Ziel existiert nicht", "Destination is not creatable" : "Ziel kann nicht erstellt werden", "Dot files are not allowed" : "Dateinamen mit einem Punkt am Anfang sind nicht erlaubt", + "%1$s (renamed)" : "%1$s (umbenannt)", + "renamed file" : "Umbenannte Datei", "\"%1$s\" is a forbidden file or folder name." : "\"%1$s\" ist ein verbotener Datei- oder Ordnername.", "\"%1$s\" is a forbidden prefix for file or folder names." : "\"%1$s\" ist ein verbotenes Präfix für Datei- oder Ordnernamen.", "\"%1$s\" is not allowed inside a file or folder name." : "\"%1$s\" ist in einem Datei- oder Ordnernamen unzulässig.", diff --git a/lib/l10n/en_GB.js b/lib/l10n/en_GB.js index 2494ab4aca4..d6a8da225ba 100644 --- a/lib/l10n/en_GB.js +++ b/lib/l10n/en_GB.js @@ -93,6 +93,8 @@ OC.L10N.register( "Destination does not exist" : "Destination does not exist", "Destination is not creatable" : "Destination is not creatable", "Dot files are not allowed" : "Dot files are not allowed", + "%1$s (renamed)" : "%1$s (renamed)", + "renamed file" : "renamed file", "\"%1$s\" is a forbidden file or folder name." : "\"%1$s\" is a forbidden file or folder name.", "\"%1$s\" is a forbidden prefix for file or folder names." : "\"%1$s\" is a forbidden prefix for file or folder names.", "\"%1$s\" is not allowed inside a file or folder name." : "\"%1$s\" is not allowed inside a file or folder name.", diff --git a/lib/l10n/en_GB.json b/lib/l10n/en_GB.json index 08e84f39af1..e1bd150aca6 100644 --- a/lib/l10n/en_GB.json +++ b/lib/l10n/en_GB.json @@ -91,6 +91,8 @@ "Destination does not exist" : "Destination does not exist", "Destination is not creatable" : "Destination is not creatable", "Dot files are not allowed" : "Dot files are not allowed", + "%1$s (renamed)" : "%1$s (renamed)", + "renamed file" : "renamed file", "\"%1$s\" is a forbidden file or folder name." : "\"%1$s\" is a forbidden file or folder name.", "\"%1$s\" is a forbidden prefix for file or folder names." : "\"%1$s\" is a forbidden prefix for file or folder names.", "\"%1$s\" is not allowed inside a file or folder name." : "\"%1$s\" is not allowed inside a file or folder name.", diff --git a/lib/l10n/es.js b/lib/l10n/es.js index a3cf61509ec..e4076a5cc2c 100644 --- a/lib/l10n/es.js +++ b/lib/l10n/es.js @@ -93,6 +93,8 @@ OC.L10N.register( "Destination does not exist" : "El destino no existe", "Destination is not creatable" : "El destino no se puede crear", "Dot files are not allowed" : "Los archivos Dot no están permitidos", + "%1$s (renamed)" : "%1$s (renombrado)", + "renamed file" : "archivo renombrado", "\"%1$s\" is a forbidden file or folder name." : "\"%1$s\" Es un nombre de archivo o carpeta no permitido.", "\"%1$s\" is a forbidden prefix for file or folder names." : "%1$s es un prefijo prohibido para nombres de archivo o carpeta.", "\"%1$s\" is not allowed inside a file or folder name." : "«%1$s» no se permite en el nombre de un archivo o carpeta.", diff --git a/lib/l10n/es.json b/lib/l10n/es.json index 34d4db2eec4..ba3043cee36 100644 --- a/lib/l10n/es.json +++ b/lib/l10n/es.json @@ -91,6 +91,8 @@ "Destination does not exist" : "El destino no existe", "Destination is not creatable" : "El destino no se puede crear", "Dot files are not allowed" : "Los archivos Dot no están permitidos", + "%1$s (renamed)" : "%1$s (renombrado)", + "renamed file" : "archivo renombrado", "\"%1$s\" is a forbidden file or folder name." : "\"%1$s\" Es un nombre de archivo o carpeta no permitido.", "\"%1$s\" is a forbidden prefix for file or folder names." : "%1$s es un prefijo prohibido para nombres de archivo o carpeta.", "\"%1$s\" is not allowed inside a file or folder name." : "«%1$s» no se permite en el nombre de un archivo o carpeta.", diff --git a/lib/l10n/et_EE.js b/lib/l10n/et_EE.js index 7932c79256b..ae5999fc496 100644 --- a/lib/l10n/et_EE.js +++ b/lib/l10n/et_EE.js @@ -84,6 +84,8 @@ OC.L10N.register( "Destination does not exist" : "Sihtmeediat pole olemas", "Destination is not creatable" : "Sihtmeedia pole loodav", "Dot files are not allowed" : "Punktiga failid pole lubatud", + "%1$s (renamed)" : "%1$s (nimi on muudetud)", + "renamed file" : "muudetud nimega fail", "\"%1$s\" is a forbidden file or folder name." : "„%1$s“ on keelatud faili- või kaustanimi.", "\"%1$s\" is a forbidden prefix for file or folder names." : "„%1$s“ on keelatud faili- või kaustanime eesliide.", "\"%1$s\" is not allowed inside a file or folder name." : "„%1$s“ pole faili- või kaustanimes lubatud.", diff --git a/lib/l10n/et_EE.json b/lib/l10n/et_EE.json index 99c9583bf87..65621b2643a 100644 --- a/lib/l10n/et_EE.json +++ b/lib/l10n/et_EE.json @@ -82,6 +82,8 @@ "Destination does not exist" : "Sihtmeediat pole olemas", "Destination is not creatable" : "Sihtmeedia pole loodav", "Dot files are not allowed" : "Punktiga failid pole lubatud", + "%1$s (renamed)" : "%1$s (nimi on muudetud)", + "renamed file" : "muudetud nimega fail", "\"%1$s\" is a forbidden file or folder name." : "„%1$s“ on keelatud faili- või kaustanimi.", "\"%1$s\" is a forbidden prefix for file or folder names." : "„%1$s“ on keelatud faili- või kaustanime eesliide.", "\"%1$s\" is not allowed inside a file or folder name." : "„%1$s“ pole faili- või kaustanimes lubatud.", diff --git a/lib/l10n/ga.js b/lib/l10n/ga.js index 9b05143b101..807a668847e 100644 --- a/lib/l10n/ga.js +++ b/lib/l10n/ga.js @@ -93,6 +93,8 @@ OC.L10N.register( "Destination does not exist" : "Níl ceann scríbe ann", "Destination is not creatable" : "Níl ceann scríbe cruthaithe", "Dot files are not allowed" : "Ní cheadaítear comhaid ponc", + "%1$s (renamed)" : "%1$s (athainmnithe)", + "renamed file" : "comhad athainmnithe", "\"%1$s\" is a forbidden file or folder name." : "Is ainm toirmiscthe comhaid nó fillteáin é \"%1$s\".", "\"%1$s\" is a forbidden prefix for file or folder names." : "Réimír toirmiscthe é \"%1$s\" d'ainmneacha comhaid nó fillteán.", "\"%1$s\" is not allowed inside a file or folder name." : "Ní cheadaítear \"%1$s\" taobh istigh d'ainm comhaid nó fillteáin.", diff --git a/lib/l10n/ga.json b/lib/l10n/ga.json index ee093dac05e..657ef31f4e3 100644 --- a/lib/l10n/ga.json +++ b/lib/l10n/ga.json @@ -91,6 +91,8 @@ "Destination does not exist" : "Níl ceann scríbe ann", "Destination is not creatable" : "Níl ceann scríbe cruthaithe", "Dot files are not allowed" : "Ní cheadaítear comhaid ponc", + "%1$s (renamed)" : "%1$s (athainmnithe)", + "renamed file" : "comhad athainmnithe", "\"%1$s\" is a forbidden file or folder name." : "Is ainm toirmiscthe comhaid nó fillteáin é \"%1$s\".", "\"%1$s\" is a forbidden prefix for file or folder names." : "Réimír toirmiscthe é \"%1$s\" d'ainmneacha comhaid nó fillteán.", "\"%1$s\" is not allowed inside a file or folder name." : "Ní cheadaítear \"%1$s\" taobh istigh d'ainm comhaid nó fillteáin.", diff --git a/lib/l10n/it.js b/lib/l10n/it.js index 50750f18726..33a2c8ad6bb 100644 --- a/lib/l10n/it.js +++ b/lib/l10n/it.js @@ -82,6 +82,8 @@ OC.L10N.register( "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Il modulo con ID: %s non esiste. Abilitalo nelle impostazioni delle applicazioni o contatta il tuo amministratore.", "File is too large to convert" : "Il file è troppo grande per essere convertito", "Dot files are not allowed" : "I file con un punto iniziale non sono consentiti", + "%1$s (renamed)" : "%1$s (rinominato)", + "renamed file" : "file rinominato", "File already exists" : "Il file esiste già", "Invalid path" : "Percorso non valido", "Failed to create file from template" : "Impossibile creare un file dal modello", diff --git a/lib/l10n/it.json b/lib/l10n/it.json index c6a820d0f5d..4cd53fbed0a 100644 --- a/lib/l10n/it.json +++ b/lib/l10n/it.json @@ -80,6 +80,8 @@ "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Il modulo con ID: %s non esiste. Abilitalo nelle impostazioni delle applicazioni o contatta il tuo amministratore.", "File is too large to convert" : "Il file è troppo grande per essere convertito", "Dot files are not allowed" : "I file con un punto iniziale non sono consentiti", + "%1$s (renamed)" : "%1$s (rinominato)", + "renamed file" : "file rinominato", "File already exists" : "Il file esiste già", "Invalid path" : "Percorso non valido", "Failed to create file from template" : "Impossibile creare un file dal modello", diff --git a/lib/l10n/pl.js b/lib/l10n/pl.js index 0d9fd0cb98a..953682de0ab 100644 --- a/lib/l10n/pl.js +++ b/lib/l10n/pl.js @@ -82,6 +82,8 @@ OC.L10N.register( "Empty file" : "Pusty plik", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Moduł o ID: %s nie istnieje. Włącz go w ustawieniach aplikacji lub skontaktuj się z administratorem.", "Dot files are not allowed" : "Pliki z kropką są niedozwolone", + "%1$s (renamed)" : "%1$s (zmieniona nazwa)", + "renamed file" : "zmieniona nazwa pliku", "Invalid parent path" : "Nieprawidłowa ścieżka nadrzędna", "File already exists" : "Plik już istnieje", "Invalid path" : "Niewłaściwa ścieżka", diff --git a/lib/l10n/pl.json b/lib/l10n/pl.json index c97634e8eff..4edf90eedba 100644 --- a/lib/l10n/pl.json +++ b/lib/l10n/pl.json @@ -80,6 +80,8 @@ "Empty file" : "Pusty plik", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Moduł o ID: %s nie istnieje. Włącz go w ustawieniach aplikacji lub skontaktuj się z administratorem.", "Dot files are not allowed" : "Pliki z kropką są niedozwolone", + "%1$s (renamed)" : "%1$s (zmieniona nazwa)", + "renamed file" : "zmieniona nazwa pliku", "Invalid parent path" : "Nieprawidłowa ścieżka nadrzędna", "File already exists" : "Plik już istnieje", "Invalid path" : "Niewłaściwa ścieżka", diff --git a/lib/l10n/pt_BR.js b/lib/l10n/pt_BR.js index 975a321f27c..bf7f7728be6 100644 --- a/lib/l10n/pt_BR.js +++ b/lib/l10n/pt_BR.js @@ -93,6 +93,8 @@ OC.L10N.register( "Destination does not exist" : "Destino não existe", "Destination is not creatable" : "Destino não pode ser criado", "Dot files are not allowed" : "Arquivos Dot não são permitidos", + "%1$s (renamed)" : "%1$s (renomeado)", + "renamed file" : "arquivo renomeado", "\"%1$s\" is a forbidden file or folder name." : "\"%1$s\" é um nome de arquivo ou pasta proibido.", "\"%1$s\" is a forbidden prefix for file or folder names." : "\"%1$s\" é um prefixo proibido para nomes de arquivos ou pastas.", "\"%1$s\" is not allowed inside a file or folder name." : "\"%1$s\" não é permitido dentro de um nome de arquivo ou pasta.", @@ -141,7 +143,7 @@ OC.L10N.register( "Profile picture" : "Foto do perfil", "About" : "Sobre", "Display name" : "Nome de exibição", - "Headline" : "Título ", + "Headline" : "Título", "Organisation" : "Organização", "Role" : "Função", "Pronouns" : "Pronomes", @@ -273,7 +275,7 @@ OC.L10N.register( "A valid Login must be provided" : "Um Login válido deve ser fornecido", "Login contains whitespace at the beginning or at the end" : "O login contém espaços em branco no início ou no final", "Login must not consist of dots only" : "O login não deve consistir apenas de pontos", - "Login is too long" : "O login é muito longo", + "Login is too long" : "Login é muito longo", "Login is invalid because files already exist for this user" : "O login é inválido porque já existem arquivos para este usuário", "Account disabled" : "Conta desativada", "Login canceled by app" : "Login cancelado pelo aplicativo", diff --git a/lib/l10n/pt_BR.json b/lib/l10n/pt_BR.json index 56506c4cce0..ad7df10df2b 100644 --- a/lib/l10n/pt_BR.json +++ b/lib/l10n/pt_BR.json @@ -91,6 +91,8 @@ "Destination does not exist" : "Destino não existe", "Destination is not creatable" : "Destino não pode ser criado", "Dot files are not allowed" : "Arquivos Dot não são permitidos", + "%1$s (renamed)" : "%1$s (renomeado)", + "renamed file" : "arquivo renomeado", "\"%1$s\" is a forbidden file or folder name." : "\"%1$s\" é um nome de arquivo ou pasta proibido.", "\"%1$s\" is a forbidden prefix for file or folder names." : "\"%1$s\" é um prefixo proibido para nomes de arquivos ou pastas.", "\"%1$s\" is not allowed inside a file or folder name." : "\"%1$s\" não é permitido dentro de um nome de arquivo ou pasta.", @@ -139,7 +141,7 @@ "Profile picture" : "Foto do perfil", "About" : "Sobre", "Display name" : "Nome de exibição", - "Headline" : "Título ", + "Headline" : "Título", "Organisation" : "Organização", "Role" : "Função", "Pronouns" : "Pronomes", @@ -271,7 +273,7 @@ "A valid Login must be provided" : "Um Login válido deve ser fornecido", "Login contains whitespace at the beginning or at the end" : "O login contém espaços em branco no início ou no final", "Login must not consist of dots only" : "O login não deve consistir apenas de pontos", - "Login is too long" : "O login é muito longo", + "Login is too long" : "Login é muito longo", "Login is invalid because files already exist for this user" : "O login é inválido porque já existem arquivos para este usuário", "Account disabled" : "Conta desativada", "Login canceled by app" : "Login cancelado pelo aplicativo", diff --git a/lib/l10n/sr.js b/lib/l10n/sr.js index b62a808994d..6bbd5d62333 100644 --- a/lib/l10n/sr.js +++ b/lib/l10n/sr.js @@ -93,6 +93,8 @@ OC.L10N.register( "Destination does not exist" : "Одредиште не постоји", "Destination is not creatable" : "Одредиште не може да се креира", "Dot files are not allowed" : "Фајлови са почетном тачком нису дозвољени", + "%1$s (renamed)" : "%1$s (преименован)", + "renamed file" : "преименован фајл", "\"%1$s\" is a forbidden file or folder name." : "„%1$s” је забрањено име фајла или фолдера.", "\"%1$s\" is a forbidden prefix for file or folder names." : "„%1$s” је забрањени префикс имена фајла или фолдера.", "\"%1$s\" is not allowed inside a file or folder name." : "„%1$s” није дозвољено у имену фајла или фолдера.", diff --git a/lib/l10n/sr.json b/lib/l10n/sr.json index 7dbc69c583a..8d6b3cc72c3 100644 --- a/lib/l10n/sr.json +++ b/lib/l10n/sr.json @@ -91,6 +91,8 @@ "Destination does not exist" : "Одредиште не постоји", "Destination is not creatable" : "Одредиште не може да се креира", "Dot files are not allowed" : "Фајлови са почетном тачком нису дозвољени", + "%1$s (renamed)" : "%1$s (преименован)", + "renamed file" : "преименован фајл", "\"%1$s\" is a forbidden file or folder name." : "„%1$s” је забрањено име фајла или фолдера.", "\"%1$s\" is a forbidden prefix for file or folder names." : "„%1$s” је забрањени префикс имена фајла или фолдера.", "\"%1$s\" is not allowed inside a file or folder name." : "„%1$s” није дозвољено у имену фајла или фолдера.", diff --git a/lib/l10n/sv.js b/lib/l10n/sv.js index ed4c4af3ef0..f203a0471e7 100644 --- a/lib/l10n/sv.js +++ b/lib/l10n/sv.js @@ -93,6 +93,8 @@ OC.L10N.register( "Destination does not exist" : "Destinationen finns inte", "Destination is not creatable" : "Destinationen går inte att skapa", "Dot files are not allowed" : "Dot-filer är inte tillåtna", + "%1$s (renamed)" : "%1$s (omdöpt)", + "renamed file" : "omdöpt fil", "\"%1$s\" is a forbidden file or folder name." : "\"%1$s\" är ett förbjudet fil- eller mappnamn.", "\"%1$s\" is a forbidden prefix for file or folder names." : "\"%1$s\" är ett förbjudet prefix för fil- eller mappnamn.", "\"%1$s\" is not allowed inside a file or folder name." : "\"%1$s\" är inte tillåtet i ett fil- eller mappnamn.", diff --git a/lib/l10n/sv.json b/lib/l10n/sv.json index 55855426f34..3f3db6805e8 100644 --- a/lib/l10n/sv.json +++ b/lib/l10n/sv.json @@ -91,6 +91,8 @@ "Destination does not exist" : "Destinationen finns inte", "Destination is not creatable" : "Destinationen går inte att skapa", "Dot files are not allowed" : "Dot-filer är inte tillåtna", + "%1$s (renamed)" : "%1$s (omdöpt)", + "renamed file" : "omdöpt fil", "\"%1$s\" is a forbidden file or folder name." : "\"%1$s\" är ett förbjudet fil- eller mappnamn.", "\"%1$s\" is a forbidden prefix for file or folder names." : "\"%1$s\" är ett förbjudet prefix för fil- eller mappnamn.", "\"%1$s\" is not allowed inside a file or folder name." : "\"%1$s\" är inte tillåtet i ett fil- eller mappnamn.", diff --git a/lib/l10n/zh_CN.js b/lib/l10n/zh_CN.js index 1637350f1e9..0cc4fd8d378 100644 --- a/lib/l10n/zh_CN.js +++ b/lib/l10n/zh_CN.js @@ -93,6 +93,8 @@ OC.L10N.register( "Destination does not exist" : "目标不存在", "Destination is not creatable" : "目标不可创建", "Dot files are not allowed" : "以 . 开头的文件不被允许", + "%1$s (renamed)" : "%1$s(已重命名)", + "renamed file" : "已重命名文件", "\"%1$s\" is a forbidden file or folder name." : "\"%1$s\" 是禁止的文件或文件夹名称。", "\"%1$s\" is a forbidden prefix for file or folder names." : "\"%1$s\" 是文件或文件夹名称的禁止前缀。", "\"%1$s\" is not allowed inside a file or folder name." : "文件或文件夹名称中不允许出现 \"%1$s\"。", diff --git a/lib/l10n/zh_CN.json b/lib/l10n/zh_CN.json index dd94f1bb5cf..2ba31a10e87 100644 --- a/lib/l10n/zh_CN.json +++ b/lib/l10n/zh_CN.json @@ -91,6 +91,8 @@ "Destination does not exist" : "目标不存在", "Destination is not creatable" : "目标不可创建", "Dot files are not allowed" : "以 . 开头的文件不被允许", + "%1$s (renamed)" : "%1$s(已重命名)", + "renamed file" : "已重命名文件", "\"%1$s\" is a forbidden file or folder name." : "\"%1$s\" 是禁止的文件或文件夹名称。", "\"%1$s\" is a forbidden prefix for file or folder names." : "\"%1$s\" 是文件或文件夹名称的禁止前缀。", "\"%1$s\" is not allowed inside a file or folder name." : "文件或文件夹名称中不允许出现 \"%1$s\"。", diff --git a/lib/l10n/zh_HK.js b/lib/l10n/zh_HK.js index a85c29e400c..b4bb06b4cdc 100644 --- a/lib/l10n/zh_HK.js +++ b/lib/l10n/zh_HK.js @@ -93,6 +93,8 @@ OC.L10N.register( "Destination does not exist" : "目的地不存在", "Destination is not creatable" : "無法建立目的地", "Dot files are not allowed" : "不允許小數點開頭的檔案", + "%1$s (renamed)" : "%1$s(已重新命名)", + "renamed file" : "已重新命名的檔案", "\"%1$s\" is a forbidden file or folder name." : "「%1$s」是禁止的檔案或資料夾名稱。", "\"%1$s\" is a forbidden prefix for file or folder names." : "「%1$s」是禁止的檔案或資料夾名稱前綴。", "\"%1$s\" is not allowed inside a file or folder name." : "「%1$s」不允許出現在檔案或資料夾名稱中。", diff --git a/lib/l10n/zh_HK.json b/lib/l10n/zh_HK.json index b58db1e510c..6cc1edd0f35 100644 --- a/lib/l10n/zh_HK.json +++ b/lib/l10n/zh_HK.json @@ -91,6 +91,8 @@ "Destination does not exist" : "目的地不存在", "Destination is not creatable" : "無法建立目的地", "Dot files are not allowed" : "不允許小數點開頭的檔案", + "%1$s (renamed)" : "%1$s(已重新命名)", + "renamed file" : "已重新命名的檔案", "\"%1$s\" is a forbidden file or folder name." : "「%1$s」是禁止的檔案或資料夾名稱。", "\"%1$s\" is a forbidden prefix for file or folder names." : "「%1$s」是禁止的檔案或資料夾名稱前綴。", "\"%1$s\" is not allowed inside a file or folder name." : "「%1$s」不允許出現在檔案或資料夾名稱中。", diff --git a/lib/l10n/zh_TW.js b/lib/l10n/zh_TW.js index 4f92a865290..016689524c5 100644 --- a/lib/l10n/zh_TW.js +++ b/lib/l10n/zh_TW.js @@ -93,6 +93,8 @@ OC.L10N.register( "Destination does not exist" : "目的地不存在", "Destination is not creatable" : "無法建立目的地", "Dot files are not allowed" : "不允許小數點開頭的檔案", + "%1$s (renamed)" : "%1$s(已重新命名)", + "renamed file" : "已重新命名檔案", "\"%1$s\" is a forbidden file or folder name." : "「%1$s」是禁止的檔案或資料夾名稱。", "\"%1$s\" is a forbidden prefix for file or folder names." : "「%1$s」是禁止的檔案或資料夾名稱前綴。", "\"%1$s\" is not allowed inside a file or folder name." : "「%1$s」不允許出現在檔案或資料夾名稱中。", diff --git a/lib/l10n/zh_TW.json b/lib/l10n/zh_TW.json index 34b32540bc8..67f95c65e75 100644 --- a/lib/l10n/zh_TW.json +++ b/lib/l10n/zh_TW.json @@ -91,6 +91,8 @@ "Destination does not exist" : "目的地不存在", "Destination is not creatable" : "無法建立目的地", "Dot files are not allowed" : "不允許小數點開頭的檔案", + "%1$s (renamed)" : "%1$s(已重新命名)", + "renamed file" : "已重新命名檔案", "\"%1$s\" is a forbidden file or folder name." : "「%1$s」是禁止的檔案或資料夾名稱。", "\"%1$s\" is a forbidden prefix for file or folder names." : "「%1$s」是禁止的檔案或資料夾名稱前綴。", "\"%1$s\" is not allowed inside a file or folder name." : "「%1$s」不允許出現在檔案或資料夾名稱中。", diff --git a/lib/private/Log/LogDetails.php b/lib/private/Log/LogDetails.php index b3ae23a3770..8c1efaea20d 100644 --- a/lib/private/Log/LogDetails.php +++ b/lib/private/Log/LogDetails.php @@ -59,6 +59,10 @@ abstract class LogDetails { 'userAgent', 'version' ); + $clientReqId = $request->getHeader('X-Request-Id'); + if ($clientReqId !== '') { + $entry['clientReqId'] = $clientReqId; + } if (is_array($message)) { // Exception messages are extracted and the exception is put into a separate field diff --git a/openapi.json b/openapi.json index 61db0fbc652..b28f160dd5d 100644 --- a/openapi.json +++ b/openapi.json @@ -19696,6 +19696,19 @@ 1 ] } + }, + { + "name": "mimeFallback", + "in": "query", + "description": "Whether to fallback to the mime icon if no preview is available", + "schema": { + "type": "integer", + "default": 0, + "enum": [ + 0, + 1 + ] + } } ], "responses": { @@ -19733,6 +19746,16 @@ "schema": {} } } + }, + "303": { + "description": "Redirect to the mime icon url if mimeFallback is true", + "headers": { + "Location": { + "schema": { + "type": "string" + } + } + } } } } |