diff options
Diffstat (limited to 'apps')
135 files changed, 1875 insertions, 678 deletions
diff --git a/apps/cloud_federation_api/lib/Controller/RequestHandlerController.php b/apps/cloud_federation_api/lib/Controller/RequestHandlerController.php index 02105fb3f9c..53448119af3 100644 --- a/apps/cloud_federation_api/lib/Controller/RequestHandlerController.php +++ b/apps/cloud_federation_api/lib/Controller/RequestHandlerController.php @@ -199,6 +199,7 @@ class RequestHandlerController extends Controller { $e->getCode() ); } catch (\Exception $e) { + $this->logger->error($e->getMessage(), ['exception' => $e]); return new JSONResponse( ['message' => 'Internal error at ' . $this->urlGenerator->getBaseUrl()], Http::STATUS_BAD_REQUEST diff --git a/apps/dashboard/lib/Service/BackgroundService.php b/apps/dashboard/lib/Service/BackgroundService.php index 13ad03cf860..c98b92412cb 100644 --- a/apps/dashboard/lib/Service/BackgroundService.php +++ b/apps/dashboard/lib/Service/BackgroundService.php @@ -150,6 +150,10 @@ class BackgroundService { $userFolder = $this->rootFolder->getUserFolder($this->userId); /** @var File $file */ $file = $userFolder->get($path); + $image = new \OCP\Image(); + if ($image->loadFromFileHandle($file->fopen('r')) === false) { + throw new InvalidArgumentException('Invalid image file'); + } $this->getAppDataFolder()->newFile('background.jpg', $file->fopen('r')); } diff --git a/apps/dav/appinfo/v1/caldav.php b/apps/dav/appinfo/v1/caldav.php index 6a496a992e6..1961df5f62b 100644 --- a/apps/dav/appinfo/v1/caldav.php +++ b/apps/dav/appinfo/v1/caldav.php @@ -61,7 +61,6 @@ $userManager = \OC::$server->getUserManager(); $random = \OC::$server->getSecureRandom(); $logger = \OC::$server->get(LoggerInterface::class); $dispatcher = \OC::$server->get(\OCP\EventDispatcher\IEventDispatcher::class); -$legacyDispatcher = \OC::$server->getEventDispatcher(); $config = \OC::$server->get(\OCP\IConfig::class); $calDavBackend = new CalDavBackend( @@ -72,7 +71,6 @@ $calDavBackend = new CalDavBackend( $random, $logger, $dispatcher, - $legacyDispatcher, $config, true ); diff --git a/apps/dav/appinfo/v1/carddav.php b/apps/dav/appinfo/v1/carddav.php index a0306118781..afb4c78f5f9 100644 --- a/apps/dav/appinfo/v1/carddav.php +++ b/apps/dav/appinfo/v1/carddav.php @@ -8,6 +8,7 @@ * @author John Molakvoæ <skjnldsv@protonmail.com> * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> + * @author Thomas Citharel <nextcloud@tcit.fr> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 @@ -60,7 +61,7 @@ $principalBackend = new Principal( 'principals/' ); $db = \OC::$server->getDatabaseConnection(); -$cardDavBackend = new CardDavBackend($db, $principalBackend, \OC::$server->getUserManager(), \OC::$server->getGroupManager(), \OC::$server->get(\OCP\EventDispatcher\IEventDispatcher::class), \OC::$server->getEventDispatcher()); +$cardDavBackend = new CardDavBackend($db, $principalBackend, \OC::$server->getUserManager(), \OC::$server->getGroupManager(), \OC::$server->get(\OCP\EventDispatcher\IEventDispatcher::class)); $debugging = \OC::$server->getConfig()->getSystemValue('debug', false); diff --git a/apps/dav/composer/composer/autoload_classmap.php b/apps/dav/composer/composer/autoload_classmap.php index 0583b94a86c..f40917fbd01 100644 --- a/apps/dav/composer/composer/autoload_classmap.php +++ b/apps/dav/composer/composer/autoload_classmap.php @@ -224,6 +224,7 @@ return array( 'OCA\\DAV\\Events\\SubscriptionCreatedEvent' => $baseDir . '/../lib/Events/SubscriptionCreatedEvent.php', 'OCA\\DAV\\Events\\SubscriptionDeletedEvent' => $baseDir . '/../lib/Events/SubscriptionDeletedEvent.php', 'OCA\\DAV\\Events\\SubscriptionUpdatedEvent' => $baseDir . '/../lib/Events/SubscriptionUpdatedEvent.php', + 'OCA\\DAV\\Exception\\ServerMaintenanceMode' => $baseDir . '/../lib/Exception/ServerMaintenanceMode.php', 'OCA\\DAV\\Exception\\UnsupportedLimitOnInitialSyncException' => $baseDir . '/../lib/Exception/UnsupportedLimitOnInitialSyncException.php', 'OCA\\DAV\\Files\\BrowserErrorPagePlugin' => $baseDir . '/../lib/Files/BrowserErrorPagePlugin.php', 'OCA\\DAV\\Files\\FileSearchBackend' => $baseDir . '/../lib/Files/FileSearchBackend.php', @@ -235,10 +236,15 @@ return array( 'OCA\\DAV\\HookManager' => $baseDir . '/../lib/HookManager.php', 'OCA\\DAV\\Listener\\ActivityUpdaterListener' => $baseDir . '/../lib/Listener/ActivityUpdaterListener.php', 'OCA\\DAV\\Listener\\AddressbookListener' => $baseDir . '/../lib/Listener/AddressbookListener.php', + 'OCA\\DAV\\Listener\\BirthdayListener' => $baseDir . '/../lib/Listener/BirthdayListener.php', 'OCA\\DAV\\Listener\\CalendarContactInteractionListener' => $baseDir . '/../lib/Listener/CalendarContactInteractionListener.php', 'OCA\\DAV\\Listener\\CalendarDeletionDefaultUpdaterListener' => $baseDir . '/../lib/Listener/CalendarDeletionDefaultUpdaterListener.php', 'OCA\\DAV\\Listener\\CalendarObjectReminderUpdaterListener' => $baseDir . '/../lib/Listener/CalendarObjectReminderUpdaterListener.php', + 'OCA\\DAV\\Listener\\CalendarPublicationListener' => $baseDir . '/../lib/Listener/CalendarPublicationListener.php', + 'OCA\\DAV\\Listener\\CalendarShareUpdateListener' => $baseDir . '/../lib/Listener/CalendarShareUpdateListener.php', 'OCA\\DAV\\Listener\\CardListener' => $baseDir . '/../lib/Listener/CardListener.php', + 'OCA\\DAV\\Listener\\ClearPhotoCacheListener' => $baseDir . '/../lib/Listener/ClearPhotoCacheListener.php', + 'OCA\\DAV\\Listener\\SubscriptionListener' => $baseDir . '/../lib/Listener/SubscriptionListener.php', 'OCA\\DAV\\Migration\\BuildCalendarSearchIndex' => $baseDir . '/../lib/Migration/BuildCalendarSearchIndex.php', 'OCA\\DAV\\Migration\\BuildCalendarSearchIndexBackgroundJob' => $baseDir . '/../lib/Migration/BuildCalendarSearchIndexBackgroundJob.php', 'OCA\\DAV\\Migration\\BuildSocialSearchIndex' => $baseDir . '/../lib/Migration/BuildSocialSearchIndex.php', diff --git a/apps/dav/composer/composer/autoload_static.php b/apps/dav/composer/composer/autoload_static.php index 301be38246a..60b437c9fac 100644 --- a/apps/dav/composer/composer/autoload_static.php +++ b/apps/dav/composer/composer/autoload_static.php @@ -239,6 +239,7 @@ class ComposerStaticInitDAV 'OCA\\DAV\\Events\\SubscriptionCreatedEvent' => __DIR__ . '/..' . '/../lib/Events/SubscriptionCreatedEvent.php', 'OCA\\DAV\\Events\\SubscriptionDeletedEvent' => __DIR__ . '/..' . '/../lib/Events/SubscriptionDeletedEvent.php', 'OCA\\DAV\\Events\\SubscriptionUpdatedEvent' => __DIR__ . '/..' . '/../lib/Events/SubscriptionUpdatedEvent.php', + 'OCA\\DAV\\Exception\\ServerMaintenanceMode' => __DIR__ . '/..' . '/../lib/Exception/ServerMaintenanceMode.php', 'OCA\\DAV\\Exception\\UnsupportedLimitOnInitialSyncException' => __DIR__ . '/..' . '/../lib/Exception/UnsupportedLimitOnInitialSyncException.php', 'OCA\\DAV\\Files\\BrowserErrorPagePlugin' => __DIR__ . '/..' . '/../lib/Files/BrowserErrorPagePlugin.php', 'OCA\\DAV\\Files\\FileSearchBackend' => __DIR__ . '/..' . '/../lib/Files/FileSearchBackend.php', @@ -250,10 +251,15 @@ class ComposerStaticInitDAV 'OCA\\DAV\\HookManager' => __DIR__ . '/..' . '/../lib/HookManager.php', 'OCA\\DAV\\Listener\\ActivityUpdaterListener' => __DIR__ . '/..' . '/../lib/Listener/ActivityUpdaterListener.php', 'OCA\\DAV\\Listener\\AddressbookListener' => __DIR__ . '/..' . '/../lib/Listener/AddressbookListener.php', + 'OCA\\DAV\\Listener\\BirthdayListener' => __DIR__ . '/..' . '/../lib/Listener/BirthdayListener.php', 'OCA\\DAV\\Listener\\CalendarContactInteractionListener' => __DIR__ . '/..' . '/../lib/Listener/CalendarContactInteractionListener.php', 'OCA\\DAV\\Listener\\CalendarDeletionDefaultUpdaterListener' => __DIR__ . '/..' . '/../lib/Listener/CalendarDeletionDefaultUpdaterListener.php', 'OCA\\DAV\\Listener\\CalendarObjectReminderUpdaterListener' => __DIR__ . '/..' . '/../lib/Listener/CalendarObjectReminderUpdaterListener.php', + 'OCA\\DAV\\Listener\\CalendarPublicationListener' => __DIR__ . '/..' . '/../lib/Listener/CalendarPublicationListener.php', + 'OCA\\DAV\\Listener\\CalendarShareUpdateListener' => __DIR__ . '/..' . '/../lib/Listener/CalendarShareUpdateListener.php', 'OCA\\DAV\\Listener\\CardListener' => __DIR__ . '/..' . '/../lib/Listener/CardListener.php', + 'OCA\\DAV\\Listener\\ClearPhotoCacheListener' => __DIR__ . '/..' . '/../lib/Listener/ClearPhotoCacheListener.php', + 'OCA\\DAV\\Listener\\SubscriptionListener' => __DIR__ . '/..' . '/../lib/Listener/SubscriptionListener.php', 'OCA\\DAV\\Migration\\BuildCalendarSearchIndex' => __DIR__ . '/..' . '/../lib/Migration/BuildCalendarSearchIndex.php', 'OCA\\DAV\\Migration\\BuildCalendarSearchIndexBackgroundJob' => __DIR__ . '/..' . '/../lib/Migration/BuildCalendarSearchIndexBackgroundJob.php', 'OCA\\DAV\\Migration\\BuildSocialSearchIndex' => __DIR__ . '/..' . '/../lib/Migration/BuildSocialSearchIndex.php', diff --git a/apps/dav/l10n/sl.js b/apps/dav/l10n/sl.js index 2758b9eab04..bb07ed9b9f0 100644 --- a/apps/dav/l10n/sl.js +++ b/apps/dav/l10n/sl.js @@ -81,6 +81,7 @@ OC.L10N.register( "More options at %s" : "Več možnosti je na %s", "Contacts" : "Stiki", "You deleted address book {addressbook}" : "Izbrišete imenik {addressbook}", + "Could not write file contents" : "Ni mogoče zapisati vsebine datoteke", "System is in maintenance mode." : "Sistem je v vzdrževalnem načinu.", "Upgrade needed" : "Zahtevana je posodobitev", "Your %s needs to be configured to use HTTPS in order to use CalDAV and CardDAV with iOS/macOS." : "Za uporabo CalDAV in CardDAV v okoljih iOS/macOS je treba %s nastaviti za uporabo HTTPS.", @@ -100,7 +101,7 @@ OC.L10N.register( "to" : "do", "Delete slot" : "Izbriši možnost", "No working hours set" : "Ni navedenih delovnih ur", - "Add slot" : "Dodaj polje", + "Add slot" : "Dodaj možnost", "Monday" : "ponedeljek", "Tuesday" : "torek", "Wednesday" : "sreda", @@ -109,6 +110,7 @@ OC.L10N.register( "Saturday" : "sobota", "Sunday" : "nedelja", "Save" : "Shrani", + "Failed to load availability" : "Nalaganje seznama razpoložljivih polj je spodletelo", "Calendar server" : "Strežnik koledarja", "Send invitations to attendees" : "Pošlji povabilo udeležencem", "Automatically generate a birthday calendar" : "Samodejno ustvari koledar rojstnih dni", diff --git a/apps/dav/l10n/sl.json b/apps/dav/l10n/sl.json index 591db863702..cecfc2cb0c4 100644 --- a/apps/dav/l10n/sl.json +++ b/apps/dav/l10n/sl.json @@ -79,6 +79,7 @@ "More options at %s" : "Več možnosti je na %s", "Contacts" : "Stiki", "You deleted address book {addressbook}" : "Izbrišete imenik {addressbook}", + "Could not write file contents" : "Ni mogoče zapisati vsebine datoteke", "System is in maintenance mode." : "Sistem je v vzdrževalnem načinu.", "Upgrade needed" : "Zahtevana je posodobitev", "Your %s needs to be configured to use HTTPS in order to use CalDAV and CardDAV with iOS/macOS." : "Za uporabo CalDAV in CardDAV v okoljih iOS/macOS je treba %s nastaviti za uporabo HTTPS.", @@ -98,7 +99,7 @@ "to" : "do", "Delete slot" : "Izbriši možnost", "No working hours set" : "Ni navedenih delovnih ur", - "Add slot" : "Dodaj polje", + "Add slot" : "Dodaj možnost", "Monday" : "ponedeljek", "Tuesday" : "torek", "Wednesday" : "sreda", @@ -107,6 +108,7 @@ "Saturday" : "sobota", "Sunday" : "nedelja", "Save" : "Shrani", + "Failed to load availability" : "Nalaganje seznama razpoložljivih polj je spodletelo", "Calendar server" : "Strežnik koledarja", "Send invitations to attendees" : "Pošlji povabilo udeležencem", "Automatically generate a birthday calendar" : "Samodejno ustvari koledar rojstnih dni", diff --git a/apps/dav/lib/AppInfo/Application.php b/apps/dav/lib/AppInfo/Application.php index 580918a6450..e4eeed61dbe 100644 --- a/apps/dav/lib/AppInfo/Application.php +++ b/apps/dav/lib/AppInfo/Application.php @@ -35,18 +35,14 @@ namespace OCA\DAV\AppInfo; use Exception; use OCA\DAV\BackgroundJob\UpdateCalendarResourcesRoomsBackgroundJob; use OCA\DAV\CalDAV\Activity\Backend; -use OCA\DAV\CalDAV\BirthdayService; -use OCA\DAV\CalDAV\CalDavBackend; use OCA\DAV\CalDAV\CalendarManager; use OCA\DAV\CalDAV\CalendarProvider; -use OCA\DAV\CalDAV\Reminder\Backend as ReminderBackend; use OCA\DAV\CalDAV\Reminder\NotificationProvider\AudioProvider; use OCA\DAV\CalDAV\Reminder\NotificationProvider\EmailProvider; use OCA\DAV\CalDAV\Reminder\NotificationProvider\PushProvider; use OCA\DAV\CalDAV\Reminder\NotificationProviderManager; use OCA\DAV\CalDAV\Reminder\Notifier; -use OCA\DAV\CalDAV\WebcalCaching\RefreshWebcalService; use OCA\DAV\Capabilities; use OCA\DAV\CardDAV\CardDavBackend; use OCA\DAV\CardDAV\ContactsManager; @@ -64,19 +60,28 @@ use OCA\DAV\Events\CalendarObjectDeletedEvent; use OCA\DAV\Events\CalendarObjectMovedToTrashEvent; use OCA\DAV\Events\CalendarObjectRestoredEvent; use OCA\DAV\Events\CalendarObjectUpdatedEvent; +use OCA\DAV\Events\CalendarPublishedEvent; use OCA\DAV\Events\CalendarRestoredEvent; use OCA\DAV\Events\CalendarShareUpdatedEvent; +use OCA\DAV\Events\CalendarUnpublishedEvent; use OCA\DAV\Events\CalendarUpdatedEvent; use OCA\DAV\Events\CardCreatedEvent; use OCA\DAV\Events\CardDeletedEvent; use OCA\DAV\Events\CardUpdatedEvent; +use OCA\DAV\Events\SubscriptionCreatedEvent; +use OCA\DAV\Events\SubscriptionDeletedEvent; use OCA\DAV\HookManager; use OCA\DAV\Listener\ActivityUpdaterListener; use OCA\DAV\Listener\AddressbookListener; +use OCA\DAV\Listener\BirthdayListener; use OCA\DAV\Listener\CalendarContactInteractionListener; use OCA\DAV\Listener\CalendarDeletionDefaultUpdaterListener; use OCA\DAV\Listener\CalendarObjectReminderUpdaterListener; +use OCA\DAV\Listener\CalendarPublicationListener; +use OCA\DAV\Listener\CalendarShareUpdateListener; use OCA\DAV\Listener\CardListener; +use OCA\DAV\Listener\ClearPhotoCacheListener; +use OCA\DAV\Listener\SubscriptionListener; use OCA\DAV\Search\ContactsSearchProvider; use OCA\DAV\Search\EventsSearchProvider; use OCA\DAV\Search\TasksSearchProvider; @@ -154,6 +159,12 @@ class Application extends App implements IBootstrap { $context->registerEventListener(CalendarObjectRestoredEvent::class, ActivityUpdaterListener::class); $context->registerEventListener(CalendarObjectRestoredEvent::class, CalendarObjectReminderUpdaterListener::class); $context->registerEventListener(CalendarShareUpdatedEvent::class, CalendarContactInteractionListener::class); + $context->registerEventListener(CalendarPublishedEvent::class, CalendarPublicationListener::class); + $context->registerEventListener(CalendarUnpublishedEvent::class, CalendarPublicationListener::class); + $context->registerEventListener(CalendarShareUpdatedEvent::class, CalendarShareUpdateListener::class); + + $context->registerEventListener(SubscriptionCreatedEvent::class, SubscriptionListener::class); + $context->registerEventListener(SubscriptionDeletedEvent::class, SubscriptionListener::class); $context->registerEventListener(AddressBookCreatedEvent::class, AddressbookListener::class); @@ -163,6 +174,11 @@ class Application extends App implements IBootstrap { $context->registerEventListener(CardCreatedEvent::class, CardListener::class); $context->registerEventListener(CardDeletedEvent::class, CardListener::class); $context->registerEventListener(CardUpdatedEvent::class, CardListener::class); + $context->registerEventListener(CardCreatedEvent::class, BirthdayListener::class); + $context->registerEventListener(CardDeletedEvent::class, BirthdayListener::class); + $context->registerEventListener(CardUpdatedEvent::class, BirthdayListener::class); + $context->registerEventListener(CardDeletedEvent::class, ClearPhotoCacheListener::class); + $context->registerEventListener(CardUpdatedEvent::class, ClearPhotoCacheListener::class); $context->registerNotifierService(Notifier::class); @@ -195,44 +211,6 @@ class Application extends App implements IBootstrap { } }); - $birthdayListener = function ($event) use ($container): void { - if ($event instanceof GenericEvent) { - /** @var BirthdayService $b */ - $b = $container->query(BirthdayService::class); - $b->onCardChanged( - (int) $event->getArgument('addressBookId'), - (string) $event->getArgument('cardUri'), - (string) $event->getArgument('cardData') - ); - } - }; - - $dispatcher->addListener('\OCA\DAV\CardDAV\CardDavBackend::createCard', $birthdayListener); - $dispatcher->addListener('\OCA\DAV\CardDAV\CardDavBackend::updateCard', $birthdayListener); - $dispatcher->addListener('\OCA\DAV\CardDAV\CardDavBackend::deleteCard', function ($event) use ($container) { - if ($event instanceof GenericEvent) { - /** @var BirthdayService $b */ - $b = $container->query(BirthdayService::class); - $b->onCardDeleted( - (int) $event->getArgument('addressBookId'), - (string) $event->getArgument('cardUri') - ); - } - }); - - $clearPhotoCache = function ($event) use ($container): void { - if ($event instanceof GenericEvent) { - /** @var PhotoCache $p */ - $p = $container->query(PhotoCache::class); - $p->delete( - $event->getArgument('addressBookId'), - $event->getArgument('cardUri') - ); - } - }; - $dispatcher->addListener('\OCA\DAV\CardDAV\CardDavBackend::updateCard', $clearPhotoCache); - $dispatcher->addListener('\OCA\DAV\CardDAV\CardDavBackend::deleteCard', $clearPhotoCache); - $dispatcher->addListener('OC\AccountManager::userUpdated', function (GenericEvent $event) use ($container) { $user = $event->getSubject(); /** @var SyncService $syncService */ @@ -254,16 +232,6 @@ class Application extends App implements IBootstrap { // Here we should recalculate if reminders should be sent to new or old sharees }); - $dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::publishCalendar', function (GenericEvent $event) use ($container) { - /** @var Backend $backend */ - $backend = $container->query(Backend::class); - $backend->onCalendarPublication( - $event->getArgument('calendarData'), - $event->getArgument('public') - ); - }); - - $dispatcher->addListener('OCP\Federation\TrustedServerEvent::remove', function (GenericEvent $event) { /** @var CardDavBackend $cardDavBackend */ @@ -276,48 +244,6 @@ class Application extends App implements IBootstrap { } ); - $dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::createSubscription', - function (GenericEvent $event) use ($container, $serverContainer) { - $jobList = $serverContainer->getJobList(); - $subscriptionData = $event->getArgument('subscriptionData'); - - /** - * Initial subscription refetch - * - * @var RefreshWebcalService $refreshWebcalService - */ - $refreshWebcalService = $container->query(RefreshWebcalService::class); - $refreshWebcalService->refreshSubscription( - (string) $subscriptionData['principaluri'], - (string) $subscriptionData['uri'] - ); - - $jobList->add(\OCA\DAV\BackgroundJob\RefreshWebcalJob::class, [ - 'principaluri' => $subscriptionData['principaluri'], - 'uri' => $subscriptionData['uri'] - ]); - } - ); - - $dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::deleteSubscription', - function (GenericEvent $event) use ($container, $serverContainer) { - $jobList = $serverContainer->getJobList(); - $subscriptionData = $event->getArgument('subscriptionData'); - - $jobList->remove(\OCA\DAV\BackgroundJob\RefreshWebcalJob::class, [ - 'principaluri' => $subscriptionData['principaluri'], - 'uri' => $subscriptionData['uri'] - ]); - - /** @var CalDavBackend $calDavBackend */ - $calDavBackend = $container->get(CalDavBackend::class); - $calDavBackend->purgeAllCachedEventsForSubscription($subscriptionData['id']); - /** @var ReminderBackend $calDavBackend */ - $reminderBackend = $container->get(ReminderBackend::class); - $reminderBackend->cleanRemindersForCalendar((int) $subscriptionData['id']); - } - ); - $eventHandler = function () use ($container, $serverContainer): void { try { /** @var UpdateCalendarResourcesRoomsBackgroundJob $job */ diff --git a/apps/dav/lib/CalDAV/Activity/Backend.php b/apps/dav/lib/CalDAV/Activity/Backend.php index 84ba50b8c37..af2d790e10d 100644 --- a/apps/dav/lib/CalDAV/Activity/Backend.php +++ b/apps/dav/lib/CalDAV/Activity/Backend.php @@ -119,7 +119,7 @@ class Backend { * @param array $calendarData * @param bool $publishStatus */ - public function onCalendarPublication(array $calendarData, $publishStatus) { + public function onCalendarPublication(array $calendarData, bool $publishStatus): void { $this->triggerCalendarActivity($publishStatus ? Calendar::SUBJECT_PUBLISH : Calendar::SUBJECT_UNPUBLISH, $calendarData); } diff --git a/apps/dav/lib/CalDAV/CalDavBackend.php b/apps/dav/lib/CalDAV/CalDavBackend.php index 8bdc084cd7b..f445382ce8c 100644 --- a/apps/dav/lib/CalDAV/CalDavBackend.php +++ b/apps/dav/lib/CalDAV/CalDavBackend.php @@ -95,8 +95,6 @@ use Sabre\VObject\ParseException; use Sabre\VObject\Property; use Sabre\VObject\Reader; use Sabre\VObject\Recur\EventIterator; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Symfony\Component\EventDispatcher\GenericEvent; use function array_column; use function array_merge; use function array_values; @@ -150,7 +148,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription * @var array * @psalm-var array<string, string[]> */ - public $propertyMap = [ + public array $propertyMap = [ '{DAV:}displayname' => ['displayname', 'string'], '{urn:ietf:params:xml:ns:caldav}calendar-description' => ['description', 'string'], '{urn:ietf:params:xml:ns:caldav}calendar-timezone' => ['timezone', 'string'], @@ -164,7 +162,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription * * @var array */ - public $subscriptionPropertyMap = [ + public array $subscriptionPropertyMap = [ '{DAV:}displayname' => ['displayname', 'string'], '{http://apple.com/ns/ical/}refreshrate' => ['refreshrate', 'string'], '{http://apple.com/ns/ical/}calendar-order' => ['calendarorder', 'int'], @@ -195,7 +193,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription ]; /** @var array parameters to index */ - public static $indexParameters = [ + public static array $indexParameters = [ 'ATTENDEE' => ['CN'], 'ORGANIZER' => ['CN'], ]; @@ -203,43 +201,19 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription /** * @var string[] Map of uid => display name */ - protected $userDisplayNames; - - /** @var IDBConnection */ - private $db; - - /** @var Backend */ - private $calendarSharingBackend; - - /** @var Principal */ - private $principalBackend; - - /** @var IUserManager */ - private $userManager; - - /** @var ISecureRandom */ - private $random; + protected array $userDisplayNames; + private IDBConnection $db; + private Backend $calendarSharingBackend; + private Principal $principalBackend; + private IUserManager $userManager; + private ISecureRandom $random; private LoggerInterface $logger; + private IEventDispatcher $dispatcher; + private IConfig $config; + private bool $legacyEndpoint; + private string $dbObjectPropertiesTable = 'calendarobjects_props'; - /** @var IEventDispatcher */ - private $dispatcher; - - /** @var EventDispatcherInterface */ - private $legacyDispatcher; - - /** @var IConfig */ - private $config; - - /** @var bool */ - private $legacyEndpoint; - - /** @var string */ - private $dbObjectPropertiesTable = 'calendarobjects_props'; - - /** - * CalDavBackend constructor. - */ public function __construct(IDBConnection $db, Principal $principalBackend, IUserManager $userManager, @@ -247,7 +221,6 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription ISecureRandom $random, LoggerInterface $logger, IEventDispatcher $dispatcher, - EventDispatcherInterface $legacyDispatcher, IConfig $config, bool $legacyEndpoint = false) { $this->db = $db; @@ -257,7 +230,6 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription $this->random = $random; $this->logger = $logger; $this->dispatcher = $dispatcher; - $this->legacyDispatcher = $legacyDispatcher; $this->config = $config; $this->legacyEndpoint = $legacyEndpoint; } @@ -1301,15 +1273,6 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription $subscriptionRow = $this->getSubscriptionById($calendarId); $this->dispatcher->dispatchTyped(new CachedCalendarObjectCreatedEvent((int)$calendarId, $subscriptionRow, [], $objectRow)); - $this->legacyDispatcher->dispatch('\OCA\DAV\CalDAV\CalDavBackend::createCachedCalendarObject', new GenericEvent( - '\OCA\DAV\CalDAV\CalDavBackend::createCachedCalendarObject', - [ - 'subscriptionId' => $calendarId, - 'calendarData' => $subscriptionRow, - 'shares' => [], - 'objectData' => $objectRow, - ] - )); } return '"' . $extraData['etag'] . '"'; @@ -1366,15 +1329,6 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription $subscriptionRow = $this->getSubscriptionById($calendarId); $this->dispatcher->dispatchTyped(new CachedCalendarObjectUpdatedEvent((int)$calendarId, $subscriptionRow, [], $objectRow)); - $this->legacyDispatcher->dispatch('\OCA\DAV\CalDAV\CalDavBackend::updateCachedCalendarObject', new GenericEvent( - '\OCA\DAV\CalDAV\CalDavBackend::updateCachedCalendarObject', - [ - 'subscriptionId' => $calendarId, - 'calendarData' => $subscriptionRow, - 'shares' => [], - 'objectData' => $objectRow, - ] - )); } } @@ -1482,15 +1436,6 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription $subscriptionRow = $this->getSubscriptionById($calendarId); $this->dispatcher->dispatchTyped(new CachedCalendarObjectDeletedEvent((int)$calendarId, $subscriptionRow, [], $data)); - $this->legacyDispatcher->dispatch('\OCA\DAV\CalDAV\CalDavBackend::deleteCachedCalendarObject', new GenericEvent( - '\OCA\DAV\CalDAV\CalDavBackend::deleteCachedCalendarObject', - [ - 'subscriptionId' => $calendarId, - 'calendarData' => $subscriptionRow, - 'shares' => [], - 'objectData' => $data, - ] - )); } } else { $pathInfo = pathinfo($data['uri']); @@ -2501,12 +2446,6 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription $subscriptionRow = $this->getSubscriptionById($subscriptionId); $this->dispatcher->dispatchTyped(new SubscriptionCreatedEvent($subscriptionId, $subscriptionRow)); - $this->legacyDispatcher->dispatch('\OCA\DAV\CalDAV\CalDavBackend::createSubscription', new GenericEvent( - '\OCA\DAV\CalDAV\CalDavBackend::createSubscription', - [ - 'subscriptionId' => $subscriptionId, - 'subscriptionData' => $subscriptionRow, - ])); return $subscriptionId; } @@ -2554,13 +2493,6 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription $subscriptionRow = $this->getSubscriptionById($subscriptionId); $this->dispatcher->dispatchTyped(new SubscriptionUpdatedEvent((int)$subscriptionId, $subscriptionRow, [], $mutations)); - $this->legacyDispatcher->dispatch('\OCA\DAV\CalDAV\CalDavBackend::updateSubscription', new GenericEvent( - '\OCA\DAV\CalDAV\CalDavBackend::updateSubscription', - [ - 'subscriptionId' => $subscriptionId, - 'subscriptionData' => $subscriptionRow, - 'propertyMutations' => $mutations, - ])); return true; }); @@ -2575,13 +2507,6 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription public function deleteSubscription($subscriptionId) { $subscriptionRow = $this->getSubscriptionById($subscriptionId); - $this->legacyDispatcher->dispatch('\OCA\DAV\CalDAV\CalDavBackend::deleteSubscription', new GenericEvent( - '\OCA\DAV\CalDAV\CalDavBackend::deleteSubscription', - [ - 'subscriptionId' => $subscriptionId, - 'subscriptionData' => $this->getSubscriptionById($subscriptionId), - ])); - $query = $this->db->getQueryBuilder(); $query->delete('calendarsubscriptions') ->where($query->expr()->eq('id', $query->createNamedParameter($subscriptionId))) @@ -2879,15 +2804,6 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription $calendarRow = $this->getCalendarById($calendarId); $oldShares = $this->getShares($calendarId); - $this->legacyDispatcher->dispatch('\OCA\DAV\CalDAV\CalDavBackend::updateShares', new GenericEvent( - '\OCA\DAV\CalDAV\CalDavBackend::updateShares', - [ - 'calendarId' => $calendarId, - 'calendarData' => $calendarRow, - 'shares' => $oldShares, - 'add' => $add, - 'remove' => $remove, - ])); $this->calendarSharingBackend->updateShares($shareable, $add, $remove); $this->dispatcher->dispatchTyped(new CalendarShareUpdatedEvent((int)$calendarId, $calendarRow, $oldShares, $add, $remove)); @@ -2909,13 +2825,6 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription public function setPublishStatus($value, $calendar) { $calendarId = $calendar->getResourceId(); $calendarData = $this->getCalendarById($calendarId); - $this->legacyDispatcher->dispatch('\OCA\DAV\CalDAV\CalDavBackend::publishCalendar', new GenericEvent( - '\OCA\DAV\CalDAV\CalDavBackend::updateShares', - [ - 'calendarId' => $calendarId, - 'calendarData' => $calendarData, - 'public' => $value, - ])); $query = $this->db->getQueryBuilder(); if ($value) { diff --git a/apps/dav/lib/CalDAV/Calendar.php b/apps/dav/lib/CalDAV/Calendar.php index 75c815c3b0a..79d2244b42c 100644 --- a/apps/dav/lib/CalDAV/Calendar.php +++ b/apps/dav/lib/CalDAV/Calendar.php @@ -400,7 +400,7 @@ class Calendar extends \Sabre\CalDAV\Calendar implements IRestorable, IShareable return isset($this->calendarInfo['{http://owncloud.org/ns}public']); } - protected function isShared() { + public function isShared() { if (!isset($this->calendarInfo['{http://owncloud.org/ns}owner-principal'])) { return false; } @@ -412,6 +412,13 @@ class Calendar extends \Sabre\CalDAV\Calendar implements IRestorable, IShareable return isset($this->calendarInfo['{http://calendarserver.org/ns/}source']); } + public function isDeleted(): bool { + if (!isset($this->calendarInfo[TrashbinPlugin::PROPERTY_DELETED_AT])) { + return false; + } + return $this->calendarInfo[TrashbinPlugin::PROPERTY_DELETED_AT] !== null; + } + /** * @inheritDoc */ diff --git a/apps/dav/lib/CalDAV/PublicCalendar.php b/apps/dav/lib/CalDAV/PublicCalendar.php index 4a29c8d237a..16c7f86917d 100644 --- a/apps/dav/lib/CalDAV/PublicCalendar.php +++ b/apps/dav/lib/CalDAV/PublicCalendar.php @@ -84,7 +84,7 @@ class PublicCalendar extends Calendar { * public calendars are always shared * @return bool */ - protected function isShared() { + public function isShared() { return true; } } diff --git a/apps/dav/lib/CalDAV/Schedule/Plugin.php b/apps/dav/lib/CalDAV/Schedule/Plugin.php index 96bacce4454..74865297944 100644 --- a/apps/dav/lib/CalDAV/Schedule/Plugin.php +++ b/apps/dav/lib/CalDAV/Schedule/Plugin.php @@ -30,6 +30,7 @@ namespace OCA\DAV\CalDAV\Schedule; use DateTimeZone; use OCA\DAV\CalDAV\CalDavBackend; +use OCA\DAV\CalDAV\Calendar; use OCA\DAV\CalDAV\CalendarHome; use OCP\IConfig; use Sabre\CalDAV\ICalendar; @@ -299,12 +300,14 @@ EOF; return null; } + $isResourceOrRoom = strpos($principalUrl, 'principals/calendar-resources') === 0 || + strpos($principalUrl, 'principals/calendar-rooms') === 0; + if (strpos($principalUrl, 'principals/users') === 0) { [, $userId] = split($principalUrl); $uri = $this->config->getUserValue($userId, 'dav', 'defaultCalendar', CalDavBackend::PERSONAL_CALENDAR_URI); $displayName = CalDavBackend::PERSONAL_CALENDAR_NAME; - } elseif (strpos($principalUrl, 'principals/calendar-resources') === 0 || - strpos($principalUrl, 'principals/calendar-rooms') === 0) { + } elseif ($isResourceOrRoom) { $uri = CalDavBackend::RESOURCE_BOOKING_CALENDAR_URI; $displayName = CalDavBackend::RESOURCE_BOOKING_CALENDAR_NAME; } else { @@ -316,9 +319,40 @@ EOF; /** @var CalendarHome $calendarHome */ $calendarHome = $this->server->tree->getNodeForPath($calendarHomePath); if (!$calendarHome->childExists($uri)) { - $calendarHome->getCalDAVBackend()->createCalendar($principalUrl, $uri, [ - '{DAV:}displayname' => $displayName, - ]); + // If the default calendar doesn't exist + if ($isResourceOrRoom) { + $calendarHome->getCalDAVBackend()->createCalendar($principalUrl, $uri, [ + '{DAV:}displayname' => $displayName, + ]); + } else { + // And we're not handling scheduling on resource/room booking + $userCalendars = []; + /** + * If the default calendar of the user isn't set and the + * fallback doesn't match any of the user's calendar + * try to find the first "personal" calendar we can write to + * instead of creating a new one. + * A appropriate personal calendar to receive invites: + * - isn't a calendar subscription + * - user can write to it (no virtual/3rd-party calendars) + * - calendar isn't a share + */ + foreach ($calendarHome->getChildren() as $node) { + if ($node instanceof Calendar && !$node->isSubscription() && $node->canWrite() && !$node->isShared() && !$node->isDeleted()) { + $userCalendars[] = $node; + } + } + + if (count($userCalendars) > 0) { + // Calendar backend returns calendar by calendarorder property + $uri = $userCalendars[0]->getName(); + } else { + // Otherwise if we have really nothing, create a new calendar + $calendarHome->getCalDAVBackend()->createCalendar($principalUrl, $uri, [ + '{DAV:}displayname' => $displayName, + ]); + } + } } $result = $this->server->getPropertiesForPath($calendarHomePath . '/' . $uri, [], 1); diff --git a/apps/dav/lib/CardDAV/Activity/Backend.php b/apps/dav/lib/CardDAV/Activity/Backend.php index b713284e182..184b3f0cb10 100644 --- a/apps/dav/lib/CardDAV/Activity/Backend.php +++ b/apps/dav/lib/CardDAV/Activity/Backend.php @@ -103,7 +103,14 @@ class Backend { return; } - $principal = explode('/', $addressbookData['principaluri']); + $principalUri = $addressbookData['principaluri']; + + // We are not interested in changes from the system addressbook + if ($principalUri === 'principals/system/system') { + return; + } + + $principal = explode('/', $principalUri); $owner = array_pop($principal); $currentUser = $this->userSession->getUser(); @@ -393,7 +400,14 @@ class Backend { return; } - $principal = explode('/', $addressbookData['principaluri']); + $principalUri = $addressbookData['principaluri']; + + // We are not interested in changes from the system addressbook + if ($principalUri === 'principals/system/system') { + return; + } + + $principal = explode('/', $principalUri); $owner = array_pop($principal); $currentUser = $this->userSession->getUser(); diff --git a/apps/dav/lib/CardDAV/AddressBookImpl.php b/apps/dav/lib/CardDAV/AddressBookImpl.php index 3db20cb4220..2f7f0a22900 100644 --- a/apps/dav/lib/CardDAV/AddressBookImpl.php +++ b/apps/dav/lib/CardDAV/AddressBookImpl.php @@ -12,6 +12,7 @@ * @author Joas Schilling <coding@schilljs.com> * @author John Molakvoæ <skjnldsv@protonmail.com> * @author Julius Härtl <jus@bitgrid.net> + * @author Thomas Citharel <nextcloud@tcit.fr> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 @@ -206,7 +207,7 @@ class AddressBookImpl implements IAddressBook { } /** - * @param object $id the unique identifier to a contact + * @param int $id the unique identifier to a contact * @return bool successful or not * @since 5.0.0 */ diff --git a/apps/dav/lib/CardDAV/CardDavBackend.php b/apps/dav/lib/CardDAV/CardDavBackend.php index 864f3da9367..745ca7801b7 100644 --- a/apps/dav/lib/CardDAV/CardDavBackend.php +++ b/apps/dav/lib/CardDAV/CardDavBackend.php @@ -58,30 +58,19 @@ use Sabre\CardDAV\Plugin; use Sabre\DAV\Exception\BadRequest; use Sabre\VObject\Component\VCard; use Sabre\VObject\Reader; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Symfony\Component\EventDispatcher\GenericEvent; class CardDavBackend implements BackendInterface, SyncSupport { public const PERSONAL_ADDRESSBOOK_URI = 'contacts'; public const PERSONAL_ADDRESSBOOK_NAME = 'Contacts'; - /** @var Principal */ - private $principalBackend; - - /** @var string */ - private $dbCardsTable = 'cards'; - - /** @var string */ - private $dbCardsPropertiesTable = 'cards_properties'; - - /** @var IDBConnection */ - private $db; - - /** @var Backend */ - private $sharingBackend; + private Principal $principalBackend; + private string $dbCardsTable = 'cards'; + private string $dbCardsPropertiesTable = 'cards_properties'; + private IDBConnection $db; + private Backend $sharingBackend; /** @var array properties to index */ - public static $indexProperties = [ + public static array $indexProperties = [ 'BDAY', 'UID', 'N', 'FN', 'TITLE', 'ROLE', 'NOTE', 'NICKNAME', 'ORG', 'CATEGORIES', 'EMAIL', 'TEL', 'IMPP', 'ADR', 'URL', 'GEO', 'CLOUD', 'X-SOCIALPROFILE']; @@ -89,18 +78,10 @@ class CardDavBackend implements BackendInterface, SyncSupport { /** * @var string[] Map of uid => display name */ - protected $userDisplayNames; - - /** @var IUserManager */ - private $userManager; - - /** @var IEventDispatcher */ - private $dispatcher; - - /** @var EventDispatcherInterface */ - private $legacyDispatcher; - - private $etagCache = []; + protected array $userDisplayNames; + private IUserManager $userManager; + private IEventDispatcher $dispatcher; + private array $etagCache = []; /** * CardDavBackend constructor. @@ -110,19 +91,16 @@ class CardDavBackend implements BackendInterface, SyncSupport { * @param IUserManager $userManager * @param IGroupManager $groupManager * @param IEventDispatcher $dispatcher - * @param EventDispatcherInterface $legacyDispatcher */ public function __construct(IDBConnection $db, Principal $principalBackend, IUserManager $userManager, IGroupManager $groupManager, - IEventDispatcher $dispatcher, - EventDispatcherInterface $legacyDispatcher) { + IEventDispatcher $dispatcher) { $this->db = $db; $this->principalBackend = $principalBackend; $this->userManager = $userManager; $this->dispatcher = $dispatcher; - $this->legacyDispatcher = $legacyDispatcher; $this->sharingBackend = new Backend($this->db, $this->userManager, $groupManager, $principalBackend, 'addressbook'); } @@ -692,11 +670,6 @@ class CardDavBackend implements BackendInterface, SyncSupport { $shares = $this->getShares($addressBookId); $objectRow = $this->getCard($addressBookId, $cardUri); $this->dispatcher->dispatchTyped(new CardCreatedEvent($addressBookId, $addressBookData, $shares, $objectRow)); - $this->legacyDispatcher->dispatch('\OCA\DAV\CardDAV\CardDavBackend::createCard', - new GenericEvent(null, [ - 'addressBookId' => $addressBookId, - 'cardUri' => $cardUri, - 'cardData' => $cardData])); return '"' . $etag . '"'; } @@ -756,12 +729,6 @@ class CardDavBackend implements BackendInterface, SyncSupport { $shares = $this->getShares($addressBookId); $objectRow = $this->getCard($addressBookId, $cardUri); $this->dispatcher->dispatchTyped(new CardUpdatedEvent($addressBookId, $addressBookData, $shares, $objectRow)); - $this->legacyDispatcher->dispatch('\OCA\DAV\CardDAV\CardDavBackend::updateCard', - new GenericEvent(null, [ - 'addressBookId' => $addressBookId, - 'cardUri' => $cardUri, - 'cardData' => $cardData])); - return '"' . $etag . '"'; } @@ -793,11 +760,6 @@ class CardDavBackend implements BackendInterface, SyncSupport { if ($ret === 1) { if ($cardId !== null) { $this->dispatcher->dispatchTyped(new CardDeletedEvent($addressBookId, $addressBookData, $shares, $objectRow)); - $this->legacyDispatcher->dispatch('\OCA\DAV\CardDAV\CardDavBackend::deleteCard', - new GenericEvent(null, [ - 'addressBookId' => $addressBookId, - 'cardUri' => $cardUri])); - $this->purgeProperties($addressBookId, $cardId); } return true; diff --git a/apps/dav/lib/Command/CreateCalendar.php b/apps/dav/lib/Command/CreateCalendar.php index 2bea82a345e..24368e8864c 100644 --- a/apps/dav/lib/Command/CreateCalendar.php +++ b/apps/dav/lib/Command/CreateCalendar.php @@ -94,7 +94,6 @@ class CreateCalendar extends Command { $random = \OC::$server->getSecureRandom(); $logger = \OC::$server->get(LoggerInterface::class); $dispatcher = \OC::$server->get(IEventDispatcher::class); - $legacyDispatcher = \OC::$server->getEventDispatcher(); $config = \OC::$server->get(IConfig::class); $name = $input->getArgument('name'); @@ -106,7 +105,6 @@ class CreateCalendar extends Command { $random, $logger, $dispatcher, - $legacyDispatcher, $config ); $caldav->createCalendar("principals/users/$user", $name, []); diff --git a/apps/dav/lib/Connector/Sabre/ExceptionLoggerPlugin.php b/apps/dav/lib/Connector/Sabre/ExceptionLoggerPlugin.php index ea94b5c8933..ebf3e4021eb 100644 --- a/apps/dav/lib/Connector/Sabre/ExceptionLoggerPlugin.php +++ b/apps/dav/lib/Connector/Sabre/ExceptionLoggerPlugin.php @@ -29,6 +29,7 @@ namespace OCA\DAV\Connector\Sabre; use OCA\DAV\Connector\Sabre\Exception\FileLocked; use OCA\DAV\Connector\Sabre\Exception\PasswordLoginForbidden; +use OCA\DAV\Exception\ServerMaintenanceMode; use OCP\Files\StorageNotAvailableException; use Psr\Log\LoggerInterface; use Sabre\DAV\Exception\BadRequest; @@ -81,6 +82,7 @@ class ExceptionLoggerPlugin extends \Sabre\DAV\ServerPlugin { FileLocked::class => true, // An invalid range is requested RequestedRangeNotSatisfiable::class => true, + ServerMaintenanceMode::class => true, ]; private string $appName; @@ -114,17 +116,12 @@ class ExceptionLoggerPlugin extends \Sabre\DAV\ServerPlugin { */ public function logException(\Throwable $ex) { $exceptionClass = get_class($ex); - if (isset($this->nonFatalExceptions[$exceptionClass]) || - ( - $exceptionClass === ServiceUnavailable::class && - $ex->getMessage() === 'System in maintenance mode.' - ) - ) { + if (isset($this->nonFatalExceptions[$exceptionClass])) { $this->logger->debug($ex->getMessage(), [ 'app' => $this->appName, 'exception' => $ex, ]); - return; + return; } $this->logger->critical($ex->getMessage(), [ diff --git a/apps/dav/lib/Connector/Sabre/MaintenancePlugin.php b/apps/dav/lib/Connector/Sabre/MaintenancePlugin.php index e7e3b273b98..1fc02320805 100644 --- a/apps/dav/lib/Connector/Sabre/MaintenancePlugin.php +++ b/apps/dav/lib/Connector/Sabre/MaintenancePlugin.php @@ -27,6 +27,7 @@ */ namespace OCA\DAV\Connector\Sabre; +use OCA\DAV\Exception\ServerMaintenanceMode; use OCP\IConfig; use OCP\IL10N; use OCP\Util; @@ -82,10 +83,10 @@ class MaintenancePlugin extends ServerPlugin { */ public function checkMaintenanceMode() { if ($this->config->getSystemValueBool('maintenance')) { - throw new ServiceUnavailable($this->l10n->t('System is in maintenance mode.')); + throw new ServerMaintenanceMode($this->l10n->t('System is in maintenance mode.')); } if (Util::needUpgrade()) { - throw new ServiceUnavailable($this->l10n->t('Upgrade needed')); + throw new ServerMaintenanceMode($this->l10n->t('Upgrade needed')); } return true; diff --git a/apps/dav/lib/Events/CalendarPublishedEvent.php b/apps/dav/lib/Events/CalendarPublishedEvent.php index 7b3b95f2f77..a95e9f294c1 100644 --- a/apps/dav/lib/Events/CalendarPublishedEvent.php +++ b/apps/dav/lib/Events/CalendarPublishedEvent.php @@ -6,6 +6,7 @@ declare(strict_types=1); * @copyright Copyright (c) 2020, Georg Ehrke * * @author Georg Ehrke <oc.list@georgehrke.com> + * @author Thomas Citharel <nextcloud@tcit.fr> * * @license GNU AGPL version 3 or any later version * @@ -34,15 +35,9 @@ use OCP\EventDispatcher\Event; * @since 20.0.0 */ class CalendarPublishedEvent extends Event { - - /** @var int */ - private $calendarId; - - /** @var array */ - private $calendarData; - - /** @var string */ - private $publicUri; + private int $calendarId; + private array $calendarData; + private string $publicUri; /** * CalendarPublishedEvent constructor. diff --git a/apps/dav/lib/Events/CalendarUnpublishedEvent.php b/apps/dav/lib/Events/CalendarUnpublishedEvent.php index 0cea53c6f0d..b2536fc7aef 100644 --- a/apps/dav/lib/Events/CalendarUnpublishedEvent.php +++ b/apps/dav/lib/Events/CalendarUnpublishedEvent.php @@ -6,6 +6,7 @@ declare(strict_types=1); * @copyright Copyright (c) 2020, Georg Ehrke * * @author Georg Ehrke <oc.list@georgehrke.com> + * @author Thomas Citharel <nextcloud@tcit.fr> * * @license GNU AGPL version 3 or any later version * @@ -34,12 +35,8 @@ use OCP\EventDispatcher\Event; * @since 20.0.0 */ class CalendarUnpublishedEvent extends Event { - - /** @var int */ - private $calendarId; - - /** @var array */ - private $calendarData; + private int $calendarId; + private array $calendarData; /** * CalendarUnpublishedEvent constructor. diff --git a/apps/dav/lib/Exception/ServerMaintenanceMode.php b/apps/dav/lib/Exception/ServerMaintenanceMode.php new file mode 100644 index 00000000000..9dad9f2d4d1 --- /dev/null +++ b/apps/dav/lib/Exception/ServerMaintenanceMode.php @@ -0,0 +1,31 @@ +<?php + +declare(strict_types=1); +/** + * @copyright Copyright (c) 2022 Joas Schilling <coding@schilljs.com> + * + * @author Joas Schilling <coding@schilljs.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ +namespace OCA\DAV\Exception; + +use Sabre\DAV\Exception\ServiceUnavailable; + +class ServerMaintenanceMode extends ServiceUnavailable { + +} diff --git a/apps/dav/lib/Listener/BirthdayListener.php b/apps/dav/lib/Listener/BirthdayListener.php new file mode 100644 index 00000000000..43ad782fa9e --- /dev/null +++ b/apps/dav/lib/Listener/BirthdayListener.php @@ -0,0 +1,54 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright 2022 Thomas Citharel <nextcloud@tcit.fr> + * + * @author Thomas Citharel <nextcloud@tcit.fr> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ +namespace OCA\DAV\Listener; + +use OCA\DAV\CalDAV\BirthdayService; +use OCA\DAV\Events\CardCreatedEvent; +use OCA\DAV\Events\CardDeletedEvent; +use OCA\DAV\Events\CardUpdatedEvent; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; + +class BirthdayListener implements IEventListener { + private BirthdayService $birthdayService; + + public function __construct(BirthdayService $birthdayService) { + $this->birthdayService = $birthdayService; + } + + public function handle(Event $event): void { + if ($event instanceof CardCreatedEvent || $event instanceof CardUpdatedEvent) { + $cardData = $event->getCardData(); + + $this->birthdayService->onCardChanged($event->getAddressBookId(), $cardData['uri'], $cardData['carddata']); + } + + if ($event instanceof CardDeletedEvent) { + $cardData = $event->getCardData(); + $this->birthdayService->onCardDeleted($event->getAddressBookId(), $cardData['uri']); + } + } +} diff --git a/apps/dav/lib/Listener/CalendarPublicationListener.php b/apps/dav/lib/Listener/CalendarPublicationListener.php new file mode 100644 index 00000000000..1453694d6fb --- /dev/null +++ b/apps/dav/lib/Listener/CalendarPublicationListener.php @@ -0,0 +1,65 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright 2022 Thomas Citharel <nextcloud@tcit.fr> + * + * @author Thomas Citharel <nextcloud@tcit.fr> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ +namespace OCA\DAV\Listener; + +use OCA\DAV\CalDAV\Activity\Backend; +use OCA\DAV\Events\CalendarPublishedEvent; +use OCA\DAV\Events\CalendarUnpublishedEvent; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; +use Psr\Log\LoggerInterface; + +class CalendarPublicationListener implements IEventListener { + private Backend $activityBackend; + private LoggerInterface $logger; + + public function __construct(Backend $activityBackend, + LoggerInterface $logger) { + $this->activityBackend = $activityBackend; + $this->logger = $logger; + } + + /** + * In case the user has set their default calendar to the deleted one + */ + public function handle(Event $event): void { + if ($event instanceof CalendarPublishedEvent) { + $this->logger->debug('Creating activity for Calendar being published'); + + $this->activityBackend->onCalendarPublication( + $event->getCalendarData(), + true + ); + } elseif ($event instanceof CalendarUnpublishedEvent) { + $this->logger->debug('Creating activity for Calendar being unpublished'); + + $this->activityBackend->onCalendarPublication( + $event->getCalendarData(), + false + ); + } + } +} diff --git a/apps/dav/lib/Listener/CalendarShareUpdateListener.php b/apps/dav/lib/Listener/CalendarShareUpdateListener.php new file mode 100644 index 00000000000..88865759162 --- /dev/null +++ b/apps/dav/lib/Listener/CalendarShareUpdateListener.php @@ -0,0 +1,62 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright 2022 Thomas Citharel <nextcloud@tcit.fr> + * + * @author Thomas Citharel <nextcloud@tcit.fr> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ +namespace OCA\DAV\Listener; + +use OCA\DAV\CalDAV\Activity\Backend; +use OCA\DAV\Events\CalendarShareUpdatedEvent; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; +use Psr\Log\LoggerInterface; + +class CalendarShareUpdateListener implements IEventListener { + private Backend $activityBackend; + private LoggerInterface $logger; + + public function __construct(Backend $activityBackend, + LoggerInterface $logger) { + $this->activityBackend = $activityBackend; + $this->logger = $logger; + } + + /** + * In case the user has set their default calendar to the deleted one + */ + public function handle(Event $event): void { + if (!($event instanceof CalendarShareUpdatedEvent)) { + // Not what we subscribed to + return; + } + + $this->logger->debug("Creating activity for Calendar having it's shares updated"); + + $this->activityBackend->onCalendarUpdateShares( + $event->getCalendarData(), + $event->getOldShares(), + $event->getAdded(), + $event->getRemoved() + ); + } +} diff --git a/apps/dav/lib/Listener/ClearPhotoCacheListener.php b/apps/dav/lib/Listener/ClearPhotoCacheListener.php new file mode 100644 index 00000000000..ed02770e35d --- /dev/null +++ b/apps/dav/lib/Listener/ClearPhotoCacheListener.php @@ -0,0 +1,48 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright 2022 Thomas Citharel <nextcloud@tcit.fr> + * + * @author Thomas Citharel <nextcloud@tcit.fr> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ +namespace OCA\DAV\Listener; + +use OCA\DAV\CardDAV\PhotoCache; +use OCA\DAV\Events\CardDeletedEvent; +use OCA\DAV\Events\CardUpdatedEvent; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; + +class ClearPhotoCacheListener implements IEventListener { + private PhotoCache $photoCache; + + public function __construct(PhotoCache $photoCache) { + $this->photoCache = $photoCache; + } + + public function handle(Event $event): void { + if ($event instanceof CardUpdatedEvent || $event instanceof CardDeletedEvent) { + $cardData = $event->getCardData(); + + $this->photoCache->delete($event->getAddressBookId(), $cardData['uri']); + } + } +} diff --git a/apps/dav/lib/Listener/SubscriptionListener.php b/apps/dav/lib/Listener/SubscriptionListener.php new file mode 100644 index 00000000000..36db234aa05 --- /dev/null +++ b/apps/dav/lib/Listener/SubscriptionListener.php @@ -0,0 +1,85 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright 2022 Thomas Citharel <nextcloud@tcit.fr> + * + * @author Thomas Citharel <nextcloud@tcit.fr> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ +namespace OCA\DAV\Listener; + +use OCA\DAV\BackgroundJob\RefreshWebcalJob; +use OCA\DAV\CalDAV\Reminder\Backend as ReminderBackend; +use OCA\DAV\CalDAV\WebcalCaching\RefreshWebcalService; +use OCA\DAV\Events\SubscriptionCreatedEvent; +use OCA\DAV\Events\SubscriptionDeletedEvent; +use OCP\BackgroundJob\IJobList; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; +use Psr\Log\LoggerInterface; + +class SubscriptionListener implements IEventListener { + private IJobList $jobList; + private RefreshWebcalService $refreshWebcalService; + private ReminderBackend $reminderBackend; + private LoggerInterface $logger; + + public function __construct(IJobList $jobList, RefreshWebcalService $refreshWebcalService, ReminderBackend $reminderBackend, + LoggerInterface $logger) { + $this->jobList = $jobList; + $this->refreshWebcalService = $refreshWebcalService; + $this->reminderBackend = $reminderBackend; + $this->logger = $logger; + } + + /** + * In case the user has set their default calendar to the deleted one + */ + public function handle(Event $event): void { + if ($event instanceof SubscriptionCreatedEvent) { + $subscriptionId = $event->getSubscriptionId(); + $subscriptionData = $event->getSubscriptionData(); + + $this->logger->debug('Refreshing webcal data for subscription ' . $subscriptionId); + $this->refreshWebcalService->refreshSubscription( + (string)$subscriptionData['principaluri'], + (string)$subscriptionData['uri'] + ); + + $this->logger->debug('Scheduling webcal data refreshment for subscription ' . $subscriptionId); + $this->jobList->add(RefreshWebcalJob::class, [ + 'principaluri' => $subscriptionData['principaluri'], + 'uri' => $subscriptionData['uri'] + ]); + } elseif ($event instanceof SubscriptionDeletedEvent) { + $subscriptionId = $event->getSubscriptionId(); + $subscriptionData = $event->getSubscriptionData(); + + $this->logger->debug('Removing refresh webcal job for subscription ' . $subscriptionId); + $this->jobList->remove(RefreshWebcalJob::class, [ + 'principaluri' => $subscriptionData['principaluri'], + 'uri' => $subscriptionData['uri'] + ]); + + $this->logger->debug('Cleaning all reminders for subscription ' . $subscriptionId); + $this->reminderBackend->cleanRemindersForCalendar($subscriptionId); + } + } +} diff --git a/apps/dav/lib/RootCollection.php b/apps/dav/lib/RootCollection.php index 8a11a676609..8ad4d6aa841 100644 --- a/apps/dav/lib/RootCollection.php +++ b/apps/dav/lib/RootCollection.php @@ -7,6 +7,7 @@ * @author Georg Ehrke <oc.list@georgehrke.com> * @author Joas Schilling <coding@schilljs.com> * @author Roeland Jago Douma <roeland@famdouma.nl> + * @author Thomas Citharel <nextcloud@tcit.fr> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <vincent@nextcloud.com> * @@ -61,7 +62,6 @@ class RootCollection extends SimpleCollection { $shareManager = \OC::$server->getShareManager(); $db = \OC::$server->getDatabaseConnection(); $dispatcher = \OC::$server->get(IEventDispatcher::class); - $legacyDispatcher = \OC::$server->getEventDispatcher(); $config = \OC::$server->get(IConfig::class); $proxyMapper = \OC::$server->query(ProxyMapper::class); @@ -105,7 +105,6 @@ class RootCollection extends SimpleCollection { $random, $logger, $dispatcher, - $legacyDispatcher, $config ); $userCalendarRoot = new CalendarRoot($userPrincipalBackend, $caldavBackend, 'principals/users', $logger); @@ -140,11 +139,11 @@ class RootCollection extends SimpleCollection { ); $pluginManager = new PluginManager(\OC::$server, \OC::$server->query(IAppManager::class)); - $usersCardDavBackend = new CardDavBackend($db, $userPrincipalBackend, $userManager, $groupManager, $dispatcher, $legacyDispatcher); + $usersCardDavBackend = new CardDavBackend($db, $userPrincipalBackend, $userManager, $groupManager, $dispatcher); $usersAddressBookRoot = new AddressBookRoot($userPrincipalBackend, $usersCardDavBackend, $pluginManager, 'principals/users'); $usersAddressBookRoot->disableListing = $disableListing; - $systemCardDavBackend = new CardDavBackend($db, $userPrincipalBackend, $userManager, $groupManager, $dispatcher, $legacyDispatcher); + $systemCardDavBackend = new CardDavBackend($db, $userPrincipalBackend, $userManager, $groupManager, $dispatcher); $systemAddressBookRoot = new AddressBookRoot(new SystemPrincipalBackend(), $systemCardDavBackend, $pluginManager, 'principals/system'); $systemAddressBookRoot->disableListing = $disableListing; diff --git a/apps/dav/tests/unit/CalDAV/AbstractCalDavBackend.php b/apps/dav/tests/unit/CalDAV/AbstractCalDavBackend.php index ec966248e01..b04f8701c23 100644 --- a/apps/dav/tests/unit/CalDAV/AbstractCalDavBackend.php +++ b/apps/dav/tests/unit/CalDAV/AbstractCalDavBackend.php @@ -7,6 +7,7 @@ * @author Joas Schilling <coding@schilljs.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Roeland Jago Douma <roeland@famdouma.nl> + * @author Thomas Citharel <nextcloud@tcit.fr> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 @@ -42,7 +43,6 @@ use OCP\Share\IManager as ShareManager; use Psr\Log\LoggerInterface; use Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet; use Sabre\DAV\Xml\Property\Href; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Test\TestCase; /** @@ -65,8 +65,6 @@ abstract class AbstractCalDavBackend extends TestCase { protected $groupManager; /** @var IEventDispatcher|\PHPUnit\Framework\MockObject\MockObject */ protected $dispatcher; - /** @var EventDispatcherInterface|\PHPUnit\Framework\MockObject\MockObject */ - protected $legacyDispatcher; /** @var ISecureRandom */ private $random; @@ -84,7 +82,6 @@ abstract class AbstractCalDavBackend extends TestCase { $this->userManager = $this->createMock(IUserManager::class); $this->groupManager = $this->createMock(IGroupManager::class); $this->dispatcher = $this->createMock(IEventDispatcher::class); - $this->legacyDispatcher = $this->createMock(EventDispatcherInterface::class); $this->principal = $this->getMockBuilder(Principal::class) ->setConstructorArgs([ $this->userManager, @@ -120,7 +117,6 @@ abstract class AbstractCalDavBackend extends TestCase { $this->random, $this->logger, $this->dispatcher, - $this->legacyDispatcher, $this->config ); @@ -147,8 +143,6 @@ abstract class AbstractCalDavBackend extends TestCase { $calendars = $this->backend->getCalendarsForUser($principal); $this->dispatcher->expects(self::any()) ->method('dispatchTyped'); - $this->legacyDispatcher->expects(self::any()) - ->method('dispatch'); foreach ($calendars as $calendar) { $this->backend->deleteCalendar($calendar['id'], true); } diff --git a/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php b/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php index a6439ee6d2b..13025415f2a 100644 --- a/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php +++ b/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php @@ -151,9 +151,6 @@ class CalDavBackendTest extends AbstractCalDavBackend { $calendars = $this->backend->getCalendarsForUser(self::UNIT_TEST_USER); $this->assertCount(1, $calendars); $calendar = new Calendar($this->backend, $calendars[0], $l10n, $config, $logger); - $this->legacyDispatcher->expects($this->at(0)) - ->method('dispatch') - ->with('\OCA\DAV\CalDAV\CalDavBackend::updateShares'); $this->backend->updateShares($calendar, $add, []); $calendars = $this->backend->getCalendarsForUser(self::UNIT_TEST_USER1); $this->assertCount(1, $calendars); diff --git a/apps/dav/tests/unit/CalDAV/Listener/CalendarPublicationListenerTest.php b/apps/dav/tests/unit/CalDAV/Listener/CalendarPublicationListenerTest.php new file mode 100644 index 00000000000..c42fca1a1cf --- /dev/null +++ b/apps/dav/tests/unit/CalDAV/Listener/CalendarPublicationListenerTest.php @@ -0,0 +1,79 @@ +<?php +/** + * @copyright 2022 Thomas Citharel <nextcloud@tcit.fr> + * + * @author Thomas Citharel <nextcloud@tcit.fr> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ +namespace OCA\DAV\Tests\unit\CalDAV\Listeners; + +use OCA\DAV\CalDAV\Activity\Backend; +use OCA\DAV\Events\CalendarPublishedEvent; +use OCA\DAV\Events\CalendarUnpublishedEvent; +use OCA\DAV\Listener\CalendarPublicationListener; +use OCP\EventDispatcher\Event; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; +use Test\TestCase; + +class CalendarPublicationListenerTest extends TestCase { + + /** @var Backend|MockObject */ + private $activityBackend; + + /** @var LoggerInterface|MockObject */ + private $logger; + + private CalendarPublicationListener $calendarPublicationListener; + + /** @var CalendarPublishedEvent|MockObject */ + private $publicationEvent; + + /** @var CalendarUnpublishedEvent|MockObject */ + private $unpublicationEvent; + + protected function setUp(): void { + parent::setUp(); + + $this->activityBackend = $this->createMock(Backend::class); + $this->logger = $this->createMock(LoggerInterface::class); + $this->publicationEvent = $this->createMock(CalendarPublishedEvent::class); + $this->unpublicationEvent = $this->createMock(CalendarUnpublishedEvent::class); + $this->calendarPublicationListener = new CalendarPublicationListener($this->activityBackend, $this->logger); + } + + public function testInvalidEvent(): void { + $this->activityBackend->expects($this->never())->method('onCalendarPublication'); + $this->logger->expects($this->never())->method('debug'); + $this->calendarPublicationListener->handle(new Event()); + } + + public function testPublicationEvent(): void { + $this->publicationEvent->expects($this->once())->method('getCalendarData')->with()->willReturn([]); + $this->activityBackend->expects($this->once())->method('onCalendarPublication')->with([], true); + $this->logger->expects($this->once())->method('debug'); + $this->calendarPublicationListener->handle($this->publicationEvent); + } + + public function testUnPublicationEvent(): void { + $this->unpublicationEvent->expects($this->once())->method('getCalendarData')->with()->willReturn([]); + $this->activityBackend->expects($this->once())->method('onCalendarPublication')->with([], false); + $this->logger->expects($this->once())->method('debug'); + $this->calendarPublicationListener->handle($this->unpublicationEvent); + } +} diff --git a/apps/dav/tests/unit/CalDAV/Listener/CalendarShareUpdateListenerTest.php b/apps/dav/tests/unit/CalDAV/Listener/CalendarShareUpdateListenerTest.php new file mode 100644 index 00000000000..0252cffd5a0 --- /dev/null +++ b/apps/dav/tests/unit/CalDAV/Listener/CalendarShareUpdateListenerTest.php @@ -0,0 +1,70 @@ +<?php +/** + * @copyright 2022 Thomas Citharel <nextcloud@tcit.fr> + * + * @author Thomas Citharel <nextcloud@tcit.fr> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ +namespace OCA\DAV\Tests\unit\CalDAV\Listeners; + +use OCA\DAV\CalDAV\Activity\Backend; +use OCA\DAV\Events\CalendarShareUpdatedEvent; +use OCA\DAV\Listener\CalendarShareUpdateListener; +use OCP\EventDispatcher\Event; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; +use Test\TestCase; + +class CalendarShareUpdateListenerTest extends TestCase { + + /** @var Backend|MockObject */ + private $activityBackend; + + /** @var LoggerInterface|MockObject */ + private $logger; + + private CalendarShareUpdateListener $calendarPublicationListener; + + /** @var CalendarShareUpdatedEvent|MockObject */ + private $event; + + protected function setUp(): void { + parent::setUp(); + + $this->activityBackend = $this->createMock(Backend::class); + $this->logger = $this->createMock(LoggerInterface::class); + $this->event = $this->createMock(CalendarShareUpdatedEvent::class); + $this->calendarPublicationListener = new CalendarShareUpdateListener($this->activityBackend, $this->logger); + } + + public function testInvalidEvent(): void { + $this->activityBackend->expects($this->never())->method('onCalendarUpdateShares'); + $this->logger->expects($this->never())->method('debug'); + $this->calendarPublicationListener->handle(new Event()); + } + + public function testEvent(): void { + $this->event->expects($this->once())->method('getCalendarData')->with()->willReturn([]); + $this->event->expects($this->once())->method('getOldShares')->with()->willReturn([]); + $this->event->expects($this->once())->method('getAdded')->with()->willReturn([]); + $this->event->expects($this->once())->method('getRemoved')->with()->willReturn([]); + $this->activityBackend->expects($this->once())->method('onCalendarUpdateShares')->with([], [], [], []); + $this->logger->expects($this->once())->method('debug'); + $this->calendarPublicationListener->handle($this->event); + } +} diff --git a/apps/dav/tests/unit/CalDAV/Listener/SubscriptionListenerTest.php b/apps/dav/tests/unit/CalDAV/Listener/SubscriptionListenerTest.php new file mode 100644 index 00000000000..709ebdac7af --- /dev/null +++ b/apps/dav/tests/unit/CalDAV/Listener/SubscriptionListenerTest.php @@ -0,0 +1,95 @@ +<?php +/** + * @copyright 2022 Thomas Citharel <nextcloud@tcit.fr> + * + * @author Thomas Citharel <nextcloud@tcit.fr> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ +namespace OCA\DAV\Tests\unit\CalDAV\Listeners; + +use OCA\DAV\BackgroundJob\RefreshWebcalJob; +use OCA\DAV\CalDAV\Reminder\Backend; +use OCA\DAV\CalDAV\WebcalCaching\RefreshWebcalService; +use OCA\DAV\Events\SubscriptionCreatedEvent; +use OCA\DAV\Events\SubscriptionDeletedEvent; +use OCA\DAV\Listener\SubscriptionListener; +use OCP\BackgroundJob\IJobList; +use OCP\EventDispatcher\Event; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; +use Test\TestCase; + +class SubscriptionListenerTest extends TestCase { + + /** @var RefreshWebcalService|MockObject */ + private $refreshWebcalService; + + /** @var Backend|MockObject */ + private $reminderBackend; + + /** @var IJobList|MockObject */ + private $jobList; + + /** @var LoggerInterface|MockObject */ + private $logger; + + private SubscriptionListener $calendarPublicationListener; + + /** @var SubscriptionCreatedEvent|MockObject */ + private $subscriptionCreatedEvent; + + /** @var SubscriptionDeletedEvent|MockObject */ + private $subscriptionDeletedEvent; + + protected function setUp(): void { + parent::setUp(); + + $this->refreshWebcalService = $this->createMock(RefreshWebcalService::class); + $this->reminderBackend = $this->createMock(Backend::class); + $this->jobList = $this->createMock(IJobList::class); + $this->logger = $this->createMock(LoggerInterface::class); + $this->subscriptionCreatedEvent = $this->createMock(SubscriptionCreatedEvent::class); + $this->subscriptionDeletedEvent = $this->createMock(SubscriptionDeletedEvent::class); + $this->calendarPublicationListener = new SubscriptionListener($this->jobList, $this->refreshWebcalService, $this->reminderBackend, $this->logger); + } + + public function testInvalidEvent(): void { + $this->refreshWebcalService->expects($this->never())->method('refreshSubscription'); + $this->jobList->expects($this->never())->method('add'); + $this->logger->expects($this->never())->method('debug'); + $this->calendarPublicationListener->handle(new Event()); + } + + public function testCreateSubscriptionEvent(): void { + $this->subscriptionCreatedEvent->expects($this->once())->method('getSubscriptionId')->with()->willReturn(5); + $this->subscriptionCreatedEvent->expects($this->once())->method('getSubscriptionData')->with()->willReturn(['principaluri' => 'principaluri', 'uri' => 'uri']); + $this->refreshWebcalService->expects($this->once())->method('refreshSubscription')->with('principaluri', 'uri'); + $this->jobList->expects($this->once())->method('add')->with(RefreshWebcalJob::class, ['principaluri' => 'principaluri', 'uri' => 'uri']); + $this->logger->expects($this->exactly(2))->method('debug'); + $this->calendarPublicationListener->handle($this->subscriptionCreatedEvent); + } + + public function testDeleteSubscriptionEvent(): void { + $this->subscriptionDeletedEvent->expects($this->once())->method('getSubscriptionId')->with()->willReturn(5); + $this->subscriptionDeletedEvent->expects($this->once())->method('getSubscriptionData')->with()->willReturn(['principaluri' => 'principaluri', 'uri' => 'uri']); + $this->jobList->expects($this->once())->method('remove')->with(RefreshWebcalJob::class, ['principaluri' => 'principaluri', 'uri' => 'uri']); + $this->reminderBackend->expects($this->once())->method('cleanRemindersForCalendar')->with(5); + $this->logger->expects($this->exactly(2))->method('debug'); + $this->calendarPublicationListener->handle($this->subscriptionDeletedEvent); + } +} diff --git a/apps/dav/tests/unit/CalDAV/PublicCalendarRootTest.php b/apps/dav/tests/unit/CalDAV/PublicCalendarRootTest.php index 80e43d279dc..23c1c2ae896 100644 --- a/apps/dav/tests/unit/CalDAV/PublicCalendarRootTest.php +++ b/apps/dav/tests/unit/CalDAV/PublicCalendarRootTest.php @@ -42,7 +42,6 @@ use OCP\IL10N; use OCP\IUserManager; use OCP\Security\ISecureRandom; use Psr\Log\LoggerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Test\TestCase; /** @@ -84,7 +83,6 @@ class PublicCalendarRootTest extends TestCase { $this->random = \OC::$server->getSecureRandom(); $this->logger = $this->createMock(LoggerInterface::class); $dispatcher = $this->createMock(IEventDispatcher::class); - $legacyDispatcher = $this->createMock(EventDispatcherInterface::class); $config = $this->createMock(IConfig::class); $this->principal->expects($this->any())->method('getGroupMembership') @@ -103,7 +101,6 @@ class PublicCalendarRootTest extends TestCase { $this->random, $this->logger, $dispatcher, - $legacyDispatcher, $config ); $this->l10n = $this->getMockBuilder(IL10N::class) diff --git a/apps/dav/tests/unit/CalDAV/Schedule/PluginTest.php b/apps/dav/tests/unit/CalDAV/Schedule/PluginTest.php index 2518cc3d91a..b651379c2bd 100644 --- a/apps/dav/tests/unit/CalDAV/Schedule/PluginTest.php +++ b/apps/dav/tests/unit/CalDAV/Schedule/PluginTest.php @@ -27,11 +27,15 @@ namespace OCA\DAV\Tests\unit\CalDAV\Schedule; use OCA\DAV\CalDAV\CalDavBackend; +use OCA\DAV\CalDAV\Calendar; use OCA\DAV\CalDAV\CalendarHome; use OCA\DAV\CalDAV\Plugin as CalDAVPlugin; use OCA\DAV\CalDAV\Schedule\Plugin; +use OCA\DAV\CalDAV\Trashbin\Plugin as TrashbinPlugin; use OCP\IConfig; +use OCP\IL10N; use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; use Sabre\DAV\PropFind; use Sabre\DAV\Server; use Sabre\DAV\Tree; @@ -73,17 +77,22 @@ class PluginTest extends TestCase { public function testInitialize() { $plugin = new Plugin($this->config); - $this->server->expects($this->at(7)) + $this->server->expects($this->exactly(10)) ->method('on') - ->with('propFind', [$plugin, 'propFindDefaultCalendarUrl'], 90); - - $this->server->expects($this->at(8)) - ->method('on') - ->with('afterWriteContent', [$plugin, 'dispatchSchedulingResponses']); - - $this->server->expects($this->at(9)) - ->method('on') - ->with('afterCreateFile', [$plugin, 'dispatchSchedulingResponses']); + ->withConsecutive( + // Sabre\CalDAV\Schedule\Plugin events + ['method:POST', [$plugin, 'httpPost']], + ['propFind', [$plugin, 'propFind']], + ['propPatch', [$plugin, 'propPatch']], + ['calendarObjectChange', [$plugin, 'calendarObjectChange']], + ['beforeUnbind', [$plugin, 'beforeUnbind']], + ['schedule', [$plugin, 'scheduleLocalDelivery']], + ['getSupportedPrivilegeSet', [$plugin, 'getSupportedPrivilegeSet']], + // OCA\DAV\CalDAV\Schedule\Plugin events + ['propFind', [$plugin, 'propFindDefaultCalendarUrl'], 90], + ['afterWriteContent', [$plugin, 'dispatchSchedulingResponses']], + ['afterCreateFile', [$plugin, 'dispatchSchedulingResponses']] + ); $plugin->initialize($this->server); } @@ -183,6 +192,15 @@ class PluginTest extends TestCase { false, CalDavBackend::PERSONAL_CALENDAR_URI, CalDavBackend::PERSONAL_CALENDAR_NAME, + false, + true + ], + [ + 'principals/users/myuser', + 'calendars/myuser', + false, + CalDavBackend::PERSONAL_CALENDAR_URI, + CalDavBackend::PERSONAL_CALENDAR_NAME, false ], [ @@ -201,6 +219,7 @@ class PluginTest extends TestCase { CalDavBackend::PERSONAL_CALENDAR_NAME, true, false, + false, ], [ 'principals/users/myuser', @@ -240,14 +259,14 @@ class PluginTest extends TestCase { /** * @dataProvider propFindDefaultCalendarUrlProvider * @param string $principalUri - * @param string $calendarHome + * @param string|null $calendarHome * @param bool $isResource * @param string $calendarUri * @param string $displayName * @param bool $exists * @param bool $propertiesForPath */ - public function testPropFindDefaultCalendarUrl(string $principalUri, ?string $calendarHome, bool $isResource, string $calendarUri, string $displayName, bool $exists, bool $propertiesForPath = true) { + public function testPropFindDefaultCalendarUrl(string $principalUri, ?string $calendarHome, bool $isResource, string $calendarUri, string $displayName, bool $exists, bool $hasExistingCalendars = false, bool $propertiesForPath = true) { /** @var PropFind $propFind */ $propFind = new PropFind( $principalUri, @@ -290,6 +309,7 @@ class PluginTest extends TestCase { $this->assertNull($propFind->get(Plugin::SCHEDULE_DEFAULT_CALENDAR_URL)); return; } + if (!$isResource) { $this->config->expects($this->once()) ->method('getUserValue') @@ -303,18 +323,47 @@ class PluginTest extends TestCase { ->with($calendarUri) ->willReturn($exists); + $calendarBackend = $this->createMock(CalDavBackend::class); + $calendarUri = $hasExistingCalendars ? 'custom' : $calendarUri; + $displayName = $hasExistingCalendars ? 'Custom Calendar' : $displayName; + + $existingCalendars = $hasExistingCalendars ? [ + new Calendar( + $calendarBackend, + ['uri' => 'deleted', '{DAV:}displayname' => 'A deleted calendar', TrashbinPlugin::PROPERTY_DELETED_AT => 42], + $this->createMock(IL10N::class), + $this->config, + $this->createMock(LoggerInterface::class) + ), + new Calendar( + $calendarBackend, + ['uri' => $calendarUri, '{DAV:}displayname' => $displayName], + $this->createMock(IL10N::class), + $this->config, + $this->createMock(LoggerInterface::class) + ) + ] : []; + if (!$exists) { - $calendarBackend = $this->createMock(CalDavBackend::class); - $calendarBackend->expects($this->once()) + if (!$hasExistingCalendars) { + $calendarBackend->expects($this->once()) ->method('createCalendar') ->with($principalUri, $calendarUri, [ '{DAV:}displayname' => $displayName, ]); - $calendarHomeObject->expects($this->once()) - ->method('getCalDAVBackend') - ->with() - ->willReturn($calendarBackend); + $calendarHomeObject->expects($this->once()) + ->method('getCalDAVBackend') + ->with() + ->willReturn($calendarBackend); + } + + if (!$isResource) { + $calendarHomeObject->expects($this->once()) + ->method('getChildren') + ->with() + ->willReturn($existingCalendars); + } } /** @var Tree|MockObject $tree */ diff --git a/apps/dav/tests/unit/CardDAV/Activity/BackendTest.php b/apps/dav/tests/unit/CardDAV/Activity/BackendTest.php new file mode 100644 index 00000000000..bd88294ce81 --- /dev/null +++ b/apps/dav/tests/unit/CardDAV/Activity/BackendTest.php @@ -0,0 +1,503 @@ +<?php +/** + * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> + * + * @author Christoph Wurst <christoph@winzerhof-wurst.at> + * @author Joas Schilling <coding@schilljs.com> + * @author Roeland Jago Douma <roeland@famdouma.nl> + * @author Thomas Citharel <nextcloud@tcit.fr> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ +namespace OCA\DAV\Tests\unit\CardDAV\Activity; + +use OCA\DAV\CardDAV\Activity\Backend; +use OCA\DAV\CardDAV\Activity\Provider\Addressbook; +use OCA\DAV\CardDAV\Activity\Provider\Card; +use OCP\Activity\IEvent; +use OCP\Activity\IManager; +use OCP\App\IAppManager; +use OCP\IGroup; +use OCP\IGroupManager; +use OCP\IUser; +use OCP\IUserSession; +use PHPUnit\Framework\MockObject\MockObject; +use Test\TestCase; + +class BackendTest extends TestCase { + + /** @var IManager|MockObject */ + protected $activityManager; + + /** @var IGroupManager|MockObject */ + protected $groupManager; + + /** @var IUserSession|MockObject */ + protected $userSession; + + /** @var IAppManager|MockObject */ + protected $appManager; + + protected function setUp(): void { + parent::setUp(); + $this->activityManager = $this->createMock(IManager::class); + $this->groupManager = $this->createMock(IGroupManager::class); + $this->userSession = $this->createMock(IUserSession::class); + $this->appManager = $this->createMock(IAppManager::class); + } + + /** + * @param array $methods + * @return Backend|MockObject + */ + protected function getBackend(array $methods = []) { + if (empty($methods)) { + return new Backend( + $this->activityManager, + $this->groupManager, + $this->userSession, + $this->appManager + ); + } else { + return $this->getMockBuilder(Backend::class) + ->setConstructorArgs([ + $this->activityManager, + $this->groupManager, + $this->userSession, + $this->appManager, + ]) + ->onlyMethods($methods) + ->getMock(); + } + } + + public function dataCallTriggerAddressBookActivity(): array { + return [ + ['onAddressbookCreate', [['data']], Addressbook::SUBJECT_ADD, [['data'], [], []]], + ['onAddressbookUpdate', [['data'], ['shares'], ['changed-properties']], Addressbook::SUBJECT_UPDATE, [['data'], ['shares'], ['changed-properties']]], + ['onAddressbookDelete', [['data'], ['shares']], Addressbook::SUBJECT_DELETE, [['data'], ['shares'], []]], + ]; + } + + /** + * @dataProvider dataCallTriggerAddressBookActivity + */ + public function testCallTriggerAddressBookActivity(string $method, array $payload, string $expectedSubject, array $expectedPayload) { + $backend = $this->getBackend(['triggerAddressbookActivity']); + $backend->expects($this->once()) + ->method('triggerAddressbookActivity') + ->willReturnCallback(function () use ($expectedPayload, $expectedSubject) { + $arguments = func_get_args(); + $this->assertSame($expectedSubject, array_shift($arguments)); + $this->assertEquals($expectedPayload, $arguments); + }); + + call_user_func_array([$backend, $method], $payload); + } + + public function dataTriggerAddressBookActivity(): array { + return [ + // Add addressbook + [Addressbook::SUBJECT_ADD, [], [], [], '', '', null, []], + [Addressbook::SUBJECT_ADD, [ + 'principaluri' => 'principal/user/admin', + 'id' => 42, + 'uri' => 'this-uri', + '{DAV:}displayname' => 'Name of addressbook', + ], [], [], '', 'admin', null, ['admin']], + [Addressbook::SUBJECT_ADD, [ + 'principaluri' => 'principal/user/admin', + 'id' => 42, + 'uri' => 'this-uri', + '{DAV:}displayname' => 'Name of addressbook', + ], [], [], 'test2', 'test2', null, ['admin']], + + // Update addressbook + [Addressbook::SUBJECT_UPDATE, [], [], [], '', '', null, []], + // No visible change - owner only + [Addressbook::SUBJECT_UPDATE, [ + 'principaluri' => 'principal/user/admin', + 'id' => 42, + 'uri' => 'this-uri', + '{DAV:}displayname' => 'Name of addressbook', + ], ['shares'], [], '', 'admin', null, ['admin']], + // Visible change + [Addressbook::SUBJECT_UPDATE, [ + 'principaluri' => 'principal/user/admin', + 'id' => 42, + 'uri' => 'this-uri', + '{DAV:}displayname' => 'Name of addressbook', + ], ['shares'], ['{DAV:}displayname' => 'Name'], '', 'admin', ['user1'], ['user1', 'admin']], + [Addressbook::SUBJECT_UPDATE, [ + 'principaluri' => 'principal/user/admin', + 'id' => 42, + 'uri' => 'this-uri', + '{DAV:}displayname' => 'Name of addressbook', + ], ['shares'], ['{DAV:}displayname' => 'Name'], 'test2', 'test2', ['user1'], ['user1', 'admin']], + + // Delete addressbook + [Addressbook::SUBJECT_DELETE, [], [], [], '', '', null, []], + [Addressbook::SUBJECT_DELETE, [ + 'principaluri' => 'principal/user/admin', + 'id' => 42, + 'uri' => 'this-uri', + '{DAV:}displayname' => 'Name of addressbook', + ], ['shares'], [], '', 'admin', [], ['admin']], + [Addressbook::SUBJECT_DELETE, [ + 'principaluri' => 'principal/user/admin', + 'id' => 42, + 'uri' => 'this-uri', + '{DAV:}displayname' => 'Name of addressbook', + ], ['shares'], [], '', 'admin', ['user1'], ['user1', 'admin']], + [Addressbook::SUBJECT_DELETE, [ + 'principaluri' => 'principal/user/admin', + 'id' => 42, + 'uri' => 'this-uri', + '{DAV:}displayname' => 'Name of addressbook', + ], ['shares'], [], 'test2', 'test2', ['user1'], ['user1', 'admin']], + ]; + } + + /** + * @dataProvider dataTriggerAddressBookActivity + * @param string $action + * @param array $data + * @param array $shares + * @param array $changedProperties + * @param string $currentUser + * @param string $author + * @param string[]|null $shareUsers + * @param string[] $users + */ + public function testTriggerAddressBookActivity(string $action, array $data, array $shares, array $changedProperties, string $currentUser, string $author, ?array $shareUsers, array $users) { + $backend = $this->getBackend(['getUsersForShares']); + + if ($shareUsers === null) { + $backend->expects($this->never()) + ->method('getUsersForShares'); + } else { + $backend->expects($this->once()) + ->method('getUsersForShares') + ->with($shares) + ->willReturn($shareUsers); + } + + if ($author !== '') { + if ($currentUser !== '') { + $this->userSession->expects($this->once()) + ->method('getUser') + ->willReturn($this->getUserMock($currentUser)); + } else { + $this->userSession->expects($this->once()) + ->method('getUser') + ->willReturn(null); + } + + $event = $this->createMock(IEvent::class); + $this->activityManager->expects($this->once()) + ->method('generateEvent') + ->willReturn($event); + + $event->expects($this->once()) + ->method('setApp') + ->with('dav') + ->willReturnSelf(); + $event->expects($this->once()) + ->method('setObject') + ->with('addressbook', $data['id']) + ->willReturnSelf(); + $event->expects($this->once()) + ->method('setType') + ->with('addressbook') + ->willReturnSelf(); + $event->expects($this->once()) + ->method('setAuthor') + ->with($author) + ->willReturnSelf(); + + $event->expects($this->exactly(sizeof($users))) + ->method('setAffectedUser') + ->willReturnSelf(); + $event->expects($this->exactly(sizeof($users))) + ->method('setSubject') + ->willReturnSelf(); + $this->activityManager->expects($this->exactly(sizeof($users))) + ->method('publish') + ->with($event); + } else { + $this->activityManager->expects($this->never()) + ->method('generateEvent'); + } + + $this->invokePrivate($backend, 'triggerAddressbookActivity', [$action, $data, $shares, $changedProperties]); + } + + public function testNoAddressbookActivityCreatedForSystemAddressbook(): void { + $backend = $this->getBackend(); + $this->activityManager->expects($this->never()) + ->method('generateEvent'); + $this->assertEmpty($this->invokePrivate($backend, 'triggerAddressbookActivity', [Addressbook::SUBJECT_ADD, ['principaluri' => 'principals/system/system'], [], [], '', '', null, []])); + } + + public function dataTriggerCardActivity(): array { + $cardData = "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 3.4.8//EN\r\nUID:test-user\r\nFN:test-user\r\nN:test-user;;;;\r\nEND:VCARD\r\n\r\n"; + + return [ + // Add card + [Card::SUBJECT_ADD, [], [], [], '', '', null, []], + [Card::SUBJECT_ADD, [ + 'principaluri' => 'principal/user/admin', + 'id' => 42, + 'uri' => 'this-uri', + '{DAV:}displayname' => 'Name of addressbook', + ], [], [ + 'carddata' => $cardData + ], '', 'admin', [], ['admin']], + [Card::SUBJECT_ADD, [ + 'principaluri' => 'principal/user/admin', + 'id' => 42, + 'uri' => 'this-uri', + '{DAV:}displayname' => 'Name of addressbook', + ], [], ['carddata' => $cardData], 'test2', 'test2', [], ['admin']], + + // Update card + [Card::SUBJECT_UPDATE, [], [], [], '', '', null, []], + // No visible change - owner only + [Card::SUBJECT_UPDATE, [ + 'principaluri' => 'principal/user/admin', + 'id' => 42, + 'uri' => 'this-uri', + '{DAV:}displayname' => 'Name of addressbook', + ], ['shares'], ['carddata' => $cardData], '', 'admin', [], ['admin']], + // Visible change + [Card::SUBJECT_UPDATE, [ + 'principaluri' => 'principal/user/admin', + 'id' => 42, + 'uri' => 'this-uri', + '{DAV:}displayname' => 'Name of addressbook', + ], ['shares'], ['carddata' => $cardData], '', 'admin', ['user1'], ['user1', 'admin']], + [Card::SUBJECT_UPDATE, [ + 'principaluri' => 'principal/user/admin', + 'id' => 42, + 'uri' => 'this-uri', + '{DAV:}displayname' => 'Name of addressbook', + ], ['shares'], ['carddata' => $cardData], 'test2', 'test2', ['user1'], ['user1', 'admin']], + + // Delete card + [Card::SUBJECT_DELETE, [], [], ['carddata' => $cardData], '', '', null, []], + [Card::SUBJECT_DELETE, [ + 'principaluri' => 'principal/user/admin', + 'id' => 42, + 'uri' => 'this-uri', + '{DAV:}displayname' => 'Name of addressbook', + ], ['shares'], ['carddata' => $cardData], '', 'admin', [], ['admin']], + [Card::SUBJECT_DELETE, [ + 'principaluri' => 'principal/user/admin', + 'id' => 42, + 'uri' => 'this-uri', + '{DAV:}displayname' => 'Name of addressbook', + ], ['shares'], ['carddata' => $cardData], '', 'admin', ['user1'], ['user1', 'admin']], + [Card::SUBJECT_DELETE, [ + 'principaluri' => 'principal/user/admin', + 'id' => 42, + 'uri' => 'this-uri', + '{DAV:}displayname' => 'Name of addressbook', + ], ['shares'], ['carddata' => $cardData], 'test2', 'test2', ['user1'], ['user1', 'admin']], + ]; + } + + /** + * @dataProvider dataTriggerCardActivity + * @param string $action + * @param array $addressBookData + * @param array $shares + * @param array $cardData + * @param string $currentUser + * @param string $author + * @param string[]|null $shareUsers + * @param string[] $users + */ + public function testTriggerCardActivity(string $action, array $addressBookData, array $shares, array $cardData, string $currentUser, string $author, ?array $shareUsers, array $users) { + $backend = $this->getBackend(['getUsersForShares']); + + if ($shareUsers === null) { + $backend->expects($this->never()) + ->method('getUsersForShares'); + } else { + $backend->expects($this->once()) + ->method('getUsersForShares') + ->with($shares) + ->willReturn($shareUsers); + } + + if ($author !== '') { + if ($currentUser !== '') { + $this->userSession->expects($this->once()) + ->method('getUser') + ->willReturn($this->getUserMock($currentUser)); + } else { + $this->userSession->expects($this->once()) + ->method('getUser') + ->willReturn(null); + } + + $event = $this->createMock(IEvent::class); + $this->activityManager->expects($this->once()) + ->method('generateEvent') + ->willReturn($event); + + $event->expects($this->once()) + ->method('setApp') + ->with('dav') + ->willReturnSelf(); + $event->expects($this->once()) + ->method('setObject') + ->with('addressbook', $addressBookData['id']) + ->willReturnSelf(); + $event->expects($this->once()) + ->method('setType') + ->with('card') + ->willReturnSelf(); + $event->expects($this->once()) + ->method('setAuthor') + ->with($author) + ->willReturnSelf(); + + $event->expects($this->exactly(sizeof($users))) + ->method('setAffectedUser') + ->willReturnSelf(); + $event->expects($this->exactly(sizeof($users))) + ->method('setSubject') + ->willReturnSelf(); + $this->activityManager->expects($this->exactly(sizeof($users))) + ->method('publish') + ->with($event); + } else { + $this->activityManager->expects($this->never()) + ->method('generateEvent'); + } + + $this->invokePrivate($backend, 'triggerCardActivity', [$action, $addressBookData, $shares, $cardData]); + } + + public function testNoCardActivityCreatedForSystemAddressbook(): void { + $backend = $this->getBackend(); + $this->activityManager->expects($this->never()) + ->method('generateEvent'); + $this->assertEmpty($this->invokePrivate($backend, 'triggerCardActivity', [Card::SUBJECT_UPDATE, ['principaluri' => 'principals/system/system'], [], []])); + } + + public function dataGetUsersForShares(): array { + return [ + [ + [], + [], + [], + ], + [ + [ + ['{http://owncloud.org/ns}principal' => 'principal/users/user1'], + ['{http://owncloud.org/ns}principal' => 'principal/users/user2'], + ['{http://owncloud.org/ns}principal' => 'principal/users/user2'], + ['{http://owncloud.org/ns}principal' => 'principal/users/user2'], + ['{http://owncloud.org/ns}principal' => 'principal/users/user3'], + ], + [], + ['user1', 'user2', 'user3'], + ], + [ + [ + ['{http://owncloud.org/ns}principal' => 'principal/users/user1'], + ['{http://owncloud.org/ns}principal' => 'principal/users/user2'], + ['{http://owncloud.org/ns}principal' => 'principal/users/user2'], + ['{http://owncloud.org/ns}principal' => 'principal/groups/group2'], + ['{http://owncloud.org/ns}principal' => 'principal/groups/group3'], + ], + ['group2' => null, 'group3' => null], + ['user1', 'user2'], + ], + [ + [ + ['{http://owncloud.org/ns}principal' => 'principal/users/user1'], + ['{http://owncloud.org/ns}principal' => 'principal/users/user2'], + ['{http://owncloud.org/ns}principal' => 'principal/users/user2'], + ['{http://owncloud.org/ns}principal' => 'principal/groups/group2'], + ['{http://owncloud.org/ns}principal' => 'principal/groups/group3'], + ], + ['group2' => ['user1', 'user2', 'user3'], 'group3' => ['user2', 'user3', 'user4']], + ['user1', 'user2', 'user3', 'user4'], + ], + ]; + } + + /** + * @dataProvider dataGetUsersForShares + * @param array $shares + * @param array $groups + * @param array $expected + */ + public function testGetUsersForShares(array $shares, array $groups, array $expected) { + $backend = $this->getBackend(); + + $getGroups = []; + foreach ($groups as $gid => $members) { + if ($members === null) { + $getGroups[] = [$gid, null]; + continue; + } + + $group = $this->createMock(IGroup::class); + $group->expects($this->once()) + ->method('getUsers') + ->willReturn($this->getUsers($members)); + + $getGroups[] = [$gid, $group]; + } + + $this->groupManager->expects($this->exactly(sizeof($getGroups))) + ->method('get') + ->willReturnMap($getGroups); + + $users = $this->invokePrivate($backend, 'getUsersForShares', [$shares]); + sort($users); + $this->assertEquals($expected, $users); + } + + /** + * @param string[] $users + * @return IUser[]|MockObject[] + */ + protected function getUsers(array $users): array { + $list = []; + foreach ($users as $user) { + $list[] = $this->getUserMock($user); + } + return $list; + } + + /** + * @param string $uid + * @return IUser|MockObject + */ + protected function getUserMock(string $uid) { + $user = $this->createMock(IUser::class); + $user->expects($this->once()) + ->method('getUID') + ->willReturn($uid); + return $user; + } +} diff --git a/apps/dav/tests/unit/CardDAV/CardDavBackendTest.php b/apps/dav/tests/unit/CardDAV/CardDavBackendTest.php index 7eda691d199..5bfbab07b29 100644 --- a/apps/dav/tests/unit/CardDAV/CardDavBackendTest.php +++ b/apps/dav/tests/unit/CardDAV/CardDavBackendTest.php @@ -13,6 +13,7 @@ * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> + * @author Thomas Citharel <nextcloud@tcit.fr> * @author Thomas Müller <thomas.mueller@tmit.eu> * * @license AGPL-3.0 @@ -52,8 +53,6 @@ use Sabre\DAV\Exception\BadRequest; use Sabre\DAV\PropPatch; use Sabre\VObject\Component\VCard; use Sabre\VObject\Property\Text; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Symfony\Component\EventDispatcher\GenericEvent; use Test\TestCase; /** @@ -77,10 +76,7 @@ class CardDavBackendTest extends TestCase { /** @var IGroupManager|\PHPUnit\Framework\MockObject\MockObject */ private $groupManager; - /** @var EventDispatcherInterface|\PHPUnit\Framework\MockObject\MockObject */ - private $legacyDispatcher; - - /** @var IEventDispatcher|\PHPUnit\Framework\MockObject\MockObject */ + /** @var IEventDispatcher|MockObject */ private $dispatcher; /** @var IDBConnection */ @@ -155,11 +151,10 @@ class CardDavBackendTest extends TestCase { ->withAnyParameters() ->willReturn([self::UNIT_TEST_GROUP]); $this->dispatcher = $this->createMock(IEventDispatcher::class); - $this->legacyDispatcher = $this->createMock(EventDispatcherInterface::class); $this->db = \OC::$server->getDatabaseConnection(); - $this->backend = new CardDavBackend($this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher, $this->legacyDispatcher); + $this->backend = new CardDavBackend($this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher); // start every test with a empty cards_properties and cards table $query = $this->db->getQueryBuilder(); $query->delete('cards_properties')->execute(); @@ -249,8 +244,8 @@ class CardDavBackendTest extends TestCase { /** @var CardDavBackend | \PHPUnit\Framework\MockObject\MockObject $backend */ $backend = $this->getMockBuilder(CardDavBackend::class) - ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher, $this->legacyDispatcher]) - ->setMethods(['updateProperties', 'purgeProperties'])->getMock(); + ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher]) + ->onlyMethods(['updateProperties', 'purgeProperties'])->getMock(); // create a new address book $backend->createAddressBook(self::UNIT_TEST_USER, 'Example', []); @@ -264,13 +259,9 @@ class CardDavBackendTest extends TestCase { $backend->expects($this->at(1))->method('updateProperties')->with($bookId, $uri, $this->vcardTest1); // Expect event - $this->legacyDispatcher->expects($this->at(0)) - ->method('dispatch') - ->with('\OCA\DAV\CardDAV\CardDavBackend::createCard', $this->callback(function (GenericEvent $e) use ($bookId, $uri) { - return $e->getArgument('addressBookId') === $bookId && - $e->getArgument('cardUri') === $uri && - $e->getArgument('cardData') === $this->vcardTest0; - })); + $this->dispatcher + ->expects($this->exactly(3)) + ->method('dispatchTyped'); // create a card $backend->createCard($bookId, $uri, $this->vcardTest0); @@ -290,28 +281,11 @@ class CardDavBackendTest extends TestCase { $this->assertArrayHasKey('size', $card); $this->assertEquals($this->vcardTest0, $card['carddata']); - // Expect event - $this->legacyDispatcher->expects($this->at(0)) - ->method('dispatch') - ->with('\OCA\DAV\CardDAV\CardDavBackend::updateCard', $this->callback(function (GenericEvent $e) use ($bookId, $uri) { - return $e->getArgument('addressBookId') === $bookId && - $e->getArgument('cardUri') === $uri && - $e->getArgument('cardData') === $this->vcardTest1; - })); - // update the card $backend->updateCard($bookId, $uri, $this->vcardTest1); $card = $backend->getCard($bookId, $uri); $this->assertEquals($this->vcardTest1, $card['carddata']); - // Expect event - $this->legacyDispatcher->expects($this->at(0)) - ->method('dispatch') - ->with('\OCA\DAV\CardDAV\CardDavBackend::deleteCard', $this->callback(function (GenericEvent $e) use ($bookId, $uri) { - return $e->getArgument('addressBookId') === $bookId && - $e->getArgument('cardUri') === $uri; - })); - // delete the card $backend->expects($this->once())->method('purgeProperties')->with($bookId, $card['id']); $backend->deleteCard($bookId, $uri); @@ -321,7 +295,7 @@ class CardDavBackendTest extends TestCase { public function testMultiCard() { $this->backend = $this->getMockBuilder(CardDavBackend::class) - ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher, $this->legacyDispatcher]) + ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher]) ->setMethods(['updateProperties'])->getMock(); // create a new address book @@ -374,8 +348,8 @@ class CardDavBackendTest extends TestCase { public function testMultipleUIDOnDifferentAddressbooks() { $this->backend = $this->getMockBuilder(CardDavBackend::class) - ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher, $this->legacyDispatcher]) - ->setMethods(['updateProperties'])->getMock(); + ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher]) + ->onlyMethods(['updateProperties'])->getMock(); // create 2 new address books $this->backend->createAddressBook(self::UNIT_TEST_USER, 'Example', []); @@ -396,7 +370,7 @@ class CardDavBackendTest extends TestCase { public function testMultipleUIDDenied() { $this->backend = $this->getMockBuilder(CardDavBackend::class) - ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher, $this->legacyDispatcher]) + ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher]) ->setMethods(['updateProperties'])->getMock(); // create a new address book @@ -417,7 +391,7 @@ class CardDavBackendTest extends TestCase { public function testNoValidUID() { $this->backend = $this->getMockBuilder(CardDavBackend::class) - ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher, $this->legacyDispatcher]) + ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher]) ->setMethods(['updateProperties'])->getMock(); // create a new address book @@ -434,8 +408,8 @@ class CardDavBackendTest extends TestCase { public function testDeleteWithoutCard() { $this->backend = $this->getMockBuilder(CardDavBackend::class) - ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher, $this->legacyDispatcher]) - ->setMethods([ + ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher]) + ->onlyMethods([ 'getCardId', 'addChange', 'purgeProperties', @@ -474,7 +448,7 @@ class CardDavBackendTest extends TestCase { public function testSyncSupport() { $this->backend = $this->getMockBuilder(CardDavBackend::class) - ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher, $this->legacyDispatcher]) + ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher]) ->setMethods(['updateProperties'])->getMock(); // create a new address book @@ -540,8 +514,8 @@ class CardDavBackendTest extends TestCase { $cardId = 2; $backend = $this->getMockBuilder(CardDavBackend::class) - ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher, $this->legacyDispatcher]) - ->setMethods(['getCardId'])->getMock(); + ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher]) + ->onlyMethods(['getCardId'])->getMock(); $backend->expects($this->any())->method('getCardId')->willReturn($cardId); @@ -630,8 +604,8 @@ class CardDavBackendTest extends TestCase { $qResult->closeCursor(); $this->assertSame(1, count($result)); - $this->assertSame(1 ,(int)$result[0]['addressbookid']); - $this->assertSame(2 ,(int)$result[0]['cardid']); + $this->assertSame(1, (int)$result[0]['addressbookid']); + $this->assertSame(2, (int)$result[0]['cardid']); } public function testGetCardId() { diff --git a/apps/dav/tests/unit/Connector/Sabre/ExceptionLoggerPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/ExceptionLoggerPluginTest.php index 3627a177969..a553c0687e0 100644 --- a/apps/dav/tests/unit/Connector/Sabre/ExceptionLoggerPluginTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/ExceptionLoggerPluginTest.php @@ -32,6 +32,7 @@ use OC\Log; use OC\SystemConfig; use OCA\DAV\Connector\Sabre\Exception\InvalidPath; use OCA\DAV\Connector\Sabre\ExceptionLoggerPlugin; +use OCA\DAV\Exception\ServerMaintenanceMode; use Psr\Log\LoggerInterface; use Sabre\DAV\Exception\NotFound; use Sabre\DAV\Exception\ServiceUnavailable; @@ -84,8 +85,10 @@ class ExceptionLoggerPluginTest extends TestCase { public function providesExceptions() { return [ ['debug', new NotFound()], - ['debug', new ServiceUnavailable('System in maintenance mode.')], - ['critical', new ServiceUnavailable('Upgrade needed')], + ['debug', new ServerMaintenanceMode('System is in maintenance mode.')], + // Faking a translation + ['debug', new ServerMaintenanceMode('Syst3m 1s 1n m41nt3n4nc3 m0d3.')], + ['debug', new ServerMaintenanceMode('Upgrade needed')], ['critical', new InvalidPath('This path leads to nowhere')] ]; } diff --git a/apps/encryption/l10n/ja.js b/apps/encryption/l10n/ja.js index 6d2cbef3157..a0308d1414f 100644 --- a/apps/encryption/l10n/ja.js +++ b/apps/encryption/l10n/ja.js @@ -28,11 +28,15 @@ OC.L10N.register( "Bad Signature" : "不正な署名", "Missing Signature" : "署名が存在しません", "one-time password for server-side-encryption" : "サーバーサイド暗号化のワンタイムパスワード", + "Cannot decrypt this file, probably this is a shared file. Please ask the file owner to reshare the file with you." : "このファイルを復号化できません、共有ファイルの可能性があります。ファイルの所有者にお願いして、ファイルを共有しなおしてもらってください。", + "Cannot read this file, probably this is a shared file. Please ask the file owner to reshare the file with you." : "このファイルを読み取ることができません、共有ファイルの可能性があります。ファイルの所有者にお願いして、ファイルを共有しなおしてもらってください。", "Default encryption module" : "デフォルトの暗号化モジュール", "Default encryption module for server-side encryption" : "サーバーサイド暗号化のデフォルト暗号化モジュール", "In order to use this encryption module you need to enable server-side\n\t\tencryption in the admin settings. Once enabled this module will encrypt\n\t\tall your files transparently. The encryption is based on AES 256 keys.\n\t\tThe module won't touch existing files, only new files will be encrypted\n\t\tafter server-side encryption was enabled. It is also not possible to\n\t\tdisable the encryption again and switch back to a unencrypted system.\n\t\tPlease read the documentation to know all implications before you decide\n\t\tto enable server-side encryption." : "この暗号化モジュールを使うには管理者画面でサーバーサイド暗号化を有効にする\n\t\t必要があります。このモジュールを一旦有効にすると全てのファイルが\n\t\t意識することなく暗号化されます。AES 256bit の鍵で暗号化されます。\n\t\tこのモジュールは既存ファイルはそのままで、サーバーサイド暗号化を\n\t\t有効にした後の新しいファイルのみ暗号化します。 一旦暗号化したシステムを\n\t\t無効化して元の暗号化されていない状態に戻すことはできません。\n\t\tサーバーサイド暗号化するかどうか決める前にドキュメントをよく読んで\n\t\t全ての要件を確認してください。", + "Hey there,\n\nThe administration enabled server-side-encryption. Your files were encrypted using the password \"%s\".\n\nPlease login to the web interface, go to the section \"Basic encryption module\" of your personal settings and update your encryption password by entering this password into the \"Old log-in password\" field and your current login-password.\n\n" : "こんにちは\n\n管理者がサーバーサイド暗号化を有効にしました。\"%s\"というパスワードであなたのファイルが暗号化されました。\n\nWeb画面からログインして、個人設定画面の\"基本暗号化モジュール\"セクションにいき、暗号化パスワードの更新をお願いします。\"旧ログインパスワード\"部分に上記パスワードを入力し、現在のログインパスワードで更新します。\n", "The share will expire on %s." : "共有は %s で有効期限が切れます。", "Cheers!" : "それでは!", + "Hey there,<br><br>The administration enabled server-side-encryption. Your files were encrypted using the password <strong>%s</strong>.<br><br>Please login to the web interface, go to the section \"Basic encryption module\" of your personal settings and update your encryption password by entering this password into the \"Old log-in password\" field and your current login-password.<br><br>" : "こんにちは、<br><br>管理者がサーバーサイド暗号化を有効にしました。<strong>%s</strong>というパスワードであなたのファイルが暗号化されました。<br><br>Web画面からログインして、個人設定画面の\"基本暗号化モジュール\"のセクションにいき、暗号化パスワードの更新をお願いします。\"旧ログインパスワード\"部分に上記パスワードを入力し、現在のログインパスワードで更新します。<br><br>", "Encryption app is enabled but your keys are not initialized, please log-out and log-in again" : "暗号化アプリは有効ですが、あなたの暗号化キーは初期化されていません。ログアウトした後に、再度ログインしてください", "Encrypt the home storage" : "メインストレージ暗号化", "Enabling this option encrypts all files stored on the main storage, otherwise only files on external storage will be encrypted" : "このオプションを有効にすると、外部ストレージ接続ストレージだけが暗号化されるのではなく、メインストレージのファイルすべてが暗号化されます。", @@ -57,6 +61,7 @@ OC.L10N.register( "Enabling this option will allow you to reobtain access to your encrypted files in case of password loss" : "このオプションを有効にすると、パスワードを紛失した場合も、暗号化されたファイルに再度アクセスすることができるようになります。", "Enabled" : "有効", "Disabled" : "無効", + "Hey there,\n\nthe admin enabled server-side-encryption. Your files were encrypted using the password \"%s\".\n\nPlease login to the web interface, go to the section \"basic encryption module\" of your personal settings and update your encryption password by entering this password into the \"old log-in password\" field and your current login-password.\n\n" : "こんにちは\n\n管理者がサーバーサイド暗号化を有効にしました。\"%s\"というパスワードであなたのファイルが暗号化されました。\n\nWeb画面からログインして、個人設定画面の\"基本暗号化モジュール\"セクションにいき、暗号化パスワードの更新をお願いします。\"旧ログインパスワード\"部分に上記パスワードを入力し、現在のログインパスワードで更新します。\n", "Hey there,<br><br>the admin enabled server-side-encryption. Your files were encrypted using the password <strong>%s</strong>.<br><br>Please login to the web interface, go to the section \"basic encryption module\" of your personal settings and update your encryption password by entering this password into the \"old log-in password\" field and your current login-password.<br><br>" : "こんにちは、<br><br>管理者がサーバーサイド暗号化を有効にしました。<strong>%s</strong>というパスワードであなたのファイルが暗号化されました。<br><br>Web画面からログインして、個人設定画面の\"基本暗号化モジュール\"のセクションにいき、暗号化パスワードの更新をお願いします。 \"旧ログインパスワード”部分に上記パスワードを入力し、現在のログインパスワードで更新します。<br><br>" }, "nplurals=1; plural=0;"); diff --git a/apps/encryption/l10n/ja.json b/apps/encryption/l10n/ja.json index e605d57d627..9849803982f 100644 --- a/apps/encryption/l10n/ja.json +++ b/apps/encryption/l10n/ja.json @@ -26,11 +26,15 @@ "Bad Signature" : "不正な署名", "Missing Signature" : "署名が存在しません", "one-time password for server-side-encryption" : "サーバーサイド暗号化のワンタイムパスワード", + "Cannot decrypt this file, probably this is a shared file. Please ask the file owner to reshare the file with you." : "このファイルを復号化できません、共有ファイルの可能性があります。ファイルの所有者にお願いして、ファイルを共有しなおしてもらってください。", + "Cannot read this file, probably this is a shared file. Please ask the file owner to reshare the file with you." : "このファイルを読み取ることができません、共有ファイルの可能性があります。ファイルの所有者にお願いして、ファイルを共有しなおしてもらってください。", "Default encryption module" : "デフォルトの暗号化モジュール", "Default encryption module for server-side encryption" : "サーバーサイド暗号化のデフォルト暗号化モジュール", "In order to use this encryption module you need to enable server-side\n\t\tencryption in the admin settings. Once enabled this module will encrypt\n\t\tall your files transparently. The encryption is based on AES 256 keys.\n\t\tThe module won't touch existing files, only new files will be encrypted\n\t\tafter server-side encryption was enabled. It is also not possible to\n\t\tdisable the encryption again and switch back to a unencrypted system.\n\t\tPlease read the documentation to know all implications before you decide\n\t\tto enable server-side encryption." : "この暗号化モジュールを使うには管理者画面でサーバーサイド暗号化を有効にする\n\t\t必要があります。このモジュールを一旦有効にすると全てのファイルが\n\t\t意識することなく暗号化されます。AES 256bit の鍵で暗号化されます。\n\t\tこのモジュールは既存ファイルはそのままで、サーバーサイド暗号化を\n\t\t有効にした後の新しいファイルのみ暗号化します。 一旦暗号化したシステムを\n\t\t無効化して元の暗号化されていない状態に戻すことはできません。\n\t\tサーバーサイド暗号化するかどうか決める前にドキュメントをよく読んで\n\t\t全ての要件を確認してください。", + "Hey there,\n\nThe administration enabled server-side-encryption. Your files were encrypted using the password \"%s\".\n\nPlease login to the web interface, go to the section \"Basic encryption module\" of your personal settings and update your encryption password by entering this password into the \"Old log-in password\" field and your current login-password.\n\n" : "こんにちは\n\n管理者がサーバーサイド暗号化を有効にしました。\"%s\"というパスワードであなたのファイルが暗号化されました。\n\nWeb画面からログインして、個人設定画面の\"基本暗号化モジュール\"セクションにいき、暗号化パスワードの更新をお願いします。\"旧ログインパスワード\"部分に上記パスワードを入力し、現在のログインパスワードで更新します。\n", "The share will expire on %s." : "共有は %s で有効期限が切れます。", "Cheers!" : "それでは!", + "Hey there,<br><br>The administration enabled server-side-encryption. Your files were encrypted using the password <strong>%s</strong>.<br><br>Please login to the web interface, go to the section \"Basic encryption module\" of your personal settings and update your encryption password by entering this password into the \"Old log-in password\" field and your current login-password.<br><br>" : "こんにちは、<br><br>管理者がサーバーサイド暗号化を有効にしました。<strong>%s</strong>というパスワードであなたのファイルが暗号化されました。<br><br>Web画面からログインして、個人設定画面の\"基本暗号化モジュール\"のセクションにいき、暗号化パスワードの更新をお願いします。\"旧ログインパスワード\"部分に上記パスワードを入力し、現在のログインパスワードで更新します。<br><br>", "Encryption app is enabled but your keys are not initialized, please log-out and log-in again" : "暗号化アプリは有効ですが、あなたの暗号化キーは初期化されていません。ログアウトした後に、再度ログインしてください", "Encrypt the home storage" : "メインストレージ暗号化", "Enabling this option encrypts all files stored on the main storage, otherwise only files on external storage will be encrypted" : "このオプションを有効にすると、外部ストレージ接続ストレージだけが暗号化されるのではなく、メインストレージのファイルすべてが暗号化されます。", @@ -55,6 +59,7 @@ "Enabling this option will allow you to reobtain access to your encrypted files in case of password loss" : "このオプションを有効にすると、パスワードを紛失した場合も、暗号化されたファイルに再度アクセスすることができるようになります。", "Enabled" : "有効", "Disabled" : "無効", + "Hey there,\n\nthe admin enabled server-side-encryption. Your files were encrypted using the password \"%s\".\n\nPlease login to the web interface, go to the section \"basic encryption module\" of your personal settings and update your encryption password by entering this password into the \"old log-in password\" field and your current login-password.\n\n" : "こんにちは\n\n管理者がサーバーサイド暗号化を有効にしました。\"%s\"というパスワードであなたのファイルが暗号化されました。\n\nWeb画面からログインして、個人設定画面の\"基本暗号化モジュール\"セクションにいき、暗号化パスワードの更新をお願いします。\"旧ログインパスワード\"部分に上記パスワードを入力し、現在のログインパスワードで更新します。\n", "Hey there,<br><br>the admin enabled server-side-encryption. Your files were encrypted using the password <strong>%s</strong>.<br><br>Please login to the web interface, go to the section \"basic encryption module\" of your personal settings and update your encryption password by entering this password into the \"old log-in password\" field and your current login-password.<br><br>" : "こんにちは、<br><br>管理者がサーバーサイド暗号化を有効にしました。<strong>%s</strong>というパスワードであなたのファイルが暗号化されました。<br><br>Web画面からログインして、個人設定画面の\"基本暗号化モジュール\"のセクションにいき、暗号化パスワードの更新をお願いします。 \"旧ログインパスワード”部分に上記パスワードを入力し、現在のログインパスワードで更新します。<br><br>" },"pluralForm" :"nplurals=1; plural=0;" }
\ No newline at end of file diff --git a/apps/encryption/l10n/nb.js b/apps/encryption/l10n/nb.js index 295157a0698..b5675e4b60f 100644 --- a/apps/encryption/l10n/nb.js +++ b/apps/encryption/l10n/nb.js @@ -28,10 +28,15 @@ OC.L10N.register( "Bad Signature" : "Feil signatur", "Missing Signature" : "Manglende signatur", "one-time password for server-side-encryption" : "engangspassord for kryptering på serverdelen", + "Cannot decrypt this file, probably this is a shared file. Please ask the file owner to reshare the file with you." : "Kan ikke dekryptere denne filen, kanskje fordi det er en delt fil. Be fileieren om å dele filen pånytt.", + "Cannot read this file, probably this is a shared file. Please ask the file owner to reshare the file with you." : "Kan ikke lese denne filen, kanskje fordi det er en delt fil. Be fileieren om å dele filen pånytt.", "Default encryption module" : "Standard krypteringsmodul", "Default encryption module for server-side encryption" : "Standard krypteringsmodul for kryptering på serverdelen", + "In order to use this encryption module you need to enable server-side\n\t\tencryption in the admin settings. Once enabled this module will encrypt\n\t\tall your files transparently. The encryption is based on AES 256 keys.\n\t\tThe module won't touch existing files, only new files will be encrypted\n\t\tafter server-side encryption was enabled. It is also not possible to\n\t\tdisable the encryption again and switch back to a unencrypted system.\n\t\tPlease read the documentation to know all implications before you decide\n\t\tto enable server-side encryption." : "For å kunne bruke denne krypteringsmodulen må du kunne aktivere kryptering på serveren i administrasjonsinnstillingene. Når dette er aktivert vil modulen kryptere alle filene dine. Krypteringen er basert på AES 256-nøkler. Modulen vil ikke røre eksisterende filer, kun nye filer vil bli kryptert etter at dette ble aktivert. Det er heller ikke mulig å deaktivere krypteringen igjen for å bytte tilbake til et ukryptert system.\nVennligst les dokumentasjonen for å skjønne alle følgende før du bestemmer deg for å aktivere kryptering på serveren.", + "Hey there,\n\nThe administration enabled server-side-encryption. Your files were encrypted using the password \"%s\".\n\nPlease login to the web interface, go to the section \"Basic encryption module\" of your personal settings and update your encryption password by entering this password into the \"Old log-in password\" field and your current login-password.\n\n" : "Hei,\n\nAdministratoren har aktivert kryptering på serveren. Dine filer ble kryptert med passordet \"%s\".\n\nVennligst logg inn på nettgrensesnittet, gå til seksjonen \"Enkel krypteringsmodul\" i dine personlige innstillinger, og oppdater kryperingspassordet ditt ved å taste dette passordet i feltet \"Gammelt innloggingspassord\" og ditt nåværende passord for innlogging.\n\n", "The share will expire on %s." : "Delingen vil opphøre %s.", "Cheers!" : "Ha det!", + "Hey there,<br><br>The administration enabled server-side-encryption. Your files were encrypted using the password <strong>%s</strong>.<br><br>Please login to the web interface, go to the section \"Basic encryption module\" of your personal settings and update your encryption password by entering this password into the \"Old log-in password\" field and your current login-password.<br><br>" : "Hei,<br><br>Administatoren har aktivert kryptering på serveren. Dine filer ble kryptert med passordet <strong>%s</strong>.<br><br>Vennligst logg inn på nettgrensesnittet, gå til seksjonen \"Enkel krypteringsmodul\" i dine personlige innstillinger, og oppdater kryperingspassordet ditt ved å taste dette passordet i feltet \"Gammelt innloggingspassord\" og ditt nåværende passord for innlogging.<br><br>", "Encryption app is enabled but your keys are not initialized, please log-out and log-in again" : "Krypterings-appen er aktivert men nøklene dine er ikke satt opp. Logg ut og inn igjen.", "Encrypt the home storage" : "Krypter hjemmelageret", "Enabling this option encrypts all files stored on the main storage, otherwise only files on external storage will be encrypted" : "Aktivering av dette valget krypterer alle filer som er lagret på hovedlageret. Ellers vil kun filer på eksterne lagre bli kryptert.", @@ -56,6 +61,7 @@ OC.L10N.register( "Enabling this option will allow you to reobtain access to your encrypted files in case of password loss" : "Aktivering av dette valget tillater deg å gjenerobre tilgang til dine krypterte filer i tilfelle du mister passordet ditt.", "Enabled" : "Aktivert", "Disabled" : "Inaktiv", + "Hey there,\n\nthe admin enabled server-side-encryption. Your files were encrypted using the password \"%s\".\n\nPlease login to the web interface, go to the section \"basic encryption module\" of your personal settings and update your encryption password by entering this password into the \"old log-in password\" field and your current login-password.\n\n" : "Hei,\n\nAdministratoren har skrudd på kryptering på serversiden. Filene dine er blitt kryptert med passordet \"%s\".\n\nLogg inn på web-grensesnittet, gå til seksjonen \"grunnleggende krypteringsmodul\" i dine personlige innstillinger og oppdater krypteringspassordet ditt ved å legge inn dette passordet i feltet \"gammelt påloggingspassord\" sammen med ditt nåværende påloggingspassord.Hey there,\n\n", "Hey there,<br><br>the admin enabled server-side-encryption. Your files were encrypted using the password <strong>%s</strong>.<br><br>Please login to the web interface, go to the section \"basic encryption module\" of your personal settings and update your encryption password by entering this password into the \"old log-in password\" field and your current login-password.<br><br>" : "Hei,<br><br>Administratoren har skrudd på kryptering på serversiden. Filene dine er blitt kryptert med passordet <strong>%s</strong>.<br><br>Logg inn på web-grensesnittet, gå til seksjonen \"grunnleggende krypteringsmodul\" i dine personlige innstillinger og oppdater krypteringspassordet ditt ved å legge inn dette passordet i feltet \"gammelt påloggingspassord\" sammen med ditt nåværende påloggingspassord.<br><br>" }, "nplurals=2; plural=(n != 1);"); diff --git a/apps/encryption/l10n/nb.json b/apps/encryption/l10n/nb.json index 9680e34cbb1..0c3ecbf5656 100644 --- a/apps/encryption/l10n/nb.json +++ b/apps/encryption/l10n/nb.json @@ -26,10 +26,15 @@ "Bad Signature" : "Feil signatur", "Missing Signature" : "Manglende signatur", "one-time password for server-side-encryption" : "engangspassord for kryptering på serverdelen", + "Cannot decrypt this file, probably this is a shared file. Please ask the file owner to reshare the file with you." : "Kan ikke dekryptere denne filen, kanskje fordi det er en delt fil. Be fileieren om å dele filen pånytt.", + "Cannot read this file, probably this is a shared file. Please ask the file owner to reshare the file with you." : "Kan ikke lese denne filen, kanskje fordi det er en delt fil. Be fileieren om å dele filen pånytt.", "Default encryption module" : "Standard krypteringsmodul", "Default encryption module for server-side encryption" : "Standard krypteringsmodul for kryptering på serverdelen", + "In order to use this encryption module you need to enable server-side\n\t\tencryption in the admin settings. Once enabled this module will encrypt\n\t\tall your files transparently. The encryption is based on AES 256 keys.\n\t\tThe module won't touch existing files, only new files will be encrypted\n\t\tafter server-side encryption was enabled. It is also not possible to\n\t\tdisable the encryption again and switch back to a unencrypted system.\n\t\tPlease read the documentation to know all implications before you decide\n\t\tto enable server-side encryption." : "For å kunne bruke denne krypteringsmodulen må du kunne aktivere kryptering på serveren i administrasjonsinnstillingene. Når dette er aktivert vil modulen kryptere alle filene dine. Krypteringen er basert på AES 256-nøkler. Modulen vil ikke røre eksisterende filer, kun nye filer vil bli kryptert etter at dette ble aktivert. Det er heller ikke mulig å deaktivere krypteringen igjen for å bytte tilbake til et ukryptert system.\nVennligst les dokumentasjonen for å skjønne alle følgende før du bestemmer deg for å aktivere kryptering på serveren.", + "Hey there,\n\nThe administration enabled server-side-encryption. Your files were encrypted using the password \"%s\".\n\nPlease login to the web interface, go to the section \"Basic encryption module\" of your personal settings and update your encryption password by entering this password into the \"Old log-in password\" field and your current login-password.\n\n" : "Hei,\n\nAdministratoren har aktivert kryptering på serveren. Dine filer ble kryptert med passordet \"%s\".\n\nVennligst logg inn på nettgrensesnittet, gå til seksjonen \"Enkel krypteringsmodul\" i dine personlige innstillinger, og oppdater kryperingspassordet ditt ved å taste dette passordet i feltet \"Gammelt innloggingspassord\" og ditt nåværende passord for innlogging.\n\n", "The share will expire on %s." : "Delingen vil opphøre %s.", "Cheers!" : "Ha det!", + "Hey there,<br><br>The administration enabled server-side-encryption. Your files were encrypted using the password <strong>%s</strong>.<br><br>Please login to the web interface, go to the section \"Basic encryption module\" of your personal settings and update your encryption password by entering this password into the \"Old log-in password\" field and your current login-password.<br><br>" : "Hei,<br><br>Administatoren har aktivert kryptering på serveren. Dine filer ble kryptert med passordet <strong>%s</strong>.<br><br>Vennligst logg inn på nettgrensesnittet, gå til seksjonen \"Enkel krypteringsmodul\" i dine personlige innstillinger, og oppdater kryperingspassordet ditt ved å taste dette passordet i feltet \"Gammelt innloggingspassord\" og ditt nåværende passord for innlogging.<br><br>", "Encryption app is enabled but your keys are not initialized, please log-out and log-in again" : "Krypterings-appen er aktivert men nøklene dine er ikke satt opp. Logg ut og inn igjen.", "Encrypt the home storage" : "Krypter hjemmelageret", "Enabling this option encrypts all files stored on the main storage, otherwise only files on external storage will be encrypted" : "Aktivering av dette valget krypterer alle filer som er lagret på hovedlageret. Ellers vil kun filer på eksterne lagre bli kryptert.", @@ -54,6 +59,7 @@ "Enabling this option will allow you to reobtain access to your encrypted files in case of password loss" : "Aktivering av dette valget tillater deg å gjenerobre tilgang til dine krypterte filer i tilfelle du mister passordet ditt.", "Enabled" : "Aktivert", "Disabled" : "Inaktiv", + "Hey there,\n\nthe admin enabled server-side-encryption. Your files were encrypted using the password \"%s\".\n\nPlease login to the web interface, go to the section \"basic encryption module\" of your personal settings and update your encryption password by entering this password into the \"old log-in password\" field and your current login-password.\n\n" : "Hei,\n\nAdministratoren har skrudd på kryptering på serversiden. Filene dine er blitt kryptert med passordet \"%s\".\n\nLogg inn på web-grensesnittet, gå til seksjonen \"grunnleggende krypteringsmodul\" i dine personlige innstillinger og oppdater krypteringspassordet ditt ved å legge inn dette passordet i feltet \"gammelt påloggingspassord\" sammen med ditt nåværende påloggingspassord.Hey there,\n\n", "Hey there,<br><br>the admin enabled server-side-encryption. Your files were encrypted using the password <strong>%s</strong>.<br><br>Please login to the web interface, go to the section \"basic encryption module\" of your personal settings and update your encryption password by entering this password into the \"old log-in password\" field and your current login-password.<br><br>" : "Hei,<br><br>Administratoren har skrudd på kryptering på serversiden. Filene dine er blitt kryptert med passordet <strong>%s</strong>.<br><br>Logg inn på web-grensesnittet, gå til seksjonen \"grunnleggende krypteringsmodul\" i dine personlige innstillinger og oppdater krypteringspassordet ditt ved å legge inn dette passordet i feltet \"gammelt påloggingspassord\" sammen med ditt nåværende påloggingspassord.<br><br>" },"pluralForm" :"nplurals=2; plural=(n != 1);" }
\ No newline at end of file diff --git a/apps/federatedfilesharing/l10n/ja.js b/apps/federatedfilesharing/l10n/ja.js index c9a8444455e..0c716ae8f70 100644 --- a/apps/federatedfilesharing/l10n/ja.js +++ b/apps/federatedfilesharing/l10n/ja.js @@ -44,6 +44,7 @@ OC.L10N.register( "Allow users on this server to receive group shares from other servers" : "ユーザーが他のサーバーからこのサーバーにグループ共有することを許可する", "Search global and public address book for users" : "グローバルまたはユーザーの公開アドレス帳を検索する", "Allow users to publish their data to a global and public address book" : "公開アドレス帳をグローバルに公開することをユーザーに許可する", + "Unable to update federated files sharing config" : "クラウド共有設定を更新できませんでした", "Federated Cloud" : "クラウド共有", "Open documentation" : "ドキュメントを開く", "You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Nextcloud サーバーまたはその他の Open Cloud Mesh (OCM) 互換のサーバーとサービスを使用しているユーザーであれば、共有ダイアログに Federated Cloud ID を入力するだけで誰とでも共有できます。例: person@cloud.example.com", diff --git a/apps/federatedfilesharing/l10n/ja.json b/apps/federatedfilesharing/l10n/ja.json index 0916786e9a4..25e8ea2071a 100644 --- a/apps/federatedfilesharing/l10n/ja.json +++ b/apps/federatedfilesharing/l10n/ja.json @@ -42,6 +42,7 @@ "Allow users on this server to receive group shares from other servers" : "ユーザーが他のサーバーからこのサーバーにグループ共有することを許可する", "Search global and public address book for users" : "グローバルまたはユーザーの公開アドレス帳を検索する", "Allow users to publish their data to a global and public address book" : "公開アドレス帳をグローバルに公開することをユーザーに許可する", + "Unable to update federated files sharing config" : "クラウド共有設定を更新できませんでした", "Federated Cloud" : "クラウド共有", "Open documentation" : "ドキュメントを開く", "You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Nextcloud サーバーまたはその他の Open Cloud Mesh (OCM) 互換のサーバーとサービスを使用しているユーザーであれば、共有ダイアログに Federated Cloud ID を入力するだけで誰とでも共有できます。例: person@cloud.example.com", diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 701b635fac3..510582ae998 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -3579,7 +3579,7 @@ * Shows a "permission denied" notification */ _showPermissionDeniedNotification: function() { - var message = t('files', 'You don’t have permission to upload or create files here'); + var message = t('files', 'You do not have permission to upload or create files here'); OC.Notification.show(message, {type: 'error'}); }, diff --git a/apps/files/l10n/fr.js b/apps/files/l10n/fr.js index aea338cd858..11d2821f015 100644 --- a/apps/files/l10n/fr.js +++ b/apps/files/l10n/fr.js @@ -41,6 +41,8 @@ OC.L10N.register( "Details" : "Détails", "Please select tag(s) to add to the selection" : "Veuillez sélectionner la ou les étiquette(s) à ajouter à la sélection", "Apply tag(s) to selection" : "Appliquer la ou les étiquette(s) à la sélection", + "Select directory \"{dirName}\"" : "Sélectionner le dossier \"{dirName}\"", + "Select file \"{fileName}\"" : "Sélectionner le fichier \"{fileName}\"", "Pending" : "En attente", "Unable to determine date" : "Impossible de déterminer la date", "This operation is forbidden" : "Cette opération est interdite", diff --git a/apps/files/l10n/fr.json b/apps/files/l10n/fr.json index 35287edf6c6..9a389656857 100644 --- a/apps/files/l10n/fr.json +++ b/apps/files/l10n/fr.json @@ -39,6 +39,8 @@ "Details" : "Détails", "Please select tag(s) to add to the selection" : "Veuillez sélectionner la ou les étiquette(s) à ajouter à la sélection", "Apply tag(s) to selection" : "Appliquer la ou les étiquette(s) à la sélection", + "Select directory \"{dirName}\"" : "Sélectionner le dossier \"{dirName}\"", + "Select file \"{fileName}\"" : "Sélectionner le fichier \"{fileName}\"", "Pending" : "En attente", "Unable to determine date" : "Impossible de déterminer la date", "This operation is forbidden" : "Cette opération est interdite", diff --git a/apps/files/l10n/ja.js b/apps/files/l10n/ja.js index d8fb5614360..474f1ff7d6c 100644 --- a/apps/files/l10n/ja.js +++ b/apps/files/l10n/ja.js @@ -41,6 +41,8 @@ OC.L10N.register( "Details" : "詳細", "Please select tag(s) to add to the selection" : "選択項目に付与するタグを選択してください", "Apply tag(s) to selection" : "選択項目にタグを適用", + "Select directory \"{dirName}\"" : "ディレクトリを選択: \"{dirName}\"", + "Select file \"{fileName}\"" : "ファイルを選択: \"{fileName}\"", "Pending" : "保留中", "Unable to determine date" : "更新日不明", "This operation is forbidden" : "この操作は禁止されています", diff --git a/apps/files/l10n/ja.json b/apps/files/l10n/ja.json index 9a1817b3c24..17c10e110e4 100644 --- a/apps/files/l10n/ja.json +++ b/apps/files/l10n/ja.json @@ -39,6 +39,8 @@ "Details" : "詳細", "Please select tag(s) to add to the selection" : "選択項目に付与するタグを選択してください", "Apply tag(s) to selection" : "選択項目にタグを適用", + "Select directory \"{dirName}\"" : "ディレクトリを選択: \"{dirName}\"", + "Select file \"{fileName}\"" : "ファイルを選択: \"{fileName}\"", "Pending" : "保留中", "Unable to determine date" : "更新日不明", "This operation is forbidden" : "この操作は禁止されています", diff --git a/apps/files/lib/Command/Scan.php b/apps/files/lib/Command/Scan.php index 47f1caabc78..0a7a53dc0bf 100644 --- a/apps/files/lib/Command/Scan.php +++ b/apps/files/lib/Command/Scan.php @@ -291,7 +291,7 @@ class Scan extends Base { protected function formatExecTime() { $secs = round($this->execTime); # convert seconds into HH:MM:SS form - return sprintf('%02d:%02d:%02d', ($secs / 3600), ($secs / 60 % 60), $secs % 60); + return sprintf('%02d:%02d:%02d', (int)($secs / 3600), ( (int)($secs / 60) % 60), $secs % 60); } protected function reconnectToDatabase(OutputInterface $output): Connection { diff --git a/apps/files/src/components/TransferOwnershipDialogue.vue b/apps/files/src/components/TransferOwnershipDialogue.vue index 22e2a22b009..8e96cdebf54 100644 --- a/apps/files/src/components/TransferOwnershipDialogue.vue +++ b/apps/files/src/components/TransferOwnershipDialogue.vue @@ -213,7 +213,7 @@ export default { logger.error('Could not send ownership transfer request', { error }) if (error?.response?.status === 403) { - this.submitError = t('files', 'Cannot transfer ownership of a file or folder you don\'t own') + this.submitError = t('files', 'Cannot transfer ownership of a file or folder you do not own') } else { this.submitError = error.message || t('files', 'Unknown error') } diff --git a/apps/files/templates/list.php b/apps/files/templates/list.php index 25558bf16f7..9087c86a4a2 100644 --- a/apps/files/templates/list.php +++ b/apps/files/templates/list.php @@ -6,7 +6,7 @@ <div id="file_action_panel"></div> <div class="notCreatable notPublic hidden"> <div class="icon-alert-outline"></div> - <?php p($l->t('You don’t have permission to upload or create files here'))?> + <?php p($l->t('You do not have permission to upload or create files here'))?> </div> <?php /* Note: the template attributes are here only for the public page. These are normally loaded through ajax instead (updateStorageStatistics). diff --git a/apps/files_external/l10n/sl.js b/apps/files_external/l10n/sl.js index a844d881e1b..0698ba4cc83 100644 --- a/apps/files_external/l10n/sl.js +++ b/apps/files_external/l10n/sl.js @@ -20,7 +20,7 @@ OC.L10N.register( "Never" : "Nikoli", "Once every direct access" : "Enkrat ob neposrednem dostopu", "Read only" : "Le za branje", - "Disconnect" : "Prekinjeni povezavo", + "Disconnect" : "Prekini povezavo", "Admin defined" : "Skrbnik je določen", "Delete storage?" : "Ali ste prepričani, da želite izbrisati shrambo?", "Saved" : "Shranjeno", @@ -117,7 +117,7 @@ OC.L10N.register( "\"%1$s\" is not installed. Mounting of %2$s is not possible. Please ask your system administrator to install it." : "Paket »%1$s« ni nameščen. Priklapljanje %2$s zato ni mogoče. Stopite v stik s skrbnikom sistema, da namesti ustrezne pakete.", "External storage support" : "Podpora zunanji dhrambi", "Adds basic external storage support" : "Doda osnovno zunanjo podporo shrambe", - "This application enables administrators to configure connections to external storage providers, such as FTP servers, S3 or SWIFT object stores, other Nextcloud servers, WebDAV servers, and more. Administrators can choose which types of storage to enable and can mount these storage locations for a user, a group, or the entire system. Users will see a new folder appear in their root Nextcloud directory, which they can access and use like any other Nextcloud folder. External storage also allows users to share files stored in these external locations. In these cases, the credentials for the owner of the file are used when the recipient requests the file from external storage, thereby ensuring that the recipient can access the shared file.\n\nExternal storage can be configured using the GUI or at the command line. This second option provides the advanced user with more flexibility for configuring bulk external storage mounts and setting mount priorities. More information is available in the external storage GUI documentation and the external storage Configuration File documentation." : "Program omogoča skrbnikom nastavljanje zunanjih shramb in ponudnikov, kot so strežniki FTP, shrambe S3 ali SWIFT, drugih strežnikov Nextcloud, strežnikov WebDAV in še mnogo več. Skrbniki lahko določijo, katere vrste pomnilnikov bodo omogočili in jih odprli za uporabnika, skupino ali za celoten sistem. Uporabniki bodo v svojem korenskem imeniku Nextcloud videli novo dodano mapo, do katere lahko dostopajo enako kot do drugih map Nextcloud. Zunanji pomnilnik uporabnikom omogoča tudi skupno rabo datotek, shranjenih na zunanjih naslovih. V teh primerih se poverilnice lastnika datoteke uporabijo, ko prejemnik datoteko zahteva iz zunanjega pomnilnika, to zagotovi, da lahko prejemnik dostopa do vseh datoteke v skupni rabi.\n\nZunanji pomnilnik je mogoče povezati prek spletnega vmesnika ali prek ukazne vrstice. Slednja možnost omogoča zahtevnejšim uporabnikom večjo prilagodljivost paketnega nastavljanja zunanjih nosilcev za shranjevanje in določanje prednosti namestitve in uporabe. Več podrobnosti je zbranih v dokumentaciji splenega vmesnika za zunanji pomnilnik in dokumentaciji o nastavitvenih datotekah zunanjega pomnilnika.", + "This application enables administrators to configure connections to external storage providers, such as FTP servers, S3 or SWIFT object stores, other Nextcloud servers, WebDAV servers, and more. Administrators can choose which types of storage to enable and can mount these storage locations for a user, a group, or the entire system. Users will see a new folder appear in their root Nextcloud directory, which they can access and use like any other Nextcloud folder. External storage also allows users to share files stored in these external locations. In these cases, the credentials for the owner of the file are used when the recipient requests the file from external storage, thereby ensuring that the recipient can access the shared file.\n\nExternal storage can be configured using the GUI or at the command line. This second option provides the advanced user with more flexibility for configuring bulk external storage mounts and setting mount priorities. More information is available in the external storage GUI documentation and the external storage Configuration File documentation." : "Program omogoča skrbnikom nastavljanje zunanjih shramb in ponudnikov, kot so strežniki FTP, shrambe S3 ali SWIFT, drugih strežnikov Nextcloud, strežnikov WebDAV in še mnogo več. Skrbniki lahko določijo, katere vrste pomnilnikov bodo omogočili in jih odprli za uporabnika, skupino ali za celoten sistem. Uporabniki bodo v svojem korenskem imeniku Nextcloud videli novo dodano mapo, do katere lahko dostopajo enako kot do drugih map Nextcloud. Zunanji pomnilnik uporabnikom omogoča tudi skupno rabo datotek, shranjenih na zunanjih naslovih. V teh primerih se poverilnice lastnika datoteke uporabijo, ko prejemnik datoteko zahteva iz zunanjega pomnilnika, to zagotovi, da lahko prejemnik dostopa do vseh datoteke v skupni rabi.\n\nZunanji pomnilnik je mogoče povezati prek spletnega vmesnika ali prek ukazne vrstice. Slednja možnost omogoča zahtevnejšim uporabnikom večjo prilagodljivost paketnega nastavljanja zunanjih nosilcev za shranjevanje in določanje prednosti namestitve in uporabe. Več podrobnosti je zbranih v dokumentaciji spletnega vmesnika za zunanji pomnilnik in dokumentaciji o nastavitvenih datotekah zunanjega pomnilnika.", "No external storage configured or you don't have the permission to configure them" : "Ni nastavljene zunanje shrambe oziroma ni ustreznih dovoljenj za uporabo.", "Name" : "Ime", "Storage type" : "Vrsta shrambe", diff --git a/apps/files_external/l10n/sl.json b/apps/files_external/l10n/sl.json index a2316055895..5c6b15104a2 100644 --- a/apps/files_external/l10n/sl.json +++ b/apps/files_external/l10n/sl.json @@ -18,7 +18,7 @@ "Never" : "Nikoli", "Once every direct access" : "Enkrat ob neposrednem dostopu", "Read only" : "Le za branje", - "Disconnect" : "Prekinjeni povezavo", + "Disconnect" : "Prekini povezavo", "Admin defined" : "Skrbnik je določen", "Delete storage?" : "Ali ste prepričani, da želite izbrisati shrambo?", "Saved" : "Shranjeno", @@ -115,7 +115,7 @@ "\"%1$s\" is not installed. Mounting of %2$s is not possible. Please ask your system administrator to install it." : "Paket »%1$s« ni nameščen. Priklapljanje %2$s zato ni mogoče. Stopite v stik s skrbnikom sistema, da namesti ustrezne pakete.", "External storage support" : "Podpora zunanji dhrambi", "Adds basic external storage support" : "Doda osnovno zunanjo podporo shrambe", - "This application enables administrators to configure connections to external storage providers, such as FTP servers, S3 or SWIFT object stores, other Nextcloud servers, WebDAV servers, and more. Administrators can choose which types of storage to enable and can mount these storage locations for a user, a group, or the entire system. Users will see a new folder appear in their root Nextcloud directory, which they can access and use like any other Nextcloud folder. External storage also allows users to share files stored in these external locations. In these cases, the credentials for the owner of the file are used when the recipient requests the file from external storage, thereby ensuring that the recipient can access the shared file.\n\nExternal storage can be configured using the GUI or at the command line. This second option provides the advanced user with more flexibility for configuring bulk external storage mounts and setting mount priorities. More information is available in the external storage GUI documentation and the external storage Configuration File documentation." : "Program omogoča skrbnikom nastavljanje zunanjih shramb in ponudnikov, kot so strežniki FTP, shrambe S3 ali SWIFT, drugih strežnikov Nextcloud, strežnikov WebDAV in še mnogo več. Skrbniki lahko določijo, katere vrste pomnilnikov bodo omogočili in jih odprli za uporabnika, skupino ali za celoten sistem. Uporabniki bodo v svojem korenskem imeniku Nextcloud videli novo dodano mapo, do katere lahko dostopajo enako kot do drugih map Nextcloud. Zunanji pomnilnik uporabnikom omogoča tudi skupno rabo datotek, shranjenih na zunanjih naslovih. V teh primerih se poverilnice lastnika datoteke uporabijo, ko prejemnik datoteko zahteva iz zunanjega pomnilnika, to zagotovi, da lahko prejemnik dostopa do vseh datoteke v skupni rabi.\n\nZunanji pomnilnik je mogoče povezati prek spletnega vmesnika ali prek ukazne vrstice. Slednja možnost omogoča zahtevnejšim uporabnikom večjo prilagodljivost paketnega nastavljanja zunanjih nosilcev za shranjevanje in določanje prednosti namestitve in uporabe. Več podrobnosti je zbranih v dokumentaciji splenega vmesnika za zunanji pomnilnik in dokumentaciji o nastavitvenih datotekah zunanjega pomnilnika.", + "This application enables administrators to configure connections to external storage providers, such as FTP servers, S3 or SWIFT object stores, other Nextcloud servers, WebDAV servers, and more. Administrators can choose which types of storage to enable and can mount these storage locations for a user, a group, or the entire system. Users will see a new folder appear in their root Nextcloud directory, which they can access and use like any other Nextcloud folder. External storage also allows users to share files stored in these external locations. In these cases, the credentials for the owner of the file are used when the recipient requests the file from external storage, thereby ensuring that the recipient can access the shared file.\n\nExternal storage can be configured using the GUI or at the command line. This second option provides the advanced user with more flexibility for configuring bulk external storage mounts and setting mount priorities. More information is available in the external storage GUI documentation and the external storage Configuration File documentation." : "Program omogoča skrbnikom nastavljanje zunanjih shramb in ponudnikov, kot so strežniki FTP, shrambe S3 ali SWIFT, drugih strežnikov Nextcloud, strežnikov WebDAV in še mnogo več. Skrbniki lahko določijo, katere vrste pomnilnikov bodo omogočili in jih odprli za uporabnika, skupino ali za celoten sistem. Uporabniki bodo v svojem korenskem imeniku Nextcloud videli novo dodano mapo, do katere lahko dostopajo enako kot do drugih map Nextcloud. Zunanji pomnilnik uporabnikom omogoča tudi skupno rabo datotek, shranjenih na zunanjih naslovih. V teh primerih se poverilnice lastnika datoteke uporabijo, ko prejemnik datoteko zahteva iz zunanjega pomnilnika, to zagotovi, da lahko prejemnik dostopa do vseh datoteke v skupni rabi.\n\nZunanji pomnilnik je mogoče povezati prek spletnega vmesnika ali prek ukazne vrstice. Slednja možnost omogoča zahtevnejšim uporabnikom večjo prilagodljivost paketnega nastavljanja zunanjih nosilcev za shranjevanje in določanje prednosti namestitve in uporabe. Več podrobnosti je zbranih v dokumentaciji spletnega vmesnika za zunanji pomnilnik in dokumentaciji o nastavitvenih datotekah zunanjega pomnilnika.", "No external storage configured or you don't have the permission to configure them" : "Ni nastavljene zunanje shrambe oziroma ni ustreznih dovoljenj za uporabo.", "Name" : "Ime", "Storage type" : "Vrsta shrambe", diff --git a/apps/files_sharing/l10n/cs.js b/apps/files_sharing/l10n/cs.js index dafd8e32b36..4b00fea08d5 100644 --- a/apps/files_sharing/l10n/cs.js +++ b/apps/files_sharing/l10n/cs.js @@ -192,6 +192,8 @@ OC.L10N.register( "Share label" : "Štítek sdílení", "Hide download" : "Skrýt stahování", "Password protect" : "Chránit heslem", + "Password expires {passwordExpirationTime}" : "Platnost hesla skončí {passwordExpirationTime}", + "Password expired" : "Platnost hesla skončila", "Video verification" : "Ověřování pomocí videa", "Enter a note for the share recipient" : "Zadejte poznámku pro příjemce sdílení", "Add another link" : "Přidat další odkaz", diff --git a/apps/files_sharing/l10n/cs.json b/apps/files_sharing/l10n/cs.json index 366c7e78e3b..0e285c40df8 100644 --- a/apps/files_sharing/l10n/cs.json +++ b/apps/files_sharing/l10n/cs.json @@ -190,6 +190,8 @@ "Share label" : "Štítek sdílení", "Hide download" : "Skrýt stahování", "Password protect" : "Chránit heslem", + "Password expires {passwordExpirationTime}" : "Platnost hesla skončí {passwordExpirationTime}", + "Password expired" : "Platnost hesla skončila", "Video verification" : "Ověřování pomocí videa", "Enter a note for the share recipient" : "Zadejte poznámku pro příjemce sdílení", "Add another link" : "Přidat další odkaz", diff --git a/apps/files_sharing/l10n/de_DE.js b/apps/files_sharing/l10n/de_DE.js index 0615dc622b1..67ea6d4a15a 100644 --- a/apps/files_sharing/l10n/de_DE.js +++ b/apps/files_sharing/l10n/de_DE.js @@ -192,6 +192,8 @@ OC.L10N.register( "Share label" : "Freigabe-Label", "Hide download" : "Download verbergen", "Password protect" : "Passwortschutz", + "Password expires {passwordExpirationTime}" : "Passwort läuft ab um {passwordExpirationTime}", + "Password expired" : "Passwort abgelaufen", "Video verification" : "Videoüberprüfung", "Enter a note for the share recipient" : "Notiz für Empfänger der Freigabe eingeben", "Add another link" : "Weiteren Link hinzufügen", diff --git a/apps/files_sharing/l10n/de_DE.json b/apps/files_sharing/l10n/de_DE.json index face0f1379d..1c146e93a8f 100644 --- a/apps/files_sharing/l10n/de_DE.json +++ b/apps/files_sharing/l10n/de_DE.json @@ -190,6 +190,8 @@ "Share label" : "Freigabe-Label", "Hide download" : "Download verbergen", "Password protect" : "Passwortschutz", + "Password expires {passwordExpirationTime}" : "Passwort läuft ab um {passwordExpirationTime}", + "Password expired" : "Passwort abgelaufen", "Video verification" : "Videoüberprüfung", "Enter a note for the share recipient" : "Notiz für Empfänger der Freigabe eingeben", "Add another link" : "Weiteren Link hinzufügen", diff --git a/apps/files_sharing/l10n/eu.js b/apps/files_sharing/l10n/eu.js index a0eb8cddc86..8f39be7aa6c 100644 --- a/apps/files_sharing/l10n/eu.js +++ b/apps/files_sharing/l10n/eu.js @@ -192,6 +192,8 @@ OC.L10N.register( "Share label" : "partekatu etiketa", "Hide download" : "Ezkutatu deskarga", "Password protect" : "Babestu pasahitzarekin", + "Password expires {passwordExpirationTime}" : "Pasahitza {passwordExpirationTime} iraungiko du", + "Password expired" : "Pasahitza iraungi da", "Video verification" : "Bideo egiaztapena", "Enter a note for the share recipient" : "Sartu ohar bat partekatzearen hartzailearentzat", "Add another link" : "Gehitu beste esteka bat", diff --git a/apps/files_sharing/l10n/eu.json b/apps/files_sharing/l10n/eu.json index 6aaed4ab9fd..fb2ef312a6f 100644 --- a/apps/files_sharing/l10n/eu.json +++ b/apps/files_sharing/l10n/eu.json @@ -190,6 +190,8 @@ "Share label" : "partekatu etiketa", "Hide download" : "Ezkutatu deskarga", "Password protect" : "Babestu pasahitzarekin", + "Password expires {passwordExpirationTime}" : "Pasahitza {passwordExpirationTime} iraungiko du", + "Password expired" : "Pasahitza iraungi da", "Video verification" : "Bideo egiaztapena", "Enter a note for the share recipient" : "Sartu ohar bat partekatzearen hartzailearentzat", "Add another link" : "Gehitu beste esteka bat", diff --git a/apps/files_sharing/l10n/fr.js b/apps/files_sharing/l10n/fr.js index 0cd5e318186..dcd04d182e9 100644 --- a/apps/files_sharing/l10n/fr.js +++ b/apps/files_sharing/l10n/fr.js @@ -76,7 +76,7 @@ OC.L10N.register( "Shared with {user}" : "Partagé avec {user}", "Removed share for {user}" : "Partage supprimé pour {user}", "You removed yourself" : "Vous vous êtes retiré du partage", - "{actor} removed themselves" : "{actor} se sont retirés du partage", + "{actor} removed themselves" : "{actor} s'est retiré(e) du partage", "{actor} shared with {user}" : "{actor} a partagé avec {user}", "{actor} removed share for {user}" : "{actor} a supprimé le partage pour {user}", "Shared by {actor}" : "Partagé par {actor}", @@ -86,7 +86,7 @@ OC.L10N.register( "You shared {file} with {user}" : "Vous avez partagé {file} avec {user}", "You removed {user} from {file}" : "Vous avez supprimé {user} de {file}", "You removed yourself from {file}" : "Vous vous êtes retiré du partage du fichier {file}", - "{actor} removed themselves from {file}" : "{actor} se sont retirés du partage du fichier {file}", + "{actor} removed themselves from {file}" : "{actor} s'est retiré(e) du partage {file}", "{actor} shared {file} with {user}" : "{actor} a partagé {file} avec {user}", "{actor} removed {user} from {file}" : "{actor} a supprimé {user} de {file}", "{actor} shared {file} with you" : "{actor} a partagé {file} avec vous", diff --git a/apps/files_sharing/l10n/fr.json b/apps/files_sharing/l10n/fr.json index 01702aaf45f..b44e0e7c034 100644 --- a/apps/files_sharing/l10n/fr.json +++ b/apps/files_sharing/l10n/fr.json @@ -74,7 +74,7 @@ "Shared with {user}" : "Partagé avec {user}", "Removed share for {user}" : "Partage supprimé pour {user}", "You removed yourself" : "Vous vous êtes retiré du partage", - "{actor} removed themselves" : "{actor} se sont retirés du partage", + "{actor} removed themselves" : "{actor} s'est retiré(e) du partage", "{actor} shared with {user}" : "{actor} a partagé avec {user}", "{actor} removed share for {user}" : "{actor} a supprimé le partage pour {user}", "Shared by {actor}" : "Partagé par {actor}", @@ -84,7 +84,7 @@ "You shared {file} with {user}" : "Vous avez partagé {file} avec {user}", "You removed {user} from {file}" : "Vous avez supprimé {user} de {file}", "You removed yourself from {file}" : "Vous vous êtes retiré du partage du fichier {file}", - "{actor} removed themselves from {file}" : "{actor} se sont retirés du partage du fichier {file}", + "{actor} removed themselves from {file}" : "{actor} s'est retiré(e) du partage {file}", "{actor} shared {file} with {user}" : "{actor} a partagé {file} avec {user}", "{actor} removed {user} from {file}" : "{actor} a supprimé {user} de {file}", "{actor} shared {file} with you" : "{actor} a partagé {file} avec vous", diff --git a/apps/files_sharing/l10n/hu.js b/apps/files_sharing/l10n/hu.js index e3543493fef..3d550b13c25 100644 --- a/apps/files_sharing/l10n/hu.js +++ b/apps/files_sharing/l10n/hu.js @@ -192,6 +192,8 @@ OC.L10N.register( "Share label" : "Megosztás címkéje", "Hide download" : "Letöltés elrejtése", "Password protect" : "Jelszavas védelem", + "Password expires {passwordExpirationTime}" : "A jelszó lejárati ideje: {passwordExpirationTime}", + "Password expired" : "A jelszó lejárt", "Video verification" : "Videós ellenőrzés", "Enter a note for the share recipient" : "Adjon meg egy megjegyzést a megosztás címzettje számára", "Add another link" : "További hivatkozás hozzáadása", diff --git a/apps/files_sharing/l10n/hu.json b/apps/files_sharing/l10n/hu.json index e58dbfe45d5..9b5f14d686b 100644 --- a/apps/files_sharing/l10n/hu.json +++ b/apps/files_sharing/l10n/hu.json @@ -190,6 +190,8 @@ "Share label" : "Megosztás címkéje", "Hide download" : "Letöltés elrejtése", "Password protect" : "Jelszavas védelem", + "Password expires {passwordExpirationTime}" : "A jelszó lejárati ideje: {passwordExpirationTime}", + "Password expired" : "A jelszó lejárt", "Video verification" : "Videós ellenőrzés", "Enter a note for the share recipient" : "Adjon meg egy megjegyzést a megosztás címzettje számára", "Add another link" : "További hivatkozás hozzáadása", diff --git a/apps/files_sharing/l10n/ja.js b/apps/files_sharing/l10n/ja.js index 55ef84609bd..501b6748aef 100644 --- a/apps/files_sharing/l10n/ja.js +++ b/apps/files_sharing/l10n/ja.js @@ -157,6 +157,7 @@ OC.L10N.register( "Read" : "読み込み", "Upload" : "アップロード", "Edit" : "編集", + "Bundled permissions" : "バンドルされているパーミッション", "Allow creating" : "作成許可", "Allow deleting" : "削除許可", "Allow resharing" : "再共有を許可する", diff --git a/apps/files_sharing/l10n/ja.json b/apps/files_sharing/l10n/ja.json index b728daed117..318b9c3622f 100644 --- a/apps/files_sharing/l10n/ja.json +++ b/apps/files_sharing/l10n/ja.json @@ -155,6 +155,7 @@ "Read" : "読み込み", "Upload" : "アップロード", "Edit" : "編集", + "Bundled permissions" : "バンドルされているパーミッション", "Allow creating" : "作成許可", "Allow deleting" : "削除許可", "Allow resharing" : "再共有を許可する", diff --git a/apps/files_sharing/l10n/pl.js b/apps/files_sharing/l10n/pl.js index 3432384a11f..e879eb13bb5 100644 --- a/apps/files_sharing/l10n/pl.js +++ b/apps/files_sharing/l10n/pl.js @@ -192,6 +192,8 @@ OC.L10N.register( "Share label" : "Udostępnij etykietę", "Hide download" : "Ukryj pobieranie", "Password protect" : "Zabezpiecz hasłem", + "Password expires {passwordExpirationTime}" : "Hasło wygasa {passwordExpirationTime}", + "Password expired" : "Hasło wygasło", "Video verification" : "Weryfikacja wideo", "Enter a note for the share recipient" : "Napisz notatkę dla odbiorcy udostępnienia", "Add another link" : "Dodaj kolejny link", diff --git a/apps/files_sharing/l10n/pl.json b/apps/files_sharing/l10n/pl.json index 9dc3a87d956..8c67ef82773 100644 --- a/apps/files_sharing/l10n/pl.json +++ b/apps/files_sharing/l10n/pl.json @@ -190,6 +190,8 @@ "Share label" : "Udostępnij etykietę", "Hide download" : "Ukryj pobieranie", "Password protect" : "Zabezpiecz hasłem", + "Password expires {passwordExpirationTime}" : "Hasło wygasa {passwordExpirationTime}", + "Password expired" : "Hasło wygasło", "Video verification" : "Weryfikacja wideo", "Enter a note for the share recipient" : "Napisz notatkę dla odbiorcy udostępnienia", "Add another link" : "Dodaj kolejny link", diff --git a/apps/files_sharing/l10n/pt_BR.js b/apps/files_sharing/l10n/pt_BR.js index d650ee9093e..22bdd3e9295 100644 --- a/apps/files_sharing/l10n/pt_BR.js +++ b/apps/files_sharing/l10n/pt_BR.js @@ -192,6 +192,8 @@ OC.L10N.register( "Share label" : "Compartilhar etiqueta", "Hide download" : "Ocultar download", "Password protect" : "Proteger com senha", + "Password expires {passwordExpirationTime}" : "A senha expira em {passwordExpirationTime}", + "Password expired" : "Senha expirada", "Video verification" : "Verificação de vídeo", "Enter a note for the share recipient" : "Digite uma observação ao destinatário", "Add another link" : "Adicionar um novo link", diff --git a/apps/files_sharing/l10n/pt_BR.json b/apps/files_sharing/l10n/pt_BR.json index f0e3a34dfb4..92c12e57bf6 100644 --- a/apps/files_sharing/l10n/pt_BR.json +++ b/apps/files_sharing/l10n/pt_BR.json @@ -190,6 +190,8 @@ "Share label" : "Compartilhar etiqueta", "Hide download" : "Ocultar download", "Password protect" : "Proteger com senha", + "Password expires {passwordExpirationTime}" : "A senha expira em {passwordExpirationTime}", + "Password expired" : "Senha expirada", "Video verification" : "Verificação de vídeo", "Enter a note for the share recipient" : "Digite uma observação ao destinatário", "Add another link" : "Adicionar um novo link", diff --git a/apps/files_sharing/l10n/sl.js b/apps/files_sharing/l10n/sl.js index 41123c0d8c6..be5f860e53c 100644 --- a/apps/files_sharing/l10n/sl.js +++ b/apps/files_sharing/l10n/sl.js @@ -101,6 +101,7 @@ OC.L10N.register( "Wrong share ID, share doesn't exist" : "Napačen ID mesta uporabe; mesto ne obstaja!", "Could not delete share" : "Mesta souporabe ni mogoče izbrisati", "Please specify a file or folder path" : "Določiti je treba datoteko ali pot do mape", + "Wrong path, file/folder does not exist" : "Napačna pot; datoteka ali mapa ne obstaja", "Could not create share" : "Mesta souporabe ni mogoče ustvariti", "Invalid permissions" : "Neustrezna dovoljenja", "Please specify a valid user" : "Navesti je treba veljavnega uporabnika.", @@ -138,6 +139,7 @@ OC.L10N.register( "This application enables users to share files within Nextcloud. If enabled, the admin can choose which groups can share files. The applicable users can then share files and folders with other users and groups within Nextcloud. In addition, if the admin enables the share link feature, an external link can be used to share files with other users outside of Nextcloud. Admins can also enforce passwords, expirations dates, and enable server to server sharing via share links, as well as sharing from mobile devices.\nTurning the feature off removes shared files and folders on the server for all share recipients, and also on the sync clients and mobile apps. More information is available in the Nextcloud Documentation." : "Program omogoča uporabnikom souporabo in izmenjavo datotek v oblaku Nextcloud. Izbrana možnost omogoča skrbnikom izbor skupin, ki imajo dovoljenja za omogočanje souporabe znotraj oblaka. Če je omogočena tudi možnost souporabe prek povezave, je na voljo tudi zunanja povezava za zunanje uporabnike. Skrbniki lahko omogočijo tudi gesla, pretek povezave in objavljanje prek mobilnih naprav.\nOnemogočanje storitve odstrani vse datoteke in mape v souporabi, prav tako programe za usklajevanje in mobilne naprave. Več podrobnosti je na voljo v dokumentaciji Nextcloud.", "Sharing" : "Souporaba", "Accept user and group shares by default" : "Privzeto sprejmi uporabniško in skupinsko souporabo predmetov", + "Error while toggling options" : "Prišlo je do napake med preklapljanjem možnosti", "Reset" : "Ponastavi", "Invalid path selected" : "Izbrana je neveljavna pot", "Unknown error" : "Neznana napaka", diff --git a/apps/files_sharing/l10n/sl.json b/apps/files_sharing/l10n/sl.json index fdad4b322bb..f47903e9f6d 100644 --- a/apps/files_sharing/l10n/sl.json +++ b/apps/files_sharing/l10n/sl.json @@ -99,6 +99,7 @@ "Wrong share ID, share doesn't exist" : "Napačen ID mesta uporabe; mesto ne obstaja!", "Could not delete share" : "Mesta souporabe ni mogoče izbrisati", "Please specify a file or folder path" : "Določiti je treba datoteko ali pot do mape", + "Wrong path, file/folder does not exist" : "Napačna pot; datoteka ali mapa ne obstaja", "Could not create share" : "Mesta souporabe ni mogoče ustvariti", "Invalid permissions" : "Neustrezna dovoljenja", "Please specify a valid user" : "Navesti je treba veljavnega uporabnika.", @@ -136,6 +137,7 @@ "This application enables users to share files within Nextcloud. If enabled, the admin can choose which groups can share files. The applicable users can then share files and folders with other users and groups within Nextcloud. In addition, if the admin enables the share link feature, an external link can be used to share files with other users outside of Nextcloud. Admins can also enforce passwords, expirations dates, and enable server to server sharing via share links, as well as sharing from mobile devices.\nTurning the feature off removes shared files and folders on the server for all share recipients, and also on the sync clients and mobile apps. More information is available in the Nextcloud Documentation." : "Program omogoča uporabnikom souporabo in izmenjavo datotek v oblaku Nextcloud. Izbrana možnost omogoča skrbnikom izbor skupin, ki imajo dovoljenja za omogočanje souporabe znotraj oblaka. Če je omogočena tudi možnost souporabe prek povezave, je na voljo tudi zunanja povezava za zunanje uporabnike. Skrbniki lahko omogočijo tudi gesla, pretek povezave in objavljanje prek mobilnih naprav.\nOnemogočanje storitve odstrani vse datoteke in mape v souporabi, prav tako programe za usklajevanje in mobilne naprave. Več podrobnosti je na voljo v dokumentaciji Nextcloud.", "Sharing" : "Souporaba", "Accept user and group shares by default" : "Privzeto sprejmi uporabniško in skupinsko souporabo predmetov", + "Error while toggling options" : "Prišlo je do napake med preklapljanjem možnosti", "Reset" : "Ponastavi", "Invalid path selected" : "Izbrana je neveljavna pot", "Unknown error" : "Neznana napaka", diff --git a/apps/files_sharing/l10n/zh_HK.js b/apps/files_sharing/l10n/zh_HK.js index bc46c7d3b77..4c417087cb7 100644 --- a/apps/files_sharing/l10n/zh_HK.js +++ b/apps/files_sharing/l10n/zh_HK.js @@ -192,6 +192,8 @@ OC.L10N.register( "Share label" : "分享標籤", "Hide download" : "隱藏下載", "Password protect" : "密碼防護", + "Password expires {passwordExpirationTime}" : "密碼於 {passwordExpirationTime} 到期", + "Password expired" : "密碼已過期", "Video verification" : "視像驗證", "Enter a note for the share recipient" : "輸入分享收件人的註釋", "Add another link" : "加入另一個連結", diff --git a/apps/files_sharing/l10n/zh_HK.json b/apps/files_sharing/l10n/zh_HK.json index 74b9b95ba8c..96c51fc0b5d 100644 --- a/apps/files_sharing/l10n/zh_HK.json +++ b/apps/files_sharing/l10n/zh_HK.json @@ -190,6 +190,8 @@ "Share label" : "分享標籤", "Hide download" : "隱藏下載", "Password protect" : "密碼防護", + "Password expires {passwordExpirationTime}" : "密碼於 {passwordExpirationTime} 到期", + "Password expired" : "密碼已過期", "Video verification" : "視像驗證", "Enter a note for the share recipient" : "輸入分享收件人的註釋", "Add another link" : "加入另一個連結", diff --git a/apps/files_sharing/l10n/zh_TW.js b/apps/files_sharing/l10n/zh_TW.js index 31aac8cffe9..9ca44476ede 100644 --- a/apps/files_sharing/l10n/zh_TW.js +++ b/apps/files_sharing/l10n/zh_TW.js @@ -192,6 +192,8 @@ OC.L10N.register( "Share label" : "分享標籤", "Hide download" : "隱藏下載", "Password protect" : "密碼保護", + "Password expires {passwordExpirationTime}" : "密碼於 {passwordExpirationTime} 到期", + "Password expired" : "密碼已過期", "Video verification" : "視訊驗證", "Enter a note for the share recipient" : "輸入給分享收件者的訊息", "Add another link" : "新增其他連結", diff --git a/apps/files_sharing/l10n/zh_TW.json b/apps/files_sharing/l10n/zh_TW.json index 718c76dff5f..4da65534937 100644 --- a/apps/files_sharing/l10n/zh_TW.json +++ b/apps/files_sharing/l10n/zh_TW.json @@ -190,6 +190,8 @@ "Share label" : "分享標籤", "Hide download" : "隱藏下載", "Password protect" : "密碼保護", + "Password expires {passwordExpirationTime}" : "密碼於 {passwordExpirationTime} 到期", + "Password expired" : "密碼已過期", "Video verification" : "視訊驗證", "Enter a note for the share recipient" : "輸入給分享收件者的訊息", "Add another link" : "新增其他連結", diff --git a/apps/files_sharing/lib/Controller/RemoteController.php b/apps/files_sharing/lib/Controller/RemoteController.php index 75684220c52..47523e08639 100644 --- a/apps/files_sharing/lib/Controller/RemoteController.php +++ b/apps/files_sharing/lib/Controller/RemoteController.php @@ -86,7 +86,7 @@ class RemoteController extends OCSController { $this->logger->error('Could not accept federated share with id: ' . $id, ['app' => 'files_sharing']); - throw new OCSNotFoundException('wrong share ID, share doesn\'t exist.'); + throw new OCSNotFoundException('wrong share ID, share does not exist.'); } /** @@ -106,7 +106,7 @@ class RemoteController extends OCSController { // Make sure the user has no notification for something that does not exist anymore. $this->externalManager->processNotification($id); - throw new OCSNotFoundException('wrong share ID, share doesn\'t exist.'); + throw new OCSNotFoundException('wrong share ID, share does not exist.'); } /** diff --git a/apps/files_sharing/lib/Controller/ShareAPIController.php b/apps/files_sharing/lib/Controller/ShareAPIController.php index c0441485132..fafdb1a64cd 100644 --- a/apps/files_sharing/lib/Controller/ShareAPIController.php +++ b/apps/files_sharing/lib/Controller/ShareAPIController.php @@ -279,7 +279,7 @@ class ShareAPIController extends OCSController { } elseif ($share->getShareType() === IShare::TYPE_EMAIL) { $result['share_with'] = $share->getSharedWith(); $result['password'] = $share->getPassword(); - $result['password_expiration_time'] = $share->getPasswordExpirationTime(); + $result['password_expiration_time'] = $share->getPasswordExpirationTime() !== null ? $share->getPasswordExpirationTime()->format(\DateTime::ATOM) : null; $result['send_password_by_talk'] = $share->getSendPasswordByTalk(); $result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'EMAIL'); $result['token'] = $share->getToken(); @@ -366,7 +366,7 @@ class ShareAPIController extends OCSController { try { $share = $this->getShareById($id); } catch (ShareNotFound $e) { - throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist')); + throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist')); } try { @@ -378,7 +378,7 @@ class ShareAPIController extends OCSController { // Fall trough } - throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist')); + throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist')); } /** @@ -394,7 +394,7 @@ class ShareAPIController extends OCSController { try { $share = $this->getShareById($id); } catch (ShareNotFound $e) { - throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist')); + throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist')); } try { @@ -404,7 +404,7 @@ class ShareAPIController extends OCSController { } if (!$this->canAccessShare($share)) { - throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist')); + throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist')); } // if it's a group share or a room share @@ -1056,13 +1056,13 @@ class ShareAPIController extends OCSController { try { $share = $this->getShareById($id); } catch (ShareNotFound $e) { - throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist')); + throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist')); } $this->lock($share->getNode()); if (!$this->canAccessShare($share, false)) { - throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist')); + throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist')); } if (!$this->canEditShare($share)) { @@ -1291,11 +1291,11 @@ class ShareAPIController extends OCSController { try { $share = $this->getShareById($id); } catch (ShareNotFound $e) { - throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist')); + throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist')); } if (!$this->canAccessShare($share)) { - throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist')); + throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist')); } try { diff --git a/apps/files_sharing/lib/External/Scanner.php b/apps/files_sharing/lib/External/Scanner.php index 009e206b959..cfde56103da 100644 --- a/apps/files_sharing/lib/External/Scanner.php +++ b/apps/files_sharing/lib/External/Scanner.php @@ -29,11 +29,29 @@ use OC\ForbiddenException; use OCP\Files\NotFoundException; use OCP\Files\StorageInvalidException; use OCP\Files\StorageNotAvailableException; +use OCP\Http\Client\LocalServerException; +use Psr\Log\LoggerInterface; class Scanner extends \OC\Files\Cache\Scanner { /** @var \OCA\Files_Sharing\External\Storage */ protected $storage; + /** {@inheritDoc} */ + public function scan($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1, $lock = true) { + try { + if (!$this->storage->remoteIsOwnCloud()) { + return parent::scan($path, $recursive, $reuse, $lock); + } + } catch (LocalServerException $e) { + // Scanner doesn't have dependency injection + \OC::$server->get(LoggerInterface::class) + ->warning('Trying to scan files inside invalid external storage: ' . $this->storage->getRemote() . ' for mountpoint ' . $this->storage->getMountPoint() . ' and id ' . $this->storage->getId()); + return; + } + + $this->scanAll(); + } + /** * Scan a single file and store it in the cache. * If an exception happened while accessing the external storage, @@ -63,4 +81,56 @@ class Scanner extends \OC\Files\Cache\Scanner { $this->storage->checkStorageAvailability(); } } + + /** + * Checks the remote share for changes. + * If changes are available, scan them and update + * the cache. + * @throws NotFoundException + * @throws StorageInvalidException + * @throws \Exception + */ + public function scanAll() { + try { + $data = $this->storage->getShareInfo(); + } catch (\Exception $e) { + $this->storage->checkStorageAvailability(); + throw new \Exception( + 'Error while scanning remote share: "' . + $this->storage->getRemote() . '" ' . + $e->getMessage() + ); + } + if ($data['status'] === 'success') { + $this->addResult($data['data'], ''); + } else { + throw new \Exception( + 'Error while scanning remote share: "' . + $this->storage->getRemote() . '"' + ); + } + } + + /** + * @param array $data + * @param string $path + */ + private function addResult($data, $path) { + $id = $this->cache->put($path, $data); + if (isset($data['children'])) { + $children = []; + foreach ($data['children'] as $child) { + $children[$child['name']] = true; + $this->addResult($child, ltrim($path . '/' . $child['name'], '/')); + } + + $existingCache = $this->cache->getFolderContentsById($id); + foreach ($existingCache as $existingChild) { + // if an existing child is not in the new data, remove it + if (!isset($children[$existingChild['name']])) { + $this->cache->remove(ltrim($path . '/' . $existingChild['name'], '/')); + } + } + } + } } diff --git a/apps/files_sharing/src/components/SharingEntryLink.vue b/apps/files_sharing/src/components/SharingEntryLink.vue index ee7e8d4b930..638cdf485b0 100644 --- a/apps/files_sharing/src/components/SharingEntryLink.vue +++ b/apps/files_sharing/src/components/SharingEntryLink.vue @@ -192,6 +192,12 @@ @submit="onPasswordSubmit"> {{ t('files_sharing', 'Enter a password') }} </ActionInput> + <ActionText v-if="isEmailShareType && passwordExpirationTime" icon="icon-info"> + {{ t('files_sharing', 'Password expires {passwordExpirationTime}', {passwordExpirationTime}) }} + </ActionText> + <ActionText v-else-if="isEmailShareType && passwordExpirationTime !== null" icon="icon-error"> + {{ t('files_sharing', 'Password expired') }} + </ActionText> <!-- password protected by Talk --> <ActionCheckbox v-if="isPasswordProtectedByTalkAvailable" @@ -461,6 +467,20 @@ export default { }, }, + passwordExpirationTime() { + if (this.share.passwordExpirationTime === null) { + return null + } + + const expirationTime = moment(this.share.passwordExpirationTime) + + if (expirationTime.diff(moment()) < 0) { + return false + } + + return expirationTime.fromNow() + }, + /** * Is Talk enabled? * diff --git a/apps/files_sharing/src/mixins/ShareRequests.js b/apps/files_sharing/src/mixins/ShareRequests.js index bc6e3bf1644..e2668c15d65 100644 --- a/apps/files_sharing/src/mixins/ShareRequests.js +++ b/apps/files_sharing/src/mixins/ShareRequests.js @@ -103,8 +103,9 @@ export default { const request = await axios.put(shareUrl + `/${id}`, properties) if (!request?.data?.ocs) { throw request + } else { + return request.data.ocs.data } - return true } catch (error) { console.error('Error while updating share', error) if (error.response.status !== 400) { diff --git a/apps/files_sharing/src/mixins/SharesMixin.js b/apps/files_sharing/src/mixins/SharesMixin.js index 950b0355175..daeacfa4b8b 100644 --- a/apps/files_sharing/src/mixins/SharesMixin.js +++ b/apps/files_sharing/src/mixins/SharesMixin.js @@ -235,11 +235,14 @@ export default { this.saving = true this.errors = {} try { - await this.updateShare(this.share.id, properties) + const updatedShare = await this.updateShare(this.share.id, properties) if (propertyNames.indexOf('password') >= 0) { // reset password state after sync this.$delete(this.share, 'newPassword') + + // updates password expiration time after sync + this.share.passwordExpirationTime = updatedShare.password_expiration_time } // clear any previous errors diff --git a/apps/files_sharing/src/models/Share.js b/apps/files_sharing/src/models/Share.js index 87c2fec86f2..5644ce0c2b3 100644 --- a/apps/files_sharing/src/models/Share.js +++ b/apps/files_sharing/src/models/Share.js @@ -359,6 +359,27 @@ export default class Share { } /** + * Password expiration time + * + * @return {string} + * @readonly + * @memberof Share + */ + get passwordExpirationTime() { + return this._share.password_expiration_time + } + + /** + * Password expiration time + * + * @param {string} password exipration time + * @memberof Share + */ + set passwordExpirationTime(passwordExpirationTime) { + this._share.password_expiration_time = passwordExpirationTime + } + + /** * Password protection by Talk of the share * * @return {boolean} diff --git a/apps/files_sharing/templates/part.404.php b/apps/files_sharing/templates/part.404.php index 3ef117d7524..2aa55383847 100644 --- a/apps/files_sharing/templates/part.404.php +++ b/apps/files_sharing/templates/part.404.php @@ -1,6 +1,6 @@ <ul> <li class="error error-broken-link"> - <p><?php p($l->t('Sorry, this link doesn’t seem to work anymore.')) ?></p> + <p><?php p($l->t('Sorry, this link does not seem to work anymore.')) ?></p> <p><?php p($l->t('Reasons might be:')); ?></p> <ul> <li><?php p($l->t('the item was removed')); ?></li> diff --git a/apps/files_sharing/tests/ApiTest.php b/apps/files_sharing/tests/ApiTest.php index c7159e82163..a16a1aaf383 100644 --- a/apps/files_sharing/tests/ApiTest.php +++ b/apps/files_sharing/tests/ApiTest.php @@ -929,7 +929,7 @@ class ApiTest extends TestCase { $ocs->getShare(0); $this->fail(); } catch (OCSNotFoundException $e) { - $this->assertEquals('Wrong share ID, share doesn\'t exist', $e->getMessage()); + $this->assertEquals('Wrong share ID, share does not exist', $e->getMessage()); } $ocs->cleanup(); } diff --git a/apps/files_sharing/tests/Controller/ShareAPIControllerTest.php b/apps/files_sharing/tests/Controller/ShareAPIControllerTest.php index 5555aef1425..fd5580f19a7 100644 --- a/apps/files_sharing/tests/Controller/ShareAPIControllerTest.php +++ b/apps/files_sharing/tests/Controller/ShareAPIControllerTest.php @@ -196,7 +196,7 @@ class ShareAPIControllerTest extends TestCase { public function testDeleteShareShareNotFound() { $this->expectException(\OCP\AppFramework\OCS\OCSNotFoundException::class); - $this->expectExceptionMessage('Wrong share ID, share doesn\'t exist'); + $this->expectExceptionMessage('Wrong share ID, share does not exist'); $this->shareManager ->expects($this->exactly(6)) @@ -433,7 +433,7 @@ class ShareAPIControllerTest extends TestCase { */ public function testDeleteSharedWithGroupIDontBelongTo() { $this->expectException(\OCP\AppFramework\OCS\OCSNotFoundException::class); - $this->expectExceptionMessage('Wrong share ID, share doesn\'t exist'); + $this->expectExceptionMessage('Wrong share ID, share does not exist'); $node = $this->getMockBuilder(File::class)->getMock(); @@ -498,7 +498,7 @@ class ShareAPIControllerTest extends TestCase { ->with('ocinternal:42', 'currentUser') ->will($this->throwException(new \OC\Share20\Exception\ShareNotFound())); - $expected = new \OC\OCS\Result(null, 404, 'wrong share ID, share doesn\'t exist.'); + $expected = new \OC\OCS\Result(null, 404, 'wrong share ID, share does not exist.'); $this->assertEquals($expected, $this->ocs->getShare(42)); } */ @@ -808,7 +808,7 @@ class ShareAPIControllerTest extends TestCase { public function testGetShareInvalidNode() { $this->expectException(\OCP\AppFramework\OCS\OCSNotFoundException::class); - $this->expectExceptionMessage('Wrong share ID, share doesn\'t exist'); + $this->expectExceptionMessage('Wrong share ID, share does not exist'); $share = \OC::$server->getShareManager()->newShare(); $share->setSharedBy('initiator') @@ -2602,7 +2602,7 @@ class ShareAPIControllerTest extends TestCase { public function testUpdateShareCantAccess() { $this->expectException(\OCP\AppFramework\OCS\OCSNotFoundException::class); - $this->expectExceptionMessage('Wrong share ID, share doesn\'t exist'); + $this->expectExceptionMessage('Wrong share ID, share does not exist'); $node = $this->getMockBuilder(Folder::class)->getMock(); $share = $this->newShare(); diff --git a/apps/files_sharing/tests/External/ScannerTest.php b/apps/files_sharing/tests/External/ScannerTest.php index 2d2486737dc..57696a697eb 100644 --- a/apps/files_sharing/tests/External/ScannerTest.php +++ b/apps/files_sharing/tests/External/ScannerTest.php @@ -50,6 +50,18 @@ class ScannerTest extends TestCase { $this->scanner = new Scanner($this->storage); } + public function testScanAll() { + $this->storage->expects($this->any()) + ->method('getShareInfo') + ->willReturn(['status' => 'success', 'data' => []]); + + // FIXME add real tests, we are currently only checking for + // Declaration of OCA\Files_Sharing\External\Scanner::*() should be + // compatible with OC\Files\Cache\Scanner::*() + $this->scanner->scanAll(); + $this->addToAssertionCount(1); + } + public function testScan() { $this->storage->expects($this->any()) ->method('getShareInfo') diff --git a/apps/files_trashbin/l10n/nb.js b/apps/files_trashbin/l10n/nb.js index 718ef5a0bc7..e3017194294 100644 --- a/apps/files_trashbin/l10n/nb.js +++ b/apps/files_trashbin/l10n/nb.js @@ -3,6 +3,7 @@ OC.L10N.register( { "Deleted files" : "Slettede filer", "restored" : "gjenopprettet", + "Deleted files and folders in the trash bin" : "Slettede filer og mapper i søppelkassen", "This application enables users to restore files that were deleted from the system." : "Denne appen gjør så brukere kan gjenopprette filer som har blitt slettet fra systemet.", "This application enables users to restore files that were deleted from the system. It displays a list of deleted files in the web interface, and has options to restore those deleted files back to the users file directories or remove them permanently from the system. Restoring a file also restores related file versions, if the versions application is enabled. When a file is deleted from a share, it can be restored in the same manner, though it is no longer shared. By default, these files remain in the trash bin for 30 days.\nTo prevent a user from running out of disk space, the Deleted files app will not utilize more than 50% of the currently available free quota for deleted files. If the deleted files exceed this limit, the app deletes the oldest files until it gets below this limit. More information is available in the Deleted Files documentation." : "Denne appen gjør så brukere kan gjenopprette filer som har blitt slettet fra systemet. Den viser en liste med slettede filer i web-grensesnittet hvor man kan gjenopprette de slettede filene tilbake til brukernes mapper, eller fjerne de permanent fra systemet. Gjenoppretting av en fil gjenoppretter også relaterte fil-versjoner, hvis versjonerings-appen er aktivert. Når en fil slettes fra en deling, kan den gjenopprettes på samme måte, men er da ikke lenger delt. Som standard bevares disse filene i søppelbøtten i 30 dager. For å forhindre en bruker fra å fylle opp disken, vil ikke Deleted Files-appen bruke mer enn 50% av den tilgjengelige diskkvoten til slettede filer. Hvis de slettede filene går over denne grensen sletter appen de aldste filene til den kommer under grensen. Mer informasjon er tilgjengelig i Deleted Files' dokumentasjon.", "Restore" : "Gjenopprett", diff --git a/apps/files_trashbin/l10n/nb.json b/apps/files_trashbin/l10n/nb.json index 949b6b7bba2..e1cf576b48e 100644 --- a/apps/files_trashbin/l10n/nb.json +++ b/apps/files_trashbin/l10n/nb.json @@ -1,6 +1,7 @@ { "translations": { "Deleted files" : "Slettede filer", "restored" : "gjenopprettet", + "Deleted files and folders in the trash bin" : "Slettede filer og mapper i søppelkassen", "This application enables users to restore files that were deleted from the system." : "Denne appen gjør så brukere kan gjenopprette filer som har blitt slettet fra systemet.", "This application enables users to restore files that were deleted from the system. It displays a list of deleted files in the web interface, and has options to restore those deleted files back to the users file directories or remove them permanently from the system. Restoring a file also restores related file versions, if the versions application is enabled. When a file is deleted from a share, it can be restored in the same manner, though it is no longer shared. By default, these files remain in the trash bin for 30 days.\nTo prevent a user from running out of disk space, the Deleted files app will not utilize more than 50% of the currently available free quota for deleted files. If the deleted files exceed this limit, the app deletes the oldest files until it gets below this limit. More information is available in the Deleted Files documentation." : "Denne appen gjør så brukere kan gjenopprette filer som har blitt slettet fra systemet. Den viser en liste med slettede filer i web-grensesnittet hvor man kan gjenopprette de slettede filene tilbake til brukernes mapper, eller fjerne de permanent fra systemet. Gjenoppretting av en fil gjenoppretter også relaterte fil-versjoner, hvis versjonerings-appen er aktivert. Når en fil slettes fra en deling, kan den gjenopprettes på samme måte, men er da ikke lenger delt. Som standard bevares disse filene i søppelbøtten i 30 dager. For å forhindre en bruker fra å fylle opp disken, vil ikke Deleted Files-appen bruke mer enn 50% av den tilgjengelige diskkvoten til slettede filer. Hvis de slettede filene går over denne grensen sletter appen de aldste filene til den kommer under grensen. Mer informasjon er tilgjengelig i Deleted Files' dokumentasjon.", "Restore" : "Gjenopprett", diff --git a/apps/files_versions/appinfo/info.xml b/apps/files_versions/appinfo/info.xml index 3032d8cc934..49e48e6e436 100644 --- a/apps/files_versions/appinfo/info.xml +++ b/apps/files_versions/appinfo/info.xml @@ -5,8 +5,8 @@ <name>Versions</name> <summary>This application automatically maintains older versions of files that are changed.</summary> <description> - This application automatically maintains older versions of files that are changed. When enabled, a hidden versions folder is provisioned in every user’s directory and is used to store old file versions. A user can revert to an older version through the web interface at any time, with the replaced file becoming a version. The app automatically manages the versions folder to ensure the user doesn’t run out of Quota because of versions. - In addition to the expiry of versions, the versions app makes certain never to use more than 50% of the user’s currently available free space. If stored versions exceed this limit, the app will delete the oldest versions first until it meets this limit. More information is available in the Versions documentation. + This application automatically maintains older versions of files that are changed. When enabled, a hidden versions folder is provisioned in every user's directory and is used to store old file versions. A user can revert to an older version through the web interface at any time, with the replaced file becoming a version. The app automatically manages the versions folder to ensure the user does not run out of Quota because of versions. + In addition to the expiry of versions, the versions app makes certain never to use more than 50% of the user's currently available free space. If stored versions exceed this limit, the app will delete the oldest versions first until it meets this limit. More information is available in the Versions documentation. </description> <version>1.18.0</version> <licence>agpl</licence> diff --git a/apps/provisioning_api/lib/Controller/UsersController.php b/apps/provisioning_api/lib/Controller/UsersController.php index 38d51857ffc..a26479ba0a8 100644 --- a/apps/provisioning_api/lib/Controller/UsersController.php +++ b/apps/provisioning_api/lib/Controller/UsersController.php @@ -958,7 +958,11 @@ class UsersController extends AUserData { } catch (PropertyDoesNotExistException $e) { $userAccount->setProperty($key, $value, IAccountManager::SCOPE_PRIVATE, IAccountManager::NOT_VERIFIED); } - $this->accountManager->updateAccount($userAccount); + try { + $this->accountManager->updateAccount($userAccount); + } catch (InvalidArgumentException $e) { + throw new OCSException('Invalid ' . $e->getMessage(), 102); + } break; case IAccountManager::PROPERTY_PROFILE_ENABLED: $userAccount = $this->accountManager->getAccount($targetUser); diff --git a/apps/provisioning_api/tests/Controller/UsersControllerTest.php b/apps/provisioning_api/tests/Controller/UsersControllerTest.php index 2ce5cadb57d..6162be54a04 100644 --- a/apps/provisioning_api/tests/Controller/UsersControllerTest.php +++ b/apps/provisioning_api/tests/Controller/UsersControllerTest.php @@ -939,9 +939,10 @@ class UsersControllerTest extends TestCase { } public function testGetUserDataAsAdmin() { - $group = $this->getMockBuilder(IGroup::class) - ->disableOriginalConstructor() - ->getMock(); + $group0 = $this->createMock(IGroup::class); + $group1 = $this->createMock(IGroup::class); + $group2 = $this->createMock(IGroup::class); + $group3 = $this->createMock(IGroup::class); $loggedInUser = $this->getMockBuilder(IUser::class) ->disableOriginalConstructor() ->getMock(); @@ -975,7 +976,7 @@ class UsersControllerTest extends TestCase { $this->groupManager ->expects($this->any()) ->method('getUserGroups') - ->willReturn([$group, $group, $group]); + ->willReturn([$group0, $group1, $group2]); $this->groupManager ->expects($this->once()) ->method('getSubAdmin') @@ -983,17 +984,17 @@ class UsersControllerTest extends TestCase { $subAdminManager ->expects($this->once()) ->method('getSubAdminsGroups') - ->willReturn([$group]); - $group->expects($this->at(0)) + ->willReturn([$group3]); + $group0->expects($this->once()) ->method('getGID') ->willReturn('group0'); - $group->expects($this->at(1)) + $group1->expects($this->once()) ->method('getGID') ->willReturn('group1'); - $group->expects($this->at(2)) + $group2->expects($this->once()) ->method('getGID') ->willReturn('group2'); - $group->expects($this->at(3)) + $group3->expects($this->once()) ->method('getGID') ->willReturn('group3'); @@ -1009,10 +1010,10 @@ class UsersControllerTest extends TestCase { IAccountManager::PROPERTY_PROFILE_ENABLED => ['value' => '1'], ]); $this->config - ->expects($this->at(0)) ->method('getUserValue') - ->with('UID', 'core', 'enabled', 'true') - ->willReturn('true'); + ->willReturnMap([ + ['UID', 'core', 'enabled', 'true', 'true'], + ]); $this->api ->expects($this->once()) ->method('fillStorageInfo') @@ -1136,10 +1137,10 @@ class UsersControllerTest extends TestCase { ->method('getSubAdmin') ->willReturn($subAdminManager); $this->config - ->expects($this->at(0)) ->method('getUserValue') - ->with('UID', 'core', 'enabled', 'true') - ->willReturn('true'); + ->willReturnMap([ + ['UID', 'core', 'enabled', 'true', 'true'], + ]); $this->api ->expects($this->once()) ->method('fillStorageInfo') @@ -3622,11 +3623,12 @@ class UsersControllerTest extends TestCase { 'profile_enabled' => '1' ]; - $api->expects($this->at(0))->method('getUserData') - ->with('uid', false) - ->willReturn($expected); - $api->expects($this->at(1))->method('getUserData') - ->with('currentuser', true) + $api->expects($this->exactly(2)) + ->method('getUserData') + ->withConsecutive( + ['uid', false], + ['currentuser', true], + ) ->willReturn($expected); $this->assertSame($expected, $api->getUser('uid')->getData()); @@ -3812,11 +3814,11 @@ class UsersControllerTest extends TestCase { ->willReturn('abc@example.org'); $emailTemplate = $this->createMock(IEMailTemplate::class); $this->newUserMailHelper - ->expects($this->at(0)) + ->expects($this->once()) ->method('generateTemplate') ->willReturn($emailTemplate); $this->newUserMailHelper - ->expects($this->at(1)) + ->expects($this->once()) ->method('sendMail') ->with($targetUser, $emailTemplate); @@ -3863,11 +3865,11 @@ class UsersControllerTest extends TestCase { ->getMock(); $emailTemplate = $this->createMock(IEMailTemplate::class); $this->newUserMailHelper - ->expects($this->at(0)) + ->expects($this->once()) ->method('generateTemplate') ->willReturn($emailTemplate); $this->newUserMailHelper - ->expects($this->at(1)) + ->expects($this->once()) ->method('sendMail') ->with($targetUser, $emailTemplate); @@ -3916,11 +3918,11 @@ class UsersControllerTest extends TestCase { ->willReturn('abc@example.org'); $emailTemplate = $this->createMock(IEMailTemplate::class); $this->newUserMailHelper - ->expects($this->at(0)) + ->expects($this->once()) ->method('generateTemplate') ->willReturn($emailTemplate); $this->newUserMailHelper - ->expects($this->at(1)) + ->expects($this->once()) ->method('sendMail') ->with($targetUser, $emailTemplate) ->willThrowException(new \Exception()); diff --git a/apps/settings/l10n/hu.js b/apps/settings/l10n/hu.js index 09b419a378e..e304a0c7999 100644 --- a/apps/settings/l10n/hu.js +++ b/apps/settings/l10n/hu.js @@ -67,6 +67,7 @@ OC.L10N.register( "installing and updating apps via the App Store or Federated Cloud Sharing" : "alkalmazások telepítése és frissítése az alkalmazástár vagy a föderált felhőmegosztás segítségével", "Federated Cloud Sharing" : "Föderált felhőmegosztás", "cURL is using an outdated %1$s version (%2$s). Please update your operating system or features such as %3$s will not work reliably." : "A cURL elavult %1$s verziót (%2$s) használ. Frissítse az operációs rendszert, vagy az egyes funkciók (mint például a %3$s) megbízhatatlanul fognak működni.", + "Could not determine if TLS version of cURL is outdated or not because an error happened during the HTTPS request against https://nextcloud.com. Please check the Nextcloud log file for more details." : "Nem sikerült megállapítani, hogy a cURL TLS-verziója elavult-e vagy sem, mert hiba történt a https://nextcloud.com felé küldött HTTPS-kérés során. További részletekért ellenőrizze a Nextcloud naplófájlját.", "The PHP OPcache module is not loaded. For better performance it is recommended to load it into your PHP installation." : "A PHP OPcache modul nincs betöltve. A jobb teljesítmény érdekében javasoljuk, hogy töltse be a PHP telepítésébe.", "OPcache is disabled. For better performance, it is recommended to apply <code>opcache.enable=1</code> to your PHP configuration." : "Az OPcache ki van kapcsolva. A jobb teljesítmény érdekében ajánlatos, hogy beállítsa az <code>opcache.enable=1</code> értéket a PHP konfigurációjába.", "OPcache is configured to remove code comments. With OPcache enabled, <code>opcache.save_comments=1</code> must be set for Nextcloud to function." : "Az OPcache úgy van beállítva, hogy eltávolítja a kódban lévő megjegyzéseket. Ha az OPcache be van kapcsolva, akkor be kell állítani az <code>opcache.save_comments=1</code> értéket, hogy a Nextcloud működjön.", @@ -229,6 +230,7 @@ OC.L10N.register( "Copied!" : "Másolva!", "Copy" : "Másolás", "Could not copy app password. Please copy it manually." : "Nem lehet az alkalmazásjelszót másolni. Másolja át kézileg.", + "For the server to work properly, it’s important to configure background jobs correctly. Cron is the recommended setting. Please see the documentation for more information." : "A kiszolgáló helyes működéséhez fontos, hogy a háttérfeladatok megfelelően legyenek beállítva. A Cron beállítás javasolt. További részletekért lásd a dokumentációt.", "Last job execution ran {time}. Something seems wrong." : "Utolsó feladatfuttatás:%s. Valami nincs rendben.", "Last job ran {relativeTime}." : "Utolsó feladatfuttatás: {relativeTime}.", "Background job didn’t run yet!" : "A háttérfeladat még nem futott le!", diff --git a/apps/settings/l10n/hu.json b/apps/settings/l10n/hu.json index 988847499c6..cd5f31801f3 100644 --- a/apps/settings/l10n/hu.json +++ b/apps/settings/l10n/hu.json @@ -65,6 +65,7 @@ "installing and updating apps via the App Store or Federated Cloud Sharing" : "alkalmazások telepítése és frissítése az alkalmazástár vagy a föderált felhőmegosztás segítségével", "Federated Cloud Sharing" : "Föderált felhőmegosztás", "cURL is using an outdated %1$s version (%2$s). Please update your operating system or features such as %3$s will not work reliably." : "A cURL elavult %1$s verziót (%2$s) használ. Frissítse az operációs rendszert, vagy az egyes funkciók (mint például a %3$s) megbízhatatlanul fognak működni.", + "Could not determine if TLS version of cURL is outdated or not because an error happened during the HTTPS request against https://nextcloud.com. Please check the Nextcloud log file for more details." : "Nem sikerült megállapítani, hogy a cURL TLS-verziója elavult-e vagy sem, mert hiba történt a https://nextcloud.com felé küldött HTTPS-kérés során. További részletekért ellenőrizze a Nextcloud naplófájlját.", "The PHP OPcache module is not loaded. For better performance it is recommended to load it into your PHP installation." : "A PHP OPcache modul nincs betöltve. A jobb teljesítmény érdekében javasoljuk, hogy töltse be a PHP telepítésébe.", "OPcache is disabled. For better performance, it is recommended to apply <code>opcache.enable=1</code> to your PHP configuration." : "Az OPcache ki van kapcsolva. A jobb teljesítmény érdekében ajánlatos, hogy beállítsa az <code>opcache.enable=1</code> értéket a PHP konfigurációjába.", "OPcache is configured to remove code comments. With OPcache enabled, <code>opcache.save_comments=1</code> must be set for Nextcloud to function." : "Az OPcache úgy van beállítva, hogy eltávolítja a kódban lévő megjegyzéseket. Ha az OPcache be van kapcsolva, akkor be kell állítani az <code>opcache.save_comments=1</code> értéket, hogy a Nextcloud működjön.", @@ -227,6 +228,7 @@ "Copied!" : "Másolva!", "Copy" : "Másolás", "Could not copy app password. Please copy it manually." : "Nem lehet az alkalmazásjelszót másolni. Másolja át kézileg.", + "For the server to work properly, it’s important to configure background jobs correctly. Cron is the recommended setting. Please see the documentation for more information." : "A kiszolgáló helyes működéséhez fontos, hogy a háttérfeladatok megfelelően legyenek beállítva. A Cron beállítás javasolt. További részletekért lásd a dokumentációt.", "Last job execution ran {time}. Something seems wrong." : "Utolsó feladatfuttatás:%s. Valami nincs rendben.", "Last job ran {relativeTime}." : "Utolsó feladatfuttatás: {relativeTime}.", "Background job didn’t run yet!" : "A háttérfeladat még nem futott le!", diff --git a/apps/settings/l10n/ja.js b/apps/settings/l10n/ja.js index b6c2e950454..60d0d8ea7c7 100644 --- a/apps/settings/l10n/ja.js +++ b/apps/settings/l10n/ja.js @@ -43,6 +43,7 @@ OC.L10N.register( "You changed your email address" : "メールアドレスを変更しました", "Your email address was changed by an administrator" : "管理者がメールアドレスを変更しました", "You created app password \"{token}\"" : "アプリパスワード\"{token}\"を作成しました", + "An administrator created app password \"{token}\"" : "管理者がアプリパスワード\"{token}\"を作成しました", "You deleted app password \"{token}\"" : "アプリパスワード\"{token}\"を削除しました", "You renamed app password \"{token}\" to \"{newToken}\"" : "アプリのパスワードを{token} から {newToken} に変更しました。", "You granted filesystem access to app password \"{token}\"" : "アプリのパスワード\"{token}\"へファイルシステムアクセス件を付与しました", @@ -56,6 +57,7 @@ OC.L10N.register( "Couldn't remove app." : "アプリが削除できませんでした。", "Couldn't update app." : "アプリをアップデートできませんでした。", "Wrong password" : "パスワードが間違っています", + "Unable to change personal password" : "個人パスワードを変更できません", "Saved" : "保存しました", "No user supplied" : "ユーザーが指定されていません", "Authentication error" : "認証エラー", @@ -65,6 +67,8 @@ OC.L10N.register( "installing and updating apps via the App Store or Federated Cloud Sharing" : "アプリストアまたはクラウド連携共有から、アプリをインストールならびにアップデート", "Federated Cloud Sharing" : "統合されたクラウド共有", "cURL is using an outdated %1$s version (%2$s). Please update your operating system or features such as %3$s will not work reliably." : "%1$sバージョン (%2$s) の古い cURL を使っています。OSを更新するか、この機能 %3$sが正しく動くアプリに更新してください。", + "Could not determine if TLS version of cURL is outdated or not because an error happened during the HTTPS request against https://nextcloud.com. Please check the Nextcloud log file for more details." : "https://nextcloud.comに対するHTTPSリクエスト中にエラーが発生したため、cURLのTLSバージョンが古くなっているかどうかを判断できませんでした。詳細については、Nextcloudログファイルを確認してください。", + "The PHP OPcache module is not loaded. For better performance it is recommended to load it into your PHP installation." : "PHP OPcacheモジュールがロードされていません。パフォーマンスを向上させるには、OPcacheモジュールをPHPインストールにロードすることをお勧めします。", "Invalid SMTP password." : "SMTPパスワードが無効", "Email setting test" : "メール設定のテスト", "Well done, %s!" : "%sさん、成功です!", diff --git a/apps/settings/l10n/ja.json b/apps/settings/l10n/ja.json index 849348197e6..fae3b9eefb8 100644 --- a/apps/settings/l10n/ja.json +++ b/apps/settings/l10n/ja.json @@ -41,6 +41,7 @@ "You changed your email address" : "メールアドレスを変更しました", "Your email address was changed by an administrator" : "管理者がメールアドレスを変更しました", "You created app password \"{token}\"" : "アプリパスワード\"{token}\"を作成しました", + "An administrator created app password \"{token}\"" : "管理者がアプリパスワード\"{token}\"を作成しました", "You deleted app password \"{token}\"" : "アプリパスワード\"{token}\"を削除しました", "You renamed app password \"{token}\" to \"{newToken}\"" : "アプリのパスワードを{token} から {newToken} に変更しました。", "You granted filesystem access to app password \"{token}\"" : "アプリのパスワード\"{token}\"へファイルシステムアクセス件を付与しました", @@ -54,6 +55,7 @@ "Couldn't remove app." : "アプリが削除できませんでした。", "Couldn't update app." : "アプリをアップデートできませんでした。", "Wrong password" : "パスワードが間違っています", + "Unable to change personal password" : "個人パスワードを変更できません", "Saved" : "保存しました", "No user supplied" : "ユーザーが指定されていません", "Authentication error" : "認証エラー", @@ -63,6 +65,8 @@ "installing and updating apps via the App Store or Federated Cloud Sharing" : "アプリストアまたはクラウド連携共有から、アプリをインストールならびにアップデート", "Federated Cloud Sharing" : "統合されたクラウド共有", "cURL is using an outdated %1$s version (%2$s). Please update your operating system or features such as %3$s will not work reliably." : "%1$sバージョン (%2$s) の古い cURL を使っています。OSを更新するか、この機能 %3$sが正しく動くアプリに更新してください。", + "Could not determine if TLS version of cURL is outdated or not because an error happened during the HTTPS request against https://nextcloud.com. Please check the Nextcloud log file for more details." : "https://nextcloud.comに対するHTTPSリクエスト中にエラーが発生したため、cURLのTLSバージョンが古くなっているかどうかを判断できませんでした。詳細については、Nextcloudログファイルを確認してください。", + "The PHP OPcache module is not loaded. For better performance it is recommended to load it into your PHP installation." : "PHP OPcacheモジュールがロードされていません。パフォーマンスを向上させるには、OPcacheモジュールをPHPインストールにロードすることをお勧めします。", "Invalid SMTP password." : "SMTPパスワードが無効", "Email setting test" : "メール設定のテスト", "Well done, %s!" : "%sさん、成功です!", diff --git a/apps/settings/l10n/pt_BR.js b/apps/settings/l10n/pt_BR.js index 667a1d7542d..f823dcb88c7 100644 --- a/apps/settings/l10n/pt_BR.js +++ b/apps/settings/l10n/pt_BR.js @@ -67,6 +67,7 @@ OC.L10N.register( "installing and updating apps via the App Store or Federated Cloud Sharing" : "instalar e atualizar aplicativos por meio da App Store ou do Federated Cloud Sharing", "Federated Cloud Sharing" : "Compartilhamento de Nuvem Federada", "cURL is using an outdated %1$s version (%2$s). Please update your operating system or features such as %3$s will not work reliably." : "cURL está usando uma versão %1$s desatualizada (%2$s). Atualize seu sistema operacional ou recursos como %3$s não funcionarão adequadamente.", + "Could not determine if TLS version of cURL is outdated or not because an error happened during the HTTPS request against https://nextcloud.com. Please check the Nextcloud log file for more details." : "Não foi possível determinar se a versão TLS do cURL está desatualizada ou não porque ocorreu um erro durante a solicitação HTTPS em https://nextcloud.com. Verifique o arquivo de log do Nextcloud para obter mais detalhes.", "The PHP OPcache module is not loaded. For better performance it is recommended to load it into your PHP installation." : "O módulo PHP OPcache não está carregado. Para um melhor desempenho, é recomendável carregá-lo em sua instalação do PHP.", "OPcache is disabled. For better performance, it is recommended to apply <code>opcache.enable=1</code> to your PHP configuration." : "OPcache está desabilitado. Para um melhor desempenho, recomenda-se aplicar <code>opcache.enable=1</code> para sua configuração PHP.", "OPcache is configured to remove code comments. With OPcache enabled, <code>opcache.save_comments=1</code> must be set for Nextcloud to function." : "OPcache está configurado para remover comentários de código. Com o OPcache ativado, <code>opcache.save_comments=1</code> deve ser definido para que o Nextcloud funcione.", @@ -229,6 +230,7 @@ OC.L10N.register( "Copied!" : "Copiado!", "Copy" : "Copiar", "Could not copy app password. Please copy it manually." : "Não foi possível copiar a senha do aplicativo. Copie-a manualmente.", + "For the server to work properly, it’s important to configure background jobs correctly. Cron is the recommended setting. Please see the documentation for more information." : "Para que o servidor funcione corretamente, é importante configurar corretamente os trabalhos em segundo plano. Cron é a configuração recomendada. Consulte a documentação para obter mais informações.", "Last job execution ran {time}. Something seems wrong." : "A última execução do trabalho foi executada {time}. Algo parece errado.", "Last job ran {relativeTime}." : "A última tarefa foi executada {relativeTime}.", "Background job didn’t run yet!" : "O job em segundo plano ainda não rodou!", diff --git a/apps/settings/l10n/pt_BR.json b/apps/settings/l10n/pt_BR.json index 033b5d2c759..e09e5c7fefe 100644 --- a/apps/settings/l10n/pt_BR.json +++ b/apps/settings/l10n/pt_BR.json @@ -65,6 +65,7 @@ "installing and updating apps via the App Store or Federated Cloud Sharing" : "instalar e atualizar aplicativos por meio da App Store ou do Federated Cloud Sharing", "Federated Cloud Sharing" : "Compartilhamento de Nuvem Federada", "cURL is using an outdated %1$s version (%2$s). Please update your operating system or features such as %3$s will not work reliably." : "cURL está usando uma versão %1$s desatualizada (%2$s). Atualize seu sistema operacional ou recursos como %3$s não funcionarão adequadamente.", + "Could not determine if TLS version of cURL is outdated or not because an error happened during the HTTPS request against https://nextcloud.com. Please check the Nextcloud log file for more details." : "Não foi possível determinar se a versão TLS do cURL está desatualizada ou não porque ocorreu um erro durante a solicitação HTTPS em https://nextcloud.com. Verifique o arquivo de log do Nextcloud para obter mais detalhes.", "The PHP OPcache module is not loaded. For better performance it is recommended to load it into your PHP installation." : "O módulo PHP OPcache não está carregado. Para um melhor desempenho, é recomendável carregá-lo em sua instalação do PHP.", "OPcache is disabled. For better performance, it is recommended to apply <code>opcache.enable=1</code> to your PHP configuration." : "OPcache está desabilitado. Para um melhor desempenho, recomenda-se aplicar <code>opcache.enable=1</code> para sua configuração PHP.", "OPcache is configured to remove code comments. With OPcache enabled, <code>opcache.save_comments=1</code> must be set for Nextcloud to function." : "OPcache está configurado para remover comentários de código. Com o OPcache ativado, <code>opcache.save_comments=1</code> deve ser definido para que o Nextcloud funcione.", @@ -227,6 +228,7 @@ "Copied!" : "Copiado!", "Copy" : "Copiar", "Could not copy app password. Please copy it manually." : "Não foi possível copiar a senha do aplicativo. Copie-a manualmente.", + "For the server to work properly, it’s important to configure background jobs correctly. Cron is the recommended setting. Please see the documentation for more information." : "Para que o servidor funcione corretamente, é importante configurar corretamente os trabalhos em segundo plano. Cron é a configuração recomendada. Consulte a documentação para obter mais informações.", "Last job execution ran {time}. Something seems wrong." : "A última execução do trabalho foi executada {time}. Algo parece errado.", "Last job ran {relativeTime}." : "A última tarefa foi executada {relativeTime}.", "Background job didn’t run yet!" : "O job em segundo plano ainda não rodou!", diff --git a/apps/settings/l10n/sl.js b/apps/settings/l10n/sl.js index d136626a4ed..eca97a91d84 100644 --- a/apps/settings/l10n/sl.js +++ b/apps/settings/l10n/sl.js @@ -208,10 +208,18 @@ OC.L10N.register( "Copied!" : "Kopirano!", "Copy" : "Kopiraj", "Could not copy app password. Please copy it manually." : "Gesla programa ni mogoče kopirati. Storite to ročno.", + "Last job ran {relativeTime}." : "osvežitev je bila nazadnje izvedena {relativeTime}.", "Background job didn’t run yet!" : "Ni bilo izvedenega še nobenega opravila v ozadju.", + "AJAX" : "AJAX", + "Execute one task with each page loaded. Use case: Single user instance." : "izvede eno nalogo z vsako naloženo stranjo. Možnost uporabe: okolje enega uporabnika.", + "Webcron" : "Webcron", + "cron.php is registered at a webcron service to call cron.php every 5 minutes over HTTP. Use case: Very small instance (1–5 users depending on the usage)." : "Osveževanje z datoteko cron.php omogoča storitev webcron in sicer vsakih 5 minut po protokolu HTTP. Možnost je uporabna za manše število uporabnikov (1–5 udeležencev z nezahtevnim načinom rabe okolja).", + "Cron (Recommended)" : "Cron (priporočeno)", "To run this you need the PHP POSIX extension. See {linkstart}PHP documentation{linkend} for more details." : "Za zagon te možnosti je zahtevana razširitev PHP POSIX. Več podrobnosti je zapisanih v {linkstart}dokumentaciji PHP{linkend}.", + "Use system cron service to call the cron.php file every 5 minutes. Recommended for all instances." : "Uporabljena je sistemska storitev »cron«, ki vzpostavi povezavo z datoteko cron.php vsakih 5 minut. Priporočeno za vse vrste rabe okolja.", "Profile" : "Profil", "Enable" : "Omogoči", + "Rename group" : "Preimenuj skupino", "Remove group" : "Odstrani skupino", "You are about to remove the group {group}. The users will NOT be deleted." : "Odstranili boste skupino {group}. Uporabniki ne bodo odstranjeni.", "Please confirm the group removal " : "Potrditi je treba odstranjevanje skupine", @@ -251,6 +259,7 @@ OC.L10N.register( "Unable to update visibility of {displayId}" : "Ni mogoče posodobiti vidnosti {displayId}", "Your role" : "Vloga", "Unable to update role" : "Ni mogoče posodobiti vloge", + "Add additional email" : "Dodaj dodaten elektronski naslov", "Add" : "Dodaj", "You do not have permissions to see the details of this user" : "Ni ustreznih dovoljenj za pregled podatkov uporabnika", "Add new password" : "Vpis gesla", diff --git a/apps/settings/l10n/sl.json b/apps/settings/l10n/sl.json index d669d4cea35..8bf603a8152 100644 --- a/apps/settings/l10n/sl.json +++ b/apps/settings/l10n/sl.json @@ -206,10 +206,18 @@ "Copied!" : "Kopirano!", "Copy" : "Kopiraj", "Could not copy app password. Please copy it manually." : "Gesla programa ni mogoče kopirati. Storite to ročno.", + "Last job ran {relativeTime}." : "osvežitev je bila nazadnje izvedena {relativeTime}.", "Background job didn’t run yet!" : "Ni bilo izvedenega še nobenega opravila v ozadju.", + "AJAX" : "AJAX", + "Execute one task with each page loaded. Use case: Single user instance." : "izvede eno nalogo z vsako naloženo stranjo. Možnost uporabe: okolje enega uporabnika.", + "Webcron" : "Webcron", + "cron.php is registered at a webcron service to call cron.php every 5 minutes over HTTP. Use case: Very small instance (1–5 users depending on the usage)." : "Osveževanje z datoteko cron.php omogoča storitev webcron in sicer vsakih 5 minut po protokolu HTTP. Možnost je uporabna za manše število uporabnikov (1–5 udeležencev z nezahtevnim načinom rabe okolja).", + "Cron (Recommended)" : "Cron (priporočeno)", "To run this you need the PHP POSIX extension. See {linkstart}PHP documentation{linkend} for more details." : "Za zagon te možnosti je zahtevana razširitev PHP POSIX. Več podrobnosti je zapisanih v {linkstart}dokumentaciji PHP{linkend}.", + "Use system cron service to call the cron.php file every 5 minutes. Recommended for all instances." : "Uporabljena je sistemska storitev »cron«, ki vzpostavi povezavo z datoteko cron.php vsakih 5 minut. Priporočeno za vse vrste rabe okolja.", "Profile" : "Profil", "Enable" : "Omogoči", + "Rename group" : "Preimenuj skupino", "Remove group" : "Odstrani skupino", "You are about to remove the group {group}. The users will NOT be deleted." : "Odstranili boste skupino {group}. Uporabniki ne bodo odstranjeni.", "Please confirm the group removal " : "Potrditi je treba odstranjevanje skupine", @@ -249,6 +257,7 @@ "Unable to update visibility of {displayId}" : "Ni mogoče posodobiti vidnosti {displayId}", "Your role" : "Vloga", "Unable to update role" : "Ni mogoče posodobiti vloge", + "Add additional email" : "Dodaj dodaten elektronski naslov", "Add" : "Dodaj", "You do not have permissions to see the details of this user" : "Ni ustreznih dovoljenj za pregled podatkov uporabnika", "Add new password" : "Vpis gesla", diff --git a/apps/settings/lib/Controller/AppSettingsController.php b/apps/settings/lib/Controller/AppSettingsController.php index 77f807943cd..618d99b3d73 100644 --- a/apps/settings/lib/Controller/AppSettingsController.php +++ b/apps/settings/lib/Controller/AppSettingsController.php @@ -520,7 +520,7 @@ class AppSettingsController extends Controller { $this->appManager->clearAppsCache(); return new JSONResponse(['data' => ['appid' => $appId]]); } - return new JSONResponse(['data' => ['message' => $this->l10n->t('Couldn\'t remove app.')]], Http::STATUS_INTERNAL_SERVER_ERROR); + return new JSONResponse(['data' => ['message' => $this->l10n->t('Could not remove app.')]], Http::STATUS_INTERNAL_SERVER_ERROR); } /** @@ -542,7 +542,7 @@ class AppSettingsController extends Controller { if ($result !== false) { return new JSONResponse(['data' => ['appid' => $appId]]); } - return new JSONResponse(['data' => ['message' => $this->l10n->t('Couldn\'t update app.')]], Http::STATUS_INTERNAL_SERVER_ERROR); + return new JSONResponse(['data' => ['message' => $this->l10n->t('Could not update app.')]], Http::STATUS_INTERNAL_SERVER_ERROR); } private function sortApps($a, $b) { diff --git a/apps/settings/lib/Controller/ChangePasswordController.php b/apps/settings/lib/Controller/ChangePasswordController.php index 85e4218ebb5..7c3ab9546bc 100644 --- a/apps/settings/lib/Controller/ChangePasswordController.php +++ b/apps/settings/lib/Controller/ChangePasswordController.php @@ -243,7 +243,7 @@ class ChangePasswordController extends Controller { return new JSONResponse([ 'status' => 'error', 'data' => [ - 'message' => $this->l->t('Backend doesn\'t support password change, but the user\'s encryption key was updated.'), + 'message' => $this->l->t('Backend does not support password change, but the user\'s encryption key was updated.'), ] ]); } elseif (!$result && !$recoveryEnabledForUser) { diff --git a/apps/settings/src/components/BasicSettings/BackgroundJob.vue b/apps/settings/src/components/BasicSettings/BackgroundJob.vue index 525e82b42b8..07f5c5cab4c 100644 --- a/apps/settings/src/components/BasicSettings/BackgroundJob.vue +++ b/apps/settings/src/components/BasicSettings/BackgroundJob.vue @@ -22,7 +22,7 @@ <template> <SettingsSection :title="t('settings', 'Background jobs')" - :description="t('settings', 'For the server to work properly, it’s important to configure background jobs correctly. Cron is the recommended setting. Please see the documentation for more information.')" + :description="t('settings', 'For the server to work properly, it\'s important to configure background jobs correctly. Cron is the recommended setting. Please see the documentation for more information.')" :doc-url="backgroundJobsDocUrl"> <template v-if="lastCron !== 0"> <span v-if="oldExecution" class="error"> @@ -30,11 +30,11 @@ </span> <span v-else-if="longExecutionNotCron" class="warning"> - {{ t('settings', "Some jobs haven’t been executed since {maxAgeRelativeTime}. Please consider increasing the execution frequency.", {maxAgeRelativeTime}) }} + {{ t('settings', "Some jobs have not been executed since {maxAgeRelativeTime}. Please consider increasing the execution frequency.", {maxAgeRelativeTime}) }} </span> <span v-else-if="longExecutionCron" class="warning"> - {{ t('settings', "Some jobs haven’t been executed since {maxAgeRelativeTime}. Please consider switching to system cron.", {maxAgeRelativeTime}) }} + {{ t('settings', "Some jobs have not been executed since {maxAgeRelativeTime}. Please consider switching to system cron.", {maxAgeRelativeTime}) }} </span> <span v-else> @@ -43,7 +43,7 @@ </template> <span v-else class="error"> - {{ t('settings', 'Background job didn’t run yet!') }} + {{ t('settings', 'Background job did not run yet!') }} </span> <CheckboxRadioSwitch type="radio" diff --git a/apps/settings/tests/Controller/CheckSetupControllerTest.php b/apps/settings/tests/Controller/CheckSetupControllerTest.php index b7dd96ae653..8c9b2f216af 100644 --- a/apps/settings/tests/Controller/CheckSetupControllerTest.php +++ b/apps/settings/tests/Controller/CheckSetupControllerTest.php @@ -234,15 +234,12 @@ class CheckSetupControllerTest extends TestCase { } public function testIsInternetConnectionWorkingCorrectly() { - $this->config->expects($this->at(0)) + $this->config->expects($this->exactly(2)) ->method('getSystemValue') - ->with('has_internet_connection', true) - ->willReturn(true); - - $this->config->expects($this->at(1)) - ->method('getSystemValue') - ->with('connectivity_check_domains', ['www.nextcloud.com', 'www.startpage.com', 'www.eff.org', 'www.edri.org']) - ->willReturn(['www.nextcloud.com', 'www.startpage.com', 'www.eff.org', 'www.edri.org']); + ->withConsecutive( + ['has_internet_connection', true], + ['connectivity_check_domains', ['www.nextcloud.com', 'www.startpage.com', 'www.eff.org', 'www.edri.org']], + )->willReturnArgument(1); $client = $this->getMockBuilder('\OCP\Http\Client\IClient') ->disableOriginalConstructor()->getMock(); @@ -263,15 +260,12 @@ class CheckSetupControllerTest extends TestCase { } public function testIsInternetConnectionFail() { - $this->config->expects($this->at(0)) - ->method('getSystemValue') - ->with('has_internet_connection', true) - ->willReturn(true); - - $this->config->expects($this->at(1)) + $this->config->expects($this->exactly(2)) ->method('getSystemValue') - ->with('connectivity_check_domains', ['www.nextcloud.com', 'www.startpage.com', 'www.eff.org', 'www.edri.org']) - ->willReturn(['www.nextcloud.com', 'www.startpage.com', 'www.eff.org', 'www.edri.org']); + ->withConsecutive( + ['has_internet_connection', true], + ['connectivity_check_domains', ['www.nextcloud.com', 'www.startpage.com', 'www.eff.org', 'www.edri.org']], + )->willReturnArgument(1); $client = $this->getMockBuilder('\OCP\Http\Client\IClient') ->disableOriginalConstructor()->getMock(); @@ -432,30 +426,20 @@ class CheckSetupControllerTest extends TestCase { } public function testCheck() { - $this->config->expects($this->at(0)) - ->method('getAppValue') - ->with('files_external', 'user_certificate_scan', false) - ->willReturn('["a", "b"]'); - $this->config->expects($this->at(1)) + $this->config->expects($this->any()) ->method('getAppValue') - ->with('core', 'cronErrors') - ->willReturn(''); - $this->config->expects($this->at(3)) - ->method('getSystemValue') - ->with('connectivity_check_domains', ['www.nextcloud.com', 'www.startpage.com', 'www.eff.org', 'www.edri.org']) - ->willReturn(['www.nextcloud.com', 'www.startpage.com', 'www.eff.org', 'www.edri.org']); - $this->config->expects($this->at(4)) - ->method('getSystemValue') - ->with('memcache.local', null) - ->willReturn('SomeProvider'); - $this->config->expects($this->at(5)) - ->method('getSystemValue') - ->with('has_internet_connection', true) - ->willReturn(true); - $this->config->expects($this->at(6)) + ->willReturnMap([ + ['files_external', 'user_certificate_scan', '', '["a", "b"]'], + ['core', 'cronErrors', ''], + ]); + $this->config->expects($this->any()) ->method('getSystemValue') - ->with('appstoreenabled', true) - ->willReturn(false); + ->willReturnMap([ + ['connectivity_check_domains', ['www.nextcloud.com', 'www.startpage.com', 'www.eff.org', 'www.edri.org'], ['www.nextcloud.com', 'www.startpage.com', 'www.eff.org', 'www.edri.org']], + ['memcache.local', null, 'SomeProvider'], + ['has_internet_connection', true, true], + ['appstoreenabled', true, false], + ]); $this->request->expects($this->atLeastOnce()) ->method('getHeader') @@ -466,22 +450,14 @@ class CheckSetupControllerTest extends TestCase { $client = $this->getMockBuilder('\OCP\Http\Client\IClient') ->disableOriginalConstructor()->getMock(); - $client->expects($this->at(0)) + $client->expects($this->exactly(4)) ->method('get') - ->with('http://www.nextcloud.com/', []) - ->will($this->throwException(new \Exception())); - $client->expects($this->at(1)) - ->method('get') - ->with('http://www.startpage.com/', []) - ->will($this->throwException(new \Exception())); - $client->expects($this->at(2)) - ->method('get') - ->with('http://www.eff.org/', []) - ->will($this->throwException(new \Exception())); - $client->expects($this->at(3)) - ->method('get') - ->with('http://www.edri.org/', []) - ->will($this->throwException(new \Exception())); + ->withConsecutive( + ['http://www.nextcloud.com/', []], + ['http://www.startpage.com/', []], + ['http://www.eff.org/', []], + ['http://www.edri.org/', []] + )->will($this->throwException(new \Exception())); $this->clientService->expects($this->exactly(4)) ->method('newClient') ->willReturn($client); @@ -740,10 +716,12 @@ class CheckSetupControllerTest extends TestCase { public function testIsUsedTlsLibOutdatedWithOlderOpenSslAndWithoutAppstore() { $this->config - ->expects($this->at(0)) + ->expects($this->any()) ->method('getSystemValue') - ->with('has_internet_connection', true) - ->willReturn(true); + ->willReturnMap([ + ['has_internet_connection', true, true], + ['appstoreenabled', true, false], + ]); $this->checkSetupController ->expects($this->once()) ->method('getCurlVersion') @@ -855,7 +833,7 @@ class CheckSetupControllerTest extends TestCase { ->method('getResponse') ->willReturn($response); - $client->expects($this->at(0)) + $client->expects($this->once()) ->method('get') ->with('https://nextcloud.com/', []) ->will($this->throwException($exception)); @@ -889,7 +867,7 @@ class CheckSetupControllerTest extends TestCase { ->method('getResponse') ->willReturn($response); - $client->expects($this->at(0)) + $client->expects($this->once()) ->method('get') ->with('https://nextcloud.com/', []) ->will($this->throwException($exception)); @@ -903,7 +881,7 @@ class CheckSetupControllerTest extends TestCase { public function testIsUsedTlsLibOutdatedWithInternetDisabled() { $this->config - ->expects($this->at(0)) + ->expects($this->once()) ->method('getSystemValue') ->with('has_internet_connection', true) ->willReturn(false); @@ -912,25 +890,19 @@ class CheckSetupControllerTest extends TestCase { public function testIsUsedTlsLibOutdatedWithAppstoreDisabledAndServerToServerSharingEnabled() { $this->config - ->expects($this->at(0)) - ->method('getSystemValue') - ->with('has_internet_connection', true) - ->willReturn(true); - $this->config - ->expects($this->at(1)) + ->expects($this->exactly(2)) ->method('getSystemValue') - ->with('appstoreenabled', true) - ->willReturn(false); - $this->config - ->expects($this->at(2)) - ->method('getAppValue') - ->with('files_sharing', 'outgoing_server2server_share_enabled', 'yes') - ->willReturn('no'); + ->willReturnMap([ + ['has_internet_connection', true, true], + ['appstoreenabled', true, false], + ]); $this->config - ->expects($this->at(3)) + ->expects($this->exactly(2)) ->method('getAppValue') - ->with('files_sharing', 'incoming_server2server_share_enabled', 'yes') - ->willReturn('yes'); + ->willReturnMap([ + ['files_sharing', 'outgoing_server2server_share_enabled', 'yes', 'no'], + ['files_sharing', 'incoming_server2server_share_enabled', 'yes', 'yes'], + ]); $this->checkSetupController ->expects($this->once()) @@ -941,25 +913,19 @@ class CheckSetupControllerTest extends TestCase { public function testIsUsedTlsLibOutdatedWithAppstoreDisabledAndServerToServerSharingDisabled() { $this->config - ->expects($this->at(0)) - ->method('getSystemValue') - ->with('has_internet_connection', true) - ->willReturn(true); - $this->config - ->expects($this->at(1)) + ->expects($this->exactly(2)) ->method('getSystemValue') - ->with('appstoreenabled', true) - ->willReturn(false); - $this->config - ->expects($this->at(2)) - ->method('getAppValue') - ->with('files_sharing', 'outgoing_server2server_share_enabled', 'yes') - ->willReturn('no'); + ->willReturnMap([ + ['has_internet_connection', true, true], + ['appstoreenabled', true, false], + ]); $this->config - ->expects($this->at(3)) + ->expects($this->exactly(2)) ->method('getAppValue') - ->with('files_sharing', 'incoming_server2server_share_enabled', 'yes') - ->willReturn('no'); + ->willReturnMap([ + ['files_sharing', 'outgoing_server2server_share_enabled', 'yes', 'no'], + ['files_sharing', 'incoming_server2server_share_enabled', 'yes', 'no'], + ]); $this->checkSetupController ->expects($this->never()) diff --git a/apps/settings/tests/Mailer/NewUserMailHelperTest.php b/apps/settings/tests/Mailer/NewUserMailHelperTest.php index f2e1d87672d..2a9c1ee58ec 100644 --- a/apps/settings/tests/Mailer/NewUserMailHelperTest.php +++ b/apps/settings/tests/Mailer/NewUserMailHelperTest.php @@ -155,7 +155,7 @@ class NewUserMailHelperTest extends TestCase { ->method('setUserValue') ->with('john', 'core', 'lostpassword', 'TokenCiphertext'); $this->urlGenerator - ->expects($this->at(0)) + ->expects($this->once()) ->method('linkToRouteAbsolute') ->with('core.lost.resetform', ['userId' => 'john', 'token' => 'MySuperLongSecureRandomToken']) ->willReturn('https://example.com/resetPassword/MySuperLongSecureRandomToken'); @@ -163,10 +163,6 @@ class NewUserMailHelperTest extends TestCase { ->expects($this->any()) ->method('getDisplayName') ->willReturn('john'); - $user - ->expects($this->at(5)) - ->method('getUID') - ->willReturn('john'); $this->defaults ->expects($this->any()) ->method('getName') @@ -384,10 +380,12 @@ EOF; public function testGenerateTemplateWithoutPasswordResetToken() { $this->urlGenerator - ->expects($this->at(0)) + ->expects($this->any()) ->method('getAbsoluteURL') - ->with('/') - ->willReturn('https://example.com/'); + ->willReturnMap([ + ['/','https://example.com/'], + ['myLogo',''], + ]); /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */ $user = $this->createMock(IUser::class); @@ -616,10 +614,12 @@ EOF; public function testGenerateTemplateWithoutUserId() { $this->urlGenerator - ->expects($this->at(0)) + ->expects($this->any()) ->method('getAbsoluteURL') - ->with('/') - ->willReturn('https://example.com/'); + ->willReturnMap([ + ['/', 'https://example.com/'], + ['myLogo', ''], + ]); /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */ $user = $this->createMock(IUser::class); @@ -837,30 +837,30 @@ EOF; /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */ $user = $this->createMock(IUser::class); $user - ->expects($this->at(0)) + ->expects($this->once()) ->method('getEMailAddress') ->willReturn('recipient@example.com'); $user - ->expects($this->at(1)) + ->expects($this->once()) ->method('getDisplayName') ->willReturn('John Doe'); /** @var IEMailTemplate|\PHPUnit\Framework\MockObject\MockObject $emailTemplate */ $emailTemplate = $this->createMock(IEMailTemplate::class); $message = $this->createMock(Message::class); $message - ->expects($this->at(0)) + ->expects($this->once()) ->method('setTo') ->with(['recipient@example.com' => 'John Doe']); $message - ->expects($this->at(1)) + ->expects($this->once()) ->method('setFrom') ->with(['no-reply@nextcloud.com' => 'TestCloud']); $message - ->expects($this->at(2)) + ->expects($this->once()) ->method('useTemplate') ->with($emailTemplate); $this->defaults - ->expects($this->exactly(1)) + ->expects($this->once()) ->method('getName') ->willReturn('TestCloud'); $this->mailer diff --git a/apps/settings/tests/Middleware/SubadminMiddlewareTest.php b/apps/settings/tests/Middleware/SubadminMiddlewareTest.php index a6317173439..a1330857e02 100644 --- a/apps/settings/tests/Middleware/SubadminMiddlewareTest.php +++ b/apps/settings/tests/Middleware/SubadminMiddlewareTest.php @@ -68,15 +68,12 @@ class SubadminMiddlewareTest extends \Test\TestCase { $this->expectException(\OC\AppFramework\Middleware\Security\Exceptions\NotAdminException::class); $this->reflector - ->expects($this->at(0)) + ->expects($this->exactly(2)) ->method('hasAnnotation') - ->with('NoSubAdminRequired') - ->willReturn(false); - $this->reflector - ->expects($this->at(1)) - ->method('hasAnnotation') - ->with('AuthorizedAdminSetting') - ->willReturn(false); + ->withConsecutive( + ['NoSubAdminRequired'], + ['AuthorizedAdminSetting'], + )->willReturn(false); $this->subadminMiddleware->beforeController($this->controller, 'foo'); } @@ -92,15 +89,12 @@ class SubadminMiddlewareTest extends \Test\TestCase { public function testBeforeControllerAsSubAdminWithoutExemption() { $this->reflector - ->expects($this->at(0)) - ->method('hasAnnotation') - ->with('NoSubAdminRequired') - ->willReturn(false); - $this->reflector - ->expects($this->at(1)) + ->expects($this->exactly(2)) ->method('hasAnnotation') - ->with('AuthorizedAdminSetting') - ->willReturn(false); + ->withConsecutive( + ['NoSubAdminRequired'], + ['AuthorizedAdminSetting'], + )->willReturn(false); $this->subadminMiddlewareAsSubAdmin->beforeController($this->controller, 'foo'); } diff --git a/apps/settings/tests/Settings/Admin/MailTest.php b/apps/settings/tests/Settings/Admin/MailTest.php index 5fd3f1600c5..7a70065ff50 100644 --- a/apps/settings/tests/Settings/Admin/MailTest.php +++ b/apps/settings/tests/Settings/Admin/MailTest.php @@ -55,60 +55,21 @@ class MailTest extends TestCase { public function testGetForm() { $this->config - ->expects($this->at(0)) + ->expects($this->any()) ->method('getSystemValue') - ->with('mail_domain', '') - ->willReturn('mx.nextcloud.com'); - $this->config - ->expects($this->at(1)) - ->method('getSystemValue') - ->with('mail_from_address', '') - ->willReturn('no-reply@nextcloud.com'); - $this->config - ->expects($this->at(2)) - ->method('getSystemValue') - ->with('mail_smtpmode', '') - ->willReturn('smtp'); - $this->config - ->expects($this->at(3)) - ->method('getSystemValue') - ->with('mail_smtpsecure', '') - ->willReturn(true); - $this->config - ->expects($this->at(4)) - ->method('getSystemValue') - ->with('mail_smtphost', '') - ->willReturn('smtp.nextcloud.com'); - $this->config - ->expects($this->at(5)) - ->method('getSystemValue') - ->with('mail_smtpport', '') - ->willReturn(25); - $this->config - ->expects($this->at(6)) - ->method('getSystemValue') - ->with('mail_smtpauthtype', '') - ->willReturn('login'); - $this->config - ->expects($this->at(7)) - ->method('getSystemValue') - ->with('mail_smtpauth', false) - ->willReturn(true); - $this->config - ->expects($this->at(8)) - ->method('getSystemValue') - ->with('mail_smtpname', '') - ->willReturn('smtp.sender.com'); - $this->config - ->expects($this->at(9)) - ->method('getSystemValue') - ->with('mail_smtppassword', '') - ->willReturn('mypassword'); - $this->config - ->expects($this->at(10)) - ->method('getSystemValue') - ->with('mail_sendmailmode', 'smtp') - ->willReturn('smtp'); + ->willReturnMap([ + ['mail_domain', '', 'mx.nextcloud.com'], + ['mail_from_address', '', 'no-reply@nextcloud.com'], + ['mail_smtpmode', '', 'smtp'], + ['mail_smtpsecure', '', true], + ['mail_smtphost', '', 'smtp.nextcloud.com'], + ['mail_smtpport', '', 25], + ['mail_smtpauthtype', '', 'login'], + ['mail_smtpauth', false, true], + ['mail_smtpname', '', 'smtp.sender.com'], + ['mail_smtppassword', '', 'mypassword'], + ['mail_sendmailmode', 'smtp', 'smtp'], + ]); $expected = new TemplateResponse( 'settings', diff --git a/apps/settings/tests/Settings/Admin/ServerTest.php b/apps/settings/tests/Settings/Admin/ServerTest.php index c5958c29421..74d9a5d53fd 100644 --- a/apps/settings/tests/Settings/Admin/ServerTest.php +++ b/apps/settings/tests/Settings/Admin/ServerTest.php @@ -93,20 +93,13 @@ class ServerTest extends TestCase { ->method('cronMaxAge') ->willReturn(1337); $this->config - ->expects($this->at(0)) + ->expects($this->any()) ->method('getAppValue') - ->with('core', 'backgroundjobs_mode', 'ajax') - ->willReturn('ajax'); - $this->config - ->expects($this->at(1)) - ->method('getAppValue') - ->with('core', 'lastcron', '0') - ->willReturn('0'); - $this->config - ->expects($this->at(2)) - ->method('getAppValue') - ->with('core', 'cronErrors') - ->willReturn(''); + ->willReturnMap([ + ['core', 'backgroundjobs_mode', 'ajax', 'ajax'], + ['core', 'lastcron', '0', '0'], + ['core', 'cronErrors', ''], + ]); $this->profileManager ->expects($this->exactly(2)) ->method('isProfileEnabled') diff --git a/apps/settings/tests/Settings/Personal/Security/AuthtokensTest.php b/apps/settings/tests/Settings/Personal/Security/AuthtokensTest.php index 8fae0a44d8f..5ccec936555 100644 --- a/apps/settings/tests/Settings/Personal/Security/AuthtokensTest.php +++ b/apps/settings/tests/Settings/Personal/Security/AuthtokensTest.php @@ -96,33 +96,34 @@ class AuthtokensTest extends TestCase { ->method('getToken') ->with('session123') ->willReturn($sessionToken); - $this->initialState->expects($this->at(0)) + $this->initialState->expects($this->exactly(2)) ->method('provideInitialState') - ->with('app_tokens', [ + ->withConsecutive( [ - 'id' => 100, - 'name' => null, - 'lastActivity' => 0, - 'type' => 0, - 'canDelete' => false, - 'current' => true, - 'scope' => ['filesystem' => true], - 'canRename' => false, + 'app_tokens', [ + [ + 'id' => 100, + 'name' => null, + 'lastActivity' => 0, + 'type' => 0, + 'canDelete' => false, + 'current' => true, + 'scope' => ['filesystem' => true], + 'canRename' => false, + ], + [ + 'id' => 200, + 'name' => null, + 'lastActivity' => 0, + 'type' => 0, + 'canDelete' => true, + 'scope' => ['filesystem' => true], + 'canRename' => true, + ], + ] ], - [ - 'id' => 200, - 'name' => null, - 'lastActivity' => 0, - 'type' => 0, - 'canDelete' => true, - 'scope' => ['filesystem' => true], - 'canRename' => true, - ], - ]); - - $this->initialState->expects($this->at(1)) - ->method('provideInitialState') - ->with('can_create_app_token', true); + ['can_create_app_token', true], + ); $form = $this->section->getForm(); diff --git a/apps/sharebymail/l10n/cs.js b/apps/sharebymail/l10n/cs.js index b50f72f9d2c..d6b2a4a3b6c 100644 --- a/apps/sharebymail/l10n/cs.js +++ b/apps/sharebymail/l10n/cs.js @@ -39,6 +39,7 @@ OC.L10N.register( "Password to access »%1$s« shared to you by %2$s" : "Heslo pro přístup k „%1$s“, které vám nasdílel(a) %2$s", "Password to access »%s«" : "Heslo pro přístup k „%s “", "It is protected with the following password:" : "Je chráněno následujícím heslem:", + "This password will expire at %s" : "Platnost tohoto hesla skončí %s", "%1$s shared »%2$s« with you and wants to add:" : "%1$s sdílí „%2$s“ a dodává:", "%1$s shared »%2$s« with you and wants to add" : "%1$s sdílí „%2$s“ a dodává", "»%s« added a note to a file shared with you" : "„%s“ dodává poznámku k nasdílenému souboru", diff --git a/apps/sharebymail/l10n/cs.json b/apps/sharebymail/l10n/cs.json index 165c32dd02e..e91a35599be 100644 --- a/apps/sharebymail/l10n/cs.json +++ b/apps/sharebymail/l10n/cs.json @@ -37,6 +37,7 @@ "Password to access »%1$s« shared to you by %2$s" : "Heslo pro přístup k „%1$s“, které vám nasdílel(a) %2$s", "Password to access »%s«" : "Heslo pro přístup k „%s “", "It is protected with the following password:" : "Je chráněno následujícím heslem:", + "This password will expire at %s" : "Platnost tohoto hesla skončí %s", "%1$s shared »%2$s« with you and wants to add:" : "%1$s sdílí „%2$s“ a dodává:", "%1$s shared »%2$s« with you and wants to add" : "%1$s sdílí „%2$s“ a dodává", "»%s« added a note to a file shared with you" : "„%s“ dodává poznámku k nasdílenému souboru", diff --git a/apps/sharebymail/l10n/de_DE.js b/apps/sharebymail/l10n/de_DE.js index 8083430755c..328dbfeb147 100644 --- a/apps/sharebymail/l10n/de_DE.js +++ b/apps/sharebymail/l10n/de_DE.js @@ -39,6 +39,7 @@ OC.L10N.register( "Password to access »%1$s« shared to you by %2$s" : "Das Passwort zum Zugriff auf %1$s wurde durch %2$s mit Ihnen geteilt.", "Password to access »%s«" : "Passwort um auf »%s« zu zugreifen", "It is protected with the following password:" : "Dies ist mit dem folgendem Passwort geschützt:", + "This password will expire at %s" : "Dieses Passwort wird um %s ablaufen.", "%1$s shared »%2$s« with you and wants to add:" : "%1$s hat » %2$s« mit Ihnen geteilt und möchte folgendes hinzufügen:", "%1$s shared »%2$s« with you and wants to add" : "%1$s hat »%2$s« mit Ihnen geteilt und möchte folgendes hinzufügen", "»%s« added a note to a file shared with you" : "»%s« hat eine Bemerkung zu einer mit Ihnen geteilten Datei hinzugefügt", diff --git a/apps/sharebymail/l10n/de_DE.json b/apps/sharebymail/l10n/de_DE.json index d5f46749482..64fcad2eff2 100644 --- a/apps/sharebymail/l10n/de_DE.json +++ b/apps/sharebymail/l10n/de_DE.json @@ -37,6 +37,7 @@ "Password to access »%1$s« shared to you by %2$s" : "Das Passwort zum Zugriff auf %1$s wurde durch %2$s mit Ihnen geteilt.", "Password to access »%s«" : "Passwort um auf »%s« zu zugreifen", "It is protected with the following password:" : "Dies ist mit dem folgendem Passwort geschützt:", + "This password will expire at %s" : "Dieses Passwort wird um %s ablaufen.", "%1$s shared »%2$s« with you and wants to add:" : "%1$s hat » %2$s« mit Ihnen geteilt und möchte folgendes hinzufügen:", "%1$s shared »%2$s« with you and wants to add" : "%1$s hat »%2$s« mit Ihnen geteilt und möchte folgendes hinzufügen", "»%s« added a note to a file shared with you" : "»%s« hat eine Bemerkung zu einer mit Ihnen geteilten Datei hinzugefügt", diff --git a/apps/sharebymail/l10n/eu.js b/apps/sharebymail/l10n/eu.js index ff4509f0e33..b4c4739c1dd 100644 --- a/apps/sharebymail/l10n/eu.js +++ b/apps/sharebymail/l10n/eu.js @@ -39,6 +39,7 @@ OC.L10N.register( "Password to access »%1$s« shared to you by %2$s" : "»%1$s« atzitzeko pasahitza partekatu du zurekin %2$s(e)k", "Password to access »%s«" : "»%s« atzitzeko pasahitza", "It is protected with the following password:" : "Honako pasahitz honekin babestuta dago:", + "This password will expire at %s" : "Pasahitz honek %s(e)(t)an iraungiko du", "%1$s shared »%2$s« with you and wants to add:" : "%1$serabiltzaileak »%2$s« partekatu du zurekin eta hau gehitu nahi du:", "%1$s shared »%2$s« with you and wants to add" : "%1$serabiltzaileak »%2$s« partekatu du zurekin eta hau gehitu nahi du", "»%s« added a note to a file shared with you" : "»%s« erabiltzaileak nota bat gehitu dio partekatu dizun fitxategi batean", diff --git a/apps/sharebymail/l10n/eu.json b/apps/sharebymail/l10n/eu.json index 46fac066e09..00c5a1cd7f5 100644 --- a/apps/sharebymail/l10n/eu.json +++ b/apps/sharebymail/l10n/eu.json @@ -37,6 +37,7 @@ "Password to access »%1$s« shared to you by %2$s" : "»%1$s« atzitzeko pasahitza partekatu du zurekin %2$s(e)k", "Password to access »%s«" : "»%s« atzitzeko pasahitza", "It is protected with the following password:" : "Honako pasahitz honekin babestuta dago:", + "This password will expire at %s" : "Pasahitz honek %s(e)(t)an iraungiko du", "%1$s shared »%2$s« with you and wants to add:" : "%1$serabiltzaileak »%2$s« partekatu du zurekin eta hau gehitu nahi du:", "%1$s shared »%2$s« with you and wants to add" : "%1$serabiltzaileak »%2$s« partekatu du zurekin eta hau gehitu nahi du", "»%s« added a note to a file shared with you" : "»%s« erabiltzaileak nota bat gehitu dio partekatu dizun fitxategi batean", diff --git a/apps/sharebymail/l10n/hu.js b/apps/sharebymail/l10n/hu.js index 7e6518af7c8..708afa9bc2d 100644 --- a/apps/sharebymail/l10n/hu.js +++ b/apps/sharebymail/l10n/hu.js @@ -39,6 +39,7 @@ OC.L10N.register( "Password to access »%1$s« shared to you by %2$s" : "Jelszó a(z) %2$s által megosztott „%1$s” eléréséhez", "Password to access »%s«" : "Jelszó a(z) „%s” eléréséhez", "It is protected with the following password:" : "A következő jelszó védi:", + "This password will expire at %s" : "Ez a jelszó ekkor jár le: %s.", "%1$s shared »%2$s« with you and wants to add:" : "%1$s megosztotta Önnel a(z) „%2$s” elemet, és a következőt fűzi hozzá:", "%1$s shared »%2$s« with you and wants to add" : "%1$s megosztotta Önnel a(z) „%2$s” elemet, és a következőt fűzi hozzá", "»%s« added a note to a file shared with you" : "„%s” megjegyzést fűzött az Önnel megosztott fájlhoz", diff --git a/apps/sharebymail/l10n/hu.json b/apps/sharebymail/l10n/hu.json index 81c143c9736..c55bfbe5209 100644 --- a/apps/sharebymail/l10n/hu.json +++ b/apps/sharebymail/l10n/hu.json @@ -37,6 +37,7 @@ "Password to access »%1$s« shared to you by %2$s" : "Jelszó a(z) %2$s által megosztott „%1$s” eléréséhez", "Password to access »%s«" : "Jelszó a(z) „%s” eléréséhez", "It is protected with the following password:" : "A következő jelszó védi:", + "This password will expire at %s" : "Ez a jelszó ekkor jár le: %s.", "%1$s shared »%2$s« with you and wants to add:" : "%1$s megosztotta Önnel a(z) „%2$s” elemet, és a következőt fűzi hozzá:", "%1$s shared »%2$s« with you and wants to add" : "%1$s megosztotta Önnel a(z) „%2$s” elemet, és a következőt fűzi hozzá", "»%s« added a note to a file shared with you" : "„%s” megjegyzést fűzött az Önnel megosztott fájlhoz", diff --git a/apps/sharebymail/l10n/ja.js b/apps/sharebymail/l10n/ja.js index 58b2b6ef10e..16d90555657 100644 --- a/apps/sharebymail/l10n/ja.js +++ b/apps/sharebymail/l10n/ja.js @@ -27,6 +27,7 @@ OC.L10N.register( "Share by mail" : "メールで共有", "Sharing %1$s failed, because this item is already shared with user %2$s" : "ユーザー%2$sによりすでに共有されているため%1$s を共有できませんでした。", "We cannot send you the auto-generated password. Please set a valid email address in your personal settings and try again." : "あなたに自動生成したパスワードを送信できませんでした。個人設定画面から正しいメールアドレスを設定して再度実施してください。", + "Failed to send share by email. Got an invalid email address" : "共有メールの送信に失敗しました。無効なメールアドレスが入力されています", "Failed to send share by email" : "メールで共有の送信に失敗しました", "%1$s shared »%2$s« with you" : "%1$sが あなたと >> %2$s <<を共有しました", "%1$s shared »%2$s« with you." : "%1$sが あなたと >> %2$s <<を共有しました。", @@ -49,6 +50,7 @@ OC.L10N.register( "Share provider which allows you to share files by mail" : "メールでファイルを共有できる共有プロバイダー", "Allows users to share a personalized link to a file or folder by putting in an email address." : "ユーザーがメールアドレスを使ってファイルやフォルダーへの個人リンクを共有することを許可します。", "Send password by mail" : "メールでパスワード送信", - "Reply to initiator" : "返信先を共有開始者にする" + "Reply to initiator" : "返信先を共有開始者にする", + "Unable to update share by mail config" : "メールで共有設定の更新に失敗しました" }, "nplurals=1; plural=0;"); diff --git a/apps/sharebymail/l10n/ja.json b/apps/sharebymail/l10n/ja.json index 5a0b5636a18..d3440b65443 100644 --- a/apps/sharebymail/l10n/ja.json +++ b/apps/sharebymail/l10n/ja.json @@ -25,6 +25,7 @@ "Share by mail" : "メールで共有", "Sharing %1$s failed, because this item is already shared with user %2$s" : "ユーザー%2$sによりすでに共有されているため%1$s を共有できませんでした。", "We cannot send you the auto-generated password. Please set a valid email address in your personal settings and try again." : "あなたに自動生成したパスワードを送信できませんでした。個人設定画面から正しいメールアドレスを設定して再度実施してください。", + "Failed to send share by email. Got an invalid email address" : "共有メールの送信に失敗しました。無効なメールアドレスが入力されています", "Failed to send share by email" : "メールで共有の送信に失敗しました", "%1$s shared »%2$s« with you" : "%1$sが あなたと >> %2$s <<を共有しました", "%1$s shared »%2$s« with you." : "%1$sが あなたと >> %2$s <<を共有しました。", @@ -47,6 +48,7 @@ "Share provider which allows you to share files by mail" : "メールでファイルを共有できる共有プロバイダー", "Allows users to share a personalized link to a file or folder by putting in an email address." : "ユーザーがメールアドレスを使ってファイルやフォルダーへの個人リンクを共有することを許可します。", "Send password by mail" : "メールでパスワード送信", - "Reply to initiator" : "返信先を共有開始者にする" + "Reply to initiator" : "返信先を共有開始者にする", + "Unable to update share by mail config" : "メールで共有設定の更新に失敗しました" },"pluralForm" :"nplurals=1; plural=0;" }
\ No newline at end of file diff --git a/apps/sharebymail/l10n/pl.js b/apps/sharebymail/l10n/pl.js index a7a3b75aead..e416177aaf9 100644 --- a/apps/sharebymail/l10n/pl.js +++ b/apps/sharebymail/l10n/pl.js @@ -39,6 +39,7 @@ OC.L10N.register( "Password to access »%1$s« shared to you by %2$s" : "Hasło dostępu do %1$s udostępnionego przez %2$s.", "Password to access »%s«" : "Hasło dostępu do »%s«", "It is protected with the following password:" : "Zasób jest chroniony następującym hasłem:", + "This password will expire at %s" : "To hasło wygaśnie w %s", "%1$s shared »%2$s« with you and wants to add:" : "%1$s udostępnił »%2$s« z informacją:", "%1$s shared »%2$s« with you and wants to add" : "%1$s udostępnił »%2$s« z informacją dla Ciebie", "»%s« added a note to a file shared with you" : "»%s« wysłał dodatkową informację dot. udostępnionego zasobu", diff --git a/apps/sharebymail/l10n/pl.json b/apps/sharebymail/l10n/pl.json index 7764091bda2..88ba7bf967f 100644 --- a/apps/sharebymail/l10n/pl.json +++ b/apps/sharebymail/l10n/pl.json @@ -37,6 +37,7 @@ "Password to access »%1$s« shared to you by %2$s" : "Hasło dostępu do %1$s udostępnionego przez %2$s.", "Password to access »%s«" : "Hasło dostępu do »%s«", "It is protected with the following password:" : "Zasób jest chroniony następującym hasłem:", + "This password will expire at %s" : "To hasło wygaśnie w %s", "%1$s shared »%2$s« with you and wants to add:" : "%1$s udostępnił »%2$s« z informacją:", "%1$s shared »%2$s« with you and wants to add" : "%1$s udostępnił »%2$s« z informacją dla Ciebie", "»%s« added a note to a file shared with you" : "»%s« wysłał dodatkową informację dot. udostępnionego zasobu", diff --git a/apps/sharebymail/l10n/pt_BR.js b/apps/sharebymail/l10n/pt_BR.js index 4c718a1b7ca..50eab4da10d 100644 --- a/apps/sharebymail/l10n/pt_BR.js +++ b/apps/sharebymail/l10n/pt_BR.js @@ -39,6 +39,7 @@ OC.L10N.register( "Password to access »%1$s« shared to you by %2$s" : "Senha de acesso »%1$s« compartilhada com você por %2$s", "Password to access »%s«" : "Senha para acessar »%s«", "It is protected with the following password:" : "Está protegida com a seguinte senha:", + "This password will expire at %s" : "Esta senha expira em%s", "%1$s shared »%2$s« with you and wants to add:" : "%1$s compartilhou »%2$s« com você e quer adicionar:", "%1$s shared »%2$s« with you and wants to add" : "%1$s compartilhou »%2$s« com você e quer adicionar", "»%s« added a note to a file shared with you" : "»%s« adicionou uma anotação num arquivo compartilhado com você", diff --git a/apps/sharebymail/l10n/pt_BR.json b/apps/sharebymail/l10n/pt_BR.json index f5bca54f636..0f36fa090b9 100644 --- a/apps/sharebymail/l10n/pt_BR.json +++ b/apps/sharebymail/l10n/pt_BR.json @@ -37,6 +37,7 @@ "Password to access »%1$s« shared to you by %2$s" : "Senha de acesso »%1$s« compartilhada com você por %2$s", "Password to access »%s«" : "Senha para acessar »%s«", "It is protected with the following password:" : "Está protegida com a seguinte senha:", + "This password will expire at %s" : "Esta senha expira em%s", "%1$s shared »%2$s« with you and wants to add:" : "%1$s compartilhou »%2$s« com você e quer adicionar:", "%1$s shared »%2$s« with you and wants to add" : "%1$s compartilhou »%2$s« com você e quer adicionar", "»%s« added a note to a file shared with you" : "»%s« adicionou uma anotação num arquivo compartilhado com você", diff --git a/apps/sharebymail/l10n/zh_HK.js b/apps/sharebymail/l10n/zh_HK.js index fdb2c08ddfc..a7b9b59c569 100644 --- a/apps/sharebymail/l10n/zh_HK.js +++ b/apps/sharebymail/l10n/zh_HK.js @@ -39,6 +39,7 @@ OC.L10N.register( "Password to access »%1$s« shared to you by %2$s" : "%2$s 與您分享了的訪問 »%1$s« 的密碼", "Password to access »%s«" : "存取 »%s« 的密碼", "It is protected with the following password:" : "其已被以下密碼保護:", + "This password will expire at %s" : "此密碼將於 %s 到期", "%1$s shared »%2$s« with you and wants to add:" : "%1$s 與您分享了 »%2$s« 並希望添加:", "%1$s shared »%2$s« with you and wants to add" : "%1$s 與您分享了 »%2$s« 並希望添加", "»%s« added a note to a file shared with you" : "»%s« 在與您分享的檔案中添加了備註", diff --git a/apps/sharebymail/l10n/zh_HK.json b/apps/sharebymail/l10n/zh_HK.json index ceeae211402..dbbfb9a7fb6 100644 --- a/apps/sharebymail/l10n/zh_HK.json +++ b/apps/sharebymail/l10n/zh_HK.json @@ -37,6 +37,7 @@ "Password to access »%1$s« shared to you by %2$s" : "%2$s 與您分享了的訪問 »%1$s« 的密碼", "Password to access »%s«" : "存取 »%s« 的密碼", "It is protected with the following password:" : "其已被以下密碼保護:", + "This password will expire at %s" : "此密碼將於 %s 到期", "%1$s shared »%2$s« with you and wants to add:" : "%1$s 與您分享了 »%2$s« 並希望添加:", "%1$s shared »%2$s« with you and wants to add" : "%1$s 與您分享了 »%2$s« 並希望添加", "»%s« added a note to a file shared with you" : "»%s« 在與您分享的檔案中添加了備註", diff --git a/apps/sharebymail/l10n/zh_TW.js b/apps/sharebymail/l10n/zh_TW.js index b2f83a85bf3..fd7e82cabb0 100644 --- a/apps/sharebymail/l10n/zh_TW.js +++ b/apps/sharebymail/l10n/zh_TW.js @@ -39,6 +39,7 @@ OC.L10N.register( "Password to access »%1$s« shared to you by %2$s" : "存取 »%1$s« 的密碼已透過 %2$s 與您分享", "Password to access »%s«" : "存取 »%s« 的密碼", "It is protected with the following password:" : "其受以下密碼保護:", + "This password will expire at %s" : "密碼將於 %s 到期", "%1$s shared »%2$s« with you and wants to add:" : "%1$s 與您分享了 »%2$s«,且想要新增:", "%1$s shared »%2$s« with you and wants to add" : "%1$s 與您分享了 »%2$s«,且想要新增", "»%s« added a note to a file shared with you" : "»%s« 在與您分享的檔案新增了註記", diff --git a/apps/sharebymail/l10n/zh_TW.json b/apps/sharebymail/l10n/zh_TW.json index 85e354c3a2e..060fffb9302 100644 --- a/apps/sharebymail/l10n/zh_TW.json +++ b/apps/sharebymail/l10n/zh_TW.json @@ -37,6 +37,7 @@ "Password to access »%1$s« shared to you by %2$s" : "存取 »%1$s« 的密碼已透過 %2$s 與您分享", "Password to access »%s«" : "存取 »%s« 的密碼", "It is protected with the following password:" : "其受以下密碼保護:", + "This password will expire at %s" : "密碼將於 %s 到期", "%1$s shared »%2$s« with you and wants to add:" : "%1$s 與您分享了 »%2$s«,且想要新增:", "%1$s shared »%2$s« with you and wants to add" : "%1$s 與您分享了 »%2$s«,且想要新增", "»%s« added a note to a file shared with you" : "»%s« 在與您分享的檔案新增了註記", diff --git a/apps/sharebymail/lib/ShareByMailProvider.php b/apps/sharebymail/lib/ShareByMailProvider.php index 1aa2307a27d..ccfebe0bded 100644 --- a/apps/sharebymail/lib/ShareByMailProvider.php +++ b/apps/sharebymail/lib/ShareByMailProvider.php @@ -198,7 +198,7 @@ class ShareByMailProvider implements IShareProvider { // Sends share password to receiver when it's a permanent one (otherwise she will have to request it via the showShare UI) // or to owner when the password shall be given during a Talk session - if ($this->config->getSystemValue('sharing.enable_mail_link_password_expiration', false) === true || $share->getSendPasswordByTalk()) { + if ($this->config->getSystemValue('sharing.enable_mail_link_password_expiration', false) === false || $share->getSendPasswordByTalk()) { $send = $this->sendPassword($share, $password); if ($passwordEnforced && $send === false) { $this->sendPasswordToOwner($share, $password); @@ -503,6 +503,13 @@ class ShareByMailProvider implements IShareProvider { $emailTemplate->addBodyText($this->l->t('It is protected with the following password:')); $emailTemplate->addBodyText($password); + if ($this->config->getSystemValue('sharing.enable_mail_link_password_expiration', false) === true) { + $expirationTime = new \DateTime(); + $expirationInterval = $this->config->getSystemValue('sharing.mail_link_password_expiration_interval', 3600); + $expirationTime = $expirationTime->add(new \DateInterval('PT' . $expirationInterval . 'S')); + $emailTemplate->addBodyText($this->l->t('This password will expire at %s', [$expirationTime->format('r')])); + } + // The "From" contains the sharers name $instanceName = $this->defaults->getName(); $senderName = $instanceName; @@ -627,7 +634,16 @@ class ShareByMailProvider implements IShareProvider { $emailTemplate->addBodyText($bodyPart); $emailTemplate->addBodyText($this->l->t('This is the password:')); $emailTemplate->addBodyText($password); + + if ($this->config->getSystemValue('sharing.enable_mail_link_password_expiration', false) === true) { + $expirationTime = new \DateTime(); + $expirationInterval = $this->config->getSystemValue('sharing.mail_link_password_expiration_interval', 3600); + $expirationTime = $expirationTime->add(new \DateInterval('PT' . $expirationInterval . 'S')); + $emailTemplate->addBodyText($this->l->t('This password will expire at %s', [$expirationTime->format('r')])); + } + $emailTemplate->addBodyText($this->l->t('You can choose a different password at any time in the share dialog.')); + $emailTemplate->addFooter(); $instanceName = $this->defaults->getName(); diff --git a/apps/sharebymail/tests/ShareByMailProviderTest.php b/apps/sharebymail/tests/ShareByMailProviderTest.php index b3e344d3ec5..1e5fa46b943 100644 --- a/apps/sharebymail/tests/ShareByMailProviderTest.php +++ b/apps/sharebymail/tests/ShareByMailProviderTest.php @@ -252,7 +252,7 @@ class ShareByMailProviderTest extends TestCase { ); } - public function testCreateSendPasswordByMailWithPasswordAndWithoutEnforcedPasswordProtection() { + public function testCreateSendPasswordByMailWithPasswordAndWithoutEnforcedPasswordProtectionWithPermanentPassword() { $share = $this->getMockBuilder(IShare::class)->getMock(); $share->expects($this->any())->method('getSharedWith')->willReturn('receiver@example.com'); $share->expects($this->any())->method('getSendPasswordByTalk')->willReturn(false); @@ -285,7 +285,7 @@ class ShareByMailProviderTest extends TestCase { ); } - public function testCreateSendPasswordByMailWithPasswordAndWithoutEnforcedPasswordProtectionWithPermanentPassword() { + public function testCreateSendPasswordByMailWithPasswordAndWithoutEnforcedPasswordProtectionWithoutPermanentPassword() { $share = $this->getMockBuilder(IShare::class)->getMock(); $share->expects($this->any())->method('getSharedWith')->willReturn('receiver@example.com'); $share->expects($this->any())->method('getSendPasswordByTalk')->willReturn(false); @@ -310,21 +310,16 @@ class ShareByMailProviderTest extends TestCase { // The given password (but not the autogenerated password) should be // mailed to the receiver of the share because permanent passwords are enforced. $this->shareManager->expects($this->any())->method('shareApiLinkEnforcePassword')->willReturn(false); - $this->config->expects($this->once())->method('getSystemValue')->with('sharing.enable_mail_link_password_expiration')->willReturn(true); - $this->settingsManager->expects($this->any())->method('sendPasswordByMail')->willReturn(true); $instance->expects($this->never())->method('autoGeneratePassword'); - - $message = $this->createMock(IMessage::class); - $message->expects($this->once())->method('setTo')->with(['receiver@example.com']); - $this->mailer->expects($this->once())->method('createMessage')->willReturn($message); - $this->mailer->expects($this->once())->method('createEMailTemplate')->with('sharebymail.RecipientPasswordNotification', [ - 'filename' => 'filename', - 'password' => 'password', - 'initiator' => 'owner', - 'initiatorEmail' => null, - 'shareWith' => 'receiver@example.com', - ]); - $this->mailer->expects($this->once())->method('send'); + $this->config->expects($this->any())->method('getSystemValue')->withConsecutive( + ['sharing.enable_mail_link_password_expiration'], + ['sharing.enable_mail_link_password_expiration'], + ['sharing.mail_link_password_expiration_interval'] + )->willReturnOnConsecutiveCalls( + true, + true, + 3600 + ); $this->assertSame('shareObject', $instance->create($share) @@ -363,7 +358,7 @@ class ShareByMailProviderTest extends TestCase { // The autogenerated password should be mailed to the receiver of the share because permanent passwords are enforced. $this->shareManager->expects($this->any())->method('shareApiLinkEnforcePassword')->willReturn(true); - $this->config->expects($this->once())->method('getSystemValue')->with('sharing.enable_mail_link_password_expiration')->willReturn(true); + $this->config->expects($this->any())->method('getSystemValue')->with('sharing.enable_mail_link_password_expiration')->willReturn(false); $this->settingsManager->expects($this->any())->method('sendPasswordByMail')->willReturn(true); $message = $this->createMock(IMessage::class); @@ -408,7 +403,7 @@ class ShareByMailProviderTest extends TestCase { // The given password (but not the autogenerated password) should be // mailed to the receiver of the share. $this->shareManager->expects($this->any())->method('shareApiLinkEnforcePassword')->willReturn(true); - $this->config->expects($this->once())->method('getSystemValue')->with('sharing.enable_mail_link_password_expiration')->willReturn(true); + $this->config->expects($this->any())->method('getSystemValue')->with('sharing.enable_mail_link_password_expiration')->willReturn(false); $this->settingsManager->expects($this->any())->method('sendPasswordByMail')->willReturn(true); $instance->expects($this->never())->method('autoGeneratePassword'); @@ -429,7 +424,7 @@ class ShareByMailProviderTest extends TestCase { ); } - public function testCreateSendPasswordByTalkWithEnforcedPasswordProtection() { + public function testCreateSendPasswordByTalkWithEnforcedPasswordProtectionWithPermanentPassword() { $share = $this->getMockBuilder(IShare::class)->getMock(); $share->expects($this->any())->method('getSharedWith')->willReturn('receiver@example.com'); $share->expects($this->any())->method('getSendPasswordByTalk')->willReturn(true); @@ -453,7 +448,7 @@ class ShareByMailProviderTest extends TestCase { // The autogenerated password should be mailed to the owner of the share. $this->shareManager->expects($this->any())->method('shareApiLinkEnforcePassword')->willReturn(true); - $this->config->expects($this->once())->method('getSystemValue')->with('sharing.enable_mail_link_password_expiration')->willReturn(false); + $this->config->expects($this->any())->method('getSystemValue')->with('sharing.enable_mail_link_password_expiration')->willReturn(false); $this->settingsManager->expects($this->any())->method('sendPasswordByMail')->willReturn(true); $instance->expects($this->once())->method('autoGeneratePassword')->with($share)->willReturn('autogeneratedPassword'); diff --git a/apps/theming/src/UserThemes.vue b/apps/theming/src/UserThemes.vue index ee514f049a5..f3e51f0a375 100644 --- a/apps/theming/src/UserThemes.vue +++ b/apps/theming/src/UserThemes.vue @@ -81,7 +81,7 @@ export default { descriptionDetail() { return t( 'theming', - 'If you find any issues, don’t hesitate to report them on {issuetracker}our issue tracker{linkend}. And if you want to get involved, come join {designteam}our design team{linkend}!' + 'If you find any issues, do not hesitate to report them on {issuetracker}our issue tracker{linkend}. And if you want to get involved, come join {designteam}our design team{linkend}!' ) .replace('{issuetracker}', this.issuetrackerLink) .replace('{designteam}', this.designteamLink) diff --git a/apps/user_ldap/l10n/ja.js b/apps/user_ldap/l10n/ja.js index bce2a9dfcea..59024d7253a 100644 --- a/apps/user_ldap/l10n/ja.js +++ b/apps/user_ldap/l10n/ja.js @@ -180,6 +180,7 @@ OC.L10N.register( "\"$home\" Placeholder Field" : "\"$home\" 属性設定", "$home in an external storage configuration will be replaced with the value of the specified attribute" : "外部ストレージ設定の $home 変数には、指定した属性の値が入ります", "Internal Username" : "内部ユーザー名", + "By default the internal username will be created from the UUID attribute. It makes sure that the username is unique and characters do not need to be converted. The internal username has the restriction that only these characters are allowed: [a-zA-Z0-9_.@-]. Other characters are replaced with their ASCII correspondence or simply omitted. On collisions a number will be added/increased. The internal username is used to identify a user internally. It is also the default name for the user home folder. It is also a part of remote URLs, for instance for all DAV services. With this setting, the default behavior can be overridden. Changes will have effect only on newly mapped (added) LDAP users. Leave it empty for default behavior." : "デフォルトでは、内部的なユーザー名がUUID属性から作成されます。これにより、ユーザー名がユニークであり、かつ文字の変換が不要であることを保証します。内部ユーザー名には、[ a-zA-Z0-9_.@- ] の文字のみが有効であるという制限があり、その他の文字は対応する ASCII コードに変換されるか単に無視されます。そのため、他のユーザー名との衝突の回数が増加するでしょう。内部ユーザー名は、内部的にユーザーを識別するために用いられ、また、Nextcloud におけるデフォルトのホームフォルダー名としても用いられます。例えば*DAVサービスのように、リモートURLの一部でもあります。この設定により、デフォルトの振る舞いを再定義します。これは、たとえばすべての* DAVサービスのリモートURLの一部でもあります。この設定を使用すると、デフォルトの動作を上書きできます。変更は、新しくマップされた(追加された)LDAPユーザーにのみ影響します。デフォルトの動作のために空のままにします。", "Internal Username Attribute:" : "内部ユーザー名属性:", "Override UUID detection" : "UUID検出を再定義する", "By default, the UUID attribute is automatically detected. The UUID attribute is used to doubtlessly identify LDAP users and groups. Also, the internal username will be created based on the UUID, if not specified otherwise above. You can override the setting and pass an attribute of your choice. You must make sure that the attribute of your choice can be fetched for both users and groups and it is unique. Leave it empty for default behavior. Changes will have effect only on newly mapped (added) LDAP users and groups." : "デフォルトでは、UUID 属性は自動的に検出されます。UUID属性は、LDAPユーザーとLDAPグループを間違いなく識別するために利用されます。また、もしこれを指定しない場合は、内部ユーザー名はUUIDに基づいて作成されます。この設定は再定義することができ、あなたの選択した属性を用いることができます。選択した属性がユーザーとグループの両方に対して適用でき、かつユニークであることを確認してください。空であればデフォルトの振る舞いとなります。変更は、新しくマッピング(追加)されたLDAPユーザーとLDAPグループに対してのみ有効となります。", diff --git a/apps/user_ldap/l10n/ja.json b/apps/user_ldap/l10n/ja.json index 2f096353bd8..a2bc3982e49 100644 --- a/apps/user_ldap/l10n/ja.json +++ b/apps/user_ldap/l10n/ja.json @@ -178,6 +178,7 @@ "\"$home\" Placeholder Field" : "\"$home\" 属性設定", "$home in an external storage configuration will be replaced with the value of the specified attribute" : "外部ストレージ設定の $home 変数には、指定した属性の値が入ります", "Internal Username" : "内部ユーザー名", + "By default the internal username will be created from the UUID attribute. It makes sure that the username is unique and characters do not need to be converted. The internal username has the restriction that only these characters are allowed: [a-zA-Z0-9_.@-]. Other characters are replaced with their ASCII correspondence or simply omitted. On collisions a number will be added/increased. The internal username is used to identify a user internally. It is also the default name for the user home folder. It is also a part of remote URLs, for instance for all DAV services. With this setting, the default behavior can be overridden. Changes will have effect only on newly mapped (added) LDAP users. Leave it empty for default behavior." : "デフォルトでは、内部的なユーザー名がUUID属性から作成されます。これにより、ユーザー名がユニークであり、かつ文字の変換が不要であることを保証します。内部ユーザー名には、[ a-zA-Z0-9_.@- ] の文字のみが有効であるという制限があり、その他の文字は対応する ASCII コードに変換されるか単に無視されます。そのため、他のユーザー名との衝突の回数が増加するでしょう。内部ユーザー名は、内部的にユーザーを識別するために用いられ、また、Nextcloud におけるデフォルトのホームフォルダー名としても用いられます。例えば*DAVサービスのように、リモートURLの一部でもあります。この設定により、デフォルトの振る舞いを再定義します。これは、たとえばすべての* DAVサービスのリモートURLの一部でもあります。この設定を使用すると、デフォルトの動作を上書きできます。変更は、新しくマップされた(追加された)LDAPユーザーにのみ影響します。デフォルトの動作のために空のままにします。", "Internal Username Attribute:" : "内部ユーザー名属性:", "Override UUID detection" : "UUID検出を再定義する", "By default, the UUID attribute is automatically detected. The UUID attribute is used to doubtlessly identify LDAP users and groups. Also, the internal username will be created based on the UUID, if not specified otherwise above. You can override the setting and pass an attribute of your choice. You must make sure that the attribute of your choice can be fetched for both users and groups and it is unique. Leave it empty for default behavior. Changes will have effect only on newly mapped (added) LDAP users and groups." : "デフォルトでは、UUID 属性は自動的に検出されます。UUID属性は、LDAPユーザーとLDAPグループを間違いなく識別するために利用されます。また、もしこれを指定しない場合は、内部ユーザー名はUUIDに基づいて作成されます。この設定は再定義することができ、あなたの選択した属性を用いることができます。選択した属性がユーザーとグループの両方に対して適用でき、かつユニークであることを確認してください。空であればデフォルトの振る舞いとなります。変更は、新しくマッピング(追加)されたLDAPユーザーとLDAPグループに対してのみ有効となります。", diff --git a/apps/weather_status/l10n/ja.js b/apps/weather_status/l10n/ja.js index 8d8919f894c..8d00b1a96cf 100644 --- a/apps/weather_status/l10n/ja.js +++ b/apps/weather_status/l10n/ja.js @@ -11,6 +11,28 @@ OC.L10N.register( "Detect location" : "地域を検出", "Set custom address" : "地域を入力", "Favorites" : "お気に入り", + "{temperature} {unit} clear sky later today" : "{temperature} {unit} 今日遅く快晴", + "{temperature} {unit} clear sky" : "{temperature} {unit} 快晴", + "{temperature} {unit} cloudy later today" : "{temperature} {unit} 今日遅く曇り", + "{temperature} {unit} cloudy" : "{temperature} {unit} 曇り", + "{temperature} {unit} fair weather later today" : "{temperature} {unit} 今日遅く晴れ", + "{temperature} {unit} fair weather" : "{temperature} {unit} 晴れ", + "{temperature} {unit} partly cloudy later today" : "{temperature} {unit} 今日遅く所々曇り", + "{temperature} {unit} partly cloudy" : "{temperature} {unit} 所々曇り", + "{temperature} {unit} foggy later today" : "{temperature} {unit} 今日遅く霧", + "{temperature} {unit} foggy" : "{temperature} {unit} 霧", + "{temperature} {unit} light rainfall later today" : "{temperature} {unit} 今日遅く小雨", + "{temperature} {unit} light rainfall" : "{temperature} {unit} 小雨", + "{temperature} {unit} rainfall later today" : "{temperature} {unit} 今日遅く雨", + "{temperature} {unit} rainfall" : "{temperature} {unit} 雨", + "{temperature} {unit} heavy rainfall later today" : "{temperature} {unit} 今日遅く大雨", + "{temperature} {unit} heavy rainfall" : "{temperature} {unit} 大雨", + "{temperature} {unit} rainfall showers later today" : "{temperature} {unit} 今日遅くにわか雨", + "{temperature} {unit} rainfall showers" : "{temperature} {unit} にわか雨", + "{temperature} {unit} light rainfall showers later today" : "{temperature} {unit} 今日遅く小雨", + "{temperature} {unit} light rainfall showers" : "{temperature} {unit} 小雨", + "{temperature} {unit} heavy rainfall showers later today" : "{temperature} {unit} 今日遅く大雨", + "{temperature} {unit} heavy rainfall showers" : "{temperature} {unit} 大雨", "More weather for {adr}" : "{adr} の詳しい天気", "Loading weather" : "天気を読み込み中", "Remove from favorites" : "お気に入りから削除", diff --git a/apps/weather_status/l10n/ja.json b/apps/weather_status/l10n/ja.json index 4c5d826c9d8..b70b4baed75 100644 --- a/apps/weather_status/l10n/ja.json +++ b/apps/weather_status/l10n/ja.json @@ -9,6 +9,28 @@ "Detect location" : "地域を検出", "Set custom address" : "地域を入力", "Favorites" : "お気に入り", + "{temperature} {unit} clear sky later today" : "{temperature} {unit} 今日遅く快晴", + "{temperature} {unit} clear sky" : "{temperature} {unit} 快晴", + "{temperature} {unit} cloudy later today" : "{temperature} {unit} 今日遅く曇り", + "{temperature} {unit} cloudy" : "{temperature} {unit} 曇り", + "{temperature} {unit} fair weather later today" : "{temperature} {unit} 今日遅く晴れ", + "{temperature} {unit} fair weather" : "{temperature} {unit} 晴れ", + "{temperature} {unit} partly cloudy later today" : "{temperature} {unit} 今日遅く所々曇り", + "{temperature} {unit} partly cloudy" : "{temperature} {unit} 所々曇り", + "{temperature} {unit} foggy later today" : "{temperature} {unit} 今日遅く霧", + "{temperature} {unit} foggy" : "{temperature} {unit} 霧", + "{temperature} {unit} light rainfall later today" : "{temperature} {unit} 今日遅く小雨", + "{temperature} {unit} light rainfall" : "{temperature} {unit} 小雨", + "{temperature} {unit} rainfall later today" : "{temperature} {unit} 今日遅く雨", + "{temperature} {unit} rainfall" : "{temperature} {unit} 雨", + "{temperature} {unit} heavy rainfall later today" : "{temperature} {unit} 今日遅く大雨", + "{temperature} {unit} heavy rainfall" : "{temperature} {unit} 大雨", + "{temperature} {unit} rainfall showers later today" : "{temperature} {unit} 今日遅くにわか雨", + "{temperature} {unit} rainfall showers" : "{temperature} {unit} にわか雨", + "{temperature} {unit} light rainfall showers later today" : "{temperature} {unit} 今日遅く小雨", + "{temperature} {unit} light rainfall showers" : "{temperature} {unit} 小雨", + "{temperature} {unit} heavy rainfall showers later today" : "{temperature} {unit} 今日遅く大雨", + "{temperature} {unit} heavy rainfall showers" : "{temperature} {unit} 大雨", "More weather for {adr}" : "{adr} の詳しい天気", "Loading weather" : "天気を読み込み中", "Remove from favorites" : "お気に入りから削除", |