diff options
Diffstat (limited to 'apps')
103 files changed, 809 insertions, 207 deletions
diff --git a/apps/admin_audit/l10n/ast.js b/apps/admin_audit/l10n/ast.js new file mode 100644 index 00000000000..862f01ddb6d --- /dev/null +++ b/apps/admin_audit/l10n/ast.js @@ -0,0 +1,7 @@ +OC.L10N.register( + "admin_audit", + { + "Auditing / Logging" : "Auditoría / Rexistru", + "Provides logging abilities for Nextcloud such as logging file accesses or otherwise sensitive actions." : "Forne la capacidá de rexistrar, por exemplu, l'accesu a los ficheros o a otres aiciones sensibles de Nextcloud" +}, +"nplurals=2; plural=(n != 1);"); diff --git a/apps/admin_audit/l10n/ast.json b/apps/admin_audit/l10n/ast.json new file mode 100644 index 00000000000..61715be4851 --- /dev/null +++ b/apps/admin_audit/l10n/ast.json @@ -0,0 +1,5 @@ +{ "translations": { + "Auditing / Logging" : "Auditoría / Rexistru", + "Provides logging abilities for Nextcloud such as logging file accesses or otherwise sensitive actions." : "Forne la capacidá de rexistrar, por exemplu, l'accesu a los ficheros o a otres aiciones sensibles de Nextcloud" +},"pluralForm" :"nplurals=2; plural=(n != 1);" +}
\ No newline at end of file diff --git a/apps/admin_audit/l10n/eo.js b/apps/admin_audit/l10n/eo.js new file mode 100644 index 00000000000..3ab916b0b54 --- /dev/null +++ b/apps/admin_audit/l10n/eo.js @@ -0,0 +1,7 @@ +OC.L10N.register( + "admin_audit", + { + "Auditing / Logging" : "Ekzamenado / Protokolado", + "Provides logging abilities for Nextcloud such as logging file accesses or otherwise sensitive actions." : "Provizi protokolado-kapablojn por Nextcloud, kiel ekzemple protokolado de aliroj al dosieroj aŭ aliaj delikataj agoj." +}, +"nplurals=2; plural=(n != 1);"); diff --git a/apps/admin_audit/l10n/eo.json b/apps/admin_audit/l10n/eo.json new file mode 100644 index 00000000000..eef1c6821c5 --- /dev/null +++ b/apps/admin_audit/l10n/eo.json @@ -0,0 +1,5 @@ +{ "translations": { + "Auditing / Logging" : "Ekzamenado / Protokolado", + "Provides logging abilities for Nextcloud such as logging file accesses or otherwise sensitive actions." : "Provizi protokolado-kapablojn por Nextcloud, kiel ekzemple protokolado de aliroj al dosieroj aŭ aliaj delikataj agoj." +},"pluralForm" :"nplurals=2; plural=(n != 1);" +}
\ No newline at end of file diff --git a/apps/cloud_federation_api/l10n/eo.js b/apps/cloud_federation_api/l10n/eo.js new file mode 100644 index 00000000000..d01810bba74 --- /dev/null +++ b/apps/cloud_federation_api/l10n/eo.js @@ -0,0 +1,8 @@ +OC.L10N.register( + "cloud_federation_api", + { + "Cloud Federation API" : "API de Nuba Federacio", + "Enable clouds to communicate with each other and exchange data" : "Ebligi interkomunikadon inter diversaj nuboj kaj la interŝanĝon de datumoj", + "The Cloud Federation API enables various Nextcloud instances to communicate with each other and to exchange data." : "La API de Nuba Federacio ebligas diversajn instancojn de Nextcloud interkomuniki kaj interŝanĝi datumojn inter si." +}, +"nplurals=2; plural=(n != 1);"); diff --git a/apps/cloud_federation_api/l10n/eo.json b/apps/cloud_federation_api/l10n/eo.json new file mode 100644 index 00000000000..17dd80538d8 --- /dev/null +++ b/apps/cloud_federation_api/l10n/eo.json @@ -0,0 +1,6 @@ +{ "translations": { + "Cloud Federation API" : "API de Nuba Federacio", + "Enable clouds to communicate with each other and exchange data" : "Ebligi interkomunikadon inter diversaj nuboj kaj la interŝanĝon de datumoj", + "The Cloud Federation API enables various Nextcloud instances to communicate with each other and to exchange data." : "La API de Nuba Federacio ebligas diversajn instancojn de Nextcloud interkomuniki kaj interŝanĝi datumojn inter si." +},"pluralForm" :"nplurals=2; plural=(n != 1);" +}
\ No newline at end of file diff --git a/apps/dashboard/l10n/uk.js b/apps/dashboard/l10n/uk.js index ec06ae3cfdd..93bbf92c540 100644 --- a/apps/dashboard/l10n/uk.js +++ b/apps/dashboard/l10n/uk.js @@ -3,6 +3,8 @@ OC.L10N.register( { "Dashboard" : "Майстерня", "Dashboard app" : "Застосунок з панеллю віджетів ", + "Start your day informed\n\nThe Nextcloud Dashboard is your starting point of the day, giving you an overview of your upcoming appointments, urgent emails, chat messages, incoming tickets, latest tweets and much more! Users can add the widgets they like and change the background to their liking." : "Почніть свій день поінформовано\n\nІнформаційна панель Nextcloud - це ваша відправна точка дня, яка дає вам огляд майбутніх зустрічей, термінових електронних листів, повідомлень в чаті, призначених завдань, останніх твітів і багато іншого! Користувачі можуть додавати віджети, які їм подобаються, і змінювати фон на свій смак.", + "\"{title} icon\"" : "\"{title} \"іконка\"", "Customize" : "Редагувати", "Edit widgets" : "Редагувати віджети", "Get more widgets from the App Store" : "Більше віджетів у каталозі застосунків", diff --git a/apps/dashboard/l10n/uk.json b/apps/dashboard/l10n/uk.json index 7d6d51956c3..decc964d3f4 100644 --- a/apps/dashboard/l10n/uk.json +++ b/apps/dashboard/l10n/uk.json @@ -1,6 +1,8 @@ { "translations": { "Dashboard" : "Майстерня", "Dashboard app" : "Застосунок з панеллю віджетів ", + "Start your day informed\n\nThe Nextcloud Dashboard is your starting point of the day, giving you an overview of your upcoming appointments, urgent emails, chat messages, incoming tickets, latest tweets and much more! Users can add the widgets they like and change the background to their liking." : "Почніть свій день поінформовано\n\nІнформаційна панель Nextcloud - це ваша відправна точка дня, яка дає вам огляд майбутніх зустрічей, термінових електронних листів, повідомлень в чаті, призначених завдань, останніх твітів і багато іншого! Користувачі можуть додавати віджети, які їм подобаються, і змінювати фон на свій смак.", + "\"{title} icon\"" : "\"{title} \"іконка\"", "Customize" : "Редагувати", "Edit widgets" : "Редагувати віджети", "Get more widgets from the App Store" : "Більше віджетів у каталозі застосунків", diff --git a/apps/dav/appinfo/info.xml b/apps/dav/appinfo/info.xml index b37e73fa5b6..9140c674716 100644 --- a/apps/dav/appinfo/info.xml +++ b/apps/dav/appinfo/info.xml @@ -5,7 +5,7 @@ <name>WebDAV</name> <summary>WebDAV endpoint</summary> <description>WebDAV endpoint</description> - <version>1.26.0</version> + <version>1.27.0</version> <licence>agpl</licence> <author>owncloud.org</author> <namespace>DAV</namespace> diff --git a/apps/dav/composer/composer/autoload_classmap.php b/apps/dav/composer/composer/autoload_classmap.php index ab7d3e71928..2cbf361eaf7 100644 --- a/apps/dav/composer/composer/autoload_classmap.php +++ b/apps/dav/composer/composer/autoload_classmap.php @@ -293,6 +293,7 @@ return array( 'OCA\\DAV\\Migration\\Version1017Date20210216083742' => $baseDir . '/../lib/Migration/Version1017Date20210216083742.php', 'OCA\\DAV\\Migration\\Version1018Date20210312100735' => $baseDir . '/../lib/Migration/Version1018Date20210312100735.php', 'OCA\\DAV\\Migration\\Version1024Date20211221144219' => $baseDir . '/../lib/Migration/Version1024Date20211221144219.php', + 'OCA\\DAV\\Migration\\Version1027Date20230504122946' => $baseDir . '/../lib/Migration/Version1027Date20230504122946.php', 'OCA\\DAV\\Profiler\\ProfilerPlugin' => $baseDir . '/../lib/Profiler/ProfilerPlugin.php', 'OCA\\DAV\\Provisioning\\Apple\\AppleProvisioningNode' => $baseDir . '/../lib/Provisioning/Apple/AppleProvisioningNode.php', 'OCA\\DAV\\Provisioning\\Apple\\AppleProvisioningPlugin' => $baseDir . '/../lib/Provisioning/Apple/AppleProvisioningPlugin.php', diff --git a/apps/dav/composer/composer/autoload_static.php b/apps/dav/composer/composer/autoload_static.php index e0e1f86bdbb..f939841f2e2 100644 --- a/apps/dav/composer/composer/autoload_static.php +++ b/apps/dav/composer/composer/autoload_static.php @@ -308,6 +308,7 @@ class ComposerStaticInitDAV 'OCA\\DAV\\Migration\\Version1017Date20210216083742' => __DIR__ . '/..' . '/../lib/Migration/Version1017Date20210216083742.php', 'OCA\\DAV\\Migration\\Version1018Date20210312100735' => __DIR__ . '/..' . '/../lib/Migration/Version1018Date20210312100735.php', 'OCA\\DAV\\Migration\\Version1024Date20211221144219' => __DIR__ . '/..' . '/../lib/Migration/Version1024Date20211221144219.php', + 'OCA\\DAV\\Migration\\Version1027Date20230504122946' => __DIR__ . '/..' . '/../lib/Migration/Version1027Date20230504122946.php', 'OCA\\DAV\\Profiler\\ProfilerPlugin' => __DIR__ . '/..' . '/../lib/Profiler/ProfilerPlugin.php', 'OCA\\DAV\\Provisioning\\Apple\\AppleProvisioningNode' => __DIR__ . '/..' . '/../lib/Provisioning/Apple/AppleProvisioningNode.php', 'OCA\\DAV\\Provisioning\\Apple\\AppleProvisioningPlugin' => __DIR__ . '/..' . '/../lib/Provisioning/Apple/AppleProvisioningPlugin.php', diff --git a/apps/dav/l10n/it.js b/apps/dav/l10n/it.js index d4f12d7ff44..d394091ac15 100644 --- a/apps/dav/l10n/it.js +++ b/apps/dav/l10n/it.js @@ -125,6 +125,7 @@ OC.L10N.register( "Due on %s by %s" : "Scade il %s per %s", "Due on %s" : "Scade il %s", "Migrated calendar (%1$s)" : "Calendario migrato (%1$s)", + "Calendars including events, details and attendees" : "Calendari inclusi eventi, dettagli e partecipanti", "Contacts and groups" : "Contatti e gruppi", "WebDAV" : "WebDAV", "WebDAV endpoint" : "Terminatore WebDAV", diff --git a/apps/dav/l10n/it.json b/apps/dav/l10n/it.json index 0c04c8960be..9f0195aee28 100644 --- a/apps/dav/l10n/it.json +++ b/apps/dav/l10n/it.json @@ -123,6 +123,7 @@ "Due on %s by %s" : "Scade il %s per %s", "Due on %s" : "Scade il %s", "Migrated calendar (%1$s)" : "Calendario migrato (%1$s)", + "Calendars including events, details and attendees" : "Calendari inclusi eventi, dettagli e partecipanti", "Contacts and groups" : "Contatti e gruppi", "WebDAV" : "WebDAV", "WebDAV endpoint" : "Terminatore WebDAV", diff --git a/apps/dav/l10n/pl.js b/apps/dav/l10n/pl.js index d0454c424da..af760bd33d1 100644 --- a/apps/dav/l10n/pl.js +++ b/apps/dav/l10n/pl.js @@ -75,6 +75,9 @@ OC.L10N.register( "Cancelled: %1$s" : "Anulowane: %1$s", "\"%1$s\" has been canceled" : "\"%1$s\" zostało anulowane", "Re: %1$s" : "Odp: %1$s", + "%1$s has accepted your invitation" : "Twoje zaproszenie zostało zaakceptowane przez %1$s", + "%1$s has tentatively accepted your invitation" : "Twoje zaproszenie zostało wstępnie zaakceptowane przez %1$s", + "%1$s has declined your invitation" : "Twoje zaproszenie zostało odrzucone przez %1$s", "%1$s has responded to your invitation" : "%1$s odpowiedział/a na Twoje zaproszenie", "Invitation updated: %1$s" : "Zaktualizowano zaproszenie: %1$s", "%1$s updated the event \"%2$s\"" : "%1$s zaktualizował/a wydarzenie \"%2$s\"", diff --git a/apps/dav/l10n/pl.json b/apps/dav/l10n/pl.json index 2560da78ef1..5332f810cac 100644 --- a/apps/dav/l10n/pl.json +++ b/apps/dav/l10n/pl.json @@ -73,6 +73,9 @@ "Cancelled: %1$s" : "Anulowane: %1$s", "\"%1$s\" has been canceled" : "\"%1$s\" zostało anulowane", "Re: %1$s" : "Odp: %1$s", + "%1$s has accepted your invitation" : "Twoje zaproszenie zostało zaakceptowane przez %1$s", + "%1$s has tentatively accepted your invitation" : "Twoje zaproszenie zostało wstępnie zaakceptowane przez %1$s", + "%1$s has declined your invitation" : "Twoje zaproszenie zostało odrzucone przez %1$s", "%1$s has responded to your invitation" : "%1$s odpowiedział/a na Twoje zaproszenie", "Invitation updated: %1$s" : "Zaktualizowano zaproszenie: %1$s", "%1$s updated the event \"%2$s\"" : "%1$s zaktualizował/a wydarzenie \"%2$s\"", diff --git a/apps/dav/l10n/uk.js b/apps/dav/l10n/uk.js index 965f3dd3f50..4ee15546809 100644 --- a/apps/dav/l10n/uk.js +++ b/apps/dav/l10n/uk.js @@ -75,6 +75,9 @@ OC.L10N.register( "Cancelled: %1$s" : "Скасовано: %1$s", "\"%1$s\" has been canceled" : "\"%1$s\" скасовано", "Re: %1$s" : "Re: %1$s", + "%1$s has accepted your invitation" : "%1$s прийняв ваше запрошення", + "%1$s has tentatively accepted your invitation" : "%1$s попередньо прийняв ваше запрошення", + "%1$s has declined your invitation" : "%1$s відмовився від вашого запрошення", "%1$s has responded to your invitation" : "%1$sвідповів(-ла) на ваше запрошення", "Invitation updated: %1$s" : "Запрошення оновлено: %1$s", "%1$s updated the event \"%2$s\"" : "%1$s оновив подію \"%2$s\"", diff --git a/apps/dav/l10n/uk.json b/apps/dav/l10n/uk.json index c06144693b7..33f9363dbb6 100644 --- a/apps/dav/l10n/uk.json +++ b/apps/dav/l10n/uk.json @@ -73,6 +73,9 @@ "Cancelled: %1$s" : "Скасовано: %1$s", "\"%1$s\" has been canceled" : "\"%1$s\" скасовано", "Re: %1$s" : "Re: %1$s", + "%1$s has accepted your invitation" : "%1$s прийняв ваше запрошення", + "%1$s has tentatively accepted your invitation" : "%1$s попередньо прийняв ваше запрошення", + "%1$s has declined your invitation" : "%1$s відмовився від вашого запрошення", "%1$s has responded to your invitation" : "%1$sвідповів(-ла) на ваше запрошення", "Invitation updated: %1$s" : "Запрошення оновлено: %1$s", "%1$s updated the event \"%2$s\"" : "%1$s оновив подію \"%2$s\"", diff --git a/apps/dav/lib/CalDAV/Reminder/ReminderService.php b/apps/dav/lib/CalDAV/Reminder/ReminderService.php index bca154a48e8..984e29f1e4d 100644 --- a/apps/dav/lib/CalDAV/Reminder/ReminderService.php +++ b/apps/dav/lib/CalDAV/Reminder/ReminderService.php @@ -179,7 +179,7 @@ class ReminderService { continue; } - if ($this->config->getAppValue('dav', 'sendEventRemindersToSharedGroupMembers', 'yes') === 'no') { + if ($this->config->getAppValue('dav', 'sendEventRemindersToSharedUsers', 'yes') === 'no') { $users = $this->getAllUsersWithWriteAccessToCalendar($reminder['calendar_id']); } else { $users = []; diff --git a/apps/dav/lib/CalDAV/Schedule/IMipPlugin.php b/apps/dav/lib/CalDAV/Schedule/IMipPlugin.php index 329197445dd..e164e420ae3 100644 --- a/apps/dav/lib/CalDAV/Schedule/IMipPlugin.php +++ b/apps/dav/lib/CalDAV/Schedule/IMipPlugin.php @@ -299,14 +299,12 @@ class IMipPlugin extends SabreIMipPlugin { $message->useTemplate($template); - $vCalendar = $this->imipService->generateVCalendar($iTipMessage, $vEvent); - - $attachment = $this->mailer->createAttachment( - $vCalendar->serialize(), + $itip_msg = $iTipMessage->message->serialize(); + $message->attachInline( + $itip_msg, 'event.ics', - 'text/calendar; method=' . $iTipMessage->method + 'text/calendar; method=' . $iTipMessage->method, ); - $message->attach($attachment); try { $failed = $this->mailer->send($message); diff --git a/apps/dav/lib/CardDAV/Converter.php b/apps/dav/lib/CardDAV/Converter.php index 4c7d6f9075f..e35bc41abd2 100644 --- a/apps/dav/lib/CardDAV/Converter.php +++ b/apps/dav/lib/CardDAV/Converter.php @@ -35,7 +35,6 @@ use Sabre\VObject\Component\VCard; use Sabre\VObject\Property\Text; class Converter { - /** @var IAccountManager */ private $accountManager; @@ -44,7 +43,7 @@ class Converter { } public function createCardFromUser(IUser $user): ?VCard { - $userProperties = $this->accountManager->getAccount($user)->getProperties(); + $userProperties = $this->accountManager->getAccount($user)->getAllProperties(); $uid = $user->getUID(); $cloudId = $user->getCloudId(); @@ -57,46 +56,49 @@ class Converter { $publish = false; foreach ($userProperties as $property) { - $shareWithTrustedServers = - $property->getScope() === IAccountManager::SCOPE_FEDERATED || - $property->getScope() === IAccountManager::SCOPE_PUBLISHED; - - $emptyValue = $property->getValue() === ''; - - if ($shareWithTrustedServers && !$emptyValue) { - $publish = true; - switch ($property->getName()) { - case IAccountManager::PROPERTY_DISPLAYNAME: - $vCard->add(new Text($vCard, 'FN', $property->getValue())); - $vCard->add(new Text($vCard, 'N', $this->splitFullName($property->getValue()))); - break; - case IAccountManager::PROPERTY_AVATAR: - if ($image !== null) { - $vCard->add('PHOTO', $image->data(), ['ENCODING' => 'b', 'TYPE' => $image->mimeType()]); - } - break; - case IAccountManager::PROPERTY_EMAIL: - $vCard->add(new Text($vCard, 'EMAIL', $property->getValue(), ['TYPE' => 'OTHER'])); - break; - case IAccountManager::PROPERTY_WEBSITE: - $vCard->add(new Text($vCard, 'URL', $property->getValue())); - break; - case IAccountManager::PROPERTY_PHONE: - $vCard->add(new Text($vCard, 'TEL', $property->getValue(), ['TYPE' => 'OTHER'])); - break; - case IAccountManager::PROPERTY_ADDRESS: - $vCard->add(new Text($vCard, 'ADR', $property->getValue(), ['TYPE' => 'OTHER'])); - break; - case IAccountManager::PROPERTY_TWITTER: - $vCard->add(new Text($vCard, 'X-SOCIALPROFILE', $property->getValue(), ['TYPE' => 'TWITTER'])); - break; - case IAccountManager::PROPERTY_ORGANISATION: - $vCard->add(new Text($vCard, 'ORG', $property->getValue())); - break; - case IAccountManager::PROPERTY_ROLE: - $vCard->add(new Text($vCard, 'TITLE', $property->getValue())); - break; - } + if (empty($property->getValue())) { + continue; + } + + $scope = $property->getScope(); + // Do not write private data to the system address book at all + if ($scope === IAccountManager::SCOPE_PRIVATE || empty($scope)) { + continue; + } + + $publish = true; + switch ($property->getName()) { + case IAccountManager::PROPERTY_DISPLAYNAME: + $vCard->add(new Text($vCard, 'FN', $property->getValue(), ['X-NC-SCOPE' => $scope])); + $vCard->add(new Text($vCard, 'N', $this->splitFullName($property->getValue()), ['X-NC-SCOPE' => $scope])); + break; + case IAccountManager::PROPERTY_AVATAR: + if ($image !== null) { + $vCard->add('PHOTO', $image->data(), ['ENCODING' => 'b', 'TYPE' => $image->mimeType(), ['X-NC-SCOPE' => $scope]]); + } + break; + case IAccountManager::COLLECTION_EMAIL: + case IAccountManager::PROPERTY_EMAIL: + $vCard->add(new Text($vCard, 'EMAIL', $property->getValue(), ['TYPE' => 'OTHER', 'X-NC-SCOPE' => $scope])); + break; + case IAccountManager::PROPERTY_WEBSITE: + $vCard->add(new Text($vCard, 'URL', $property->getValue(), ['X-NC-SCOPE' => $scope])); + break; + case IAccountManager::PROPERTY_PHONE: + $vCard->add(new Text($vCard, 'TEL', $property->getValue(), ['TYPE' => 'OTHER', 'X-NC-SCOPE' => $scope])); + break; + case IAccountManager::PROPERTY_ADDRESS: + $vCard->add(new Text($vCard, 'ADR', $property->getValue(), ['TYPE' => 'OTHER', 'X-NC-SCOPE' => $scope])); + break; + case IAccountManager::PROPERTY_TWITTER: + $vCard->add(new Text($vCard, 'X-SOCIALPROFILE', $property->getValue(), ['TYPE' => 'TWITTER', 'X-NC-SCOPE' => $scope])); + break; + case IAccountManager::PROPERTY_ORGANISATION: + $vCard->add(new Text($vCard, 'ORG', $property->getValue(), ['X-NC-SCOPE' => $scope])); + break; + case IAccountManager::PROPERTY_ROLE: + $vCard->add(new Text($vCard, 'TITLE', $property->getValue(), ['X-NC-SCOPE' => $scope])); + break; } } diff --git a/apps/dav/lib/CardDAV/SystemAddressbook.php b/apps/dav/lib/CardDAV/SystemAddressbook.php index 502e353acb3..a803a1e6b24 100644 --- a/apps/dav/lib/CardDAV/SystemAddressbook.php +++ b/apps/dav/lib/CardDAV/SystemAddressbook.php @@ -27,20 +27,34 @@ declare(strict_types=1); */ namespace OCA\DAV\CardDAV; +use OCA\DAV\Exception\UnsupportedLimitOnInitialSyncException; +use OCA\Federation\TrustedServers; +use OCP\Accounts\IAccountManager; use OCP\IConfig; use OCP\IL10N; +use OCP\IRequest; +use Sabre\CardDAV\Backend\SyncSupport; use Sabre\CardDAV\Backend\BackendInterface; +use Sabre\CardDAV\Card; +use Sabre\DAV\Exception\Forbidden; +use Sabre\DAV\Exception\NotFound; +use Sabre\VObject\Component\VCard; +use Sabre\VObject\Reader; class SystemAddressbook extends AddressBook { /** @var IConfig */ private $config; + private ?TrustedServers $trustedServers; + private ?IRequest $request; - public function __construct(BackendInterface $carddavBackend, array $addressBookInfo, IL10N $l10n, IConfig $config) { + public function __construct(BackendInterface $carddavBackend, array $addressBookInfo, IL10N $l10n, IConfig $config, ?IRequest $request = null, ?TrustedServers $trustedServers = null) { parent::__construct($carddavBackend, $addressBookInfo, $l10n); $this->config = $config; + $this->request = $request; + $this->trustedServers = $trustedServers; } - public function getChildren() { + public function getChildren(): array { $shareEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes'; $shareEnumerationGroup = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes'; $shareEnumerationPhone = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes'; @@ -50,4 +64,165 @@ class SystemAddressbook extends AddressBook { return parent::getChildren(); } + + /** + * @param array $paths + * @return Card[] + * @throws NotFound + */ + public function getMultipleChildren($paths): array { + if (!$this->isFederation()) { + return parent::getMultipleChildren($paths); + } + + $objs = $this->carddavBackend->getMultipleCards($this->addressBookInfo['id'], $paths); + $children = []; + /** @var array $obj */ + foreach ($objs as $obj) { + if (empty($obj)) { + continue; + } + $carddata = $this->extractCarddata($obj); + if (empty($carddata)) { + continue; + } else { + $obj['carddata'] = $carddata; + } + $children[] = new Card($this->carddavBackend, $this->addressBookInfo, $obj); + } + return $children; + } + + /** + * @param string $name + * @return Card + * @throws NotFound + * @throws Forbidden + */ + public function getChild($name): Card { + if (!$this->isFederation()) { + return parent::getChild($name); + } + + $obj = $this->carddavBackend->getCard($this->addressBookInfo['id'], $name); + if (!$obj) { + throw new NotFound('Card not found'); + } + $carddata = $this->extractCarddata($obj); + if (empty($carddata)) { + throw new Forbidden(); + } else { + $obj['carddata'] = $carddata; + } + return new Card($this->carddavBackend, $this->addressBookInfo, $obj); + } + + /** + * @throws UnsupportedLimitOnInitialSyncException + */ + public function getChanges($syncToken, $syncLevel, $limit = null) { + if (!$syncToken && $limit) { + throw new UnsupportedLimitOnInitialSyncException(); + } + + if (!$this->carddavBackend instanceof SyncSupport) { + return null; + } + + if (!$this->isFederation()) { + return parent::getChanges($syncToken, $syncLevel, $limit); + } + + $changed = $this->carddavBackend->getChangesForAddressBook( + $this->addressBookInfo['id'], + $syncToken, + $syncLevel, + $limit + ); + + if (empty($changed)) { + return $changed; + } + + $added = $modified = $deleted = []; + foreach ($changed['added'] as $uri) { + try { + $this->getChild($uri); + $added[] = $uri; + } catch (NotFound | Forbidden $e) { + $deleted[] = $uri; + } + } + foreach ($changed['modified'] as $uri) { + try { + $this->getChild($uri); + $modified[] = $uri; + } catch (NotFound | Forbidden $e) { + $deleted[] = $uri; + } + } + $changed['added'] = $added; + $changed['modified'] = $modified; + $changed['deleted'] = $deleted; + return $changed; + } + + private function isFederation(): bool { + if ($this->trustedServers === null || $this->request === null) { + return false; + } + + /** @psalm-suppress NoInterfaceProperties */ + if ($this->request->server['PHP_AUTH_USER'] !== 'system') { + return false; + } + + /** @psalm-suppress NoInterfaceProperties */ + $sharedSecret = $this->request->server['PHP_AUTH_PW']; + if ($sharedSecret === null) { + return false; + } + + $servers = $this->trustedServers->getServers(); + $trusted = array_filter($servers, function ($trustedServer) use ($sharedSecret) { + return $trustedServer['shared_secret'] === $sharedSecret; + }); + // Authentication is fine, but it's not for a federated share + if (empty($trusted)) { + return false; + } + + return true; + } + + /** + * If the validation doesn't work the card is "not found" so we + * return empty carddata even if the carddata might exist in the local backend. + * This can happen when a user sets the required properties + * FN, N to a local scope only but the request is from + * a federated share. + * + * @see https://github.com/nextcloud/server/issues/38042 + * + * @param array $obj + * @return string|null + */ + private function extractCarddata(array $obj): ?string { + $obj['acl'] = $this->getChildACL(); + $cardData = $obj['carddata']; + /** @var VCard $vCard */ + $vCard = Reader::read($cardData); + foreach ($vCard->children() as $child) { + $scope = $child->offsetGet('X-NC-SCOPE'); + if ($scope !== null && $scope->getValue() === IAccountManager::SCOPE_LOCAL) { + $vCard->remove($child); + } + } + $messages = $vCard->validate(); + if (!empty($messages)) { + return null; + } + + return $vCard->serialize(); + } } diff --git a/apps/dav/lib/CardDAV/UserAddressBooks.php b/apps/dav/lib/CardDAV/UserAddressBooks.php index 98957301120..85795604f28 100644 --- a/apps/dav/lib/CardDAV/UserAddressBooks.php +++ b/apps/dav/lib/CardDAV/UserAddressBooks.php @@ -30,8 +30,13 @@ namespace OCA\DAV\CardDAV; use OCA\DAV\AppInfo\PluginManager; use OCA\DAV\CardDAV\Integration\IAddressBookProvider; use OCA\DAV\CardDAV\Integration\ExternalAddressBook; +use OCA\Federation\TrustedServers; +use OCP\AppFramework\QueryException; use OCP\IConfig; use OCP\IL10N; +use OCP\IRequest; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; use Sabre\CardDAV\Backend; use Sabre\DAV\Exception\MethodNotAllowed; use Sabre\CardDAV\IAddressBook; @@ -73,7 +78,15 @@ class UserAddressBooks extends \Sabre\CardDAV\AddressBookHome { /** @var IAddressBook[] $objects */ $objects = array_map(function (array $addressBook) { if ($addressBook['principaluri'] === 'principals/system/system') { - return new SystemAddressbook($this->carddavBackend, $addressBook, $this->l10n, $this->config); + $trustedServers = null; + $request = null; + try { + $trustedServers = \OC::$server->get(TrustedServers::class); + $request = \OC::$server->get(IRequest::class); + } catch (NotFoundExceptionInterface | ContainerExceptionInterface $e) { + // nothing to do, the request / trusted servers don't exist + } + return new SystemAddressbook($this->carddavBackend, $addressBook, $this->l10n, $this->config, $request, $trustedServers); } return new AddressBook($this->carddavBackend, $addressBook, $this->l10n); diff --git a/apps/dav/lib/Connector/Sabre/File.php b/apps/dav/lib/Connector/Sabre/File.php index b0f17417d21..a7cafeb4a5e 100644 --- a/apps/dav/lib/Connector/Sabre/File.php +++ b/apps/dav/lib/Connector/Sabre/File.php @@ -422,14 +422,15 @@ class File extends Node implements IFile { } } - /** - * @param string $path - */ - private function emitPreHooks($exists, $path = null) { + private function emitPreHooks(bool $exists, ?string $path = null): bool { if (is_null($path)) { $path = $this->path; } $hookPath = Filesystem::getView()->getRelativePath($this->fileView->getAbsolutePath($path)); + if ($hookPath === null) { + // We only trigger hooks from inside default view + return true; + } $run = true; if (!$exists) { @@ -450,14 +451,15 @@ class File extends Node implements IFile { return $run; } - /** - * @param string $path - */ - private function emitPostHooks($exists, $path = null) { + private function emitPostHooks(bool $exists, ?string $path = null): void { if (is_null($path)) { $path = $this->path; } $hookPath = Filesystem::getView()->getRelativePath($this->fileView->getAbsolutePath($path)); + if ($hookPath === null) { + // We only trigger hooks from inside default view + return; + } if (!$exists) { \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_create, [ \OC\Files\Filesystem::signal_param_path => $hookPath diff --git a/apps/dav/lib/Migration/Version1027Date20230504122946.php b/apps/dav/lib/Migration/Version1027Date20230504122946.php new file mode 100644 index 00000000000..e9ae174f56e --- /dev/null +++ b/apps/dav/lib/Migration/Version1027Date20230504122946.php @@ -0,0 +1,54 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2023 Anna Larch <anna.larch@gmx.net> + * + * @author Anna Larch <anna.larch@gmx.net> + * + * @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\Migration; + +use Closure; +use OCA\DAV\CardDAV\SyncService; +use OCP\DB\ISchemaWrapper; +use OCP\Migration\IOutput; +use OCP\Migration\SimpleMigrationStep; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; +use Psr\Log\LoggerInterface; + +class Version1027Date20230504122946 extends SimpleMigrationStep { + private SyncService $syncService; + private LoggerInterface $logger; + + public function __construct(SyncService $syncService, LoggerInterface $logger) { + $this->syncService = $syncService; + $this->logger = $logger; + } + /** + * @param IOutput $output + * @param Closure(): ISchemaWrapper $schemaClosure + * @param array $options + */ + public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void { + $this->syncService->syncInstance(); + } +} diff --git a/apps/dav/lib/Settings/CalDAVSettings.php b/apps/dav/lib/Settings/CalDAVSettings.php index 6d60b2611e0..b3825a6a37c 100644 --- a/apps/dav/lib/Settings/CalDAVSettings.php +++ b/apps/dav/lib/Settings/CalDAVSettings.php @@ -46,7 +46,7 @@ class CalDAVSettings implements IDelegatedSettings { 'sendInvitations' => 'yes', 'generateBirthdayCalendar' => 'yes', 'sendEventReminders' => 'yes', - 'sendEventRemindersToSharedGroupMembers' => 'yes', + 'sendEventRemindersToSharedUsers' => 'yes', 'sendEventRemindersPush' => 'no', ]; diff --git a/apps/dav/lib/Upload/ChunkingV2Plugin.php b/apps/dav/lib/Upload/ChunkingV2Plugin.php index cb7c802125c..6b660fb7c6f 100644 --- a/apps/dav/lib/Upload/ChunkingV2Plugin.php +++ b/apps/dav/lib/Upload/ChunkingV2Plugin.php @@ -255,17 +255,15 @@ class ChunkingV2Plugin extends ServerPlugin { public function beforeDelete(RequestInterface $request, ResponseInterface $response) { try { - $this->prepareUpload($request->getPath()); - if (!$this->uploadFolder instanceof UploadFolder) { - return true; - } - - [$storage, $storagePath] = $this->getUploadStorage($this->uploadPath); - $storage->cancelChunkedWrite($storagePath, $this->uploadId); - return true; - } catch (NotFound $e) { + $this->prepareUpload(dirname($request->getPath())); + $this->checkPrerequisites(); + } catch (StorageInvalidException|BadRequest|NotFound $e) { return true; } + + [$storage, $storagePath] = $this->getUploadStorage($this->uploadPath); + $storage->cancelChunkedWrite($storagePath, $this->uploadId); + return true; } /** diff --git a/apps/dav/lib/UserMigration/CalendarMigrator.php b/apps/dav/lib/UserMigration/CalendarMigrator.php index 057f7dce77d..e5b404e785f 100644 --- a/apps/dav/lib/UserMigration/CalendarMigrator.php +++ b/apps/dav/lib/UserMigration/CalendarMigrator.php @@ -211,7 +211,7 @@ class CalendarMigrator implements IMigrator, ISizeEstimationMigrator { /** * {@inheritDoc} */ - public function getEstimatedExportSize(IUser $user): int { + public function getEstimatedExportSize(IUser $user): int|float { $calendarExports = $this->getCalendarExports($user, new NullOutput()); $calendarCount = count($calendarExports); @@ -230,7 +230,7 @@ class CalendarMigrator implements IMigrator, ISizeEstimationMigrator { // 450B for each component (events, todos, alarms, etc.) $size += ($componentCount * 450) / 1024; - return (int)ceil($size); + return ceil($size); } /** diff --git a/apps/dav/lib/UserMigration/ContactsMigrator.php b/apps/dav/lib/UserMigration/ContactsMigrator.php index 196d0a6110a..58e267ab28c 100644 --- a/apps/dav/lib/UserMigration/ContactsMigrator.php +++ b/apps/dav/lib/UserMigration/ContactsMigrator.php @@ -202,7 +202,7 @@ class ContactsMigrator implements IMigrator, ISizeEstimationMigrator { /** * {@inheritDoc} */ - public function getEstimatedExportSize(IUser $user): int { + public function getEstimatedExportSize(IUser $user): int|float { $addressBookExports = $this->getAddressBookExports($user, new NullOutput()); $addressBookCount = count($addressBookExports); @@ -217,7 +217,7 @@ class ContactsMigrator implements IMigrator, ISizeEstimationMigrator { // 350B for each contact $size += ($contactsCount * 350) / 1024; - return (int)ceil($size); + return ceil($size); } /** diff --git a/apps/dav/src/settings.js b/apps/dav/src/settings.js index a99db386d69..56b9c7f3fb9 100644 --- a/apps/dav/src/settings.js +++ b/apps/dav/src/settings.js @@ -16,9 +16,9 @@ const CalDavSettingsView = new View({ 'generateBirthdayCalendar' ), sendEventReminders: loadState('dav', 'sendEventReminders'), - sendEventRemindersToSharedGroupMembers: loadState( + sendEventRemindersToSharedUsers: loadState( 'dav', - 'sendEventRemindersToSharedGroupMembers' + 'sendEventRemindersToSharedUsers' ), sendEventRemindersPush: loadState('dav', 'sendEventRemindersPush'), } diff --git a/apps/dav/src/views/CalDavSettings.spec.js b/apps/dav/src/views/CalDavSettings.spec.js index 7bc6e2f7b40..311c8923dc3 100644 --- a/apps/dav/src/views/CalDavSettings.spec.js +++ b/apps/dav/src/views/CalDavSettings.spec.js @@ -43,7 +43,7 @@ describe('CalDavSettings', () => { sendInvitations: true, generateBirthdayCalendar: true, sendEventReminders: true, - sendEventRemindersToSharedGroupMembers: true, + sendEventRemindersToSharedUsers: true, sendEventRemindersPush: true, } }, @@ -65,10 +65,10 @@ describe('CalDavSettings', () => { 'Send notifications for events' ) expect(sendEventReminders).toBeChecked() - const sendEventRemindersToSharedGroupMembers = TLUtils.getByLabelText( + const sendEventRemindersToSharedUsers = TLUtils.getByLabelText( 'Send reminder notifications to calendar sharees as well' ) - expect(sendEventRemindersToSharedGroupMembers).toBeChecked() + expect(sendEventRemindersToSharedUsers).toBeChecked() const sendEventRemindersPush = TLUtils.getByLabelText( 'Enable notifications for events via push' ) @@ -114,7 +114,7 @@ describe('CalDavSettings', () => { 'no' ) - expect(sendEventRemindersToSharedGroupMembers).toBeDisabled() + expect(sendEventRemindersToSharedUsers).toBeDisabled() expect(sendEventRemindersPush).toBeDisabled() OCP.AppConfig.setValue.mockClear() @@ -126,7 +126,7 @@ describe('CalDavSettings', () => { 'yes' ) - expect(sendEventRemindersToSharedGroupMembers).toBeEnabled() + expect(sendEventRemindersToSharedUsers).toBeEnabled() expect(sendEventRemindersPush).toBeEnabled() */ }) diff --git a/apps/dav/src/views/CalDavSettings.vue b/apps/dav/src/views/CalDavSettings.vue index 6755cbd171d..ff870f4b2d4 100644 --- a/apps/dav/src/views/CalDavSettings.vue +++ b/apps/dav/src/views/CalDavSettings.vue @@ -51,7 +51,7 @@ </p> <p class="indented"> <NcCheckboxRadioSwitch id="caldavSendEventRemindersToSharedGroupMembers" - :checked.sync="sendEventRemindersToSharedGroupMembers" + :checked.sync="sendEventRemindersToSharedUsers" type="switch" :disabled="!sendEventReminders"> {{ $t('dav', 'Send reminder notifications to calendar sharees as well' ) }} @@ -130,10 +130,10 @@ export default { sendEventReminders(value) { OCP.AppConfig.setValue('dav', 'sendEventReminders', value ? 'yes' : 'no') }, - sendEventRemindersToSharedGroupMembers(value) { + sendEventRemindersToSharedUsers(value) { OCP.AppConfig.setValue( 'dav', - 'sendEventRemindersToSharedGroupMembers', + 'sendEventRemindersToSharedUsers', value ? 'yes' : 'no' ) }, diff --git a/apps/dav/tests/unit/CardDAV/ConverterTest.php b/apps/dav/tests/unit/CardDAV/ConverterTest.php index fe45e4e5430..f4ef0332c6e 100644 --- a/apps/dav/tests/unit/CardDAV/ConverterTest.php +++ b/apps/dav/tests/unit/CardDAV/ConverterTest.php @@ -70,17 +70,15 @@ class ConverterTest extends TestCase { public function getAccountManager(IUser $user) { $account = $this->createMock(IAccount::class); $account->expects($this->any()) - ->method('getProperties') + ->method('getAllProperties') ->willReturnCallback(function () use ($user) { - return [ - $this->getAccountPropertyMock(IAccountManager::PROPERTY_DISPLAYNAME, $user->getDisplayName(), IAccountManager::SCOPE_FEDERATED), - $this->getAccountPropertyMock(IAccountManager::PROPERTY_ADDRESS, '', IAccountManager::SCOPE_LOCAL), - $this->getAccountPropertyMock(IAccountManager::PROPERTY_WEBSITE, '', IAccountManager::SCOPE_LOCAL), - $this->getAccountPropertyMock(IAccountManager::PROPERTY_EMAIL, $user->getEMailAddress(), IAccountManager::SCOPE_FEDERATED), - $this->getAccountPropertyMock(IAccountManager::PROPERTY_AVATAR, $user->getAvatarImage(-1)->data(), IAccountManager::SCOPE_FEDERATED), - $this->getAccountPropertyMock(IAccountManager::PROPERTY_PHONE, '', IAccountManager::SCOPE_LOCAL), - $this->getAccountPropertyMock(IAccountManager::PROPERTY_TWITTER, '', IAccountManager::SCOPE_LOCAL), - ]; + yield $this->getAccountPropertyMock(IAccountManager::PROPERTY_DISPLAYNAME, $user->getDisplayName(), IAccountManager::SCOPE_FEDERATED); + yield $this->getAccountPropertyMock(IAccountManager::PROPERTY_ADDRESS, '', IAccountManager::SCOPE_LOCAL); + yield $this->getAccountPropertyMock(IAccountManager::PROPERTY_WEBSITE, '', IAccountManager::SCOPE_LOCAL); + yield $this->getAccountPropertyMock(IAccountManager::PROPERTY_EMAIL, $user->getEMailAddress(), IAccountManager::SCOPE_FEDERATED); + yield $this->getAccountPropertyMock(IAccountManager::PROPERTY_AVATAR, $user->getAvatarImage(-1)->data(), IAccountManager::SCOPE_FEDERATED); + yield $this->getAccountPropertyMock(IAccountManager::PROPERTY_PHONE, '', IAccountManager::SCOPE_LOCAL); + yield $this->getAccountPropertyMock(IAccountManager::PROPERTY_TWITTER, '', IAccountManager::SCOPE_LOCAL); }); $accountManager = $this->getMockBuilder(IAccountManager::class) diff --git a/apps/dav/tests/unit/CardDAV/SystemAddressBookTest.php b/apps/dav/tests/unit/CardDAV/SystemAddressBookTest.php new file mode 100644 index 00000000000..73393d75615 --- /dev/null +++ b/apps/dav/tests/unit/CardDAV/SystemAddressBookTest.php @@ -0,0 +1,123 @@ +<?php + +declare(strict_types=1); + +/* + * @copyright 2023 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @author 2023 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @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; + +use OC\AppFramework\Http\Request; +use OCA\DAV\CardDAV\SystemAddressbook; +use OCA\Federation\TrustedServers; +use OCP\Accounts\IAccountManager; +use OCP\IConfig; +use OCP\IL10N; +use OCP\IRequest; +use PHPUnit\Framework\MockObject\MockObject; +use Sabre\CardDAV\Backend\BackendInterface; +use Sabre\VObject\Component\VCard; +use Sabre\VObject\Reader; +use Test\TestCase; + +class SystemAddressBookTest extends TestCase { + + private MockObject|BackendInterface $cardDavBackend; + private array $addressBookInfo; + private IL10N|MockObject $l10n; + private IConfig|MockObject $config; + private IRequest|MockObject $request; + private array $server; + private TrustedServers|MockObject $trustedServers; + private SystemAddressbook $addressBook; + + protected function setUp(): void { + parent::setUp(); + + $this->cardDavBackend = $this->createMock(BackendInterface::class); + $this->addressBookInfo = [ + 'id' => 123, + '{DAV:}displayname' => 'Accounts', + 'principaluri' => 'principals/system/system', + ]; + $this->l10n = $this->createMock(IL10N::class); + $this->config = $this->createMock(IConfig::class); + $this->request = $this->createMock(Request::class); + $this->server = [ + 'PHP_AUTH_USER' => 'system', + 'PHP_AUTH_PW' => 'shared123', + ]; + $this->request->method('__get')->with('server')->willReturn($this->server); + $this->trustedServers = $this->createMock(TrustedServers::class); + + $this->addressBook = new SystemAddressbook( + $this->cardDavBackend, + $this->addressBookInfo, + $this->l10n, + $this->config, + $this->request, + $this->trustedServers, + ); + } + + public function testGetFilteredChildForFederation(): void { + $this->trustedServers->expects(self::once()) + ->method('getServers') + ->willReturn([ + [ + 'shared_secret' => 'shared123', + ], + ]); + $vcfWithScopes = <<<VCF +BEGIN:VCARD +VERSION:3.0 +PRODID:-//Sabre//Sabre VObject 4.4.2//EN +UID:admin +FN;X-NC-SCOPE=v2-federated:admin +N;X-NC-SCOPE=v2-federated:admin;;;; +ADR;TYPE=OTHER;X-NC-SCOPE=v2-local:Testing test test test;;;;;; +EMAIL;TYPE=OTHER;X-NC-SCOPE=v2-federated:miau_lalala@gmx.net +TEL;TYPE=OTHER;X-NC-SCOPE=v2-local:+435454454544 +CLOUD:admin@http://localhost +END:VCARD +VCF; + $originalCard = [ + 'carddata' => $vcfWithScopes, + ]; + $this->cardDavBackend->expects(self::once()) + ->method('getCard') + ->with(123, 'user.vcf') + ->willReturn($originalCard); + + $card = $this->addressBook->getChild("user.vcf"); + + /** @var VCard $vCard */ + $vCard = Reader::read($card->get()); + foreach ($vCard->children() as $child) { + $scope = $child->offsetGet('X-NC-SCOPE'); + if ($scope !== null) { + self::assertNotEquals(IAccountManager::SCOPE_PRIVATE, $scope->getValue()); + self::assertNotEquals(IAccountManager::SCOPE_LOCAL, $scope->getValue()); + } + } + } + +} diff --git a/apps/dav/tests/unit/Settings/CalDAVSettingsTest.php b/apps/dav/tests/unit/Settings/CalDAVSettingsTest.php index 6bf1707c437..7d285a830d9 100644 --- a/apps/dav/tests/unit/Settings/CalDAVSettingsTest.php +++ b/apps/dav/tests/unit/Settings/CalDAVSettingsTest.php @@ -61,7 +61,7 @@ class CalDAVSettingsTest extends TestCase { ['dav', 'sendInvitations', 'yes'], ['dav', 'generateBirthdayCalendar', 'yes'], ['dav', 'sendEventReminders', 'yes'], - ['dav', 'sendEventRemindersToSharedGroupMembers', 'yes'], + ['dav', 'sendEventRemindersToSharedUsers', 'yes'], ['dav', 'sendEventRemindersPush', 'no'], ) ->will($this->onConsecutiveCalls('yes', 'no', 'yes', 'yes', 'yes')); @@ -76,7 +76,7 @@ class CalDAVSettingsTest extends TestCase { ['sendInvitations', true], ['generateBirthdayCalendar', false], ['sendEventReminders', true], - ['sendEventRemindersToSharedGroupMembers', true], + ['sendEventRemindersToSharedUsers', true], ['sendEventRemindersPush', true], ); $result = $this->settings->getForm(); diff --git a/apps/federation/lib/SyncFederationAddressBooks.php b/apps/federation/lib/SyncFederationAddressBooks.php index 401fd19bd75..3c3c8c872c7 100644 --- a/apps/federation/lib/SyncFederationAddressBooks.php +++ b/apps/federation/lib/SyncFederationAddressBooks.php @@ -82,10 +82,14 @@ class SyncFederationAddressBooks { } catch (\Exception $ex) { if ($ex->getCode() === Http::STATUS_UNAUTHORIZED) { $this->dbHandler->setServerStatus($url, TrustedServers::STATUS_ACCESS_REVOKED); - $this->logger->error("Server sync for $url failed because of revoked access."); + $this->logger->error("Server sync for $url failed because of revoked access.", [ + 'exception' => $ex, + ]); } else { $this->dbHandler->setServerStatus($url, TrustedServers::STATUS_FAILURE); - $this->logger->error("Server sync for $url failed."); + $this->logger->error("Server sync for $url failed.", [ + 'exception' => $ex, + ]); } $callback($url, $ex); } diff --git a/apps/files/l10n/el.js b/apps/files/l10n/el.js index 54ca0d31035..6f4c6db4fe8 100644 --- a/apps/files/l10n/el.js +++ b/apps/files/l10n/el.js @@ -109,6 +109,8 @@ OC.L10N.register( "Create new folder" : "Δημιουργία νέου φακέλου", "Upload file" : "Μεταφόρτωση αρχείου", "Recent" : "Πρόσφατα", + "This file has the tag {tag}" : "Αυτό το αρχείο έχει την ετικέτα {tag}", + "This file has the tags {firstTags} and {lastTag}" : "Αυτό το αρχείο έχει τις ετικέτες {firstTags} και {lastTag}", "Not favorited" : "Δεν είναι αγαπημένο", "Remove from favorites" : "Αφαίρεση από τα αγαπημένα", "Add to favorites" : "Προσθήκη στα αγαπημένα", diff --git a/apps/files/l10n/el.json b/apps/files/l10n/el.json index e19ca8bc181..62bab6e341b 100644 --- a/apps/files/l10n/el.json +++ b/apps/files/l10n/el.json @@ -107,6 +107,8 @@ "Create new folder" : "Δημιουργία νέου φακέλου", "Upload file" : "Μεταφόρτωση αρχείου", "Recent" : "Πρόσφατα", + "This file has the tag {tag}" : "Αυτό το αρχείο έχει την ετικέτα {tag}", + "This file has the tags {firstTags} and {lastTag}" : "Αυτό το αρχείο έχει τις ετικέτες {firstTags} και {lastTag}", "Not favorited" : "Δεν είναι αγαπημένο", "Remove from favorites" : "Αφαίρεση από τα αγαπημένα", "Add to favorites" : "Προσθήκη στα αγαπημένα", diff --git a/apps/files/l10n/en_GB.js b/apps/files/l10n/en_GB.js index c73292f069f..e0708ccdd23 100644 --- a/apps/files/l10n/en_GB.js +++ b/apps/files/l10n/en_GB.js @@ -109,6 +109,8 @@ OC.L10N.register( "Create new folder" : "Create new folder", "Upload file" : "Upload file", "Recent" : "Recent", + "This file has the tag {tag}" : "This file has the tag {tag}", + "This file has the tags {firstTags} and {lastTag}" : "This file has the tags {firstTags} and {lastTag}", "Not favorited" : "Not favourited", "Remove from favorites" : "Remove from favourites", "Add to favorites" : "Add to favourites", diff --git a/apps/files/l10n/en_GB.json b/apps/files/l10n/en_GB.json index c74a72b87db..ea827a9f8a1 100644 --- a/apps/files/l10n/en_GB.json +++ b/apps/files/l10n/en_GB.json @@ -107,6 +107,8 @@ "Create new folder" : "Create new folder", "Upload file" : "Upload file", "Recent" : "Recent", + "This file has the tag {tag}" : "This file has the tag {tag}", + "This file has the tags {firstTags} and {lastTag}" : "This file has the tags {firstTags} and {lastTag}", "Not favorited" : "Not favourited", "Remove from favorites" : "Remove from favourites", "Add to favorites" : "Add to favourites", diff --git a/apps/files/l10n/es.js b/apps/files/l10n/es.js index 61432fe570e..5893b81ab5d 100644 --- a/apps/files/l10n/es.js +++ b/apps/files/l10n/es.js @@ -109,6 +109,8 @@ OC.L10N.register( "Create new folder" : "Crear nueva carpeta", "Upload file" : "Subir archivo", "Recent" : "Reciente", + "This file has the tag {tag}" : "Este archivo tiene la etiqueta {tag}", + "This file has the tags {firstTags} and {lastTag}" : "Este archivo tiene las etiquetas {firstTags} y {lastTag}", "Not favorited" : "Quitado como favorito", "Remove from favorites" : "Quitar de favoritos", "Add to favorites" : "Añadir a favoritos", diff --git a/apps/files/l10n/es.json b/apps/files/l10n/es.json index cabb9dca6c3..58c2d1a304e 100644 --- a/apps/files/l10n/es.json +++ b/apps/files/l10n/es.json @@ -107,6 +107,8 @@ "Create new folder" : "Crear nueva carpeta", "Upload file" : "Subir archivo", "Recent" : "Reciente", + "This file has the tag {tag}" : "Este archivo tiene la etiqueta {tag}", + "This file has the tags {firstTags} and {lastTag}" : "Este archivo tiene las etiquetas {firstTags} y {lastTag}", "Not favorited" : "Quitado como favorito", "Remove from favorites" : "Quitar de favoritos", "Add to favorites" : "Añadir a favoritos", diff --git a/apps/files/l10n/fr.js b/apps/files/l10n/fr.js index fafff6d98e6..bbda95dafb5 100644 --- a/apps/files/l10n/fr.js +++ b/apps/files/l10n/fr.js @@ -109,6 +109,8 @@ OC.L10N.register( "Create new folder" : "Créer un nouveau dossier", "Upload file" : "Téléverser un fichier", "Recent" : "Récent", + "This file has the tag {tag}" : "Ce fichier a l'étiquette {tag}", + "This file has the tags {firstTags} and {lastTag}" : "Ce fichier a les étiquettes {firstTags} et {lastTag}", "Not favorited" : "Non marqué comme favori", "Remove from favorites" : "Retirer des favoris", "Add to favorites" : "Ajouter aux favoris", @@ -174,6 +176,7 @@ OC.L10N.register( "Download file {name}" : "Télécharger le fichier {name}", "\"{displayName}\" action executed successfully" : "Action \"{displayName}\" exécutée avec succès", "\"{displayName}\" action failed" : "Échec de l'action \"{displayName}\"", + "Total rows summary" : "Récapitulatif du nombre total de lignes", "Select all" : "Tout sélectionner", "Unselect all" : "Tout désélectionner", "\"{displayName}\" failed on some elements " : "\"{displayName}\" a échoué pour avec certains éléments", @@ -229,6 +232,7 @@ OC.L10N.register( "Delete permanently" : "Supprimer définitivement", "Set up templates folder" : "Configurer le dossier des modèles", "Templates" : "Modèles", + "Create new templates folder" : "Créer un nouveau dossier de modèles", "Unable to initialize the templates directory" : "Impossible d'initialiser le dossier des modèles", "Toggle %1$s sublist" : "Basculer %1$s sous-liste", "Toggle grid view" : "Activer/Désactiver l'affichage mosaïque", diff --git a/apps/files/l10n/fr.json b/apps/files/l10n/fr.json index 6486b97d32e..19a6da272a7 100644 --- a/apps/files/l10n/fr.json +++ b/apps/files/l10n/fr.json @@ -107,6 +107,8 @@ "Create new folder" : "Créer un nouveau dossier", "Upload file" : "Téléverser un fichier", "Recent" : "Récent", + "This file has the tag {tag}" : "Ce fichier a l'étiquette {tag}", + "This file has the tags {firstTags} and {lastTag}" : "Ce fichier a les étiquettes {firstTags} et {lastTag}", "Not favorited" : "Non marqué comme favori", "Remove from favorites" : "Retirer des favoris", "Add to favorites" : "Ajouter aux favoris", @@ -172,6 +174,7 @@ "Download file {name}" : "Télécharger le fichier {name}", "\"{displayName}\" action executed successfully" : "Action \"{displayName}\" exécutée avec succès", "\"{displayName}\" action failed" : "Échec de l'action \"{displayName}\"", + "Total rows summary" : "Récapitulatif du nombre total de lignes", "Select all" : "Tout sélectionner", "Unselect all" : "Tout désélectionner", "\"{displayName}\" failed on some elements " : "\"{displayName}\" a échoué pour avec certains éléments", @@ -227,6 +230,7 @@ "Delete permanently" : "Supprimer définitivement", "Set up templates folder" : "Configurer le dossier des modèles", "Templates" : "Modèles", + "Create new templates folder" : "Créer un nouveau dossier de modèles", "Unable to initialize the templates directory" : "Impossible d'initialiser le dossier des modèles", "Toggle %1$s sublist" : "Basculer %1$s sous-liste", "Toggle grid view" : "Activer/Désactiver l'affichage mosaïque", diff --git a/apps/files/l10n/sr.js b/apps/files/l10n/sr.js index 6979b7d8c59..d35a8150333 100644 --- a/apps/files/l10n/sr.js +++ b/apps/files/l10n/sr.js @@ -109,6 +109,8 @@ OC.L10N.register( "Create new folder" : "Направи нову фасциклу", "Upload file" : "Отпреми фајл", "Recent" : "Скорашњи", + "This file has the tag {tag}" : "Овај фајл има ознаку {tag}", + "This file has the tags {firstTags} and {lastTag}" : "Овај фајл има ознаке {firstTags} и {lastTag}", "Not favorited" : "Није омиљено", "Remove from favorites" : "Избаци из омиљених", "Add to favorites" : "Додај у омиљене", diff --git a/apps/files/l10n/sr.json b/apps/files/l10n/sr.json index 8a0d1944f0d..0f478f45e16 100644 --- a/apps/files/l10n/sr.json +++ b/apps/files/l10n/sr.json @@ -107,6 +107,8 @@ "Create new folder" : "Направи нову фасциклу", "Upload file" : "Отпреми фајл", "Recent" : "Скорашњи", + "This file has the tag {tag}" : "Овај фајл има ознаку {tag}", + "This file has the tags {firstTags} and {lastTag}" : "Овај фајл има ознаке {firstTags} и {lastTag}", "Not favorited" : "Није омиљено", "Remove from favorites" : "Избаци из омиљених", "Add to favorites" : "Додај у омиљене", diff --git a/apps/files/l10n/tr.js b/apps/files/l10n/tr.js index 1948442a290..d1a33520874 100644 --- a/apps/files/l10n/tr.js +++ b/apps/files/l10n/tr.js @@ -109,6 +109,8 @@ OC.L10N.register( "Create new folder" : "Klasör ekle", "Upload file" : "Dosya yükle", "Recent" : "Son", + "This file has the tag {tag}" : "Dosya {tag} olarak etiketlenmiş", + "This file has the tags {firstTags} and {lastTag}" : "Dosya {firstTags} ve {lastTag} olarak etiketlenmiş", "Not favorited" : "Sık kullanılanlarda değil", "Remove from favorites" : "Sık kullanılanlardan kaldır", "Add to favorites" : "Sık kullanılanlara ekle", @@ -230,6 +232,7 @@ OC.L10N.register( "Delete permanently" : "Kalıcı olarak sil", "Set up templates folder" : "Kalıp klasörünü ayarlayın", "Templates" : "Kalıplar", + "Create new templates folder" : "Yeni kalıp klasörü oluştur", "Unable to initialize the templates directory" : "Kalıp klasörü hazırlanamadı", "Toggle %1$s sublist" : "%1$s alt listesini aç/kapat", "Toggle grid view" : "Tablo görünümünü değiştir", diff --git a/apps/files/l10n/tr.json b/apps/files/l10n/tr.json index 9a6dfb0505c..5138396ffe0 100644 --- a/apps/files/l10n/tr.json +++ b/apps/files/l10n/tr.json @@ -107,6 +107,8 @@ "Create new folder" : "Klasör ekle", "Upload file" : "Dosya yükle", "Recent" : "Son", + "This file has the tag {tag}" : "Dosya {tag} olarak etiketlenmiş", + "This file has the tags {firstTags} and {lastTag}" : "Dosya {firstTags} ve {lastTag} olarak etiketlenmiş", "Not favorited" : "Sık kullanılanlarda değil", "Remove from favorites" : "Sık kullanılanlardan kaldır", "Add to favorites" : "Sık kullanılanlara ekle", @@ -228,6 +230,7 @@ "Delete permanently" : "Kalıcı olarak sil", "Set up templates folder" : "Kalıp klasörünü ayarlayın", "Templates" : "Kalıplar", + "Create new templates folder" : "Yeni kalıp klasörü oluştur", "Unable to initialize the templates directory" : "Kalıp klasörü hazırlanamadı", "Toggle %1$s sublist" : "%1$s alt listesini aç/kapat", "Toggle grid view" : "Tablo görünümünü değiştir", diff --git a/apps/files/l10n/uk.js b/apps/files/l10n/uk.js index 3ec993ffd2c..dff9c213f15 100644 --- a/apps/files/l10n/uk.js +++ b/apps/files/l10n/uk.js @@ -109,6 +109,8 @@ OC.L10N.register( "Create new folder" : "Створити новий каталог", "Upload file" : "Завантажити файл", "Recent" : "Останні", + "This file has the tag {tag}" : "Цей файл має тег {tag}", + "This file has the tags {firstTags} and {lastTag}" : "Цей файл має теги {firstTags} та {lastTag}", "Not favorited" : "Без зірочки", "Remove from favorites" : "Прибрати з вподобаного", "Add to favorites" : "Вподобати", @@ -230,6 +232,7 @@ OC.L10N.register( "Delete permanently" : "Вилучити назавжди", "Set up templates folder" : "Встановити каталог з шаблонами", "Templates" : "Шаблони", + "Create new templates folder" : "Створіть нову папку шаблонів", "Unable to initialize the templates directory" : "Неможливо встановити каталог з шаблонами", "Toggle %1$s sublist" : "Перемкнути вкладений список %1$s", "Toggle grid view" : "Перемкнути подання сіткою", diff --git a/apps/files/l10n/uk.json b/apps/files/l10n/uk.json index 5ea26244c66..2cf7e122922 100644 --- a/apps/files/l10n/uk.json +++ b/apps/files/l10n/uk.json @@ -107,6 +107,8 @@ "Create new folder" : "Створити новий каталог", "Upload file" : "Завантажити файл", "Recent" : "Останні", + "This file has the tag {tag}" : "Цей файл має тег {tag}", + "This file has the tags {firstTags} and {lastTag}" : "Цей файл має теги {firstTags} та {lastTag}", "Not favorited" : "Без зірочки", "Remove from favorites" : "Прибрати з вподобаного", "Add to favorites" : "Вподобати", @@ -228,6 +230,7 @@ "Delete permanently" : "Вилучити назавжди", "Set up templates folder" : "Встановити каталог з шаблонами", "Templates" : "Шаблони", + "Create new templates folder" : "Створіть нову папку шаблонів", "Unable to initialize the templates directory" : "Неможливо встановити каталог з шаблонами", "Toggle %1$s sublist" : "Перемкнути вкладений список %1$s", "Toggle grid view" : "Перемкнути подання сіткою", diff --git a/apps/files/l10n/zh_CN.js b/apps/files/l10n/zh_CN.js index 0d6978e5fbd..327e23ac172 100644 --- a/apps/files/l10n/zh_CN.js +++ b/apps/files/l10n/zh_CN.js @@ -109,6 +109,8 @@ OC.L10N.register( "Create new folder" : "创建新文件夹", "Upload file" : "上传文件", "Recent" : "最近", + "This file has the tag {tag}" : "这个文件有 {tag} 标签", + "This file has the tags {firstTags} and {lastTag}" : "这个文件有 {firstTags} 和 {lastTag} 标签", "Not favorited" : "未收藏", "Remove from favorites" : "从收藏中移除", "Add to favorites" : "添加到收藏夹", @@ -176,9 +178,11 @@ OC.L10N.register( "\"{displayName}\" action failed" : "\"{displayName}\" 操作执行失败", "Select all" : "全部选择", "Unselect all" : "取消全选", + "\"{displayName}\" batch action executed successfully" : "批量操作 \"{displayName}\" 运行成功", "ascending" : "升序", "descending" : "降序", "Sort list by {column} ({direction})" : "按 {column} ({direction}) 排序列表", + "This list is not fully rendered for performances reasons. The files will be rendered as you navigate through the list." : "该列表因为性能原因没有完全加载。文件将会在您浏览列表的时候加载。", "Storage informations" : "存储信息", "{usedQuotaByte} used" : "{usedQuotaByte} 已使用", "{relative}% used" : "{relative}% 已使用", @@ -187,6 +191,7 @@ OC.L10N.register( "Choose file or folder to transfer" : "选择要转移的文件或文件夹", "Change" : "修改", "New owner" : "新的拥有者", + "Search for an account" : "搜索一个账户", "Choose a file or folder to transfer" : "选择要转移的文件或文件夹", "Transfer" : "转移", "Transfer {path} to {userid}" : "将 {path} 转移给 {userid}", @@ -225,6 +230,7 @@ OC.L10N.register( "Delete permanently" : "彻底删除", "Set up templates folder" : "设置模板文件夹", "Templates" : "模板", + "Create new templates folder" : "新建模板文件夹", "Unable to initialize the templates directory" : "无法初始化模板目录", "Toggle %1$s sublist" : "切换 %1$s 子列表", "Toggle grid view" : "切换网格视图", diff --git a/apps/files/l10n/zh_CN.json b/apps/files/l10n/zh_CN.json index 97a7c05d76c..149b51ed282 100644 --- a/apps/files/l10n/zh_CN.json +++ b/apps/files/l10n/zh_CN.json @@ -107,6 +107,8 @@ "Create new folder" : "创建新文件夹", "Upload file" : "上传文件", "Recent" : "最近", + "This file has the tag {tag}" : "这个文件有 {tag} 标签", + "This file has the tags {firstTags} and {lastTag}" : "这个文件有 {firstTags} 和 {lastTag} 标签", "Not favorited" : "未收藏", "Remove from favorites" : "从收藏中移除", "Add to favorites" : "添加到收藏夹", @@ -174,9 +176,11 @@ "\"{displayName}\" action failed" : "\"{displayName}\" 操作执行失败", "Select all" : "全部选择", "Unselect all" : "取消全选", + "\"{displayName}\" batch action executed successfully" : "批量操作 \"{displayName}\" 运行成功", "ascending" : "升序", "descending" : "降序", "Sort list by {column} ({direction})" : "按 {column} ({direction}) 排序列表", + "This list is not fully rendered for performances reasons. The files will be rendered as you navigate through the list." : "该列表因为性能原因没有完全加载。文件将会在您浏览列表的时候加载。", "Storage informations" : "存储信息", "{usedQuotaByte} used" : "{usedQuotaByte} 已使用", "{relative}% used" : "{relative}% 已使用", @@ -185,6 +189,7 @@ "Choose file or folder to transfer" : "选择要转移的文件或文件夹", "Change" : "修改", "New owner" : "新的拥有者", + "Search for an account" : "搜索一个账户", "Choose a file or folder to transfer" : "选择要转移的文件或文件夹", "Transfer" : "转移", "Transfer {path} to {userid}" : "将 {path} 转移给 {userid}", @@ -223,6 +228,7 @@ "Delete permanently" : "彻底删除", "Set up templates folder" : "设置模板文件夹", "Templates" : "模板", + "Create new templates folder" : "新建模板文件夹", "Unable to initialize the templates directory" : "无法初始化模板目录", "Toggle %1$s sublist" : "切换 %1$s 子列表", "Toggle grid view" : "切换网格视图", diff --git a/apps/files/l10n/zh_HK.js b/apps/files/l10n/zh_HK.js index 61c7b00c1c2..c51b90cebe2 100644 --- a/apps/files/l10n/zh_HK.js +++ b/apps/files/l10n/zh_HK.js @@ -109,6 +109,8 @@ OC.L10N.register( "Create new folder" : "新增資料夾", "Upload file" : "上傳檔案", "Recent" : "最新", + "This file has the tag {tag}" : "此檔案有標籤 {tag}", + "This file has the tags {firstTags} and {lastTag}" : "此檔案有標籤 {firstTags} 與 {lastTag}", "Not favorited" : "未加入至最愛", "Remove from favorites" : "從最愛中移除", "Add to favorites" : "加到我的最愛", diff --git a/apps/files/l10n/zh_HK.json b/apps/files/l10n/zh_HK.json index c9dbf1a731a..33e803bfdd6 100644 --- a/apps/files/l10n/zh_HK.json +++ b/apps/files/l10n/zh_HK.json @@ -107,6 +107,8 @@ "Create new folder" : "新增資料夾", "Upload file" : "上傳檔案", "Recent" : "最新", + "This file has the tag {tag}" : "此檔案有標籤 {tag}", + "This file has the tags {firstTags} and {lastTag}" : "此檔案有標籤 {firstTags} 與 {lastTag}", "Not favorited" : "未加入至最愛", "Remove from favorites" : "從最愛中移除", "Add to favorites" : "加到我的最愛", diff --git a/apps/files/lib/Command/Scan.php b/apps/files/lib/Command/Scan.php index a59665c56e9..6c7a607d2af 100644 --- a/apps/files/lib/Command/Scan.php +++ b/apps/files/lib/Command/Scan.php @@ -58,6 +58,7 @@ class Scan extends Base { protected float $execTime = 0; protected int $foldersCounter = 0; protected int $filesCounter = 0; + protected int $errorsCounter = 0; private IRootFolder $root; private MetadataManager $metadataManager; @@ -148,10 +149,12 @@ class Scan extends Base { $scanner->listen('\OC\Files\Utils\Scanner', 'StorageNotAvailable', function (StorageNotAvailableException $e) use ($output) { $output->writeln('Error while scanning, storage not available (' . $e->getMessage() . ')', OutputInterface::VERBOSITY_VERBOSE); + ++$this->errorsCounter; }); $scanner->listen('\OC\Files\Utils\Scanner', 'normalizedNameMismatch', function ($fullPath) use ($output) { $output->writeln("\t<error>Entry \"" . $fullPath . '" will not be accessible due to incompatible encoding</error>'); + ++$this->errorsCounter; }); try { @@ -164,14 +167,17 @@ class Scan extends Base { $output->writeln("<error>Home storage for user $user not writable or 'files' subdirectory missing</error>"); $output->writeln(' ' . $e->getMessage()); $output->writeln('Make sure you\'re running the scan command only as the user the web server runs as'); + ++$this->errorsCounter; } catch (InterruptedException $e) { # exit the function if ctrl-c has been pressed $output->writeln('Interrupted by user'); } catch (NotFoundException $e) { $output->writeln('<error>Path not found: ' . $e->getMessage() . '</error>'); + ++$this->errorsCounter; } catch (\Exception $e) { $output->writeln('<error>Exception during scan: ' . $e->getMessage() . '</error>'); $output->writeln('<error>' . $e->getTraceAsString() . '</error>'); + ++$this->errorsCounter; } } @@ -192,11 +198,6 @@ class Scan extends Base { $users = $input->getArgument('user_id'); } - # restrict the verbosity level to VERBOSITY_VERBOSE - if ($output->getVerbosity() > OutputInterface::VERBOSITY_VERBOSE) { - $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); - } - # check quantity of users to be process and show it on the command line $users_total = count($users); if ($users_total === 0) { @@ -204,7 +205,7 @@ class Scan extends Base { return 1; } - $this->initTools(); + $this->initTools($output); $user_count = 0; foreach ($users as $user) { @@ -236,15 +237,19 @@ class Scan extends Base { /** * Initialises some useful tools for the Command */ - protected function initTools() { + protected function initTools(OutputInterface $output) { // Start the timer $this->execTime = -microtime(true); // Convert PHP errors to exceptions - set_error_handler([$this, 'exceptionErrorHandler'], E_ALL); + set_error_handler( + fn (int $severity, string $message, string $file, int $line): bool => + $this->exceptionErrorHandler($output, $severity, $message, $file, $line), + E_ALL + ); } /** - * Processes PHP errors as exceptions in order to be able to keep track of problems + * Processes PHP errors in order to be able to show them in the output * * @see https://www.php.net/manual/en/function.set-error-handler.php * @@ -252,15 +257,17 @@ class Scan extends Base { * @param string $message * @param string $file the filename that the error was raised in * @param int $line the line number the error was raised - * - * @throws \ErrorException */ - public function exceptionErrorHandler($severity, $message, $file, $line) { - if (!(error_reporting() & $severity)) { - // This error code is not included in error_reporting - return; + public function exceptionErrorHandler(OutputInterface $output, int $severity, string $message, string $file, int $line): bool { + if (($severity === E_DEPRECATED) || ($severity === E_USER_DEPRECATED)) { + // Do not show deprecation warnings + return false; } - throw new \ErrorException($message, 0, $severity, $file, $line); + $e = new \ErrorException($message, 0, $severity, $file, $line); + $output->writeln('<error>Error during scan: ' . $e->getMessage() . '</error>'); + $output->writeln('<error>' . $e->getTraceAsString() . '</error>', OutputInterface::VERBOSITY_VERY_VERBOSE); + ++$this->errorsCounter; + return true; } /** @@ -271,28 +278,18 @@ class Scan extends Base { $this->execTime += microtime(true); $headers = [ - 'Folders', 'Files', 'Elapsed time' + 'Folders', + 'Files', + 'Errors', + 'Elapsed time', ]; - - $this->showSummary($headers, null, $output); - } - - /** - * Shows a summary of operations - * - * @param string[] $headers - * @param string[] $rows - * @param OutputInterface $output - */ - protected function showSummary($headers, $rows, OutputInterface $output) { $niceDate = $this->formatExecTime(); - if (!$rows) { - $rows = [ - $this->foldersCounter, - $this->filesCounter, - $niceDate, - ]; - } + $rows = [ + $this->foldersCounter, + $this->filesCounter, + $this->errorsCounter, + $niceDate, + ]; $table = new Table($output); $table ->setHeaders($headers) diff --git a/apps/files_external/l10n/uk.js b/apps/files_external/l10n/uk.js index c68c9ef042c..a5efe11cacc 100644 --- a/apps/files_external/l10n/uk.js +++ b/apps/files_external/l10n/uk.js @@ -10,6 +10,7 @@ OC.L10N.register( "Error configuring OAuth2" : "Помилка налаштування OAuth2", "Generate keys" : "Створити ключі", "Error generating key pair" : "Помилка створення ключової пари", + "Type to select user or group." : "Почніть друкувати, щоб вибрати користувача або групу.", "(Group)" : "(група)", "Compatibility with Mac NFD encoding (slow)" : "Сумісність із кодуванням Mac NFD (повільно)", "Enable encryption" : "Увімкнути шифрування", @@ -89,6 +90,7 @@ OC.L10N.register( "Hostname" : "Ім'я хоста", "Port" : "Порт", "Region" : "Регіон", + "Storage Class" : "Клас сховища", "Enable SSL" : "Включити SSL", "Enable Path Style" : "Включити стиль шляху", "Legacy (v2) authentication" : "Legacy (v2) автентифікація", @@ -134,6 +136,7 @@ OC.L10N.register( "Available for" : "Доступний для", "Click to recheck the configuration" : "Клацніть для повторної перевірки налаштувань", "Add storage" : "Додати сховище", + "All users" : "Всі користувачі", "Advanced settings" : "Розширені налаштування", "Allow users to mount external storage" : "Дозволити користувачам монтувати зовнішні сховища", "Global credentials can be used to authenticate with multiple external storages that have the same credentials." : "Глобальні облікові дані можна використовувати для автентифікації з кількома зовнішніми сховищами, які мають однакові облікові дані.", diff --git a/apps/files_external/l10n/uk.json b/apps/files_external/l10n/uk.json index 053dd1025ce..67126750632 100644 --- a/apps/files_external/l10n/uk.json +++ b/apps/files_external/l10n/uk.json @@ -8,6 +8,7 @@ "Error configuring OAuth2" : "Помилка налаштування OAuth2", "Generate keys" : "Створити ключі", "Error generating key pair" : "Помилка створення ключової пари", + "Type to select user or group." : "Почніть друкувати, щоб вибрати користувача або групу.", "(Group)" : "(група)", "Compatibility with Mac NFD encoding (slow)" : "Сумісність із кодуванням Mac NFD (повільно)", "Enable encryption" : "Увімкнути шифрування", @@ -87,6 +88,7 @@ "Hostname" : "Ім'я хоста", "Port" : "Порт", "Region" : "Регіон", + "Storage Class" : "Клас сховища", "Enable SSL" : "Включити SSL", "Enable Path Style" : "Включити стиль шляху", "Legacy (v2) authentication" : "Legacy (v2) автентифікація", @@ -132,6 +134,7 @@ "Available for" : "Доступний для", "Click to recheck the configuration" : "Клацніть для повторної перевірки налаштувань", "Add storage" : "Додати сховище", + "All users" : "Всі користувачі", "Advanced settings" : "Розширені налаштування", "Allow users to mount external storage" : "Дозволити користувачам монтувати зовнішні сховища", "Global credentials can be used to authenticate with multiple external storages that have the same credentials." : "Глобальні облікові дані можна використовувати для автентифікації з кількома зовнішніми сховищами, які мають однакові облікові дані.", diff --git a/apps/files_sharing/l10n/el.js b/apps/files_sharing/l10n/el.js index adf1325a1d9..66322a86572 100644 --- a/apps/files_sharing/l10n/el.js +++ b/apps/files_sharing/l10n/el.js @@ -169,6 +169,7 @@ OC.L10N.register( "Shared with the group {user} by {owner}" : "Διαμοιράστηκε με την ομάδα {user} του {owner}", "Shared with the conversation {user} by {owner}" : "Διαμοιράστηκε με τη συζήτηση {user} από {owner}", "Shared with {user} by {owner}" : "Διαμοιράστηκε με {user} από τον {owner}", + "Allow download" : "Να επιτρέπεται η λήψη", "Added by {initiator}" : "Προστέθηκε από {initiator}", "Via “{folder}”" : "Μέσω “{folder}”", "Internal link" : "Εσωτερικός σύνδεσμος", @@ -195,7 +196,9 @@ OC.L10N.register( "Shared via link by {initiator}" : "Διαμοιράστηκε μέσω συνδέσμου {initiator}", "Share link ({label})" : "Σύνδεσμος κοινής χρήσης ({label})", "Share link" : "Διαμοιρασμός συνδέσμου", + "Actions for \"{title}\"" : "Ενέργειες για \"{title}\"", "Error, please enter proper password and/or expiration date" : "Σφάλμα, παρακαλώ εισάγετε τον σωστό κωδικό πρόσβασης και/ή ημερομηνία λήξης", + "Link share created" : "Δημιουργήθηκε ο σύνδεσμος κοινής χρήσης", "Search for share recipients" : "Αναζήτηση για παραλήπτες διαμοιρασμού", "No recommendations. Start typing." : "Δεν υπάρχουν συστάσεις. Αρχίστε να πληκτρολογείτε.", "Resharing is not allowed" : "Δεν επιτρέπεται ο επαναμοιρασμός", diff --git a/apps/files_sharing/l10n/el.json b/apps/files_sharing/l10n/el.json index 8a242e1e72c..c0ec927acb2 100644 --- a/apps/files_sharing/l10n/el.json +++ b/apps/files_sharing/l10n/el.json @@ -167,6 +167,7 @@ "Shared with the group {user} by {owner}" : "Διαμοιράστηκε με την ομάδα {user} του {owner}", "Shared with the conversation {user} by {owner}" : "Διαμοιράστηκε με τη συζήτηση {user} από {owner}", "Shared with {user} by {owner}" : "Διαμοιράστηκε με {user} από τον {owner}", + "Allow download" : "Να επιτρέπεται η λήψη", "Added by {initiator}" : "Προστέθηκε από {initiator}", "Via “{folder}”" : "Μέσω “{folder}”", "Internal link" : "Εσωτερικός σύνδεσμος", @@ -193,7 +194,9 @@ "Shared via link by {initiator}" : "Διαμοιράστηκε μέσω συνδέσμου {initiator}", "Share link ({label})" : "Σύνδεσμος κοινής χρήσης ({label})", "Share link" : "Διαμοιρασμός συνδέσμου", + "Actions for \"{title}\"" : "Ενέργειες για \"{title}\"", "Error, please enter proper password and/or expiration date" : "Σφάλμα, παρακαλώ εισάγετε τον σωστό κωδικό πρόσβασης και/ή ημερομηνία λήξης", + "Link share created" : "Δημιουργήθηκε ο σύνδεσμος κοινής χρήσης", "Search for share recipients" : "Αναζήτηση για παραλήπτες διαμοιρασμού", "No recommendations. Start typing." : "Δεν υπάρχουν συστάσεις. Αρχίστε να πληκτρολογείτε.", "Resharing is not allowed" : "Δεν επιτρέπεται ο επαναμοιρασμός", diff --git a/apps/files_sharing/l10n/fr.js b/apps/files_sharing/l10n/fr.js index 17d94f9f7cd..b86c1064534 100644 --- a/apps/files_sharing/l10n/fr.js +++ b/apps/files_sharing/l10n/fr.js @@ -120,6 +120,7 @@ OC.L10N.register( "You cannot share to a Circle if the app is not enabled" : "Vous ne pouvez pas partager au Cercle si l'application n'est pas activée", "Please specify a valid circle" : "Veuillez entrer un cercle valide", "Sharing %s failed because the back end does not support room shares" : "Le partage %s a échoué parce que l'arrière-plan ne prend pas en charge les partages.", + "Sharing %s failed because the back end does not support ScienceMesh shares" : "Le partage de %s a échoué car le serveur ne supporte pas les partages ScienceMesh", "Unknown share type" : "Type de partage inconnu", "Not a directory" : "N'est pas un dossier", "Could not lock node" : "Impossible de verrouiller le nœud", @@ -225,6 +226,7 @@ OC.L10N.register( "Circle" : "Cercle", "Talk conversation" : "Conversation Talk", "Deck board" : "Tableau Deck", + "ScienceMesh" : "ScienceMesh", "on {server}" : "sur {server}", "Others with access" : "Autres utilisateurs ayant accès", "No other users with access found" : "Aucun autre utilisateur ayant un accès n'a été trouvé", diff --git a/apps/files_sharing/l10n/fr.json b/apps/files_sharing/l10n/fr.json index bcbab1f4560..4681e1c5504 100644 --- a/apps/files_sharing/l10n/fr.json +++ b/apps/files_sharing/l10n/fr.json @@ -118,6 +118,7 @@ "You cannot share to a Circle if the app is not enabled" : "Vous ne pouvez pas partager au Cercle si l'application n'est pas activée", "Please specify a valid circle" : "Veuillez entrer un cercle valide", "Sharing %s failed because the back end does not support room shares" : "Le partage %s a échoué parce que l'arrière-plan ne prend pas en charge les partages.", + "Sharing %s failed because the back end does not support ScienceMesh shares" : "Le partage de %s a échoué car le serveur ne supporte pas les partages ScienceMesh", "Unknown share type" : "Type de partage inconnu", "Not a directory" : "N'est pas un dossier", "Could not lock node" : "Impossible de verrouiller le nœud", @@ -223,6 +224,7 @@ "Circle" : "Cercle", "Talk conversation" : "Conversation Talk", "Deck board" : "Tableau Deck", + "ScienceMesh" : "ScienceMesh", "on {server}" : "sur {server}", "Others with access" : "Autres utilisateurs ayant accès", "No other users with access found" : "Aucun autre utilisateur ayant un accès n'a été trouvé", diff --git a/apps/files_sharing/l10n/uk.js b/apps/files_sharing/l10n/uk.js index cf0c6922a94..c9dfccfaeb3 100644 --- a/apps/files_sharing/l10n/uk.js +++ b/apps/files_sharing/l10n/uk.js @@ -120,6 +120,7 @@ OC.L10N.register( "You cannot share to a Circle if the app is not enabled" : "Ви не можете поділитися у Circle, оскільки застосунок не активний", "Please specify a valid circle" : "Будь ласка, зазначте дійсне коло", "Sharing %s failed because the back end does not support room shares" : "Помилка спільного доступу %s, оскільки серверна частина не підтримує спільний доступ до кімнат", + "Sharing %s failed because the back end does not support ScienceMesh shares" : "Спільне використання %s не вдалося, оскільки бекенд не підтримує спільне використання ScienceMesh", "Unknown share type" : "Невідомий тип спільного ресурсу", "Not a directory" : "Не каталог", "Could not lock node" : "Не вдалося заблокувати вузол", @@ -225,6 +226,7 @@ OC.L10N.register( "Circle" : "Коло", "Talk conversation" : "Розмова Talk", "Deck board" : "Дошка Deck", + "ScienceMesh" : "ScienceMesh", "on {server}" : "на {server}", "Others with access" : "Доступ для інших", "No other users with access found" : "Більше немає користувачів, які мають доступ", @@ -275,6 +277,8 @@ OC.L10N.register( "Copy to clipboard" : "Копіювати до буферу обміну", "Sorry, this link doesn’t seem to work anymore." : "На жаль, посилання більше не дійсне.", "Toggle grid view" : "Перемкнути подання сіткою", + "Sharing %s failed because the back end does not support sciencemesh shares" : "Поширення %s не вдалося, оскільки бекенд не підтримує ресурси типа ScienceMesh", + "Science Mesh" : "Science Mesh", "Error generating password from password_policy" : "Помилка під час створення паролів з password_policy" }, "nplurals=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != 11 ? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || (n % 100 >=11 && n % 100 <=14 )) ? 2: 3);"); diff --git a/apps/files_sharing/l10n/uk.json b/apps/files_sharing/l10n/uk.json index 7bdfb6151ac..26efb0187c1 100644 --- a/apps/files_sharing/l10n/uk.json +++ b/apps/files_sharing/l10n/uk.json @@ -118,6 +118,7 @@ "You cannot share to a Circle if the app is not enabled" : "Ви не можете поділитися у Circle, оскільки застосунок не активний", "Please specify a valid circle" : "Будь ласка, зазначте дійсне коло", "Sharing %s failed because the back end does not support room shares" : "Помилка спільного доступу %s, оскільки серверна частина не підтримує спільний доступ до кімнат", + "Sharing %s failed because the back end does not support ScienceMesh shares" : "Спільне використання %s не вдалося, оскільки бекенд не підтримує спільне використання ScienceMesh", "Unknown share type" : "Невідомий тип спільного ресурсу", "Not a directory" : "Не каталог", "Could not lock node" : "Не вдалося заблокувати вузол", @@ -223,6 +224,7 @@ "Circle" : "Коло", "Talk conversation" : "Розмова Talk", "Deck board" : "Дошка Deck", + "ScienceMesh" : "ScienceMesh", "on {server}" : "на {server}", "Others with access" : "Доступ для інших", "No other users with access found" : "Більше немає користувачів, які мають доступ", @@ -273,6 +275,8 @@ "Copy to clipboard" : "Копіювати до буферу обміну", "Sorry, this link doesn’t seem to work anymore." : "На жаль, посилання більше не дійсне.", "Toggle grid view" : "Перемкнути подання сіткою", + "Sharing %s failed because the back end does not support sciencemesh shares" : "Поширення %s не вдалося, оскільки бекенд не підтримує ресурси типа ScienceMesh", + "Science Mesh" : "Science Mesh", "Error generating password from password_policy" : "Помилка під час створення паролів з password_policy" },"pluralForm" :"nplurals=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != 11 ? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || (n % 100 >=11 && n % 100 <=14 )) ? 2: 3);" }
\ No newline at end of file diff --git a/apps/files_sharing/src/components/SharingEntryLink.vue b/apps/files_sharing/src/components/SharingEntryLink.vue index e45690ba2a0..bf0e94f35c1 100644 --- a/apps/files_sharing/src/components/SharingEntryLink.vue +++ b/apps/files_sharing/src/components/SharingEntryLink.vue @@ -161,6 +161,7 @@ class="share-link-password" :class="{ error: errors.password}" :disabled="saving" + :show-trailing-button="hasUnsavedPassword" :required="config.enforcePasswordForPublicLink" :value="hasUnsavedPassword ? share.newPassword : '***************'" icon="icon-password" diff --git a/apps/files_trashbin/l10n/uk.js b/apps/files_trashbin/l10n/uk.js index 42d574e8225..2a3ea3cc96e 100644 --- a/apps/files_trashbin/l10n/uk.js +++ b/apps/files_trashbin/l10n/uk.js @@ -7,6 +7,7 @@ OC.L10N.register( "This application enables users to restore files that were deleted from the system." : "Цей застосунок дозволяє користувачам відновлювати файли, які було вилучено у системі.", "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." : "Цей застосунок дозволяє користувачам відновлювати файли, які було вилучено у системі. Він показує список вилучених у вебінтерфейсі файлів, дозволяє відновлювати вилучені файліи до каталогів користувача, звідки файл було вилучено, або остаточно вилучати файли з системи. Відновлення файлу також відновлює віповідні версії файлу, якщо підтримку версій увімкнено у системі. Якщо файл було вилучено зі спільного ресурсу, то його також можна відновити, при цьому його буде відновлено уже як не спільний ресурс. Типово вилучені файли зберігаються у кошику протягом 30 днів.\nДля запобігання понадмірного використання дискового простору вилучені файли не можуть займати понад 50% поточного доступного простору за квотою для вилучених файлів. Якщо розмір вилучених файлів перевищує зазначене обмеження, застосунок вилучатиме без можливості відновлення найстаріші файли аж до досягнення обмеження. Додаткову інформацію можна знайти у документації до застосунку.", "Restore" : "Відновити", + "List of files that have been deleted." : "Список файлів, які було видалено.", "Deleted" : "Вилучено", "No deleted files" : "Кошик порожній", "You will be able to recover deleted files from here" : "Тут ви можете відновити файли, які було вилучено", diff --git a/apps/files_trashbin/l10n/uk.json b/apps/files_trashbin/l10n/uk.json index ce856f1a152..27df9d08b5d 100644 --- a/apps/files_trashbin/l10n/uk.json +++ b/apps/files_trashbin/l10n/uk.json @@ -5,6 +5,7 @@ "This application enables users to restore files that were deleted from the system." : "Цей застосунок дозволяє користувачам відновлювати файли, які було вилучено у системі.", "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." : "Цей застосунок дозволяє користувачам відновлювати файли, які було вилучено у системі. Він показує список вилучених у вебінтерфейсі файлів, дозволяє відновлювати вилучені файліи до каталогів користувача, звідки файл було вилучено, або остаточно вилучати файли з системи. Відновлення файлу також відновлює віповідні версії файлу, якщо підтримку версій увімкнено у системі. Якщо файл було вилучено зі спільного ресурсу, то його також можна відновити, при цьому його буде відновлено уже як не спільний ресурс. Типово вилучені файли зберігаються у кошику протягом 30 днів.\nДля запобігання понадмірного використання дискового простору вилучені файли не можуть займати понад 50% поточного доступного простору за квотою для вилучених файлів. Якщо розмір вилучених файлів перевищує зазначене обмеження, застосунок вилучатиме без можливості відновлення найстаріші файли аж до досягнення обмеження. Додаткову інформацію можна знайти у документації до застосунку.", "Restore" : "Відновити", + "List of files that have been deleted." : "Список файлів, які було видалено.", "Deleted" : "Вилучено", "No deleted files" : "Кошик порожній", "You will be able to recover deleted files from here" : "Тут ви можете відновити файли, які було вилучено", diff --git a/apps/files_trashbin/lib/UserMigration/TrashbinMigrator.php b/apps/files_trashbin/lib/UserMigration/TrashbinMigrator.php index 70338a469d3..842721eeac9 100644 --- a/apps/files_trashbin/lib/UserMigration/TrashbinMigrator.php +++ b/apps/files_trashbin/lib/UserMigration/TrashbinMigrator.php @@ -67,7 +67,7 @@ class TrashbinMigrator implements IMigrator, ISizeEstimationMigrator { /** * {@inheritDoc} */ - public function getEstimatedExportSize(IUser $user): int { + public function getEstimatedExportSize(IUser $user): int|float { $uid = $user->getUID(); try { @@ -75,7 +75,7 @@ class TrashbinMigrator implements IMigrator, ISizeEstimationMigrator { if (!$trashbinFolder instanceof Folder) { return 0; } - return (int)ceil($trashbinFolder->getSize() / 1024); + return ceil($trashbinFolder->getSize() / 1024); } catch (\Throwable $e) { return 0; } diff --git a/apps/files_versions/l10n/pl.js b/apps/files_versions/l10n/pl.js index b9d8dd4687a..e55af7f1c3d 100644 --- a/apps/files_versions/l10n/pl.js +++ b/apps/files_versions/l10n/pl.js @@ -4,13 +4,16 @@ OC.L10N.register( "Versions" : "Wersje", "This application automatically maintains older versions of files that are changed." : "Aplikacja przechowuje starsze wersje plików poddanych modyfikacji.", "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.\n\t\tIn 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." : "Aplikacja automatycznie utrzymuje starsze wersje zmienionych plików. Po włączeniu, ukryty katalog versions jest udostępniany w każdym katalogu użytkownika i służy do przechowywania starych wersji plików. Użytkownik może w dowolnym momencie powrócić do starszej wersji za pośrednictwem strony internetowej, a zastąpiony plik staje się jego wersją. Aplikacja automatycznie zarządza katalogiem versions, aby zapewnić użytkownikowi ciągłość wersji.\n\t\tOprócz wygaśnięcia wersji, aplikacja Versions zapewnia, że nigdy nie będzie wykorzystywać więcej niż 50% dostępnej przestrzeni użytkownika. Jeśli przechowywane wersje przekroczą ten limit, aplikacja usunie najpierw najstarsze wersje, aż do osiągnięcia tego limitu. Więcej informacji można znaleźć w dokumentacji Versions.", + "Name this version" : "Nazwij tę wersję", "Edit version name" : "Edytuj nazwę wersji", "Restore version" : "Przywróć wersję", "Download version" : "Pobierz wersję", "Delete version" : "Usuń wersję", "Version name" : "Nazwa wersji", + "Named versions are persisted, and excluded from automatic cleanups when your storage quota is full." : "Nazwane wersje są utrwalane i wykluczane z automatycznego czyszczenia, gdy limit przechowywania zostanie przekroczony.", "Remove version name" : "Usuń nazwę wersji", "Save version name" : "Zapis nazwę wersji", + "Initial version restored" : "Przywrócono wersję początkową", "Version restored" : "Wersja przywrócona", "Could not restore version" : "Nie można przywrócić wersji", "Could not set version name" : "Nie można ustawić nazwy wersji", diff --git a/apps/files_versions/l10n/pl.json b/apps/files_versions/l10n/pl.json index a9cd03e6fee..93ccd7b4ad4 100644 --- a/apps/files_versions/l10n/pl.json +++ b/apps/files_versions/l10n/pl.json @@ -2,13 +2,16 @@ "Versions" : "Wersje", "This application automatically maintains older versions of files that are changed." : "Aplikacja przechowuje starsze wersje plików poddanych modyfikacji.", "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.\n\t\tIn 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." : "Aplikacja automatycznie utrzymuje starsze wersje zmienionych plików. Po włączeniu, ukryty katalog versions jest udostępniany w każdym katalogu użytkownika i służy do przechowywania starych wersji plików. Użytkownik może w dowolnym momencie powrócić do starszej wersji za pośrednictwem strony internetowej, a zastąpiony plik staje się jego wersją. Aplikacja automatycznie zarządza katalogiem versions, aby zapewnić użytkownikowi ciągłość wersji.\n\t\tOprócz wygaśnięcia wersji, aplikacja Versions zapewnia, że nigdy nie będzie wykorzystywać więcej niż 50% dostępnej przestrzeni użytkownika. Jeśli przechowywane wersje przekroczą ten limit, aplikacja usunie najpierw najstarsze wersje, aż do osiągnięcia tego limitu. Więcej informacji można znaleźć w dokumentacji Versions.", + "Name this version" : "Nazwij tę wersję", "Edit version name" : "Edytuj nazwę wersji", "Restore version" : "Przywróć wersję", "Download version" : "Pobierz wersję", "Delete version" : "Usuń wersję", "Version name" : "Nazwa wersji", + "Named versions are persisted, and excluded from automatic cleanups when your storage quota is full." : "Nazwane wersje są utrwalane i wykluczane z automatycznego czyszczenia, gdy limit przechowywania zostanie przekroczony.", "Remove version name" : "Usuń nazwę wersji", "Save version name" : "Zapis nazwę wersji", + "Initial version restored" : "Przywrócono wersję początkową", "Version restored" : "Wersja przywrócona", "Could not restore version" : "Nie można przywrócić wersji", "Could not set version name" : "Nie można ustawić nazwy wersji", diff --git a/apps/files_versions/lib/Storage.php b/apps/files_versions/lib/Storage.php index 9141e6c4c65..cd13e74e786 100644 --- a/apps/files_versions/lib/Storage.php +++ b/apps/files_versions/lib/Storage.php @@ -608,7 +608,12 @@ class Storage { foreach ($versions as $version) { $internalPath = $version->getInternalPath(); \OC_Hook::emit('\OCP\Versions', 'preDelete', ['path' => $internalPath, 'trigger' => self::DELETE_TRIGGER_RETENTION_CONSTRAINT]); - $versionsMapper->delete($versionEntities[$version->getId()]); + + $versionEntity = $versionEntities[$version->getId()]; + if (!is_null($versionEntity)) { + $versionsMapper->delete($versionEntity); + } + $version->delete(); \OC_Hook::emit('\OCP\Versions', 'delete', ['path' => $internalPath, 'trigger' => self::DELETE_TRIGGER_RETENTION_CONSTRAINT]); } diff --git a/apps/settings/l10n/it.js b/apps/settings/l10n/it.js index ea21ccf1c75..209a0100ce3 100644 --- a/apps/settings/l10n/it.js +++ b/apps/settings/l10n/it.js @@ -444,6 +444,7 @@ OC.L10N.register( "Allow username autocompletion to users within the same groups" : "Consenti il completamento del nome utente agli utenti degli stessi gruppi", "Allow username autocompletion to users based on phone number integration" : "Consenti il completamento del nome utente agli utenti basati sull'integrazione del numero di telefono", "If autocompletion \"same group\" and \"phone number integration\" are enabled a match in either is enough to show the user." : "Se completamento di \"stesso gruppo\" e \"integrazione numero di telefono\" sono attivi, una corrispondenza in uno dei due è sufficiente per mostrare l'utente.", + "Allow autocompletion when entering the full name or email address (ignoring missing phonebook match and being in the same group)" : "Consenti il completamento automatico quando inserisci il nome completo o l'indirizzo e-mail (ignorando la corrispondenza della rubrica mancante e l'appartenenza allo stesso gruppo)", "Show disclaimer text on the public link upload page (only shown when the file list is hidden)" : "Mostra il testo della liberatoria sulla pagina di caricamento del collegamento pubblico (visualizzato solo quando l'elenco dei file è nascosto)", "This text will be shown on the public link upload page when the file list is hidden." : "Questo testo sarà mostrato sulla pagina di caricamento del collegamento pubblico quando l'elenco dei file è nascosto.", "Default share permissions" : "Permessi predefiniti di condivisione", diff --git a/apps/settings/l10n/it.json b/apps/settings/l10n/it.json index 045e5f53c18..7083ecfe96c 100644 --- a/apps/settings/l10n/it.json +++ b/apps/settings/l10n/it.json @@ -442,6 +442,7 @@ "Allow username autocompletion to users within the same groups" : "Consenti il completamento del nome utente agli utenti degli stessi gruppi", "Allow username autocompletion to users based on phone number integration" : "Consenti il completamento del nome utente agli utenti basati sull'integrazione del numero di telefono", "If autocompletion \"same group\" and \"phone number integration\" are enabled a match in either is enough to show the user." : "Se completamento di \"stesso gruppo\" e \"integrazione numero di telefono\" sono attivi, una corrispondenza in uno dei due è sufficiente per mostrare l'utente.", + "Allow autocompletion when entering the full name or email address (ignoring missing phonebook match and being in the same group)" : "Consenti il completamento automatico quando inserisci il nome completo o l'indirizzo e-mail (ignorando la corrispondenza della rubrica mancante e l'appartenenza allo stesso gruppo)", "Show disclaimer text on the public link upload page (only shown when the file list is hidden)" : "Mostra il testo della liberatoria sulla pagina di caricamento del collegamento pubblico (visualizzato solo quando l'elenco dei file è nascosto)", "This text will be shown on the public link upload page when the file list is hidden." : "Questo testo sarà mostrato sulla pagina di caricamento del collegamento pubblico quando l'elenco dei file è nascosto.", "Default share permissions" : "Permessi predefiniti di condivisione", diff --git a/apps/settings/l10n/sr.js b/apps/settings/l10n/sr.js index 893626fe6aa..5a875feae2d 100644 --- a/apps/settings/l10n/sr.js +++ b/apps/settings/l10n/sr.js @@ -231,12 +231,12 @@ OC.L10N.register( "Copied!" : "Копирано!", "Copy" : "Копирај", "Could not copy app password. Please copy it manually." : "Не могу да копирам апликативну лозинку. Копирајте је ручно.", - "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." : "Да би сервер исправно радио, важно је да се правилно подесе позадински послови. Cron је препоручено подешавање. За још информација, молимо вас да погледате документацију .", + "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." : "Да би сервер исправно радио, важно је да се правилно подесе позадински послови. Cron је препоручено подешавање. За још информација, молимо вас да погледате документацију.", "Last job execution ran {time}. Something seems wrong." : "Последњи посао се извршавао {time}. Изгледа да нешто није у реду.", - "Last job ran {relativeTime}." : "Последњи посао се изврашавао {relativeTime}.", + "Last job ran {relativeTime}." : "Последњи посао се извршавао {relativeTime}.", "Background job did not run yet!" : "Позадински посао се још увек није покренуо!", "AJAX" : "AJAX", - "Execute one task with each page loaded. Use case: Single user instance." : "Извршава један задатак за сваку учитану страницу. Случај употребе: инстанца са једним корисником", + "Execute one task with each page loaded. Use case: Single user instance." : "Извршава један задатак за сваку учитану страницу. Случај употребе: инстанца са једним корисником.", "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)." : "cron.php се регуструје у webcron сервису тако да се cron.php позива преко HTTP сваких 5 минута. Случај употребе: врло мала инстанца (1–5 корисника, у зависности од употребе).", "Cron (Recommended)" : "Cron (Препоручено)", @@ -441,7 +441,7 @@ OC.L10N.register( "Login" : "Пријава", "SSL/TLS" : "SSL/TLS", "Open documentation" : "Отвори документацију", - "It is important to set up this server to be able to send emails, like for password reset and notifications." : "Важно је да подесите сервер да може да шаље е-пошту, када нпр. треба послати ресетовање лозинке или нека обавештења.", + "It is important to set up this server to be able to send emails, like for password reset and notifications." : "Важно је да подесите сервер тако да може да шаље е-пошту када нпр. треба послати ресетовање лозинке или нека обавештења.", "Send mode" : "Режим слања", "Encryption" : "Шифровање", "Sendmail mode" : "Sendmail режим", diff --git a/apps/settings/l10n/sr.json b/apps/settings/l10n/sr.json index ddc8fa1d262..0efdb13fc6a 100644 --- a/apps/settings/l10n/sr.json +++ b/apps/settings/l10n/sr.json @@ -229,12 +229,12 @@ "Copied!" : "Копирано!", "Copy" : "Копирај", "Could not copy app password. Please copy it manually." : "Не могу да копирам апликативну лозинку. Копирајте је ручно.", - "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." : "Да би сервер исправно радио, важно је да се правилно подесе позадински послови. Cron је препоручено подешавање. За још информација, молимо вас да погледате документацију .", + "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." : "Да би сервер исправно радио, важно је да се правилно подесе позадински послови. Cron је препоручено подешавање. За још информација, молимо вас да погледате документацију.", "Last job execution ran {time}. Something seems wrong." : "Последњи посао се извршавао {time}. Изгледа да нешто није у реду.", - "Last job ran {relativeTime}." : "Последњи посао се изврашавао {relativeTime}.", + "Last job ran {relativeTime}." : "Последњи посао се извршавао {relativeTime}.", "Background job did not run yet!" : "Позадински посао се још увек није покренуо!", "AJAX" : "AJAX", - "Execute one task with each page loaded. Use case: Single user instance." : "Извршава један задатак за сваку учитану страницу. Случај употребе: инстанца са једним корисником", + "Execute one task with each page loaded. Use case: Single user instance." : "Извршава један задатак за сваку учитану страницу. Случај употребе: инстанца са једним корисником.", "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)." : "cron.php се регуструје у webcron сервису тако да се cron.php позива преко HTTP сваких 5 минута. Случај употребе: врло мала инстанца (1–5 корисника, у зависности од употребе).", "Cron (Recommended)" : "Cron (Препоручено)", @@ -439,7 +439,7 @@ "Login" : "Пријава", "SSL/TLS" : "SSL/TLS", "Open documentation" : "Отвори документацију", - "It is important to set up this server to be able to send emails, like for password reset and notifications." : "Важно је да подесите сервер да може да шаље е-пошту, када нпр. треба послати ресетовање лозинке или нека обавештења.", + "It is important to set up this server to be able to send emails, like for password reset and notifications." : "Важно је да подесите сервер тако да може да шаље е-пошту када нпр. треба послати ресетовање лозинке или нека обавештења.", "Send mode" : "Режим слања", "Encryption" : "Шифровање", "Sendmail mode" : "Sendmail режим", diff --git a/apps/settings/l10n/uk.js b/apps/settings/l10n/uk.js index 73c091cb1d5..d4f41aa0f59 100644 --- a/apps/settings/l10n/uk.js +++ b/apps/settings/l10n/uk.js @@ -273,6 +273,7 @@ OC.L10N.register( "Upload profile picture" : "Завантажте зображення профілю", "Choose profile picture from Files" : "Виберіть зображення профілю з Файлів", "Remove profile picture" : "Вилучити зображення профілю", + "The file must be a PNG or JPG" : "Файл повинен бути у форматі PNG або JPG", "Picture provided by original account" : "Картинка надана оригінальним обліковим записом", "Cancel" : "Скасувати", "Set as profile picture" : "Установити як зображення профілю", diff --git a/apps/settings/l10n/uk.json b/apps/settings/l10n/uk.json index da78b17a0fc..c72d31ca4e0 100644 --- a/apps/settings/l10n/uk.json +++ b/apps/settings/l10n/uk.json @@ -271,6 +271,7 @@ "Upload profile picture" : "Завантажте зображення профілю", "Choose profile picture from Files" : "Виберіть зображення профілю з Файлів", "Remove profile picture" : "Вилучити зображення профілю", + "The file must be a PNG or JPG" : "Файл повинен бути у форматі PNG або JPG", "Picture provided by original account" : "Картинка надана оригінальним обліковим записом", "Cancel" : "Скасувати", "Set as profile picture" : "Установити як зображення профілю", diff --git a/apps/settings/lib/UserMigration/AccountMigrator.php b/apps/settings/lib/UserMigration/AccountMigrator.php index e8c70624224..a779ad76c8d 100644 --- a/apps/settings/lib/UserMigration/AccountMigrator.php +++ b/apps/settings/lib/UserMigration/AccountMigrator.php @@ -84,7 +84,7 @@ class AccountMigrator implements IMigrator, ISizeEstimationMigrator { /** * {@inheritDoc} */ - public function getEstimatedExportSize(IUser $user): int { + public function getEstimatedExportSize(IUser $user): int|float { $size = 100; // 100KiB for account JSON try { @@ -97,7 +97,7 @@ class AccountMigrator implements IMigrator, ISizeEstimationMigrator { // Skip avatar in size estimate on failure } - return (int)ceil($size); + return ceil($size); } /** diff --git a/apps/settings/src/components/UserList/UserRow.vue b/apps/settings/src/components/UserList/UserRow.vue index f840b8cdf77..ed42db76e07 100644 --- a/apps/settings/src/components/UserList/UserRow.vue +++ b/apps/settings/src/components/UserList/UserRow.vue @@ -76,6 +76,7 @@ <form :class="{'icon-loading-small': loading.displayName}" class="displayName" @submit.prevent="updateDisplayName"> + <label class="hidden-visually" :for="'displayName'+user.id+rand">{{ t('settings', 'Edit display name') }}</label> <input :id="'displayName'+user.id+rand" ref="displayName" :disabled="loading.displayName||loading.all" @@ -102,6 +103,7 @@ :class="{'icon-loading-small': loading.password}" class="password" @submit.prevent="updatePassword"> + <label class="hidden-visually" :for="'password'+user.id+rand">{{ t('settings', 'Add new password') }}</label> <input :id="'password'+user.id+rand" ref="password" :disabled="loading.password || loading.all" @@ -121,6 +123,7 @@ <form :class="{'icon-loading-small': loading.mailAddress}" class="mailAddress" @submit.prevent="updateEmail"> + <label class="hidden-visually" :for="'mailAddress'+user.id+rand">{{ t('settings', 'Add new email address') }}</label> <input :id="'mailAddress'+user.id+rand" ref="mailAddress" :disabled="loading.mailAddress||loading.all" @@ -134,7 +137,9 @@ <input class="icon-confirm" type="submit" value=""> </form> <div :class="{'icon-loading-small': loading.groups}" class="groups"> - <NcMultiselect :close-on-select="false" + <label class="hidden-visually" :for="'groups'+user.id+rand">{{ t('settings', 'Add user to group') }}</label> + <NcMultiselect :id="'groups'+user.id+rand" + :close-on-select="false" :disabled="loading.groups||loading.all" :limit="2" :multiple="true" @@ -156,7 +161,9 @@ <div v-if="subAdminsGroups.length>0 && settings.isAdmin" :class="{'icon-loading-small': loading.subadmins}" class="subadmins"> - <NcMultiselect :close-on-select="false" + <label class="hidden-visually" :for="'subadmins'+user.id+rand">{{ t('settings', 'Set user as admin for') }}</label> + <NcMultiselect :id="'subadmins'+user.id+rand" + :close-on-select="false" :disabled="loading.subadmins||loading.all" :limit="2" :multiple="true" @@ -175,7 +182,9 @@ <div :title="usedSpace" :class="{'icon-loading-small': loading.quota}" class="quota"> - <NcMultiselect :allow-empty="false" + <label class="hidden-visually" :for="'quota'+user.id+rand">{{ t('settings', 'Select user quota') }}</label> + <NcMultiselect :id="'quota'+user.id+rand" + :allow-empty="false" :disabled="loading.quota||loading.all" :options="quotaOptions" :placeholder="t('settings', 'Select user quota')" @@ -191,7 +200,9 @@ <div v-if="showConfig.showLanguages" :class="{'icon-loading-small': loading.languages}" class="languages"> - <NcMultiselect :allow-empty="false" + <label class="hidden-visually" :for="'language'+user.id+rand">{{ t('settings', 'Set the language') }}</label> + <NcMultiselect :id="'language'+user.id+rand" + :allow-empty="false" :disabled="loading.languages||loading.all" :options="languages" :placeholder="t('settings', 'No language set')" diff --git a/apps/settings/templates/settings/admin/overview.php b/apps/settings/templates/settings/admin/overview.php index ef70374f270..c42e1e4efb8 100644 --- a/apps/settings/templates/settings/admin/overview.php +++ b/apps/settings/templates/settings/admin/overview.php @@ -68,7 +68,7 @@ <!-- should be the last part, so Updater can follow if enabled (it has no heading therefore). --> <h2><?php p($l->t('Version'));?></h2> <?php if ($theme->getTitle() === 'Nextcloud'): ?> - <p><strong><a href="<?php print_unescaped($theme->getBaseUrl()); ?>" rel="noreferrer noopener" target="_blank">Nextcloud Hub 4</a> (<?php p(OC_Util::getHumanVersion()) ?>)</strong></p> + <p><strong><a href="<?php print_unescaped($theme->getBaseUrl()); ?>" rel="noreferrer noopener" target="_blank">Nextcloud Hub 5</a> (<?php p(OC_Util::getHumanVersion()) ?>)</strong></p> <?php else: ?> <p><strong><a href="<?php print_unescaped($theme->getBaseUrl()); ?>" rel="noreferrer noopener" target="_blank"><?php p($theme->getTitle()); ?></a> <?php p(OC_Util::getHumanVersion()) ?></strong></p> <?php endif; ?> diff --git a/apps/systemtags/l10n/uk.js b/apps/systemtags/l10n/uk.js index 2f5aaf9e595..5ed202aa4c5 100644 --- a/apps/systemtags/l10n/uk.js +++ b/apps/systemtags/l10n/uk.js @@ -48,6 +48,16 @@ OC.L10N.register( "Collaborative tags" : "Позначки для співпраці", "Collaborative tagging functionality which shares tags among users." : "Функція спільного додавання тегів, яка ділиться тегами між користувачами.", "Collaborative tagging functionality which shares tags among users. Great for teams.\n\t(If you are a provider with a multi-tenancy installation, it is advised to deactivate this app as tags are shared.)" : "Функція спільного додавання тегів, яка ділиться тегами між користувачами. Чудово підходить для команд. \n(Якщо ви є провайдером із установкою з кількома клієнтами, радимо вимкнути цю програму, оскільки теги є спільними.)", + "Search or create collaborative tags" : "Пошук або створення спільних тегів", + "Collaborative tags …" : "Спільні теги ...", + "No tags to select, type to create a new tag" : "Немає тегів для вибору, введіть, щоб створити новий тег", + "Failed to load tags" : "Не вдалося завантажити теги", + "Failed to load selected tags" : "Не вдалося завантажити вибрані теги", + "Failed to select tag" : "Не вдалося вибрати тег", + "Failed to create tag" : "Не вдалося створити тег", + "Failed to delete tag" : "Не вдалося видалити тег", + "Failed to load last used tags" : "Не вдалося завантажити останні теги", + "Missing \"Content-Location\" header" : "Відсутній заголовок \"Content-Location\"", "Tagged files" : "Файли з позначками", "Select tags to filter by" : "Фільтр вибору позначок за", "No tags found" : "Не знайдено позначок", diff --git a/apps/systemtags/l10n/uk.json b/apps/systemtags/l10n/uk.json index 955884f27a0..c93c8862745 100644 --- a/apps/systemtags/l10n/uk.json +++ b/apps/systemtags/l10n/uk.json @@ -46,6 +46,16 @@ "Collaborative tags" : "Позначки для співпраці", "Collaborative tagging functionality which shares tags among users." : "Функція спільного додавання тегів, яка ділиться тегами між користувачами.", "Collaborative tagging functionality which shares tags among users. Great for teams.\n\t(If you are a provider with a multi-tenancy installation, it is advised to deactivate this app as tags are shared.)" : "Функція спільного додавання тегів, яка ділиться тегами між користувачами. Чудово підходить для команд. \n(Якщо ви є провайдером із установкою з кількома клієнтами, радимо вимкнути цю програму, оскільки теги є спільними.)", + "Search or create collaborative tags" : "Пошук або створення спільних тегів", + "Collaborative tags …" : "Спільні теги ...", + "No tags to select, type to create a new tag" : "Немає тегів для вибору, введіть, щоб створити новий тег", + "Failed to load tags" : "Не вдалося завантажити теги", + "Failed to load selected tags" : "Не вдалося завантажити вибрані теги", + "Failed to select tag" : "Не вдалося вибрати тег", + "Failed to create tag" : "Не вдалося створити тег", + "Failed to delete tag" : "Не вдалося видалити тег", + "Failed to load last used tags" : "Не вдалося завантажити останні теги", + "Missing \"Content-Location\" header" : "Відсутній заголовок \"Content-Location\"", "Tagged files" : "Файли з позначками", "Select tags to filter by" : "Фільтр вибору позначок за", "No tags found" : "Не знайдено позначок", diff --git a/apps/theming/css/default.css b/apps/theming/css/default.css index 3b732f1af3e..da9ad6e97de 100644 --- a/apps/theming/css/default.css +++ b/apps/theming/css/default.css @@ -63,14 +63,12 @@ --color-primary-light: #e5f0f5; --color-primary-light-text: #002a41; --color-primary-light-hover: #dbe5ea; - --color-primary-text-dark: #ededed; --color-primary-element: #006aa3; - --color-primary-element-default-hover: #329bd3; - --color-primary-element-text: #ffffff; --color-primary-element-hover: #3287b5; + --color-primary-element-text: #ffffff; --color-primary-element-light: #e5f0f5; - --color-primary-element-light-text: #002a41; --color-primary-element-light-hover: #dbe5ea; + --color-primary-element-light-text: #002a41; --color-primary-element-text-dark: #ededed; --gradient-primary-background: linear-gradient(40deg, var(--color-primary) 0%, var(--color-primary-hover) 100%); --image-background-default: url('/apps/theming/img/background/kamil-porembinski-clouds.jpg'); diff --git a/apps/theming/l10n/eo.js b/apps/theming/l10n/eo.js index d07abe4e8e6..f73e7fc8f6c 100644 --- a/apps/theming/l10n/eo.js +++ b/apps/theming/l10n/eo.js @@ -23,9 +23,12 @@ OC.L10N.register( "You are already using a custom theme. Theming app settings might be overwritten by that." : "Vi jam uzas propran etoson. Agordoj de la aplikaĵo „Etosoj“ povus esti anstataŭigitaj de ĝi.", "Theming" : "Etosoj", "Dark theme" : "Malhela etoso", + "Enable dark theme" : "Ŝaltu malhelan etoson", "Dyslexia font" : "Tiparo por limigi vortblindecon", + "Enable dyslexia font" : "Ŝaltu disleksian tiparon", "OpenDyslexic is a free typeface/font designed to mitigate some of the common reading errors caused by dyslexia." : "OpenDyslexic estas tiparo kreita por malpliigi legajn erarojn kaŭzitajn de vortblindeco (medicine „disleksio“).", "High contrast mode" : "Altkontrasta reĝimo", + "Enable high contrast mode" : "Ŝaltu altkontrastan reĝimon", "A high contrast mode to ease your navigation. Visual quality will be reduced but clarity will be increased." : "Altkontrasta reĝimo, kiu faciligas vian navigadon. Vida kvalito malpliiĝos, sed klareco pliiĝos.", "Legal notice" : "Leĝa regularo", "Privacy policy" : "Privateca regularo", @@ -46,6 +49,7 @@ OC.L10N.register( "Favicon" : "Retpaĝsimbolo", "Upload new favicon" : "Alŝuti novan retpaĝsimbolon", "Keyboard shortcuts" : "Fulmoklavoj", + "Universal access is very important to us. We follow web standards and check to make everything usable also without mouse, and assistive software such as screenreaders. We aim to be compliant with the {guidelines}Web Content Accessibility Guidelines{linkend} 2.1 on AA level, with the high contrast theme even on AAA level." : "Universala aliro estas tre grava por ni. Ni sekvas la retejo-standardojn kaj kontrolas, ĉu ĉio estas uzebla eĉ sen muso, kaj per helpa programaro, kiel ekzemple ekranlegiloj. Ni celas esti kongruaj kun la {guidelines}Reteja alireblecaj reguloj{linkend} 2.1 je la AA nivelo, kun alta kontrasta temo eĉ ĉe la AAA nivelo.", "Reset to default" : "Restarigi al defaŭltaj agordoj", "Upload" : "Alŝuti", "Remove background image" : "Forigi fonan bildon", diff --git a/apps/theming/l10n/eo.json b/apps/theming/l10n/eo.json index 03b3b9d4693..fcdfaeadad1 100644 --- a/apps/theming/l10n/eo.json +++ b/apps/theming/l10n/eo.json @@ -21,9 +21,12 @@ "You are already using a custom theme. Theming app settings might be overwritten by that." : "Vi jam uzas propran etoson. Agordoj de la aplikaĵo „Etosoj“ povus esti anstataŭigitaj de ĝi.", "Theming" : "Etosoj", "Dark theme" : "Malhela etoso", + "Enable dark theme" : "Ŝaltu malhelan etoson", "Dyslexia font" : "Tiparo por limigi vortblindecon", + "Enable dyslexia font" : "Ŝaltu disleksian tiparon", "OpenDyslexic is a free typeface/font designed to mitigate some of the common reading errors caused by dyslexia." : "OpenDyslexic estas tiparo kreita por malpliigi legajn erarojn kaŭzitajn de vortblindeco (medicine „disleksio“).", "High contrast mode" : "Altkontrasta reĝimo", + "Enable high contrast mode" : "Ŝaltu altkontrastan reĝimon", "A high contrast mode to ease your navigation. Visual quality will be reduced but clarity will be increased." : "Altkontrasta reĝimo, kiu faciligas vian navigadon. Vida kvalito malpliiĝos, sed klareco pliiĝos.", "Legal notice" : "Leĝa regularo", "Privacy policy" : "Privateca regularo", @@ -44,6 +47,7 @@ "Favicon" : "Retpaĝsimbolo", "Upload new favicon" : "Alŝuti novan retpaĝsimbolon", "Keyboard shortcuts" : "Fulmoklavoj", + "Universal access is very important to us. We follow web standards and check to make everything usable also without mouse, and assistive software such as screenreaders. We aim to be compliant with the {guidelines}Web Content Accessibility Guidelines{linkend} 2.1 on AA level, with the high contrast theme even on AAA level." : "Universala aliro estas tre grava por ni. Ni sekvas la retejo-standardojn kaj kontrolas, ĉu ĉio estas uzebla eĉ sen muso, kaj per helpa programaro, kiel ekzemple ekranlegiloj. Ni celas esti kongruaj kun la {guidelines}Reteja alireblecaj reguloj{linkend} 2.1 je la AA nivelo, kun alta kontrasta temo eĉ ĉe la AAA nivelo.", "Reset to default" : "Restarigi al defaŭltaj agordoj", "Upload" : "Alŝuti", "Remove background image" : "Forigi fonan bildon", diff --git a/apps/theming/lib/Themes/CommonThemeTrait.php b/apps/theming/lib/Themes/CommonThemeTrait.php index 17591c232bb..3a14382d3c0 100644 --- a/apps/theming/lib/Themes/CommonThemeTrait.php +++ b/apps/theming/lib/Themes/CommonThemeTrait.php @@ -38,9 +38,9 @@ trait CommonThemeTrait { * will change in between. */ protected function generatePrimaryVariables(string $colorMainBackground, string $colorMainText): array { - $colorPrimaryLight = $this->util->mix($this->primaryColor, $colorMainBackground, -80); - $colorPrimaryElement = $this->util->elementColor($this->primaryColor); - $colorPrimaryElementDefault = $this->util->elementColor($this->defaultPrimaryColor); + $isBrightColor = $this->util->isBrightColor($colorMainBackground); + $colorPrimaryElement = $this->util->elementColor($this->primaryColor, $isBrightColor); + $colorPrimaryLight = $this->util->mix($colorPrimaryElement, $colorMainBackground, -80); $colorPrimaryElementLight = $this->util->mix($colorPrimaryElement, $colorMainBackground, -80); // primary related colours @@ -61,16 +61,17 @@ trait CommonThemeTrait { '--color-primary-light' => $colorPrimaryLight, '--color-primary-light-text' => $this->util->mix($this->primaryColor, $this->util->invertTextColor($colorPrimaryLight) ? '#000000' : '#ffffff', -20), '--color-primary-light-hover' => $this->util->mix($colorPrimaryLight, $colorMainText, 90), - '--color-primary-text-dark' => $this->util->darken($this->util->invertTextColor($this->primaryColor) ? '#000000' : '#ffffff', 7), // used for buttons, inputs... '--color-primary-element' => $colorPrimaryElement, - '--color-primary-element-default-hover' => $this->util->mix($colorPrimaryElementDefault, $colorMainBackground, 60), - '--color-primary-element-text' => $this->util->invertTextColor($colorPrimaryElement) ? '#000000' : '#ffffff', '--color-primary-element-hover' => $this->util->mix($colorPrimaryElement, $colorMainBackground, 60), + '--color-primary-element-text' => $this->util->invertTextColor($colorPrimaryElement) ? '#000000' : '#ffffff', + + // used for hover/focus states '--color-primary-element-light' => $colorPrimaryElementLight, - '--color-primary-element-light-text' => $this->util->mix($colorPrimaryElement, $this->util->invertTextColor($colorPrimaryElementLight) ? '#000000' : '#ffffff', -20), '--color-primary-element-light-hover' => $this->util->mix($colorPrimaryElementLight, $colorMainText, 90), + '--color-primary-element-light-text' => $this->util->mix($colorPrimaryElement, $this->util->invertTextColor($colorPrimaryElementLight) ? '#000000' : '#ffffff', -20), + // mostly used for disabled states '--color-primary-element-text-dark' => $this->util->darken($this->util->invertTextColor($colorPrimaryElement) ? '#000000' : '#ffffff', 7), // to use like this: background-image: var(--gradient-primary-background); @@ -92,15 +93,6 @@ trait CommonThemeTrait { $variables['--image-background-default'] = "url('" . $this->themingDefaults->getBackground() . "')"; $variables['--color-background-plain'] = $this->defaultPrimaryColor; - // If primary as background has been request or if we have a custom primary colour - // let's not define the background image - if ($backgroundDeleted) { - $variables['--color-background-plain'] = $this->defaultPrimaryColor; - $variables['--image-background-plain'] = 'yes'; - // If no background image is set, we need to check against the shown primary colour - $variables['--background-image-invert-if-bright'] = $isDefaultPrimaryBright ? 'invert(100%)' : 'no'; - } - // Register image variables only if custom-defined foreach (ImageManager::SUPPORTED_IMAGE_KEYS as $image) { if ($this->imageManager->hasImage($image)) { @@ -110,8 +102,18 @@ trait CommonThemeTrait { } } + // If primary as background has been request or if we have a custom primary colour + // let's not define the background image + if ($backgroundDeleted) { + $variables['--color-background-plain'] = $this->defaultPrimaryColor; + $variables['--image-background-plain'] = 'yes'; + $variables['--image-background'] = 'no'; + // If no background image is set, we need to check against the shown primary colour + $variables['--background-image-invert-if-bright'] = $isDefaultPrimaryBright ? 'invert(100%)' : 'no'; + } + if ($hasCustomLogoHeader) { - $variables["--image-logoheader-custom"] = 'true'; + $variables['--image-logoheader-custom'] = 'true'; } return $variables; diff --git a/apps/theming/lib/Util.php b/apps/theming/lib/Util.php index a85dacd3de2..f5321dada67 100644 --- a/apps/theming/lib/Util.php +++ b/apps/theming/lib/Util.php @@ -52,10 +52,20 @@ class Util { } /** + * Should we invert the text on this background color? * @param string $color rgb color value * @return bool */ - public function invertTextColor($color) { + public function invertTextColor(string $color): bool { + return $this->isBrightColor($color); + } + + /** + * Is this color too bright ? + * @param string $color rgb color value + * @return bool + */ + public function isBrightColor(string $color): bool { $l = $this->calculateLuma($color); if ($l > 0.6) { return true; @@ -81,7 +91,7 @@ class Util { if (!$brightBackground && $luminance < 0.2) { // If the color is too dark in dark mode, we fall back to a brighter gray - return '#555555'; + return '#8c8c8c'; } return $color; diff --git a/apps/theming/src/components/BackgroundSettings.vue b/apps/theming/src/components/BackgroundSettings.vue index 6a34eb19ac5..0d07071cb93 100644 --- a/apps/theming/src/components/BackgroundSettings.vue +++ b/apps/theming/src/components/BackgroundSettings.vue @@ -329,7 +329,7 @@ export default { &__default { background-color: var(--color-primary-default); - background-image: var(--image-background-default); + background-image: var(--image-background-plain, var(--image-background-default)); } &__filepicker, &__default, &__color { diff --git a/apps/theming/src/components/admin/ColorPickerField.vue b/apps/theming/src/components/admin/ColorPickerField.vue index c9f1a8e4ef5..fd80a45e387 100644 --- a/apps/theming/src/components/admin/ColorPickerField.vue +++ b/apps/theming/src/components/admin/ColorPickerField.vue @@ -115,8 +115,22 @@ export default { width: 230px !important; border-radius: var(--border-radius-large) !important; background-color: var(--color-primary-default) !important; - &:hover { - background-color: var(--color-primary-element-default-hover) !important; + + // emulated hover state because it would not make sense + // to create a dedicated global variable for the color-primary-default + &:hover::after { + background-color: white; + content: ""; + position: absolute; + width: 100%; + height: 100%; + opacity: .2; + filter: var(--primary-invert-if-bright); + } + + // Above the ::after + &::v-deep * { + z-index: 1; } } } diff --git a/apps/theming/tests/CapabilitiesTest.php b/apps/theming/tests/CapabilitiesTest.php index 1de439a70e9..1c968383d53 100644 --- a/apps/theming/tests/CapabilitiesTest.php +++ b/apps/theming/tests/CapabilitiesTest.php @@ -111,7 +111,7 @@ class CapabilitiesTest extends TestCase { 'color-text' => '#ffffff', 'color-element' => '#000000', 'color-element-bright' => '#000000', - 'color-element-dark' => '#555555', + 'color-element-dark' => '#8c8c8c', 'logo' => 'http://localhost/logo5', 'background' => '#000000', 'background-plain' => true, @@ -127,7 +127,7 @@ class CapabilitiesTest extends TestCase { 'color-text' => '#ffffff', 'color-element' => '#000000', 'color-element-bright' => '#000000', - 'color-element-dark' => '#555555', + 'color-element-dark' => '#8c8c8c', 'logo' => 'http://localhost/logo5', 'background' => '#000000', 'background-plain' => true, diff --git a/apps/theming/tests/UtilTest.php b/apps/theming/tests/UtilTest.php index 8f6d972a2fc..789107d9fdf 100644 --- a/apps/theming/tests/UtilTest.php +++ b/apps/theming/tests/UtilTest.php @@ -112,7 +112,7 @@ class UtilTest extends TestCase { public function testElementColorOnDarkBackground() { $elementColor = $this->util->elementColor("#000000", false); - $this->assertEquals('#555555', $elementColor); + $this->assertEquals('#8c8c8c', $elementColor); } public function testElementColorOnBrightBackground() { diff --git a/apps/updatenotification/l10n/it.js b/apps/updatenotification/l10n/it.js index 28d1f494ddb..89b14eb89a2 100644 --- a/apps/updatenotification/l10n/it.js +++ b/apps/updatenotification/l10n/it.js @@ -24,6 +24,7 @@ OC.L10N.register( "The update check is not yet finished. Please refresh the page." : "Il controllo degli aggiornamenti non è ancora terminato. Aggiorna la pagina.", "Your version is up to date." : "La tua versione è aggiornata.", "A non-default update server is in use to be checked for updates:" : "Stai utilizzando un server non predefinito per controllare gli aggiornamenti:", + "You can change the update channel below which also affects the apps management page. E.g. after switching to the beta channel, beta app updates will be offered to you in the apps management page." : "Puoi modificare il canale di aggiornamento di seguito che influisce anche sulla pagina di gestione delle app. Per esempio. dopo il passaggio al canale beta, gli aggiornamenti dell'app beta ti verranno offerti nella pagina di gestione delle app.", "Update channel:" : "Canale di aggiornamento:", "You can always update to a newer version. But you can never downgrade to a more stable version." : "Puoi aggiornare sempre a una nuova versione. Ma non puoi mai tornare a una versione precedente.", "Notify members of the following groups about available updates:" : "Notifica ai membri dei seguenti gruppi la disponibilità di aggiornamenti:", diff --git a/apps/updatenotification/l10n/it.json b/apps/updatenotification/l10n/it.json index 120b32af632..b900190847b 100644 --- a/apps/updatenotification/l10n/it.json +++ b/apps/updatenotification/l10n/it.json @@ -22,6 +22,7 @@ "The update check is not yet finished. Please refresh the page." : "Il controllo degli aggiornamenti non è ancora terminato. Aggiorna la pagina.", "Your version is up to date." : "La tua versione è aggiornata.", "A non-default update server is in use to be checked for updates:" : "Stai utilizzando un server non predefinito per controllare gli aggiornamenti:", + "You can change the update channel below which also affects the apps management page. E.g. after switching to the beta channel, beta app updates will be offered to you in the apps management page." : "Puoi modificare il canale di aggiornamento di seguito che influisce anche sulla pagina di gestione delle app. Per esempio. dopo il passaggio al canale beta, gli aggiornamenti dell'app beta ti verranno offerti nella pagina di gestione delle app.", "Update channel:" : "Canale di aggiornamento:", "You can always update to a newer version. But you can never downgrade to a more stable version." : "Puoi aggiornare sempre a una nuova versione. Ma non puoi mai tornare a una versione precedente.", "Notify members of the following groups about available updates:" : "Notifica ai membri dei seguenti gruppi la disponibilità di aggiornamenti:", diff --git a/apps/updatenotification/l10n/uk.js b/apps/updatenotification/l10n/uk.js index dc7d73f6f74..3bec9e001d7 100644 --- a/apps/updatenotification/l10n/uk.js +++ b/apps/updatenotification/l10n/uk.js @@ -29,6 +29,7 @@ OC.L10N.register( "Update channel:" : "Канал оновлення:", "You can always update to a newer version. But you can never downgrade to a more stable version." : "Ви завжди зможете перейти на нову версію, проте не можна буде повернутися на стабільну версію.", "Notify members of the following groups about available updates:" : "Повідомляти учасників таких груп про доступні оновлення:", + "No groups" : "Без груп", "Only notifications for app updates are available." : "Доступні лише сповіщення про оновлення програми.", "The selected update channel makes dedicated notifications for the server obsolete." : "Вибраний канал оновлення робить спеціальні сповіщення для сервера застарілими.", "The selected update channel does not support updates of the server." : "Вибраний канал оновлення не підтримує оновлення сервера.", diff --git a/apps/updatenotification/l10n/uk.json b/apps/updatenotification/l10n/uk.json index 9df3b33d86e..18e9233d27b 100644 --- a/apps/updatenotification/l10n/uk.json +++ b/apps/updatenotification/l10n/uk.json @@ -27,6 +27,7 @@ "Update channel:" : "Канал оновлення:", "You can always update to a newer version. But you can never downgrade to a more stable version." : "Ви завжди зможете перейти на нову версію, проте не можна буде повернутися на стабільну версію.", "Notify members of the following groups about available updates:" : "Повідомляти учасників таких груп про доступні оновлення:", + "No groups" : "Без груп", "Only notifications for app updates are available." : "Доступні лише сповіщення про оновлення програми.", "The selected update channel makes dedicated notifications for the server obsolete." : "Вибраний канал оновлення робить спеціальні сповіщення для сервера застарілими.", "The selected update channel does not support updates of the server." : "Вибраний канал оновлення не підтримує оновлення сервера.", diff --git a/apps/user_status/l10n/tr.js b/apps/user_status/l10n/tr.js index ec02dbad47c..a6acfe9696e 100644 --- a/apps/user_status/l10n/tr.js +++ b/apps/user_status/l10n/tr.js @@ -1,7 +1,7 @@ OC.L10N.register( "user_status", { - "Recent statuses" : "Yakın zamandaki durumlar", + "Recent statuses" : "Son durumlar", "In a meeting" : "Toplantıda", "Commuting" : "İşe gidiyor/geliyor", "Out sick" : "Hasta", @@ -26,7 +26,7 @@ OC.L10N.register( "There was an error saving the status" : "Durum kaydedilirken bir sorun çıktı", "There was an error clearing the status" : "Durum kaldırılırken bir sorun çıktı", "There was an error reverting the status" : "Durum geri alınırken bir sorun çıktı", - "No recent status changes" : "Yakın zamanda değiştirilmiş bir durum yok", + "No recent status changes" : "Son zamanlarda durum değiştirilmemiş", "Away" : "Uzakta", "Do not disturb" : "Rahatsız etmeyin", "{status}, {timestamp}" : "{status}, {timestamp}", diff --git a/apps/user_status/l10n/tr.json b/apps/user_status/l10n/tr.json index e9d80aa9fb4..56c7def2140 100644 --- a/apps/user_status/l10n/tr.json +++ b/apps/user_status/l10n/tr.json @@ -1,5 +1,5 @@ { "translations": { - "Recent statuses" : "Yakın zamandaki durumlar", + "Recent statuses" : "Son durumlar", "In a meeting" : "Toplantıda", "Commuting" : "İşe gidiyor/geliyor", "Out sick" : "Hasta", @@ -24,7 +24,7 @@ "There was an error saving the status" : "Durum kaydedilirken bir sorun çıktı", "There was an error clearing the status" : "Durum kaldırılırken bir sorun çıktı", "There was an error reverting the status" : "Durum geri alınırken bir sorun çıktı", - "No recent status changes" : "Yakın zamanda değiştirilmiş bir durum yok", + "No recent status changes" : "Son zamanlarda durum değiştirilmemiş", "Away" : "Uzakta", "Do not disturb" : "Rahatsız etmeyin", "{status}, {timestamp}" : "{status}, {timestamp}", diff --git a/apps/user_status/l10n/uk.js b/apps/user_status/l10n/uk.js index c62a8b8287a..30a6b7787ff 100644 --- a/apps/user_status/l10n/uk.js +++ b/apps/user_status/l10n/uk.js @@ -12,13 +12,20 @@ OC.L10N.register( "View profile" : "Перегляд профілю", "Clear status after" : "Очистити статус після", "What is your status?" : "Який твій статус?", + "Previously set" : "Раніше встановлений", + "Reset status" : "Скинути статус", "Set status" : "Встановити статус", "Online status" : "Статус онлайну", "Status message" : "Повідомлення про статус", + "Your status was set automatically" : "Ваш статус встановлено автоматично", "Clear status message" : "Очистити повідомлення про стан", "Set status message" : "Встановити повідомлення про стан", + "Reset status to \"{icon} {message}\"" : "Скинути статус на \"{icon} {message}\"", + "Reset status to \"{message}\"" : "Скинути статус на \"{message}\"", + "Reset status to \"{icon}\"" : "Скинути статус на \"{icon}\"", "There was an error saving the status" : "Під час збереження статусу сталася помилка", "There was an error clearing the status" : "Під час очищення статусу сталася помилка", + "There was an error reverting the status" : "Виникла помилка при скиданні статусу", "No recent status changes" : "Останніх змін статусу немає", "Away" : "Піти", "Do not disturb" : "Не турбувати", diff --git a/apps/user_status/l10n/uk.json b/apps/user_status/l10n/uk.json index d9b7ab1a5e8..ae57b3385d3 100644 --- a/apps/user_status/l10n/uk.json +++ b/apps/user_status/l10n/uk.json @@ -10,13 +10,20 @@ "View profile" : "Перегляд профілю", "Clear status after" : "Очистити статус після", "What is your status?" : "Який твій статус?", + "Previously set" : "Раніше встановлений", + "Reset status" : "Скинути статус", "Set status" : "Встановити статус", "Online status" : "Статус онлайну", "Status message" : "Повідомлення про статус", + "Your status was set automatically" : "Ваш статус встановлено автоматично", "Clear status message" : "Очистити повідомлення про стан", "Set status message" : "Встановити повідомлення про стан", + "Reset status to \"{icon} {message}\"" : "Скинути статус на \"{icon} {message}\"", + "Reset status to \"{message}\"" : "Скинути статус на \"{message}\"", + "Reset status to \"{icon}\"" : "Скинути статус на \"{icon}\"", "There was an error saving the status" : "Під час збереження статусу сталася помилка", "There was an error clearing the status" : "Під час очищення статусу сталася помилка", + "There was an error reverting the status" : "Виникла помилка при скиданні статусу", "No recent status changes" : "Останніх змін статусу немає", "Away" : "Піти", "Do not disturb" : "Не турбувати", diff --git a/apps/user_status/lib/Controller/UserStatusController.php b/apps/user_status/lib/Controller/UserStatusController.php index aded923d07f..2d96cd90a40 100644 --- a/apps/user_status/lib/Controller/UserStatusController.php +++ b/apps/user_status/lib/Controller/UserStatusController.php @@ -169,16 +169,6 @@ class UserStatusController extends OCSController { * * @return DataResponse */ - public function clearStatus(): DataResponse { - $this->service->clearStatus($this->userId); - return new DataResponse([]); - } - - /** - * @NoAdminRequired - * - * @return DataResponse - */ public function clearMessage(): DataResponse { $this->service->clearMessage($this->userId); return new DataResponse([]); diff --git a/apps/user_status/tests/Unit/Controller/UserStatusControllerTest.php b/apps/user_status/tests/Unit/Controller/UserStatusControllerTest.php index e4d2ab61eee..ed0919eb9a5 100644 --- a/apps/user_status/tests/Unit/Controller/UserStatusControllerTest.php +++ b/apps/user_status/tests/Unit/Controller/UserStatusControllerTest.php @@ -326,15 +326,6 @@ class UserStatusControllerTest extends TestCase { ]; } - public function testClearStatus(): void { - $this->service->expects($this->once()) - ->method('clearStatus') - ->with('john.doe'); - - $response = $this->controller->clearStatus(); - $this->assertEquals([], $response->getData()); - } - public function testClearMessage(): void { $this->service->expects($this->once()) ->method('clearMessage') diff --git a/apps/weather_status/l10n/it.js b/apps/weather_status/l10n/it.js index 03fd1dd977f..f96363fcbd0 100644 --- a/apps/weather_status/l10n/it.js +++ b/apps/weather_status/l10n/it.js @@ -21,6 +21,18 @@ OC.L10N.register( "{temperature} {unit} partly cloudy" : "{temperature} {unit} parzialmente nuvoloso", "{temperature} {unit} foggy later today" : "{temperature} {unit} nebbioso più tardi in giornata", "{temperature} {unit} foggy" : "{temperature} {unit} nebbioso", + "{temperature} {unit} light rainfall later today" : "{temperature} {unit} deboli precipitazioni più tardi oggi", + "{temperature} {unit} light rainfall" : "{temperature} {unit} precipitazioni deboli", + "{temperature} {unit} rainfall later today" : "{temperature} {unit} pioggia più tardi oggi", + "{temperature} {unit} rainfall" : "{temperature} {unità} precipitazioni", + "{temperature} {unit} heavy rainfall later today" : "{temperature} {unit} forti precipitazioni più tardi oggi", + "{temperature} {unit} heavy rainfall" : "{temperature} {unit} forti precipitazioni", + "{temperature} {unit} rainfall showers later today" : "{temperature} {unit} rovesci di pioggia più tardi oggi", + "{temperature} {unit} rainfall showers" : "{temperature} {unit} rovesci di pioggia", + "{temperature} {unit} light rainfall showers later today" : "{temperature} {unit} deboli rovesci di pioggia più tardi oggi", + "{temperature} {unit} light rainfall showers" : "{temperature} {unit} deboli rovesci di pioggia", + "{temperature} {unit} heavy rainfall showers later today" : "{temperature} {unit} forti rovesci di pioggia più tardi oggi", + "{temperature} {unit} heavy rainfall showers" : "{temperature} {unit} forti rovesci di pioggia", "More weather for {adr}" : "Altro meteo per {adr}", "Loading weather" : "Caricamento meteo", "Remove from favorites" : "Rimuovi dai preferiti", diff --git a/apps/weather_status/l10n/it.json b/apps/weather_status/l10n/it.json index 2ef310af389..7e646acecfe 100644 --- a/apps/weather_status/l10n/it.json +++ b/apps/weather_status/l10n/it.json @@ -19,6 +19,18 @@ "{temperature} {unit} partly cloudy" : "{temperature} {unit} parzialmente nuvoloso", "{temperature} {unit} foggy later today" : "{temperature} {unit} nebbioso più tardi in giornata", "{temperature} {unit} foggy" : "{temperature} {unit} nebbioso", + "{temperature} {unit} light rainfall later today" : "{temperature} {unit} deboli precipitazioni più tardi oggi", + "{temperature} {unit} light rainfall" : "{temperature} {unit} precipitazioni deboli", + "{temperature} {unit} rainfall later today" : "{temperature} {unit} pioggia più tardi oggi", + "{temperature} {unit} rainfall" : "{temperature} {unità} precipitazioni", + "{temperature} {unit} heavy rainfall later today" : "{temperature} {unit} forti precipitazioni più tardi oggi", + "{temperature} {unit} heavy rainfall" : "{temperature} {unit} forti precipitazioni", + "{temperature} {unit} rainfall showers later today" : "{temperature} {unit} rovesci di pioggia più tardi oggi", + "{temperature} {unit} rainfall showers" : "{temperature} {unit} rovesci di pioggia", + "{temperature} {unit} light rainfall showers later today" : "{temperature} {unit} deboli rovesci di pioggia più tardi oggi", + "{temperature} {unit} light rainfall showers" : "{temperature} {unit} deboli rovesci di pioggia", + "{temperature} {unit} heavy rainfall showers later today" : "{temperature} {unit} forti rovesci di pioggia più tardi oggi", + "{temperature} {unit} heavy rainfall showers" : "{temperature} {unit} forti rovesci di pioggia", "More weather for {adr}" : "Altro meteo per {adr}", "Loading weather" : "Caricamento meteo", "Remove from favorites" : "Rimuovi dai preferiti", diff --git a/apps/workflowengine/l10n/tr.js b/apps/workflowengine/l10n/tr.js index 8d118b5204e..fcd8ed039c0 100644 --- a/apps/workflowengine/l10n/tr.js +++ b/apps/workflowengine/l10n/tr.js @@ -104,7 +104,7 @@ OC.L10N.register( "File system tag" : "Dosya sistemi etiketi", "is tagged with" : "şununla etiketlenmiş", "is not tagged with" : "şununla etiketlenmemiş", - "Request URL" : "İstek Adresi", + "Request URL" : "İstek adresi", "Request time" : "İstek zamanı", "between" : "şunların arasında olan", "not between" : "şunların arasında olmayan", diff --git a/apps/workflowengine/l10n/tr.json b/apps/workflowengine/l10n/tr.json index 57af176512e..fbb120ed96d 100644 --- a/apps/workflowengine/l10n/tr.json +++ b/apps/workflowengine/l10n/tr.json @@ -102,7 +102,7 @@ "File system tag" : "Dosya sistemi etiketi", "is tagged with" : "şununla etiketlenmiş", "is not tagged with" : "şununla etiketlenmemiş", - "Request URL" : "İstek Adresi", + "Request URL" : "İstek adresi", "Request time" : "İstek zamanı", "between" : "şunların arasında olan", "not between" : "şunların arasında olmayan", |