Compare commits

..

1 Commits

Author SHA1 Message Date
SebastianKrupinski f158d33d65 feat: use task processing to send emails
Signed-off-by: SebastianKrupinski <krupinskis05@gmail.com>
2025-11-27 09:57:37 -05:00
1886 changed files with 11602 additions and 14440 deletions
+1 -1
View File
@@ -69,7 +69,7 @@ updates:
target-branch: stable32
directories:
- "/"
- "/vendor-bin/behat"
- "/build/integration"
- "/vendor-bin/cs-fixer"
- "/vendor-bin/openapi-extractor"
- "/vendor-bin/phpunit"
@@ -90,7 +90,6 @@ jobs:
- name: Set up Nextcloud
run: |
composer install
mkdir data
echo '<?php $CONFIG=["${{ matrix.key }}" => ["class" => "OC\Files\ObjectStore\S3", "arguments" => ["bucket" => "nextcloud", "autocreate" => true, "key" => "nextcloud", "secret" => "bWluaW8tc2VjcmV0LWtleS1uZXh0Y2xvdWQ=", "hostname" => "localhost", "port" => 9000, "use_ssl" => false, "use_path_style" => true, "uploadPartSize" => 52428800]]];' > config/config.php
echo '<?php $CONFIG=["redis" => ["host" => "localhost", "port" => 6379], "memcache.local" => "\OC\Memcache\Redis", "memcache.distributed" => "\OC\Memcache\Redis"];' > config/redis.config.php
+6 -3
View File
@@ -131,9 +131,12 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Set up dependencies
run: |
composer install
- name: Set up production dependencies
run: composer i --no-dev
- name: Set up behat dependencies
working-directory: build/integration
run: composer i
- name: Set up Talk dependencies
if: ${{ matrix.test-suite == 'videoverification_features' }}
+27 -15
View File
@@ -26,10 +26,12 @@ jobs:
if: ${{ github.repository_owner != 'nextcloud-gmbh' }}
container: shivammathur/node:latest-i386
strategy:
fail-fast: false
matrix:
php-versions: ["8.4"]
php-versions: ["8.2", "8.3", "8.4"]
steps:
- name: Checkout server
@@ -38,22 +40,32 @@ jobs:
persist-credentials: false
submodules: true
- name: Set up dependencies
uses: docker://ghcr.io/nextcloud/continuous-integration-php8.4-32bit:latest
- name: Install tools
run: |
sudo apt-get update
sudo apt-get install -y ffmpeg imagemagick libmagickcore-6.q16-3-extra
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@bf6b4fbd49ca58e4608c9c89fba0b8d90bd2a39f #v2.35.5
with:
args: /bin/sh -c "
git config --global --add safe.directory /github/workspace &&
composer install --no-interaction"
php-version: ${{ matrix.php-versions }}
extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, imagick, intl, json, libxml, mbstring, openssl, pcntl, posix, redis, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite, apcu, ldap
coverage: none
ini-file: development
ini-values: apc.enabled=on, apc.enable_cli=on, disable_functions= # https://github.com/shivammathur/setup-php/discussions/573
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Set up dependencies
run: composer i
- name: Set up Nextcloud
uses: docker://ghcr.io/nextcloud/continuous-integration-php8.4-32bit:latest
with:
args: /bin/sh -c "
mkdir data &&
./occ maintenance:install --verbose --database=sqlite --database-name=nextcloud --database-user=autotest --database-pass=rootpassword --admin-user admin --admin-pass admin &&
php -f tests/enable_all.php"
env:
DB_PORT: 4444
run: |
mkdir data
./occ maintenance:install --verbose --database=sqlite --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=autotest --database-pass=rootpassword --admin-user admin --admin-pass admin
php -f tests/enable_all.php
- name: PHPUnit
uses: docker://ghcr.io/nextcloud/continuous-integration-php8.4-32bit:latest
with:
args: /bin/sh -c "composer run test -- --exclude-group PRIMARY-azure,PRIMARY-s3,PRIMARY-swift,Memcached,Redis,RoutingWeirdness"
run: composer run test -- --exclude-group PRIMARY-azure --exclude-group PRIMARY-s3 --exclude-group PRIMARY-swift --exclude-group Memcached --exclude-group Redis --exclude-group RoutingWeirdness
+1 -1
View File
@@ -256,7 +256,7 @@ SPDX-FileCopyrightText = "2025 Nextcloud GmbH and Nextcloud contributors"
SPDX-License-Identifier = "AGPL-3.0-or-later"
[[annotations]]
path = ["composer.json", "composer.lock", ".github/CODEOWNERS", "__tests__/tsconfig.json", "tsconfig.json", "apps/**/composer/composer.json", "apps/**/composer/composer.lock", "apps/**/composer/composer/installed.json"]
path = ["composer.json", "composer.lock", ".github/CODEOWNERS", "__tests__/tsconfig.json", "tsconfig.json", "build/integration/composer.**", "vendor-bin/**/composer.json", "vendor-bin/**/composer.lock", "apps/**/composer/composer.json", "apps/**/composer/composer.lock", "apps/**/composer/composer/installed.json"]
precedence = "aggregate"
SPDX-FileCopyrightText = "2011-2016 ownCloud, Inc., 2016-2024 Nextcloud GmbH and Nextcloud contributors"
SPDX-License-Identifier = "AGPL-3.0-only OR AGPL-3.0-or-later"
@@ -19,7 +19,6 @@ return array(
'OCA\\AdminAudit\\IAuditLogger' => $baseDir . '/../lib/IAuditLogger.php',
'OCA\\AdminAudit\\Listener\\AppManagementEventListener' => $baseDir . '/../lib/Listener/AppManagementEventListener.php',
'OCA\\AdminAudit\\Listener\\AuthEventListener' => $baseDir . '/../lib/Listener/AuthEventListener.php',
'OCA\\AdminAudit\\Listener\\CacheEventListener' => $baseDir . '/../lib/Listener/CacheEventListener.php',
'OCA\\AdminAudit\\Listener\\ConsoleEventListener' => $baseDir . '/../lib/Listener/ConsoleEventListener.php',
'OCA\\AdminAudit\\Listener\\CriticalActionPerformedEventListener' => $baseDir . '/../lib/Listener/CriticalActionPerformedEventListener.php',
'OCA\\AdminAudit\\Listener\\FileEventListener' => $baseDir . '/../lib/Listener/FileEventListener.php',
@@ -7,14 +7,14 @@ namespace Composer\Autoload;
class ComposerStaticInitAdminAudit
{
public static $prefixLengthsPsr4 = array (
'O' =>
'O' =>
array (
'OCA\\AdminAudit\\' => 15,
),
);
public static $prefixDirsPsr4 = array (
'OCA\\AdminAudit\\' =>
'OCA\\AdminAudit\\' =>
array (
0 => __DIR__ . '/..' . '/../lib',
),
@@ -34,7 +34,6 @@ class ComposerStaticInitAdminAudit
'OCA\\AdminAudit\\IAuditLogger' => __DIR__ . '/..' . '/../lib/IAuditLogger.php',
'OCA\\AdminAudit\\Listener\\AppManagementEventListener' => __DIR__ . '/..' . '/../lib/Listener/AppManagementEventListener.php',
'OCA\\AdminAudit\\Listener\\AuthEventListener' => __DIR__ . '/..' . '/../lib/Listener/AuthEventListener.php',
'OCA\\AdminAudit\\Listener\\CacheEventListener' => __DIR__ . '/..' . '/../lib/Listener/CacheEventListener.php',
'OCA\\AdminAudit\\Listener\\ConsoleEventListener' => __DIR__ . '/..' . '/../lib/Listener/ConsoleEventListener.php',
'OCA\\AdminAudit\\Listener\\CriticalActionPerformedEventListener' => __DIR__ . '/..' . '/../lib/Listener/CriticalActionPerformedEventListener.php',
'OCA\\AdminAudit\\Listener\\FileEventListener' => __DIR__ . '/..' . '/../lib/Listener/FileEventListener.php',
@@ -20,7 +20,6 @@ use OCA\AdminAudit\AuditLogger;
use OCA\AdminAudit\IAuditLogger;
use OCA\AdminAudit\Listener\AppManagementEventListener;
use OCA\AdminAudit\Listener\AuthEventListener;
use OCA\AdminAudit\Listener\CacheEventListener;
use OCA\AdminAudit\Listener\ConsoleEventListener;
use OCA\AdminAudit\Listener\CriticalActionPerformedEventListener;
use OCA\AdminAudit\Listener\FileEventListener;
@@ -41,8 +40,6 @@ use OCP\Authentication\TwoFactorAuth\TwoFactorProviderChallengeFailed;
use OCP\Authentication\TwoFactorAuth\TwoFactorProviderChallengePassed;
use OCP\Console\ConsoleEvent;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\Cache\CacheEntryInsertedEvent;
use OCP\Files\Cache\CacheEntryRemovedEvent;
use OCP\Files\Events\Node\BeforeNodeDeletedEvent;
use OCP\Files\Events\Node\BeforeNodeReadEvent;
use OCP\Files\Events\Node\NodeCopiedEvent;
@@ -126,10 +123,6 @@ class Application extends App implements IBootstrap {
// Console events
$context->registerEventListener(ConsoleEvent::class, ConsoleEventListener::class);
// Cache events
$context->registerEventListener(CacheEntryInsertedEvent::class, CacheEventListener::class);
$context->registerEventListener(CacheEntryRemovedEvent::class, CacheEventListener::class);
}
public function boot(IBootContext $context): void {
@@ -1,51 +0,0 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\AdminAudit\Listener;
use OCA\AdminAudit\Actions\Action;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\Files\Cache\CacheEntryInsertedEvent;
use OCP\Files\Cache\CacheEntryRemovedEvent;
/**
* @template-implements IEventListener<CacheEntryInsertedEvent|CacheEntryRemovedEvent>
*/
class CacheEventListener extends Action implements IEventListener {
public function handle(Event $event): void {
if ($event instanceof CacheEntryInsertedEvent) {
$this->entryInserted($event);
} elseif ($event instanceof CacheEntryRemovedEvent) {
$this->entryRemoved($event);
}
}
private function entryInserted(CacheEntryInsertedEvent $event): void {
$this->log('Cache entry inserted for fileid "%1$d", path "%2$s" on storageid "%3$d"',
[
'fileid' => $event->getFileId(),
'path' => $event->getPath(),
'storageid' => $event->getStorageId(),
],
['fileid', 'path', 'storageid']
);
}
private function entryRemoved(CacheEntryRemovedEvent $event): void {
$this->log('Cache entry removed for fileid "%1$d", path "%2$s" on storageid "%3$d"',
[
'fileid' => $event->getFileId(),
'path' => $event->getPath(),
'storageid' => $event->getStorageId(),
],
['fileid', 'path', 'storageid']
);
}
}
@@ -7,14 +7,14 @@ namespace Composer\Autoload;
class ComposerStaticInitCloudFederationAPI
{
public static $prefixLengthsPsr4 = array (
'O' =>
'O' =>
array (
'OCA\\CloudFederationAPI\\' => 23,
),
);
public static $prefixDirsPsr4 = array (
'OCA\\CloudFederationAPI\\' =>
'OCA\\CloudFederationAPI\\' =>
array (
0 => __DIR__ . '/..' . '/../lib',
),
@@ -7,14 +7,14 @@ namespace Composer\Autoload;
class ComposerStaticInitComments
{
public static $prefixLengthsPsr4 = array (
'O' =>
'O' =>
array (
'OCA\\Comments\\' => 13,
),
);
public static $prefixDirsPsr4 = array (
'OCA\\Comments\\' =>
'OCA\\Comments\\' =>
array (
0 => __DIR__ . '/..' . '/../lib',
),
-1
View File
@@ -17,7 +17,6 @@ OC.L10N.register(
"Delete comment" : "Borrar comentario",
"Cancel edit" : "Cacelar edición",
"New comment" : "Comentario nuevo",
"Write a comment …" : "Escribe un comentario ….",
"Post comment" : "Publicar comentario",
"@ for mentions, : for emoji, / for smart picker" : "@ para menciones, : para emoji, / para selector inteligente",
"Could not reload comments" : "No se pudieron recargar los comentarios",
-1
View File
@@ -15,7 +15,6 @@
"Delete comment" : "Borrar comentario",
"Cancel edit" : "Cacelar edición",
"New comment" : "Comentario nuevo",
"Write a comment …" : "Escribe un comentario ….",
"Post comment" : "Publicar comentario",
"@ for mentions, : for emoji, / for smart picker" : "@ para menciones, : para emoji, / para selector inteligente",
"Could not reload comments" : "No se pudieron recargar los comentarios",
-1
View File
@@ -15,7 +15,6 @@ OC.L10N.register(
"Delete comment" : "Poista kommentti",
"Cancel edit" : "Peruuta muokkaus",
"New comment" : "Uusi kommentti",
"Write a comment …" : "Kirjoita kommentti …",
"Post comment" : "Lähetä viesti",
"@ for mentions, : for emoji, / for smart picker" : "@ maininnoille, : emojille, / älykkäälle valitsimelle",
"Could not reload comments" : "Kommenttien lataus epäonnistui",
-1
View File
@@ -13,7 +13,6 @@
"Delete comment" : "Poista kommentti",
"Cancel edit" : "Peruuta muokkaus",
"New comment" : "Uusi kommentti",
"Write a comment …" : "Kirjoita kommentti …",
"Post comment" : "Lähetä viesti",
"@ for mentions, : for emoji, / for smart picker" : "@ maininnoille, : emojille, / älykkäälle valitsimelle",
"Could not reload comments" : "Kommenttien lataus epäonnistui",
+1 -1
View File
@@ -17,7 +17,7 @@ OC.L10N.register(
"Delete comment" : "Supprimer le commentaire",
"Cancel edit" : "Annuler les modifications",
"New comment" : "Nouveau commentaire",
"Write a comment …" : "Écrire un commentaire…",
"Write a comment …" : "Écrire un commentaire …",
"Post comment" : "Publier le commentaire",
"@ for mentions, : for emoji, / for smart picker" : "@ pour les mentions, : pour les émojis, / pour le sélecteur intelligent",
"Could not reload comments" : "Impossible de recharger les commentaires",
+1 -1
View File
@@ -15,7 +15,7 @@
"Delete comment" : "Supprimer le commentaire",
"Cancel edit" : "Annuler les modifications",
"New comment" : "Nouveau commentaire",
"Write a comment …" : "Écrire un commentaire…",
"Write a comment …" : "Écrire un commentaire …",
"Post comment" : "Publier le commentaire",
"@ for mentions, : for emoji, / for smart picker" : "@ pour les mentions, : pour les émojis, / pour le sélecteur intelligent",
"Could not reload comments" : "Impossible de recharger les commentaires",
+14 -12
View File
@@ -18,6 +18,8 @@ use OCP\IUserManager;
use OCP\L10N\IFactory;
class Provider implements IProvider {
protected ?IL10N $l = null;
public function __construct(
protected IFactory $languageFactory,
protected IURLGenerator $url,
@@ -40,9 +42,9 @@ class Provider implements IProvider {
throw new UnknownActivityException();
}
if ($event->getSubject() === 'add_comment_subject') {
$l = $this->languageFactory->get('comments', $language);
$this->l = $this->languageFactory->get('comments', $language);
if ($event->getSubject() === 'add_comment_subject') {
$this->parseMessage($event);
if ($this->activityManager->getRequirePNG()) {
$event->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/comment.png')));
@@ -52,13 +54,13 @@ class Provider implements IProvider {
if ($this->activityManager->isFormattingFilteredObject()) {
try {
return $this->parseShortVersion($event, $l);
return $this->parseShortVersion($event);
} catch (UnknownActivityException) {
// Ignore and simply use the long version...
}
}
return $this->parseLongVersion($event, $l);
return $this->parseLongVersion($event);
}
throw new UnknownActivityException();
@@ -67,15 +69,15 @@ class Provider implements IProvider {
/**
* @throws UnknownActivityException
*/
protected function parseShortVersion(IEvent $event, IL10N $l): IEvent {
protected function parseShortVersion(IEvent $event): IEvent {
$subjectParameters = $this->getSubjectParameters($event);
if ($event->getSubject() === 'add_comment_subject') {
if ($subjectParameters['actor'] === $this->activityManager->getCurrentUserId()) {
$event->setRichSubject($l->t('You commented'), []);
$event->setRichSubject($this->l->t('You commented'), []);
} else {
$author = $this->generateUserParameter($subjectParameters['actor']);
$event->setRichSubject($l->t('{author} commented'), [
$event->setRichSubject($this->l->t('{author} commented'), [
'author' => $author,
]);
}
@@ -89,24 +91,24 @@ class Provider implements IProvider {
/**
* @throws UnknownActivityException
*/
protected function parseLongVersion(IEvent $event, IL10N $l): IEvent {
protected function parseLongVersion(IEvent $event): IEvent {
$subjectParameters = $this->getSubjectParameters($event);
if ($event->getSubject() === 'add_comment_subject') {
if ($subjectParameters['actor'] === $this->activityManager->getCurrentUserId()) {
$event->setParsedSubject($l->t('You commented on %1$s', [
$event->setParsedSubject($this->l->t('You commented on %1$s', [
$subjectParameters['filePath'],
]))
->setRichSubject($l->t('You commented on {file}'), [
->setRichSubject($this->l->t('You commented on {file}'), [
'file' => $this->generateFileParameter($subjectParameters['fileId'], $subjectParameters['filePath']),
]);
} else {
$author = $this->generateUserParameter($subjectParameters['actor']);
$event->setParsedSubject($l->t('%1$s commented on %2$s', [
$event->setParsedSubject($this->l->t('%1$s commented on %2$s', [
$author['name'],
$subjectParameters['filePath'],
]))
->setRichSubject($l->t('{author} commented on {file}'), [
->setRichSubject($this->l->t('{author} commented on {file}'), [
'author' => $author,
'file' => $this->generateFileParameter($subjectParameters['fileId'], $subjectParameters['filePath']),
]);
+3 -3
View File
@@ -11,7 +11,7 @@ use OCP\IL10N;
class Setting extends ActivitySettings {
public function __construct(
protected readonly IL10N $l,
protected IL10N $l,
) {
}
@@ -23,11 +23,11 @@ class Setting extends ActivitySettings {
return $this->l->t('<strong>Comments</strong> for files');
}
public function getGroupIdentifier(): string {
public function getGroupIdentifier() {
return 'files';
}
public function getGroupName(): string {
public function getGroupName() {
return $this->l->t('Files');
}
@@ -27,10 +27,6 @@ class CommentsEntityEventListener implements IEventListener {
return;
}
if ($this->userId === null) {
return;
}
$event->addEntityCollection('files', function ($name): bool {
$nodes = $this->rootFolder->getUserFolder($this->userId)->getById((int)$name);
return !empty($nodes);
@@ -35,7 +35,8 @@ class CommentsEventListener implements IEventListener {
}
$eventType = $event->getEvent();
if ($eventType === CommentsEvent::EVENT_ADD) {
if ($eventType === CommentsEvent::EVENT_ADD
) {
$this->notificationHandler($event);
$this->activityHandler($event);
return;
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import type { Folder, View } from '@nextcloud/files'
import type { View } from '@nextcloud/files'
import { File, FileAction, Permission } from '@nextcloud/files'
import { describe, expect, test, vi } from 'vitest'
@@ -26,41 +26,15 @@ describe('Inline unread comments action display name tests', () => {
attributes: {
'comments-unread': 1,
},
root: '/files/admin',
})
expect(action).toBeInstanceOf(FileAction)
expect(action.id).toBe('comments-unread')
expect(action.displayName({
nodes: [file],
view,
folder: {} as Folder,
contents: [],
})).toBe('')
expect(action.title!({
nodes: [file],
view,
folder: {} as Folder,
contents: [],
})).toBe('1 new comment')
expect(action.iconSvgInline({
nodes: [file],
view,
folder: {} as Folder,
contents: [],
})).toMatch(/<svg.+<\/svg>/)
expect(action.enabled!({
nodes: [file],
view,
folder: {} as Folder,
contents: [],
})).toBe(true)
expect(action.inline!({
nodes: [file],
view,
folder: {} as Folder,
contents: [],
})).toBe(true)
expect(action.displayName([file], view)).toBe('')
expect(action.title!([file], view)).toBe('1 new comment')
expect(action.iconSvgInline([], view)).toMatch(/<svg.+<\/svg>/)
expect(action.enabled!([file], view)).toBe(true)
expect(action.inline!(file, view)).toBe(true)
expect(action.default).toBeUndefined()
expect(action.order).toBe(-140)
})
@@ -75,21 +49,10 @@ describe('Inline unread comments action display name tests', () => {
attributes: {
'comments-unread': 2,
},
root: '/files/admin',
})
expect(action.displayName({
nodes: [file],
view,
folder: {} as Folder,
contents: [],
})).toBe('')
expect(action.title!({
nodes: [file],
view,
folder: {} as Folder,
contents: [],
})).toBe('2 new comments')
expect(action.displayName([file], view)).toBe('')
expect(action.title!([file], view)).toBe('2 new comments')
})
})
@@ -101,16 +64,10 @@ describe('Inline unread comments action enabled tests', () => {
owner: 'admin',
mime: 'text/plain',
permissions: Permission.ALL,
attributes: {},
root: '/files/admin',
attributes: { },
})
expect(action.enabled!({
nodes: [file],
view,
folder: {} as Folder,
contents: [],
})).toBe(false)
expect(action.enabled!([file], view)).toBe(false)
})
test('Action is disabled when file does not have unread comments', () => {
@@ -123,15 +80,9 @@ describe('Inline unread comments action enabled tests', () => {
attributes: {
'comments-unread': 0,
},
root: '/files/admin',
})
expect(action.enabled!({
nodes: [file],
view,
folder: {} as Folder,
contents: [],
})).toBe(false)
expect(action.enabled!([file], view)).toBe(false)
})
test('Action is enabled when file has a single unread comment', () => {
@@ -144,15 +95,9 @@ describe('Inline unread comments action enabled tests', () => {
attributes: {
'comments-unread': 1,
},
root: '/files/admin',
})
expect(action.enabled!({
nodes: [file],
view,
folder: {} as Folder,
contents: [],
})).toBe(true)
expect(action.enabled!([file], view)).toBe(true)
})
test('Action is enabled when file has a two unread comments', () => {
@@ -165,15 +110,9 @@ describe('Inline unread comments action enabled tests', () => {
attributes: {
'comments-unread': 2,
},
root: '/files/admin',
})
expect(action.enabled!({
nodes: [file],
view,
folder: {} as Folder,
contents: [],
})).toBe(true)
expect(action.enabled!([file], view)).toBe(true)
})
})
@@ -200,15 +139,9 @@ describe('Inline unread comments action execute tests', () => {
attributes: {
'comments-unread': 1,
},
root: '/files/admin',
})
const result = await action.exec!({
nodes: [file],
view,
folder: {} as Folder,
contents: [],
})
const result = await action.exec!(file, view, '/')
expect(result).toBe(null)
expect(setActiveTabMock).toBeCalledWith('comments')
@@ -240,15 +173,9 @@ describe('Inline unread comments action execute tests', () => {
attributes: {
'comments-unread': 1,
},
root: '/files/admin',
})
const result = await action.exec!({
nodes: [file],
view,
folder: {} as Folder,
contents: [],
})
const result = await action.exec!(file, view, '/')
expect(result).toBe(false)
expect(setActiveTabMock).toBeCalledWith('comments')
@@ -2,6 +2,9 @@
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import type { Node } from '@nextcloud/files'
import CommentProcessingSvg from '@mdi/svg/svg/comment-processing.svg?raw'
import { FileAction } from '@nextcloud/files'
import { n, t } from '@nextcloud/l10n'
@@ -10,9 +13,9 @@ import logger from '../logger.js'
export const action = new FileAction({
id: 'comments-unread',
title({ nodes }) {
const unread = nodes[0]?.attributes['comments-unread'] as number | undefined
if (typeof unread === 'number' && unread >= 0) {
title(nodes: Node[]) {
const unread = nodes[0].attributes['comments-unread'] as number
if (unread >= 0) {
return n('comments', '1 new comment', '{unread} new comments', unread, { unread })
}
return t('comments', 'Comment')
@@ -23,19 +26,15 @@ export const action = new FileAction({
iconSvgInline: () => CommentProcessingSvg,
enabled({ nodes }) {
const unread = nodes[0]?.attributes?.['comments-unread'] as number | undefined
enabled(nodes: Node[]) {
const unread = nodes[0].attributes['comments-unread'] as number | undefined
return typeof unread === 'number' && unread > 0
},
async exec({ nodes }) {
if (nodes.length !== 1 || !nodes[0]) {
return false
}
async exec(node: Node) {
try {
window.OCA.Files.Sidebar.setActiveTab('comments')
await window.OCA.Files.Sidebar.open(nodes[0].path)
await window.OCA.Files.Sidebar.open(node.path)
return null
} catch (error) {
logger.error('Error while opening sidebar', { error })
+2 -9
View File
@@ -103,7 +103,6 @@
:class="{ 'comment__message--expanded': expanded }"
:text="richContent.message"
:arguments="richContent.mentions"
use-markdown
@click.native="onExpand" />
</div>
</component>
@@ -377,19 +376,13 @@ $comment-padding: 10px;
&__message {
white-space: pre-wrap;
word-break: normal;
max-height: 200px;
overflow: auto;
scrollbar-gutter: stable;
scrollbar-width: thin;
max-height: 70px;
overflow: hidden;
margin-top: -6px;
&--expanded {
max-height: none;
overflow: visible;
}
:deep(img) {
max-width: 100%;
height: auto;
}
}
}
@@ -12,7 +12,7 @@ use OCA\Comments\Activity\Listener;
use OCP\Activity\IEvent;
use OCP\Activity\IManager;
use OCP\App\IAppManager;
use OCP\Comments\Events\CommentAddedEvent;
use OCP\Comments\CommentsEvent;
use OCP\Comments\IComment;
use OCP\Files\Config\ICachedMountFileInfo;
use OCP\Files\Config\IMountProviderCollection;
@@ -66,7 +66,14 @@ class ListenerTest extends TestCase {
->method('getObjectType')
->willReturn('files');
$event = new CommentAddedEvent($comment);
/** @var CommentsEvent|MockObject $event */
$event = $this->createMock(CommentsEvent::class);
$event->expects($this->any())
->method('getComment')
->willReturn($comment);
$event->expects($this->any())
->method('getEvent')
->willReturn(CommentsEvent::EVENT_ADD);
/** @var IUser|MockObject $ownerUser */
$ownerUser = $this->createMock(IUser::class);
+12 -14
View File
@@ -12,10 +12,6 @@ use OCA\Comments\Activity\Listener as ActivityListener;
use OCA\Comments\Listener\CommentsEventListener;
use OCA\Comments\Notification\Listener as NotificationListener;
use OCP\Comments\CommentsEvent;
use OCP\Comments\Events\BeforeCommentUpdatedEvent;
use OCP\Comments\Events\CommentAddedEvent;
use OCP\Comments\Events\CommentDeletedEvent;
use OCP\Comments\Events\CommentUpdatedEvent;
use OCP\Comments\IComment;
use PHPUnit\Framework\MockObject\MockObject;
use Test\TestCase;
@@ -54,10 +50,10 @@ class EventHandlerTest extends TestCase {
public static function handledProvider(): array {
return [
['delete'],
['update'],
['pre_update'],
['add']
[CommentsEvent::EVENT_DELETE],
[CommentsEvent::EVENT_UPDATE],
[CommentsEvent::EVENT_PRE_UPDATE],
[CommentsEvent::EVENT_ADD]
];
}
@@ -69,12 +65,14 @@ class EventHandlerTest extends TestCase {
->method('getObjectType')
->willReturn('files');
$event = match ($eventType) {
'add' => new CommentAddedEvent($comment),
'pre_update' => new BeforeCommentUpdatedEvent($comment),
'update' => new CommentUpdatedEvent($comment),
'delete' => new CommentDeletedEvent($comment),
};
/** @var CommentsEvent|MockObject $event */
$event = $this->createMock(CommentsEvent::class);
$event->expects($this->atLeastOnce())
->method('getComment')
->willReturn($comment);
$event->expects($this->atLeastOnce())
->method('getEvent')
->willReturn($eventType);
$this->notificationListener->expects($this->once())
->method('evaluate')
@@ -8,10 +8,7 @@
namespace OCA\Comments\Tests\Unit\Notification;
use OCA\Comments\Notification\Listener;
use OCP\Comments\Events\BeforeCommentUpdatedEvent;
use OCP\Comments\Events\CommentAddedEvent;
use OCP\Comments\Events\CommentDeletedEvent;
use OCP\Comments\Events\CommentUpdatedEvent;
use OCP\Comments\CommentsEvent;
use OCP\Comments\IComment;
use OCP\IURLGenerator;
use OCP\IUserManager;
@@ -40,10 +37,10 @@ class ListenerTest extends TestCase {
public static function eventProvider(): array {
return [
['add', 'notify'],
['update', 'notify'],
['pre_update', 'markProcessed'],
['delete', 'markProcessed']
[CommentsEvent::EVENT_ADD, 'notify'],
[CommentsEvent::EVENT_UPDATE, 'notify'],
[CommentsEvent::EVENT_PRE_UPDATE, 'markProcessed'],
[CommentsEvent::EVENT_DELETE, 'markProcessed']
];
}
@@ -52,7 +49,7 @@ class ListenerTest extends TestCase {
* @param string $notificationMethod
*/
#[\PHPUnit\Framework\Attributes\DataProvider('eventProvider')]
public function testEvaluate(string $eventType, $notificationMethod): void {
public function testEvaluate($eventType, $notificationMethod): void {
/** @var IComment|MockObject $comment */
$comment = $this->createMock(IComment::class);
$comment->expects($this->any())
@@ -75,12 +72,14 @@ class ListenerTest extends TestCase {
->method('getId')
->willReturn('1234');
$event = match ($eventType) {
'add' => new CommentAddedEvent($comment),
'pre_update' => new BeforeCommentUpdatedEvent($comment),
'update' => new CommentUpdatedEvent($comment),
'delete' => new CommentDeletedEvent($comment),
};
/** @var CommentsEvent|MockObject $event */
$event = $this->createMock(CommentsEvent::class);
$event->expects($this->once())
->method('getComment')
->willReturn($comment);
$event->expects(($this->any()))
->method(('getEvent'))
->willReturn($eventType);
/** @var INotification|MockObject $notification */
$notification = $this->createMock(INotification::class);
@@ -125,12 +124,14 @@ class ListenerTest extends TestCase {
->method('getMentions')
->willReturn([]);
$event = match ($eventType) {
'add' => new CommentAddedEvent($comment),
'pre_update' => new BeforeCommentUpdatedEvent($comment),
'update' => new CommentUpdatedEvent($comment),
'delete' => new CommentDeletedEvent($comment),
};
/** @var CommentsEvent|MockObject $event */
$event = $this->createMock(CommentsEvent::class);
$event->expects($this->once())
->method('getComment')
->willReturn($comment);
$event->expects(($this->any()))
->method(('getEvent'))
->willReturn($eventType);
$this->notificationManager->expects($this->never())
->method('createNotification');
@@ -161,7 +162,14 @@ class ListenerTest extends TestCase {
->method('getId')
->willReturn('1234');
$event = new CommentAddedEvent($comment);
/** @var CommentsEvent|MockObject $event */
$event = $this->createMock(CommentsEvent::class);
$event->expects($this->once())
->method('getComment')
->willReturn($comment);
$event->expects(($this->any()))
->method(('getEvent'))
->willReturn(CommentsEvent::EVENT_ADD);
/** @var INotification|MockObject $notification */
$notification = $this->createMock(INotification::class);
@@ -7,14 +7,14 @@ namespace Composer\Autoload;
class ComposerStaticInitContactsInteraction
{
public static $prefixLengthsPsr4 = array (
'O' =>
'O' =>
array (
'OCA\\ContactsInteraction\\' => 24,
),
);
public static $prefixDirsPsr4 = array (
'OCA\\ContactsInteraction\\' =>
'OCA\\ContactsInteraction\\' =>
array (
0 => __DIR__ . '/..' . '/../lib',
),
@@ -7,14 +7,14 @@ namespace Composer\Autoload;
class ComposerStaticInitDashboard
{
public static $prefixLengthsPsr4 = array (
'O' =>
'O' =>
array (
'OCA\\Dashboard\\' => 14,
),
);
public static $prefixDirsPsr4 = array (
'OCA\\Dashboard\\' =>
'OCA\\Dashboard\\' =>
array (
0 => __DIR__ . '/..' . '/../lib',
),
+1 -1
View File
@@ -10,7 +10,7 @@
<name>WebDAV</name>
<summary>WebDAV endpoint</summary>
<description>WebDAV endpoint</description>
<version>1.36.0</version>
<version>1.35.0</version>
<licence>agpl</licence>
<author>owncloud.org</author>
<namespace>DAV</namespace>
+38 -46
View File
@@ -68,55 +68,47 @@ $requestUri = Server::get(IRequest::class)->getRequestUri();
$linkCheckPlugin = new PublicLinkCheckPlugin();
$filesDropPlugin = new FilesDropPlugin();
$server = $serverFactory->createServer(
true,
$baseuri,
$requestUri,
$authPlugin,
function (\Sabre\DAV\Server $server) use (
$authBackend,
$linkCheckPlugin,
$filesDropPlugin
) {
$isAjax = in_array('XMLHttpRequest', explode(',', $_SERVER['HTTP_X_REQUESTED_WITH'] ?? ''));
/** @var FederatedShareProvider $shareProvider */
$federatedShareProvider = Server::get(FederatedShareProvider::class);
if ($federatedShareProvider->isOutgoingServer2serverShareEnabled() === false && !$isAjax) {
// this is what is thrown when trying to access a non-existing share
throw new \Sabre\DAV\Exception\NotAuthenticated();
}
$server = $serverFactory->createServer(false, $baseuri, $requestUri, $authPlugin, function (\Sabre\DAV\Server $server) use ($authBackend, $linkCheckPlugin, $filesDropPlugin) {
$isAjax = in_array('XMLHttpRequest', explode(',', $_SERVER['HTTP_X_REQUESTED_WITH'] ?? ''));
/** @var FederatedShareProvider $shareProvider */
$federatedShareProvider = Server::get(FederatedShareProvider::class);
if ($federatedShareProvider->isOutgoingServer2serverShareEnabled() === false && !$isAjax) {
// this is what is thrown when trying to access a non-existing share
throw new \Sabre\DAV\Exception\NotAuthenticated();
}
$share = $authBackend->getShare();
$owner = $share->getShareOwner();
$isReadable = $share->getPermissions() & Constants::PERMISSION_READ;
$fileId = $share->getNodeId();
$share = $authBackend->getShare();
$owner = $share->getShareOwner();
$isReadable = $share->getPermissions() & Constants::PERMISSION_READ;
$fileId = $share->getNodeId();
// FIXME: should not add storage wrappers outside of preSetup, need to find a better way
$previousLog = Filesystem::logWarningWhenAddingStorageWrapper(false);
Filesystem::addStorageWrapper('sharePermissions', function ($mountPoint, $storage) use ($share) {
return new PermissionsMask(['storage' => $storage, 'mask' => $share->getPermissions() | Constants::PERMISSION_SHARE]);
});
Filesystem::addStorageWrapper('shareOwner', function ($mountPoint, $storage) use ($share) {
return new PublicOwnerWrapper(['storage' => $storage, 'owner' => $share->getShareOwner()]);
});
Filesystem::logWarningWhenAddingStorageWrapper($previousLog);
$rootFolder = Server::get(IRootFolder::class);
$userFolder = $rootFolder->getUserFolder($owner);
$node = $userFolder->getFirstNodeById($fileId);
if (!$node) {
throw new \Sabre\DAV\Exception\NotFound();
}
$linkCheckPlugin->setFileInfo($node);
// If not readable (files_drop) enable the filesdrop plugin
if (!$isReadable) {
$filesDropPlugin->enable();
}
$filesDropPlugin->setShare($share);
return new View($node->getPath());
// FIXME: should not add storage wrappers outside of preSetup, need to find a better way
$previousLog = Filesystem::logWarningWhenAddingStorageWrapper(false);
Filesystem::addStorageWrapper('sharePermissions', function ($mountPoint, $storage) use ($share) {
return new PermissionsMask(['storage' => $storage, 'mask' => $share->getPermissions() | Constants::PERMISSION_SHARE]);
});
Filesystem::addStorageWrapper('shareOwner', function ($mountPoint, $storage) use ($share) {
return new PublicOwnerWrapper(['storage' => $storage, 'owner' => $share->getShareOwner()]);
});
Filesystem::logWarningWhenAddingStorageWrapper($previousLog);
$rootFolder = Server::get(IRootFolder::class);
$userFolder = $rootFolder->getUserFolder($owner);
$node = $userFolder->getFirstNodeById($fileId);
if (!$node) {
throw new \Sabre\DAV\Exception\NotFound();
}
$linkCheckPlugin->setFileInfo($node);
// If not readable (files_drop) enable the filesdrop plugin
if (!$isReadable) {
$filesDropPlugin->enable();
}
$filesDropPlugin->setShare($share);
$view = new View($node->getPath());
return $view;
});
$server->addPlugin($linkCheckPlugin);
$server->addPlugin($filesDropPlugin);
@@ -386,7 +386,6 @@ return array(
'OCA\\DAV\\Migration\\Version1031Date20240610134258' => $baseDir . '/../lib/Migration/Version1031Date20240610134258.php',
'OCA\\DAV\\Migration\\Version1034Date20250605132605' => $baseDir . '/../lib/Migration/Version1034Date20250605132605.php',
'OCA\\DAV\\Migration\\Version1034Date20250813093701' => $baseDir . '/../lib/Migration/Version1034Date20250813093701.php',
'OCA\\DAV\\Migration\\Version1036Date20251202000000' => $baseDir . '/../lib/Migration/Version1036Date20251202000000.php',
'OCA\\DAV\\Model\\ExampleEvent' => $baseDir . '/../lib/Model/ExampleEvent.php',
'OCA\\DAV\\Paginate\\LimitedCopyIterator' => $baseDir . '/../lib/Paginate/LimitedCopyIterator.php',
'OCA\\DAV\\Paginate\\PaginateCache' => $baseDir . '/../lib/Paginate/PaginateCache.php',
@@ -7,14 +7,14 @@ namespace Composer\Autoload;
class ComposerStaticInitDAV
{
public static $prefixLengthsPsr4 = array (
'O' =>
'O' =>
array (
'OCA\\DAV\\' => 8,
),
);
public static $prefixDirsPsr4 = array (
'OCA\\DAV\\' =>
'OCA\\DAV\\' =>
array (
0 => __DIR__ . '/..' . '/../lib',
),
@@ -401,7 +401,6 @@ class ComposerStaticInitDAV
'OCA\\DAV\\Migration\\Version1031Date20240610134258' => __DIR__ . '/..' . '/../lib/Migration/Version1031Date20240610134258.php',
'OCA\\DAV\\Migration\\Version1034Date20250605132605' => __DIR__ . '/..' . '/../lib/Migration/Version1034Date20250605132605.php',
'OCA\\DAV\\Migration\\Version1034Date20250813093701' => __DIR__ . '/..' . '/../lib/Migration/Version1034Date20250813093701.php',
'OCA\\DAV\\Migration\\Version1036Date20251202000000' => __DIR__ . '/..' . '/../lib/Migration/Version1036Date20251202000000.php',
'OCA\\DAV\\Model\\ExampleEvent' => __DIR__ . '/..' . '/../lib/Model/ExampleEvent.php',
'OCA\\DAV\\Paginate\\LimitedCopyIterator' => __DIR__ . '/..' . '/../lib/Paginate/LimitedCopyIterator.php',
'OCA\\DAV\\Paginate\\PaginateCache' => __DIR__ . '/..' . '/../lib/Paginate/PaginateCache.php',
-30
View File
@@ -73,19 +73,7 @@ OC.L10N.register(
"Where: %s" : "Dove: %s",
"%1$s via %2$s" : "%1$s tramite %2$s",
"In the past on %1$s for the entire day" : "In passato ogni %1$s per l'intero giorno",
"_In %n minute on %1$s for the entire day_::_In %n minutes on %1$s for the entire day_" : ["Fra %n minuto il %1$s per l'intero giorno","Fra %n minuti il %1$s per l'intero giorno","Fra %n minuti il %1$s per l'intero giorno"],
"_In %n hour on %1$s for the entire day_::_In %n hours on %1$s for the entire day_" : ["Fra %n ora il %1$s per l'intero giorno","Fra %n ore il %1$s per l'intero giorno","Fra %n ore il %1$s per l'intero giorno"],
"_In %n day on %1$s for the entire day_::_In %n days on %1$s for the entire day_" : ["Fra %n giorno il %1$s per l'intero giorno","Fra %n giorni il %1$s per l'intero giorno","Fra %n giorni il %1$s per l'intero giorno"],
"_In %n week on %1$s for the entire day_::_In %n weeks on %1$s for the entire day_" : ["Fra %n settimana il %1$s per l'intero giorno","Fra %n settimane il %1$s per l'intero giorno","Fra %n settimane il %1$s per l'intero giorno"],
"_In %n month on %1$s for the entire day_::_In %n months on %1$s for the entire day_" : ["Fra %n mese il %1$s per l'intero giorno","Fra %n mesi il %1$s per l'intero giorno","Fra %n mesi il %1$s per l'intero giorno"],
"_In %n year on %1$s for the entire day_::_In %n years on %1$s for the entire day_" : ["Fra %n anno di %1$s per l'intero giorno","Fra %n anni di %1$s per l'intero giorno","Fra %n anni di %1$s per l'intero giorno"],
"In the past on %1$s between %2$s - %3$s" : "In passato il %1$s nelle ore %2$s - %3$s",
"_In %n minute on %1$s between %2$s - %3$s_::_In %n minutes on %1$s between %2$s - %3$s_" : ["Fra %n minuto il %1$s nelle ore %2$s - %3$s","Fra %n minuti il %1$s nelle ore %2$s - %3$s","Fra %n minuti il %1$s nelle ore %2$s - %3$s"],
"_In %n hour on %1$s between %2$s - %3$s_::_In %n hours on %1$s between %2$s - %3$s_" : ["Fra %n ora il %1$s nelle ore %2$s - %3$s","Fra %n ore il %1$s nelle ore %2$s - %3$s","Fra %n ore il %1$s nelle ore %2$s - %3$s"],
"_In %n day on %1$s between %2$s - %3$s_::_In %n days on %1$s between %2$s - %3$s_" : ["Fra %n giorno il %1$s nelle ore %2$s - %3$s","Fra %n giorni il %1$s nelle ore %2$s - %3$s","Fra %n giorni il %1$s nelle ore %2$s - %3$s"],
"_In %n week on %1$s between %2$s - %3$s_::_In %n weeks on %1$s between %2$s - %3$s_" : ["Fra %n settimana il %1$s nelle ore %2$s - %3$s","Fra %n settimane il %1$s nelle ore %2$s - %3$s","Fra %n settimane il %1$s nelle ore %2$s - %3$s"],
"_In %n month on %1$s between %2$s - %3$s_::_In %n months on %1$s between %2$s - %3$s_" : ["Fra %n mese il %1$s nelle ore %2$s - %3$s","Fra %n mesi il %1$s nelle ore %2$s - %3$s","Fra %n mesi il %1$s nelle ore %2$s - %3$s"],
"_In %n year on %1$s between %2$s - %3$s_::_In %n years on %1$s between %2$s - %3$s_" : ["Fra %n anno il %1$s nelle ore %2$s - %3$s","Fra %n anni il %1$s nelle ore %2$s - %3$s","Fra %n anni il %1$s nelle ore %2$s - %3$s"],
"Could not generate when statement" : "Impossibile generare l'istruzione \"quando\"",
"Every Day for the entire day" : "Ogni giorno per l'intero giorno",
"Every Day for the entire day until %1$s" : "Ogni Giorno per l'intero giorno fino a %1$s",
@@ -123,26 +111,8 @@ OC.L10N.register(
"On specific dates for the entire day until %1$s" : "In una specifica data per l'intero giorno fino al %1$s",
"On specific dates between %1$s - %2$s until %3$s" : "In una specifica data nelle ore %1$s - %2$s fino al %3$s",
"In the past on %1$s" : "In passato il %1$s",
"_In %n minute on %1$s_::_In %n minutes on %1$s_" : ["Fra %n minuto il %1$s","Fra %n minuti il %1$s","Fra %n minuti il %1$s"],
"_In %n hour on %1$s_::_In %n hours on %1$s_" : ["Fra %n ora il %1$s","Fra %n ore il %1$s","Fra %n ore il %1$s"],
"_In %n day on %1$s_::_In %n days on %1$s_" : ["Fra %n giorno il %1$s","Fra %n giorni il %1$s","Fra %n giorni il %1$s"],
"_In %n week on %1$s_::_In %n weeks on %1$s_" : ["Fra %n settimana il %1$s","Fra %n settimane il %1$s","Fra %n settimane il %1$s"],
"_In %n month on %1$s_::_In %n months on %1$s_" : ["Fra %n mese il %1$s","Fra %n mesi il %1$s","Fra %n mesi il %1$s"],
"_In %n year on %1$s_::_In %n years on %1$s_" : ["Fra %n anno il %1$s","Fra %n anni il %1$s","Fra %n anni il %1$s"],
"In the past on %1$s then on %2$s" : "In passato il %1$s successivamente il %2$s",
"_In %n minute on %1$s then on %2$s_::_In %n minutes on %1$s then on %2$s_" : ["Fra %n minuto %1$s successivamente il %2$s","Fra %n minuti %1$s successivamente il %2$s","Fra %n minuti %1$s successivamente il %2$s"],
"_In %n hour on %1$s then on %2$s_::_In %n hours on %1$s then on %2$s_" : ["Fra %n ora %1$s successivamente il %2$s","Fra %n ore %1$s successivamente il %2$s","Fra %n ore %1$s successivamente il %2$s"],
"_In %n day on %1$s then on %2$s_::_In %n days on %1$s then on %2$s_" : ["Fra %n giorno %1$s successivamente il %2$s","Fra %n giorni %1$s successivamente il %2$s","Fra %n giorni %1$s successivamente il %2$s"],
"_In %n week on %1$s then on %2$s_::_In %n weeks on %1$s then on %2$s_" : ["Fra %n settimana %1$s successivamente il %2$s","Fra %n settimane %1$s successivamente il %2$s","Fra %n settimane %1$s successivamente il %2$s"],
"_In %n month on %1$s then on %2$s_::_In %n months on %1$s then on %2$s_" : ["Fra %n mese %1$s successivamente il %2$s","Fra %n mesi %1$s successivamente il %2$s","Fra %n mesi %1$s successivamente il %2$s"],
"_In %n year on %1$s then on %2$s_::_In %n years on %1$s then on %2$s_" : ["Fra %n anno %1$s successivamente il %2$s","Fra %n anni %1$s successivamente il %2$s","Fra %n anni %1$s successivamente il %2$s"],
"In the past on %1$s then on %2$s and %3$s" : "In passato il %1$s successivamente il %2$s e %3$s",
"_In %n minute on %1$s then on %2$s and %3$s_::_In %n minutes on %1$s then on %2$s and %3$s_" : ["Fra %n minuto il %1$s successivamente il %2$s e %3$s","Fra %n minuti il %1$s successivamente il %2$s e %3$s","Fra %n minuti il %1$s successivamente il %2$s e %3$s"],
"_In %n hour on %1$s then on %2$s and %3$s_::_In %n hours on %1$s then on %2$s and %3$s_" : ["Fra %n ora il %1$s successivamente il %2$s e %3$s","Fra %n ore il %1$s successivamente il %2$s e %3$s","Fra %n ore il %1$s successivamente il %2$s e %3$s"],
"_In %n day on %1$s then on %2$s and %3$s_::_In %n days on %1$s then on %2$s and %3$s_" : ["Fra %n giorno il %1$s successivamente il %2$s e %3$s","Fra %n giorni il %1$s successivamente il %2$s e %3$s","Fra %n giorni il %1$s successivamente il %2$s e %3$s"],
"_In %n week on %1$s then on %2$s and %3$s_::_In %n weeks on %1$s then on %2$s and %3$s_" : ["Fra %n settimana il %1$s successivamente il %2$s e %3$s","Fra %n settimane il %1$s successivamente il %2$s e %3$s","Fra %n settimane il %1$s successivamente il %2$s e %3$s"],
"_In %n month on %1$s then on %2$s and %3$s_::_In %n months on %1$s then on %2$s and %3$s_" : ["Fra %n mese il %1$s successivamente il %2$s e %3$s","Fra %n mesi il %1$s successivamente il %2$s e %3$s","Fra %n mesi il %1$s successivamente il %2$s e %3$s"],
"_In %n year on %1$s then on %2$s and %3$s_::_In %n years on %1$s then on %2$s and %3$s_" : ["Fra %n anno il %1$s successivamente il %2$s e %3$s","Fra %n anni il %1$s successivamente il %2$s e %3$s","Fra %n anni il %1$s successivamente il %2$s e %3$s"],
"Could not generate next recurrence statement" : "Impossibile generare l'istruzione della prossima ricorrenza ",
"Cancelled: %1$s" : "Annullato: %1$s",
"\"%1$s\" has been canceled" : "\"%1$s\" è stato annullato",
-30
View File
@@ -71,19 +71,7 @@
"Where: %s" : "Dove: %s",
"%1$s via %2$s" : "%1$s tramite %2$s",
"In the past on %1$s for the entire day" : "In passato ogni %1$s per l'intero giorno",
"_In %n minute on %1$s for the entire day_::_In %n minutes on %1$s for the entire day_" : ["Fra %n minuto il %1$s per l'intero giorno","Fra %n minuti il %1$s per l'intero giorno","Fra %n minuti il %1$s per l'intero giorno"],
"_In %n hour on %1$s for the entire day_::_In %n hours on %1$s for the entire day_" : ["Fra %n ora il %1$s per l'intero giorno","Fra %n ore il %1$s per l'intero giorno","Fra %n ore il %1$s per l'intero giorno"],
"_In %n day on %1$s for the entire day_::_In %n days on %1$s for the entire day_" : ["Fra %n giorno il %1$s per l'intero giorno","Fra %n giorni il %1$s per l'intero giorno","Fra %n giorni il %1$s per l'intero giorno"],
"_In %n week on %1$s for the entire day_::_In %n weeks on %1$s for the entire day_" : ["Fra %n settimana il %1$s per l'intero giorno","Fra %n settimane il %1$s per l'intero giorno","Fra %n settimane il %1$s per l'intero giorno"],
"_In %n month on %1$s for the entire day_::_In %n months on %1$s for the entire day_" : ["Fra %n mese il %1$s per l'intero giorno","Fra %n mesi il %1$s per l'intero giorno","Fra %n mesi il %1$s per l'intero giorno"],
"_In %n year on %1$s for the entire day_::_In %n years on %1$s for the entire day_" : ["Fra %n anno di %1$s per l'intero giorno","Fra %n anni di %1$s per l'intero giorno","Fra %n anni di %1$s per l'intero giorno"],
"In the past on %1$s between %2$s - %3$s" : "In passato il %1$s nelle ore %2$s - %3$s",
"_In %n minute on %1$s between %2$s - %3$s_::_In %n minutes on %1$s between %2$s - %3$s_" : ["Fra %n minuto il %1$s nelle ore %2$s - %3$s","Fra %n minuti il %1$s nelle ore %2$s - %3$s","Fra %n minuti il %1$s nelle ore %2$s - %3$s"],
"_In %n hour on %1$s between %2$s - %3$s_::_In %n hours on %1$s between %2$s - %3$s_" : ["Fra %n ora il %1$s nelle ore %2$s - %3$s","Fra %n ore il %1$s nelle ore %2$s - %3$s","Fra %n ore il %1$s nelle ore %2$s - %3$s"],
"_In %n day on %1$s between %2$s - %3$s_::_In %n days on %1$s between %2$s - %3$s_" : ["Fra %n giorno il %1$s nelle ore %2$s - %3$s","Fra %n giorni il %1$s nelle ore %2$s - %3$s","Fra %n giorni il %1$s nelle ore %2$s - %3$s"],
"_In %n week on %1$s between %2$s - %3$s_::_In %n weeks on %1$s between %2$s - %3$s_" : ["Fra %n settimana il %1$s nelle ore %2$s - %3$s","Fra %n settimane il %1$s nelle ore %2$s - %3$s","Fra %n settimane il %1$s nelle ore %2$s - %3$s"],
"_In %n month on %1$s between %2$s - %3$s_::_In %n months on %1$s between %2$s - %3$s_" : ["Fra %n mese il %1$s nelle ore %2$s - %3$s","Fra %n mesi il %1$s nelle ore %2$s - %3$s","Fra %n mesi il %1$s nelle ore %2$s - %3$s"],
"_In %n year on %1$s between %2$s - %3$s_::_In %n years on %1$s between %2$s - %3$s_" : ["Fra %n anno il %1$s nelle ore %2$s - %3$s","Fra %n anni il %1$s nelle ore %2$s - %3$s","Fra %n anni il %1$s nelle ore %2$s - %3$s"],
"Could not generate when statement" : "Impossibile generare l'istruzione \"quando\"",
"Every Day for the entire day" : "Ogni giorno per l'intero giorno",
"Every Day for the entire day until %1$s" : "Ogni Giorno per l'intero giorno fino a %1$s",
@@ -121,26 +109,8 @@
"On specific dates for the entire day until %1$s" : "In una specifica data per l'intero giorno fino al %1$s",
"On specific dates between %1$s - %2$s until %3$s" : "In una specifica data nelle ore %1$s - %2$s fino al %3$s",
"In the past on %1$s" : "In passato il %1$s",
"_In %n minute on %1$s_::_In %n minutes on %1$s_" : ["Fra %n minuto il %1$s","Fra %n minuti il %1$s","Fra %n minuti il %1$s"],
"_In %n hour on %1$s_::_In %n hours on %1$s_" : ["Fra %n ora il %1$s","Fra %n ore il %1$s","Fra %n ore il %1$s"],
"_In %n day on %1$s_::_In %n days on %1$s_" : ["Fra %n giorno il %1$s","Fra %n giorni il %1$s","Fra %n giorni il %1$s"],
"_In %n week on %1$s_::_In %n weeks on %1$s_" : ["Fra %n settimana il %1$s","Fra %n settimane il %1$s","Fra %n settimane il %1$s"],
"_In %n month on %1$s_::_In %n months on %1$s_" : ["Fra %n mese il %1$s","Fra %n mesi il %1$s","Fra %n mesi il %1$s"],
"_In %n year on %1$s_::_In %n years on %1$s_" : ["Fra %n anno il %1$s","Fra %n anni il %1$s","Fra %n anni il %1$s"],
"In the past on %1$s then on %2$s" : "In passato il %1$s successivamente il %2$s",
"_In %n minute on %1$s then on %2$s_::_In %n minutes on %1$s then on %2$s_" : ["Fra %n minuto %1$s successivamente il %2$s","Fra %n minuti %1$s successivamente il %2$s","Fra %n minuti %1$s successivamente il %2$s"],
"_In %n hour on %1$s then on %2$s_::_In %n hours on %1$s then on %2$s_" : ["Fra %n ora %1$s successivamente il %2$s","Fra %n ore %1$s successivamente il %2$s","Fra %n ore %1$s successivamente il %2$s"],
"_In %n day on %1$s then on %2$s_::_In %n days on %1$s then on %2$s_" : ["Fra %n giorno %1$s successivamente il %2$s","Fra %n giorni %1$s successivamente il %2$s","Fra %n giorni %1$s successivamente il %2$s"],
"_In %n week on %1$s then on %2$s_::_In %n weeks on %1$s then on %2$s_" : ["Fra %n settimana %1$s successivamente il %2$s","Fra %n settimane %1$s successivamente il %2$s","Fra %n settimane %1$s successivamente il %2$s"],
"_In %n month on %1$s then on %2$s_::_In %n months on %1$s then on %2$s_" : ["Fra %n mese %1$s successivamente il %2$s","Fra %n mesi %1$s successivamente il %2$s","Fra %n mesi %1$s successivamente il %2$s"],
"_In %n year on %1$s then on %2$s_::_In %n years on %1$s then on %2$s_" : ["Fra %n anno %1$s successivamente il %2$s","Fra %n anni %1$s successivamente il %2$s","Fra %n anni %1$s successivamente il %2$s"],
"In the past on %1$s then on %2$s and %3$s" : "In passato il %1$s successivamente il %2$s e %3$s",
"_In %n minute on %1$s then on %2$s and %3$s_::_In %n minutes on %1$s then on %2$s and %3$s_" : ["Fra %n minuto il %1$s successivamente il %2$s e %3$s","Fra %n minuti il %1$s successivamente il %2$s e %3$s","Fra %n minuti il %1$s successivamente il %2$s e %3$s"],
"_In %n hour on %1$s then on %2$s and %3$s_::_In %n hours on %1$s then on %2$s and %3$s_" : ["Fra %n ora il %1$s successivamente il %2$s e %3$s","Fra %n ore il %1$s successivamente il %2$s e %3$s","Fra %n ore il %1$s successivamente il %2$s e %3$s"],
"_In %n day on %1$s then on %2$s and %3$s_::_In %n days on %1$s then on %2$s and %3$s_" : ["Fra %n giorno il %1$s successivamente il %2$s e %3$s","Fra %n giorni il %1$s successivamente il %2$s e %3$s","Fra %n giorni il %1$s successivamente il %2$s e %3$s"],
"_In %n week on %1$s then on %2$s and %3$s_::_In %n weeks on %1$s then on %2$s and %3$s_" : ["Fra %n settimana il %1$s successivamente il %2$s e %3$s","Fra %n settimane il %1$s successivamente il %2$s e %3$s","Fra %n settimane il %1$s successivamente il %2$s e %3$s"],
"_In %n month on %1$s then on %2$s and %3$s_::_In %n months on %1$s then on %2$s and %3$s_" : ["Fra %n mese il %1$s successivamente il %2$s e %3$s","Fra %n mesi il %1$s successivamente il %2$s e %3$s","Fra %n mesi il %1$s successivamente il %2$s e %3$s"],
"_In %n year on %1$s then on %2$s and %3$s_::_In %n years on %1$s then on %2$s and %3$s_" : ["Fra %n anno il %1$s successivamente il %2$s e %3$s","Fra %n anni il %1$s successivamente il %2$s e %3$s","Fra %n anni il %1$s successivamente il %2$s e %3$s"],
"Could not generate next recurrence statement" : "Impossibile generare l'istruzione della prossima ricorrenza ",
"Cancelled: %1$s" : "Annullato: %1$s",
"\"%1$s\" has been canceled" : "\"%1$s\" è stato annullato",
-30
View File
@@ -73,19 +73,7 @@ OC.L10N.register(
"Where: %s" : "Wapi: %s",
"%1$s via %2$s" : "%1$skupitia %2$s",
"In the past on %1$s for the entire day" : "Hapo awali kwenye %1$s kwa siku nzima",
"_In %n minute on %1$s for the entire day_::_In %n minutes on %1$s for the entire day_" : ["In %n minute on %1$s for the entire day","Baada ya dakika %n kwenye %1$s kwa siku nzima"],
"_In %n hour on %1$s for the entire day_::_In %n hours on %1$s for the entire day_" : ["In %n hour on %1$s for the entire day","Baada ya saa %n kwenye %1$s kwa siku nzima"],
"_In %n day on %1$s for the entire day_::_In %n days on %1$s for the entire day_" : ["In %n day on %1$s for the entire day","Baada ya siku %n tarehe %1$s kwa siku nzima"],
"_In %n week on %1$s for the entire day_::_In %n weeks on %1$s for the entire day_" : ["In %n week on %1$s for the entire day","Baada ya wiki %n mnamo %1$s kwa siku nzima"],
"_In %n month on %1$s for the entire day_::_In %n months on %1$s for the entire day_" : ["In %n month on %1$s for the entire day","Baada ya miezi %n mnamo %1$s kwa siku nzima"],
"_In %n year on %1$s for the entire day_::_In %n years on %1$s for the entire day_" : ["In %n year on %1$s for the entire day","Katika miaka %n kwenye %1$s kwa siku nzima"],
"In the past on %1$s between %2$s - %3$s" : "Hapo awali kwenye %1$s kati ya %2$s - %3$s",
"_In %n minute on %1$s between %2$s - %3$s_::_In %n minutes on %1$s between %2$s - %3$s_" : ["In %n minute on %1$s between %2$s - %3$s","Baada ya dakika %n kwenye %1$s kati ya %2$s - %3$s"],
"_In %n hour on %1$s between %2$s - %3$s_::_In %n hours on %1$s between %2$s - %3$s_" : ["In %n hour on %1$s between %2$s - %3$s","Baada ya saa %n kwenye %1$s kati ya %2$s - %3$s"],
"_In %n day on %1$s between %2$s - %3$s_::_In %n days on %1$s between %2$s - %3$s_" : ["In %n day on %1$s between %2$s - %3$s","Katika siku %n mnamo %1$s kati ya %2$s - %3$s"],
"_In %n week on %1$s between %2$s - %3$s_::_In %n weeks on %1$s between %2$s - %3$s_" : ["In %n week on %1$s between %2$s - %3$s","Katika wiki %n mnamo %1$s kati ya %2$s - %3$s"],
"_In %n month on %1$s between %2$s - %3$s_::_In %n months on %1$s between %2$s - %3$s_" : ["In %n month on %1$s between %2$s - %3$s","Katika miezi %n tarehe %1$s kati ya %2$s - %3$s"],
"_In %n year on %1$s between %2$s - %3$s_::_In %n years on %1$s between %2$s - %3$s_" : ["In %n year on %1$s between %2$s - %3$s","Katika miaka %n kwenye %1$s kati ya %2$s - %3$s"],
"Could not generate when statement" : "Haikuweza kuzalisha taarifa ya lini",
"Every Day for the entire day" : "Kila Siku kwa siku nzima",
"Every Day for the entire day until %1$s" : "Kila Siku kwa siku nzima hadi %1$s",
@@ -123,26 +111,8 @@ OC.L10N.register(
"On specific dates for the entire day until %1$s" : "Kwa tarehe mahususi kwa siku nzima hadi %1$s ",
"On specific dates between %1$s - %2$s until %3$s" : "Katika tarehe mahususi kati ya %1$s - %2$s hadi %3$s",
"In the past on %1$s" : "Hapo awali kwenye %1$s",
"_In %n minute on %1$s_::_In %n minutes on %1$s_" : ["In %n minute on %1$s","Baada ya dakika %n kwenye %1$s"],
"_In %n hour on %1$s_::_In %n hours on %1$s_" : ["In %n hour on %1$s","Baada ya saa %n kwenye %1$s"],
"_In %n day on %1$s_::_In %n days on %1$s_" : ["In %n day on %1$s","Baada ya siku %n tarehe %1$s"],
"_In %n week on %1$s_::_In %n weeks on %1$s_" : ["In %n week on %1$s","Baada ya wiki %n tarehe %1$s"],
"_In %n month on %1$s_::_In %n months on %1$s_" : ["In %n month on %1$s","Baada ya miezi %n tarehe %1$s"],
"_In %n year on %1$s_::_In %n years on %1$s_" : ["In %n year on %1$s","Katika miaka %n tarehe %1$s"],
"In the past on %1$s then on %2$s" : "Hapo awali kwenye %1$s kisha kwenye %2$s",
"_In %n minute on %1$s then on %2$s_::_In %n minutes on %1$s then on %2$s_" : ["In %n minute on %1$s then on %2$s","Baada ya dakika %n kwenye %1$s kisha %2$s"],
"_In %n hour on %1$s then on %2$s_::_In %n hours on %1$s then on %2$s_" : ["In %n hour on %1$s then on %2$s","Baada ya saa %n kwenye %1$s kisha %2$s"],
"_In %n day on %1$s then on %2$s_::_In %n days on %1$s then on %2$s_" : ["In %n day on %1$s then on %2$s","Baada ya siku %n mnamo %1$s kisha %2$s"],
"_In %n week on %1$s then on %2$s_::_In %n weeks on %1$s then on %2$s_" : ["In %n week on %1$s then on %2$s","Baada ya wiki %n mnamo %1$s kisha %2$s"],
"_In %n month on %1$s then on %2$s_::_In %n months on %1$s then on %2$s_" : ["In %n month on %1$s then on %2$s","Baada ya miezi %n mnamo %1$s kisha %2$s"],
"_In %n year on %1$s then on %2$s_::_In %n years on %1$s then on %2$s_" : ["In %n year on %1$s then on %2$s","Katika miaka %n kwenye %1$s kisha %2$s"],
"In the past on %1$s then on %2$s and %3$s" : "Hapo awali kwenye %1$s kisha kwenye %2$s na %3$s",
"_In %n minute on %1$s then on %2$s and %3$s_::_In %n minutes on %1$s then on %2$s and %3$s_" : ["In %n minute on %1$s then on %2$s and %3$s","Baada ya dakika %n kwenye %1$s kisha %2$s na %3$s"],
"_In %n hour on %1$s then on %2$s and %3$s_::_In %n hours on %1$s then on %2$s and %3$s_" : ["In %n hour on %1$s then on %2$s and %3$s","Baada ya saa %n kwenye %1$s kisha %2$s na %3$s"],
"_In %n day on %1$s then on %2$s and %3$s_::_In %n days on %1$s then on %2$s and %3$s_" : ["In %n day on %1$s then on %2$s and %3$s","Baada ya siku %n mnamo %1$s kisha %2$s na %3$s"],
"_In %n week on %1$s then on %2$s and %3$s_::_In %n weeks on %1$s then on %2$s and %3$s_" : ["In %n week on %1$s then on %2$s and %3$s","Baada ya wiki %n %1$s kisha %2$s na %3$s"],
"_In %n month on %1$s then on %2$s and %3$s_::_In %n months on %1$s then on %2$s and %3$s_" : ["In %n month on %1$s then on %2$s and %3$s","Baada ya miezi %n kwenye %1$s kisha %2$s na %3$s"],
"_In %n year on %1$s then on %2$s and %3$s_::_In %n years on %1$s then on %2$s and %3$s_" : ["In %n year on %1$s then on %2$s and %3$s","Katika miaka %n kwenye %1$s kisha %2$s na %3$s"],
"Could not generate next recurrence statement" : "Haikuweza kutoa taarifa inayofuata ya kujirudia",
"Cancelled: %1$s" : "Imeghairiwa: %1$s ",
"\"%1$s\" has been canceled" : "\"%1$s\" imeghairiwa",
-30
View File
@@ -71,19 +71,7 @@
"Where: %s" : "Wapi: %s",
"%1$s via %2$s" : "%1$skupitia %2$s",
"In the past on %1$s for the entire day" : "Hapo awali kwenye %1$s kwa siku nzima",
"_In %n minute on %1$s for the entire day_::_In %n minutes on %1$s for the entire day_" : ["In %n minute on %1$s for the entire day","Baada ya dakika %n kwenye %1$s kwa siku nzima"],
"_In %n hour on %1$s for the entire day_::_In %n hours on %1$s for the entire day_" : ["In %n hour on %1$s for the entire day","Baada ya saa %n kwenye %1$s kwa siku nzima"],
"_In %n day on %1$s for the entire day_::_In %n days on %1$s for the entire day_" : ["In %n day on %1$s for the entire day","Baada ya siku %n tarehe %1$s kwa siku nzima"],
"_In %n week on %1$s for the entire day_::_In %n weeks on %1$s for the entire day_" : ["In %n week on %1$s for the entire day","Baada ya wiki %n mnamo %1$s kwa siku nzima"],
"_In %n month on %1$s for the entire day_::_In %n months on %1$s for the entire day_" : ["In %n month on %1$s for the entire day","Baada ya miezi %n mnamo %1$s kwa siku nzima"],
"_In %n year on %1$s for the entire day_::_In %n years on %1$s for the entire day_" : ["In %n year on %1$s for the entire day","Katika miaka %n kwenye %1$s kwa siku nzima"],
"In the past on %1$s between %2$s - %3$s" : "Hapo awali kwenye %1$s kati ya %2$s - %3$s",
"_In %n minute on %1$s between %2$s - %3$s_::_In %n minutes on %1$s between %2$s - %3$s_" : ["In %n minute on %1$s between %2$s - %3$s","Baada ya dakika %n kwenye %1$s kati ya %2$s - %3$s"],
"_In %n hour on %1$s between %2$s - %3$s_::_In %n hours on %1$s between %2$s - %3$s_" : ["In %n hour on %1$s between %2$s - %3$s","Baada ya saa %n kwenye %1$s kati ya %2$s - %3$s"],
"_In %n day on %1$s between %2$s - %3$s_::_In %n days on %1$s between %2$s - %3$s_" : ["In %n day on %1$s between %2$s - %3$s","Katika siku %n mnamo %1$s kati ya %2$s - %3$s"],
"_In %n week on %1$s between %2$s - %3$s_::_In %n weeks on %1$s between %2$s - %3$s_" : ["In %n week on %1$s between %2$s - %3$s","Katika wiki %n mnamo %1$s kati ya %2$s - %3$s"],
"_In %n month on %1$s between %2$s - %3$s_::_In %n months on %1$s between %2$s - %3$s_" : ["In %n month on %1$s between %2$s - %3$s","Katika miezi %n tarehe %1$s kati ya %2$s - %3$s"],
"_In %n year on %1$s between %2$s - %3$s_::_In %n years on %1$s between %2$s - %3$s_" : ["In %n year on %1$s between %2$s - %3$s","Katika miaka %n kwenye %1$s kati ya %2$s - %3$s"],
"Could not generate when statement" : "Haikuweza kuzalisha taarifa ya lini",
"Every Day for the entire day" : "Kila Siku kwa siku nzima",
"Every Day for the entire day until %1$s" : "Kila Siku kwa siku nzima hadi %1$s",
@@ -121,26 +109,8 @@
"On specific dates for the entire day until %1$s" : "Kwa tarehe mahususi kwa siku nzima hadi %1$s ",
"On specific dates between %1$s - %2$s until %3$s" : "Katika tarehe mahususi kati ya %1$s - %2$s hadi %3$s",
"In the past on %1$s" : "Hapo awali kwenye %1$s",
"_In %n minute on %1$s_::_In %n minutes on %1$s_" : ["In %n minute on %1$s","Baada ya dakika %n kwenye %1$s"],
"_In %n hour on %1$s_::_In %n hours on %1$s_" : ["In %n hour on %1$s","Baada ya saa %n kwenye %1$s"],
"_In %n day on %1$s_::_In %n days on %1$s_" : ["In %n day on %1$s","Baada ya siku %n tarehe %1$s"],
"_In %n week on %1$s_::_In %n weeks on %1$s_" : ["In %n week on %1$s","Baada ya wiki %n tarehe %1$s"],
"_In %n month on %1$s_::_In %n months on %1$s_" : ["In %n month on %1$s","Baada ya miezi %n tarehe %1$s"],
"_In %n year on %1$s_::_In %n years on %1$s_" : ["In %n year on %1$s","Katika miaka %n tarehe %1$s"],
"In the past on %1$s then on %2$s" : "Hapo awali kwenye %1$s kisha kwenye %2$s",
"_In %n minute on %1$s then on %2$s_::_In %n minutes on %1$s then on %2$s_" : ["In %n minute on %1$s then on %2$s","Baada ya dakika %n kwenye %1$s kisha %2$s"],
"_In %n hour on %1$s then on %2$s_::_In %n hours on %1$s then on %2$s_" : ["In %n hour on %1$s then on %2$s","Baada ya saa %n kwenye %1$s kisha %2$s"],
"_In %n day on %1$s then on %2$s_::_In %n days on %1$s then on %2$s_" : ["In %n day on %1$s then on %2$s","Baada ya siku %n mnamo %1$s kisha %2$s"],
"_In %n week on %1$s then on %2$s_::_In %n weeks on %1$s then on %2$s_" : ["In %n week on %1$s then on %2$s","Baada ya wiki %n mnamo %1$s kisha %2$s"],
"_In %n month on %1$s then on %2$s_::_In %n months on %1$s then on %2$s_" : ["In %n month on %1$s then on %2$s","Baada ya miezi %n mnamo %1$s kisha %2$s"],
"_In %n year on %1$s then on %2$s_::_In %n years on %1$s then on %2$s_" : ["In %n year on %1$s then on %2$s","Katika miaka %n kwenye %1$s kisha %2$s"],
"In the past on %1$s then on %2$s and %3$s" : "Hapo awali kwenye %1$s kisha kwenye %2$s na %3$s",
"_In %n minute on %1$s then on %2$s and %3$s_::_In %n minutes on %1$s then on %2$s and %3$s_" : ["In %n minute on %1$s then on %2$s and %3$s","Baada ya dakika %n kwenye %1$s kisha %2$s na %3$s"],
"_In %n hour on %1$s then on %2$s and %3$s_::_In %n hours on %1$s then on %2$s and %3$s_" : ["In %n hour on %1$s then on %2$s and %3$s","Baada ya saa %n kwenye %1$s kisha %2$s na %3$s"],
"_In %n day on %1$s then on %2$s and %3$s_::_In %n days on %1$s then on %2$s and %3$s_" : ["In %n day on %1$s then on %2$s and %3$s","Baada ya siku %n mnamo %1$s kisha %2$s na %3$s"],
"_In %n week on %1$s then on %2$s and %3$s_::_In %n weeks on %1$s then on %2$s and %3$s_" : ["In %n week on %1$s then on %2$s and %3$s","Baada ya wiki %n %1$s kisha %2$s na %3$s"],
"_In %n month on %1$s then on %2$s and %3$s_::_In %n months on %1$s then on %2$s and %3$s_" : ["In %n month on %1$s then on %2$s and %3$s","Baada ya miezi %n kwenye %1$s kisha %2$s na %3$s"],
"_In %n year on %1$s then on %2$s and %3$s_::_In %n years on %1$s then on %2$s and %3$s_" : ["In %n year on %1$s then on %2$s and %3$s","Katika miaka %n kwenye %1$s kisha %2$s na %3$s"],
"Could not generate next recurrence statement" : "Haikuweza kutoa taarifa inayofuata ya kujirudia",
"Cancelled: %1$s" : "Imeghairiwa: %1$s ",
"\"%1$s\" has been canceled" : "\"%1$s\" imeghairiwa",
-30
View File
@@ -73,19 +73,7 @@ OC.L10N.register(
"Where: %s" : "Şurada: %s",
"%1$s via %2$s" : "%1$s, %2$s aracılığıyla",
"In the past on %1$s for the entire day" : "Tüm gün boyunca %1$s zamanında geçmişte",
"_In %n minute on %1$s for the entire day_::_In %n minutes on %1$s for the entire day_" : ["Tüm gün boyunca %1$s zamanında %n dakika içinde","Tüm gün boyunca %1$s zamanında %n dakika içinde"],
"_In %n hour on %1$s for the entire day_::_In %n hours on %1$s for the entire day_" : ["Tüm gün boyunca %1$s zamanında %n saat içinde","Tüm gün boyunca %1$s zamanında %n saat içinde"],
"_In %n day on %1$s for the entire day_::_In %n days on %1$s for the entire day_" : ["Tüm gün boyunca %1$s zamanında %n gün içinde","Tüm gün boyunca %1$s zamanında %n gün içinde"],
"_In %n week on %1$s for the entire day_::_In %n weeks on %1$s for the entire day_" : ["Tüm gün boyunca %1$s zamanında %n hafta içinde","Tüm gün boyunca %1$s zamanında %n hafta içinde"],
"_In %n month on %1$s for the entire day_::_In %n months on %1$s for the entire day_" : ["Tüm gün boyunca %1$s zamanında %n ay içinde","Tüm gün boyunca %1$s zamanında %n ay içinde"],
"_In %n year on %1$s for the entire day_::_In %n years on %1$s for the entire day_" : ["Tüm gün boyunca %1$s zamanında %n yıl içinde","Tüm gün boyunca %1$s zamanında %n yıl içinde"],
"In the past on %1$s between %2$s - %3$s" : "Geçmişte %1$s zamanında %2$s ile %3$s arasında",
"_In %n minute on %1$s between %2$s - %3$s_::_In %n minutes on %1$s between %2$s - %3$s_" : ["%n dakika içinde %1$s zamanında %2$s ile %3$s arasında","%n dakika içinde %1$s zamanında %2$s ile %3$s arasında"],
"_In %n hour on %1$s between %2$s - %3$s_::_In %n hours on %1$s between %2$s - %3$s_" : ["%n saat içinde %1$s zamanında %2$s ile %3$s arasında","%n saat içinde %1$s zamanında %2$s ile %3$s arasında"],
"_In %n day on %1$s between %2$s - %3$s_::_In %n days on %1$s between %2$s - %3$s_" : ["%n gün içinde %1$s zamanında %2$s ile %3$s arasında","%n gün içinde %1$s zamanında %2$s ile %3$s arasında"],
"_In %n week on %1$s between %2$s - %3$s_::_In %n weeks on %1$s between %2$s - %3$s_" : ["%n hafta içinde %1$s zamanında %2$s ile %3$s arasında","%n hafta içinde %1$s zamanında %2$s ile %3$s arasında"],
"_In %n month on %1$s between %2$s - %3$s_::_In %n months on %1$s between %2$s - %3$s_" : ["%n ay içinde %1$s zamanında %2$s ile %3$s arasında","%n ay içinde %1$s zamanında %2$s ile %3$s arasında"],
"_In %n year on %1$s between %2$s - %3$s_::_In %n years on %1$s between %2$s - %3$s_" : ["%n yıl içinde %1$s zamanında %2$s ile %3$s arasında","%n yıl içinde %1$s zamanında %2$s ile %3$s arasında"],
"Could not generate when statement" : "Zaman ifadesi oluşturulamadı",
"Every Day for the entire day" : "Her gün tüm gün boyunca",
"Every Day for the entire day until %1$s" : "%1$s zamanına kadar her gün tüm gün boyunca",
@@ -123,26 +111,8 @@ OC.L10N.register(
"On specific dates for the entire day until %1$s" : "Belirli tarihlerde tüm gün boyunca %1$s zamanına kadar",
"On specific dates between %1$s - %2$s until %3$s" : "Belirli tarihlerde %1$s ile %2$s arasında %3$s zamanına kadar",
"In the past on %1$s" : "%1$s zamanında geçmişte",
"_In %n minute on %1$s_::_In %n minutes on %1$s_" : ["%1$s zamanında %n dakika içinde","%1$s zamanında %n dakika içinde"],
"_In %n hour on %1$s_::_In %n hours on %1$s_" : ["%1$s zamanında %n saat içinde","%1$s zamanında %n saat içinde"],
"_In %n day on %1$s_::_In %n days on %1$s_" : ["%1$s zamanında %n gün içinde","%1$s zamanında %n gün içinde"],
"_In %n week on %1$s_::_In %n weeks on %1$s_" : ["%1$s zamanında %n hafta içinde","%1$s zamanında %n hafta içinde"],
"_In %n month on %1$s_::_In %n months on %1$s_" : ["%1$s zamanında %n ay içinde","%1$s zamanında %n ay içinde"],
"_In %n year on %1$s_::_In %n years on %1$s_" : ["%1$s zamanında %n yıl içinde","%1$s zamanında %n yıl içinde"],
"In the past on %1$s then on %2$s" : "Geçmişte %1$s zamanında ardından %2$s zamanında",
"_In %n minute on %1$s then on %2$s_::_In %n minutes on %1$s then on %2$s_" : ["%n dakika içinde %1$s zamanında ardından %2$s zamanında","%n dakika içinde %1$s zamanında ardından %2$s zamanında"],
"_In %n hour on %1$s then on %2$s_::_In %n hours on %1$s then on %2$s_" : ["%n saat içinde %1$s zamanında ardından %2$s zamanında","%n saat içinde %1$s zamanında ardından %2$s zamanında"],
"_In %n day on %1$s then on %2$s_::_In %n days on %1$s then on %2$s_" : ["%n gün içinde %1$s zamanında ardından %2$s zamanında","%n gün içinde %1$s zamanında ardından %2$s zamanında"],
"_In %n week on %1$s then on %2$s_::_In %n weeks on %1$s then on %2$s_" : ["%n hafta içinde %1$s zamanında ardından %2$s zamanında","%n hafta içinde %1$s zamanında ardından %2$s zamanında"],
"_In %n month on %1$s then on %2$s_::_In %n months on %1$s then on %2$s_" : ["%n ay içinde %1$s zamanında ardından %2$s zamanında","%n ay içinde %1$s zamanında ardından %2$s zamanında"],
"_In %n year on %1$s then on %2$s_::_In %n years on %1$s then on %2$s_" : ["%n yıl içinde %1$s zamanında ardından %2$s zamanında","%n yıl içinde %1$s zamanında ardından %2$s zamanında"],
"In the past on %1$s then on %2$s and %3$s" : "Geçmişte %1$s zamanında ardından %2$s ve %3$s zamanında",
"_In %n minute on %1$s then on %2$s and %3$s_::_In %n minutes on %1$s then on %2$s and %3$s_" : ["%n dakika içinde %1$s zamanında ardından %2$s ve %3$s zamanında","%n dakika içinde %1$s zamanında ardından %2$s ve %3$s zamanında"],
"_In %n hour on %1$s then on %2$s and %3$s_::_In %n hours on %1$s then on %2$s and %3$s_" : ["%n dakika içinde %1$s zamanında ardından %2$s ve %3$s zamanında","%n dakika içinde %1$s zamanında ardından %2$s ve %3$s zamanında"],
"_In %n day on %1$s then on %2$s and %3$s_::_In %n days on %1$s then on %2$s and %3$s_" : ["%n dakika içinde %1$s zamanında ardından %2$s ve %3$s zamanında","%n dakika içinde %1$s zamanında ardından %2$s ve %3$s zamanında"],
"_In %n week on %1$s then on %2$s and %3$s_::_In %n weeks on %1$s then on %2$s and %3$s_" : ["%n dakika içinde %1$s zamanında ardından %2$s ve %3$s zamanında","%n dakika içinde %1$s zamanında ardından %2$s ve %3$s zamanında"],
"_In %n month on %1$s then on %2$s and %3$s_::_In %n months on %1$s then on %2$s and %3$s_" : ["%n dakika içinde %1$s zamanında ardından %2$s ve %3$s zamanında","%n dakika içinde %1$s zamanında ardından %2$s ve %3$s zamanında"],
"_In %n year on %1$s then on %2$s and %3$s_::_In %n years on %1$s then on %2$s and %3$s_" : ["%n dakika içinde %1$s zamanında ardından %2$s ve %3$s zamanında","%n dakika içinde %1$s zamanında ardından %2$s ve %3$s zamanında"],
"Could not generate next recurrence statement" : "Sonraki yinelenme ifadesi oluşturulamadı",
"Cancelled: %1$s" : "İptal edildi: %1$s",
"\"%1$s\" has been canceled" : "\"%1$s\" iptal edildi",
-30
View File
@@ -71,19 +71,7 @@
"Where: %s" : "Şurada: %s",
"%1$s via %2$s" : "%1$s, %2$s aracılığıyla",
"In the past on %1$s for the entire day" : "Tüm gün boyunca %1$s zamanında geçmişte",
"_In %n minute on %1$s for the entire day_::_In %n minutes on %1$s for the entire day_" : ["Tüm gün boyunca %1$s zamanında %n dakika içinde","Tüm gün boyunca %1$s zamanında %n dakika içinde"],
"_In %n hour on %1$s for the entire day_::_In %n hours on %1$s for the entire day_" : ["Tüm gün boyunca %1$s zamanında %n saat içinde","Tüm gün boyunca %1$s zamanında %n saat içinde"],
"_In %n day on %1$s for the entire day_::_In %n days on %1$s for the entire day_" : ["Tüm gün boyunca %1$s zamanında %n gün içinde","Tüm gün boyunca %1$s zamanında %n gün içinde"],
"_In %n week on %1$s for the entire day_::_In %n weeks on %1$s for the entire day_" : ["Tüm gün boyunca %1$s zamanında %n hafta içinde","Tüm gün boyunca %1$s zamanında %n hafta içinde"],
"_In %n month on %1$s for the entire day_::_In %n months on %1$s for the entire day_" : ["Tüm gün boyunca %1$s zamanında %n ay içinde","Tüm gün boyunca %1$s zamanında %n ay içinde"],
"_In %n year on %1$s for the entire day_::_In %n years on %1$s for the entire day_" : ["Tüm gün boyunca %1$s zamanında %n yıl içinde","Tüm gün boyunca %1$s zamanında %n yıl içinde"],
"In the past on %1$s between %2$s - %3$s" : "Geçmişte %1$s zamanında %2$s ile %3$s arasında",
"_In %n minute on %1$s between %2$s - %3$s_::_In %n minutes on %1$s between %2$s - %3$s_" : ["%n dakika içinde %1$s zamanında %2$s ile %3$s arasında","%n dakika içinde %1$s zamanında %2$s ile %3$s arasında"],
"_In %n hour on %1$s between %2$s - %3$s_::_In %n hours on %1$s between %2$s - %3$s_" : ["%n saat içinde %1$s zamanında %2$s ile %3$s arasında","%n saat içinde %1$s zamanında %2$s ile %3$s arasında"],
"_In %n day on %1$s between %2$s - %3$s_::_In %n days on %1$s between %2$s - %3$s_" : ["%n gün içinde %1$s zamanında %2$s ile %3$s arasında","%n gün içinde %1$s zamanında %2$s ile %3$s arasında"],
"_In %n week on %1$s between %2$s - %3$s_::_In %n weeks on %1$s between %2$s - %3$s_" : ["%n hafta içinde %1$s zamanında %2$s ile %3$s arasında","%n hafta içinde %1$s zamanında %2$s ile %3$s arasında"],
"_In %n month on %1$s between %2$s - %3$s_::_In %n months on %1$s between %2$s - %3$s_" : ["%n ay içinde %1$s zamanında %2$s ile %3$s arasında","%n ay içinde %1$s zamanında %2$s ile %3$s arasında"],
"_In %n year on %1$s between %2$s - %3$s_::_In %n years on %1$s between %2$s - %3$s_" : ["%n yıl içinde %1$s zamanında %2$s ile %3$s arasında","%n yıl içinde %1$s zamanında %2$s ile %3$s arasında"],
"Could not generate when statement" : "Zaman ifadesi oluşturulamadı",
"Every Day for the entire day" : "Her gün tüm gün boyunca",
"Every Day for the entire day until %1$s" : "%1$s zamanına kadar her gün tüm gün boyunca",
@@ -121,26 +109,8 @@
"On specific dates for the entire day until %1$s" : "Belirli tarihlerde tüm gün boyunca %1$s zamanına kadar",
"On specific dates between %1$s - %2$s until %3$s" : "Belirli tarihlerde %1$s ile %2$s arasında %3$s zamanına kadar",
"In the past on %1$s" : "%1$s zamanında geçmişte",
"_In %n minute on %1$s_::_In %n minutes on %1$s_" : ["%1$s zamanında %n dakika içinde","%1$s zamanında %n dakika içinde"],
"_In %n hour on %1$s_::_In %n hours on %1$s_" : ["%1$s zamanında %n saat içinde","%1$s zamanında %n saat içinde"],
"_In %n day on %1$s_::_In %n days on %1$s_" : ["%1$s zamanında %n gün içinde","%1$s zamanında %n gün içinde"],
"_In %n week on %1$s_::_In %n weeks on %1$s_" : ["%1$s zamanında %n hafta içinde","%1$s zamanında %n hafta içinde"],
"_In %n month on %1$s_::_In %n months on %1$s_" : ["%1$s zamanında %n ay içinde","%1$s zamanında %n ay içinde"],
"_In %n year on %1$s_::_In %n years on %1$s_" : ["%1$s zamanında %n yıl içinde","%1$s zamanında %n yıl içinde"],
"In the past on %1$s then on %2$s" : "Geçmişte %1$s zamanında ardından %2$s zamanında",
"_In %n minute on %1$s then on %2$s_::_In %n minutes on %1$s then on %2$s_" : ["%n dakika içinde %1$s zamanında ardından %2$s zamanında","%n dakika içinde %1$s zamanında ardından %2$s zamanında"],
"_In %n hour on %1$s then on %2$s_::_In %n hours on %1$s then on %2$s_" : ["%n saat içinde %1$s zamanında ardından %2$s zamanında","%n saat içinde %1$s zamanında ardından %2$s zamanında"],
"_In %n day on %1$s then on %2$s_::_In %n days on %1$s then on %2$s_" : ["%n gün içinde %1$s zamanında ardından %2$s zamanında","%n gün içinde %1$s zamanında ardından %2$s zamanında"],
"_In %n week on %1$s then on %2$s_::_In %n weeks on %1$s then on %2$s_" : ["%n hafta içinde %1$s zamanında ardından %2$s zamanında","%n hafta içinde %1$s zamanında ardından %2$s zamanında"],
"_In %n month on %1$s then on %2$s_::_In %n months on %1$s then on %2$s_" : ["%n ay içinde %1$s zamanında ardından %2$s zamanında","%n ay içinde %1$s zamanında ardından %2$s zamanında"],
"_In %n year on %1$s then on %2$s_::_In %n years on %1$s then on %2$s_" : ["%n yıl içinde %1$s zamanında ardından %2$s zamanında","%n yıl içinde %1$s zamanında ardından %2$s zamanında"],
"In the past on %1$s then on %2$s and %3$s" : "Geçmişte %1$s zamanında ardından %2$s ve %3$s zamanında",
"_In %n minute on %1$s then on %2$s and %3$s_::_In %n minutes on %1$s then on %2$s and %3$s_" : ["%n dakika içinde %1$s zamanında ardından %2$s ve %3$s zamanında","%n dakika içinde %1$s zamanında ardından %2$s ve %3$s zamanında"],
"_In %n hour on %1$s then on %2$s and %3$s_::_In %n hours on %1$s then on %2$s and %3$s_" : ["%n dakika içinde %1$s zamanında ardından %2$s ve %3$s zamanında","%n dakika içinde %1$s zamanında ardından %2$s ve %3$s zamanında"],
"_In %n day on %1$s then on %2$s and %3$s_::_In %n days on %1$s then on %2$s and %3$s_" : ["%n dakika içinde %1$s zamanında ardından %2$s ve %3$s zamanında","%n dakika içinde %1$s zamanında ardından %2$s ve %3$s zamanında"],
"_In %n week on %1$s then on %2$s and %3$s_::_In %n weeks on %1$s then on %2$s and %3$s_" : ["%n dakika içinde %1$s zamanında ardından %2$s ve %3$s zamanında","%n dakika içinde %1$s zamanında ardından %2$s ve %3$s zamanında"],
"_In %n month on %1$s then on %2$s and %3$s_::_In %n months on %1$s then on %2$s and %3$s_" : ["%n dakika içinde %1$s zamanında ardından %2$s ve %3$s zamanında","%n dakika içinde %1$s zamanında ardından %2$s ve %3$s zamanında"],
"_In %n year on %1$s then on %2$s and %3$s_::_In %n years on %1$s then on %2$s and %3$s_" : ["%n dakika içinde %1$s zamanında ardından %2$s ve %3$s zamanında","%n dakika içinde %1$s zamanında ardından %2$s ve %3$s zamanında"],
"Could not generate next recurrence statement" : "Sonraki yinelenme ifadesi oluşturulamadı",
"Cancelled: %1$s" : "İptal edildi: %1$s",
"\"%1$s\" has been canceled" : "\"%1$s\" iptal edildi",
-30
View File
@@ -73,19 +73,7 @@ OC.L10N.register(
"Where: %s" : "地点:%s",
"%1$s via %2$s" : "%1$s 通过 %2$s",
"In the past on %1$s for the entire day" : "过去全天 %1$s ",
"_In %n minute on %1$s for the entire day_::_In %n minutes on %1$s for the entire day_" : ["在 %n 分钟后全天 %1$s"],
"_In %n hour on %1$s for the entire day_::_In %n hours on %1$s for the entire day_" : ["在 %n 小时后全天 %1$s"],
"_In %n day on %1$s for the entire day_::_In %n days on %1$s for the entire day_" : ["在 %n 天后全天 %1$s"],
"_In %n week on %1$s for the entire day_::_In %n weeks on %1$s for the entire day_" : ["在 %n 周后全天 %1$s"],
"_In %n month on %1$s for the entire day_::_In %n months on %1$s for the entire day_" : ["在 %n 个月后全天 %1$s"],
"_In %n year on %1$s for the entire day_::_In %n years on %1$s for the entire day_" : ["在 %n 年后全天 %1$s"],
"In the past on %1$s between %2$s - %3$s" : "过去 %2$s - %3$s %1$s",
"_In %n minute on %1$s between %2$s - %3$s_::_In %n minutes on %1$s between %2$s - %3$s_" : ["在 %2$s - %3$s %n 分钟后 %1$s"],
"_In %n hour on %1$s between %2$s - %3$s_::_In %n hours on %1$s between %2$s - %3$s_" : ["在 %2$s - %3$s %n 小时后 %1$s"],
"_In %n day on %1$s between %2$s - %3$s_::_In %n days on %1$s between %2$s - %3$s_" : ["在 %2$s - %3$s %n 天后 %1$s"],
"_In %n week on %1$s between %2$s - %3$s_::_In %n weeks on %1$s between %2$s - %3$s_" : ["在 %2$s - %3$s %n 周后 %1$s"],
"_In %n month on %1$s between %2$s - %3$s_::_In %n months on %1$s between %2$s - %3$s_" : ["在 %2$s - %3$s %n 个月后 %1$s"],
"_In %n year on %1$s between %2$s - %3$s_::_In %n years on %1$s between %2$s - %3$s_" : ["在 %2$s - %3$s %n 年后 %1$s"],
"Could not generate when statement" : "无法生成 when 语句",
"Every Day for the entire day" : "每天全天",
"Every Day for the entire day until %1$s" : "每天全天,直到 %1$s",
@@ -123,26 +111,8 @@ OC.L10N.register(
"On specific dates for the entire day until %1$s" : "在特定日期全天,直到 %1$s",
"On specific dates between %1$s - %2$s until %3$s" : "在 %1$s - %2$s 特定日期,直到 %3$s",
"In the past on %1$s" : "在过去 %1$s",
"_In %n minute on %1$s_::_In %n minutes on %1$s_" : ["%n 分钟后 %1$s"],
"_In %n hour on %1$s_::_In %n hours on %1$s_" : ["%n 小时后 %1$s"],
"_In %n day on %1$s_::_In %n days on %1$s_" : ["%n 天后 %1$s"],
"_In %n week on %1$s_::_In %n weeks on %1$s_" : ["%n 周后 %1$s"],
"_In %n month on %1$s_::_In %n months on %1$s_" : ["%n 个月后 %1$s"],
"_In %n year on %1$s_::_In %n years on %1$s_" : ["%n 年后 %1$s"],
"In the past on %1$s then on %2$s" : "过去 %1$s,然后 %2$s",
"_In %n minute on %1$s then on %2$s_::_In %n minutes on %1$s then on %2$s_" : ["%n 分钟后 %1$s,然后 %2$s"],
"_In %n hour on %1$s then on %2$s_::_In %n hours on %1$s then on %2$s_" : ["%n 小时后 %1$s,然后 %2$s"],
"_In %n day on %1$s then on %2$s_::_In %n days on %1$s then on %2$s_" : ["%n 天后 %1$s,然后 %2$s"],
"_In %n week on %1$s then on %2$s_::_In %n weeks on %1$s then on %2$s_" : ["%n 周后 %1$s,然后 %2$s"],
"_In %n month on %1$s then on %2$s_::_In %n months on %1$s then on %2$s_" : ["%n 个月后 %1$s,然后 %2$s"],
"_In %n year on %1$s then on %2$s_::_In %n years on %1$s then on %2$s_" : ["%n 年后 %1$s,然后 %2$s"],
"In the past on %1$s then on %2$s and %3$s" : "在过去 %1$s,然后 %2$s 和 %3$s",
"_In %n minute on %1$s then on %2$s and %3$s_::_In %n minutes on %1$s then on %2$s and %3$s_" : ["%n 分钟后 %1$s,然后 %2$s 和 %3$s"],
"_In %n hour on %1$s then on %2$s and %3$s_::_In %n hours on %1$s then on %2$s and %3$s_" : ["%n 小时后 %1$s,然后 %2$s 和 %3$s"],
"_In %n day on %1$s then on %2$s and %3$s_::_In %n days on %1$s then on %2$s and %3$s_" : ["%n 天后 %1$s,然后 %2$s 和 %3$s"],
"_In %n week on %1$s then on %2$s and %3$s_::_In %n weeks on %1$s then on %2$s and %3$s_" : ["%n 周后 %1$s,然后 %2$s 和 %3$s"],
"_In %n month on %1$s then on %2$s and %3$s_::_In %n months on %1$s then on %2$s and %3$s_" : ["%n 个月后 %1$s,然后 %2$s 和 %3$s"],
"_In %n year on %1$s then on %2$s and %3$s_::_In %n years on %1$s then on %2$s and %3$s_" : ["%n 年后 %1$s,然后 %2$s 和 %3$s"],
"Could not generate next recurrence statement" : "无法生成下一个重复语句",
"Cancelled: %1$s" : "已取消:%1$s",
"\"%1$s\" has been canceled" : "“%1$s”已取消",
-30
View File
@@ -71,19 +71,7 @@
"Where: %s" : "地点:%s",
"%1$s via %2$s" : "%1$s 通过 %2$s",
"In the past on %1$s for the entire day" : "过去全天 %1$s ",
"_In %n minute on %1$s for the entire day_::_In %n minutes on %1$s for the entire day_" : ["在 %n 分钟后全天 %1$s"],
"_In %n hour on %1$s for the entire day_::_In %n hours on %1$s for the entire day_" : ["在 %n 小时后全天 %1$s"],
"_In %n day on %1$s for the entire day_::_In %n days on %1$s for the entire day_" : ["在 %n 天后全天 %1$s"],
"_In %n week on %1$s for the entire day_::_In %n weeks on %1$s for the entire day_" : ["在 %n 周后全天 %1$s"],
"_In %n month on %1$s for the entire day_::_In %n months on %1$s for the entire day_" : ["在 %n 个月后全天 %1$s"],
"_In %n year on %1$s for the entire day_::_In %n years on %1$s for the entire day_" : ["在 %n 年后全天 %1$s"],
"In the past on %1$s between %2$s - %3$s" : "过去 %2$s - %3$s %1$s",
"_In %n minute on %1$s between %2$s - %3$s_::_In %n minutes on %1$s between %2$s - %3$s_" : ["在 %2$s - %3$s %n 分钟后 %1$s"],
"_In %n hour on %1$s between %2$s - %3$s_::_In %n hours on %1$s between %2$s - %3$s_" : ["在 %2$s - %3$s %n 小时后 %1$s"],
"_In %n day on %1$s between %2$s - %3$s_::_In %n days on %1$s between %2$s - %3$s_" : ["在 %2$s - %3$s %n 天后 %1$s"],
"_In %n week on %1$s between %2$s - %3$s_::_In %n weeks on %1$s between %2$s - %3$s_" : ["在 %2$s - %3$s %n 周后 %1$s"],
"_In %n month on %1$s between %2$s - %3$s_::_In %n months on %1$s between %2$s - %3$s_" : ["在 %2$s - %3$s %n 个月后 %1$s"],
"_In %n year on %1$s between %2$s - %3$s_::_In %n years on %1$s between %2$s - %3$s_" : ["在 %2$s - %3$s %n 年后 %1$s"],
"Could not generate when statement" : "无法生成 when 语句",
"Every Day for the entire day" : "每天全天",
"Every Day for the entire day until %1$s" : "每天全天,直到 %1$s",
@@ -121,26 +109,8 @@
"On specific dates for the entire day until %1$s" : "在特定日期全天,直到 %1$s",
"On specific dates between %1$s - %2$s until %3$s" : "在 %1$s - %2$s 特定日期,直到 %3$s",
"In the past on %1$s" : "在过去 %1$s",
"_In %n minute on %1$s_::_In %n minutes on %1$s_" : ["%n 分钟后 %1$s"],
"_In %n hour on %1$s_::_In %n hours on %1$s_" : ["%n 小时后 %1$s"],
"_In %n day on %1$s_::_In %n days on %1$s_" : ["%n 天后 %1$s"],
"_In %n week on %1$s_::_In %n weeks on %1$s_" : ["%n 周后 %1$s"],
"_In %n month on %1$s_::_In %n months on %1$s_" : ["%n 个月后 %1$s"],
"_In %n year on %1$s_::_In %n years on %1$s_" : ["%n 年后 %1$s"],
"In the past on %1$s then on %2$s" : "过去 %1$s,然后 %2$s",
"_In %n minute on %1$s then on %2$s_::_In %n minutes on %1$s then on %2$s_" : ["%n 分钟后 %1$s,然后 %2$s"],
"_In %n hour on %1$s then on %2$s_::_In %n hours on %1$s then on %2$s_" : ["%n 小时后 %1$s,然后 %2$s"],
"_In %n day on %1$s then on %2$s_::_In %n days on %1$s then on %2$s_" : ["%n 天后 %1$s,然后 %2$s"],
"_In %n week on %1$s then on %2$s_::_In %n weeks on %1$s then on %2$s_" : ["%n 周后 %1$s,然后 %2$s"],
"_In %n month on %1$s then on %2$s_::_In %n months on %1$s then on %2$s_" : ["%n 个月后 %1$s,然后 %2$s"],
"_In %n year on %1$s then on %2$s_::_In %n years on %1$s then on %2$s_" : ["%n 年后 %1$s,然后 %2$s"],
"In the past on %1$s then on %2$s and %3$s" : "在过去 %1$s,然后 %2$s 和 %3$s",
"_In %n minute on %1$s then on %2$s and %3$s_::_In %n minutes on %1$s then on %2$s and %3$s_" : ["%n 分钟后 %1$s,然后 %2$s 和 %3$s"],
"_In %n hour on %1$s then on %2$s and %3$s_::_In %n hours on %1$s then on %2$s and %3$s_" : ["%n 小时后 %1$s,然后 %2$s 和 %3$s"],
"_In %n day on %1$s then on %2$s and %3$s_::_In %n days on %1$s then on %2$s and %3$s_" : ["%n 天后 %1$s,然后 %2$s 和 %3$s"],
"_In %n week on %1$s then on %2$s and %3$s_::_In %n weeks on %1$s then on %2$s and %3$s_" : ["%n 周后 %1$s,然后 %2$s 和 %3$s"],
"_In %n month on %1$s then on %2$s and %3$s_::_In %n months on %1$s then on %2$s and %3$s_" : ["%n 个月后 %1$s,然后 %2$s 和 %3$s"],
"_In %n year on %1$s then on %2$s and %3$s_::_In %n years on %1$s then on %2$s and %3$s_" : ["%n 年后 %1$s,然后 %2$s 和 %3$s"],
"Could not generate next recurrence statement" : "无法生成下一个重复语句",
"Cancelled: %1$s" : "已取消:%1$s",
"\"%1$s\" has been canceled" : "“%1$s”已取消",
+3 -3
View File
@@ -187,9 +187,9 @@ class BirthdayService {
$originalYear = (int)$dateParts['year'];
}
$leapDay = ((int)$dateParts['month'] === 2 && (int)$dateParts['date'] === 29);
if ($dateParts['year'] === null) {
$leapDay = ((int)$dateParts['month'] === 2
&& (int)$dateParts['date'] === 29);
if ($dateParts['year'] === null || $originalYear < 1970) {
$birthday = ($leapDay ? '1972-' : '1970-')
. $dateParts['month'] . '-' . $dateParts['date'];
}
+8 -3
View File
@@ -1066,9 +1066,9 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
* @param int $calendarType
* @return array
*/
public function getLimitedCalendarObjects(int $calendarId, int $calendarType = self::CALENDAR_TYPE_CALENDAR, array $fields = []):array {
public function getLimitedCalendarObjects(int $calendarId, int $calendarType = self::CALENDAR_TYPE_CALENDAR):array {
$query = $this->db->getQueryBuilder();
$query->select($fields ?: ['id', 'uid', 'etag', 'uri', 'calendardata'])
$query->select(['id','uid', 'etag', 'uri', 'calendardata'])
->from('calendarobjects')
->where($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId)))
->andWhere($query->expr()->eq('calendartype', $query->createNamedParameter($calendarType)))
@@ -1077,7 +1077,12 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
$result = [];
while (($row = $stmt->fetchAssociative()) !== false) {
$result[$row['uid']] = $row;
$result[$row['uid']] = [
'id' => $row['id'],
'etag' => $row['etag'],
'uri' => $row['uri'],
'calendardata' => $row['calendardata'],
];
}
$stmt->closeCursor();
+17 -27
View File
@@ -23,6 +23,9 @@ use Sabre\VObject\UUIDUtil;
*/
class ImportService {
/** @var resource */
private $source;
public function __construct(
private CalDavBackend $backend,
) {
@@ -41,15 +44,18 @@ class ImportService {
if (!is_resource($source)) {
throw new InvalidArgumentException('Invalid import source must be a file resource');
}
$this->source = $source;
switch ($options->getFormat()) {
case 'ical':
return $this->importProcess($source, $calendar, $options, $this->importText(...));
return $this->importProcess($calendar, $options, $this->importText(...));
break;
case 'jcal':
return $this->importProcess($source, $calendar, $options, $this->importJson(...));
return $this->importProcess($calendar, $options, $this->importJson(...));
break;
case 'xcal':
return $this->importProcess($source, $calendar, $options, $this->importXml(...));
return $this->importProcess($calendar, $options, $this->importXml(...));
break;
default:
throw new InvalidArgumentException('Invalid import format');
@@ -59,15 +65,10 @@ class ImportService {
/**
* Generates object stream from a text formatted source (ical)
*
* @param resource $source
*
* @return Generator<\Sabre\VObject\Component\VCalendar>
*/
public function importText($source): Generator {
if (!is_resource($source)) {
throw new InvalidArgumentException('Invalid import source must be a file resource');
}
$importer = new TextImporter($source);
private function importText(): Generator {
$importer = new TextImporter($this->source);
$structure = $importer->structure();
$sObjectPrefix = $importer::OBJECT_PREFIX;
$sObjectSuffix = $importer::OBJECT_SUFFIX;
@@ -112,15 +113,10 @@ class ImportService {
/**
* Generates object stream from a xml formatted source (xcal)
*
* @param resource $source
*
* @return Generator<\Sabre\VObject\Component\VCalendar>
*/
public function importXml($source): Generator {
if (!is_resource($source)) {
throw new InvalidArgumentException('Invalid import source must be a file resource');
}
$importer = new XmlImporter($source);
private function importXml(): Generator {
$importer = new XmlImporter($this->source);
$structure = $importer->structure();
$sObjectPrefix = $importer::OBJECT_PREFIX;
$sObjectSuffix = $importer::OBJECT_SUFFIX;
@@ -159,16 +155,11 @@ class ImportService {
/**
* Generates object stream from a json formatted source (jcal)
*
* @param resource $source
*
* @return Generator<\Sabre\VObject\Component\VCalendar>
*/
public function importJson($source): Generator {
if (!is_resource($source)) {
throw new InvalidArgumentException('Invalid import source must be a file resource');
}
private function importJson(): Generator {
/** @var VCALENDAR $importer */
$importer = Reader::readJson($source);
$importer = Reader::readJson($this->source);
// calendar time zones
$timezones = [];
foreach ($importer->VTIMEZONE as $timezone) {
@@ -221,18 +212,17 @@ class ImportService {
*
* @since 32.0.0
*
* @param resource $source
* @param CalendarImportOptions $options
* @param callable $generator<CalendarImportOptions>: Generator<\Sabre\VObject\Component\VCalendar>
*
* @return array<string,array<string,string|array<string>>>
*/
public function importProcess($source, CalendarImpl $calendar, CalendarImportOptions $options, callable $generator): array {
public function importProcess(CalendarImpl $calendar, CalendarImportOptions $options, callable $generator): array {
$calendarId = $calendar->getKey();
$calendarUri = $calendar->getUri();
$principalUri = $calendar->getPrincipalUri();
$outcome = [];
foreach ($generator($source) as $vObject) {
foreach ($generator() as $vObject) {
$components = $vObject->getBaseComponents();
// determine if the object has no base component types
if (count($components) === 0) {
@@ -144,31 +144,19 @@ class EmailProvider extends AbstractProvider {
IL10N $l10n,
string $calendarDisplayName,
VEvent $vevent):void {
$template->addBodyListItem(
htmlspecialchars($calendarDisplayName),
$l10n->t('Calendar:'),
$this->getAbsoluteImagePath('actions/info.png'),
htmlspecialchars($calendarDisplayName),
);
$template->addBodyListItem($calendarDisplayName, $l10n->t('Calendar:'),
$this->getAbsoluteImagePath('actions/info.png'));
$template->addBodyListItem($this->generateDateString($l10n, $vevent), $l10n->t('Date:'),
$this->getAbsoluteImagePath('places/calendar.png'));
if (isset($vevent->LOCATION)) {
$template->addBodyListItem(
htmlspecialchars((string)$vevent->LOCATION),
$l10n->t('Where:'),
$this->getAbsoluteImagePath('actions/address.png'),
htmlspecialchars((string)$vevent->LOCATION),
);
$template->addBodyListItem((string)$vevent->LOCATION, $l10n->t('Where:'),
$this->getAbsoluteImagePath('actions/address.png'));
}
if (isset($vevent->DESCRIPTION)) {
$template->addBodyListItem(
htmlspecialchars((string)$vevent->DESCRIPTION),
$l10n->t('Description:'),
$this->getAbsoluteImagePath('actions/more.png'),
htmlspecialchars((string)$vevent->DESCRIPTION),
);
$template->addBodyListItem((string)$vevent->DESCRIPTION, $l10n->t('Description:'),
$this->getAbsoluteImagePath('actions/more.png'));
}
}
+21 -31
View File
@@ -11,8 +11,7 @@ namespace OCA\DAV\CalDAV\Schedule;
use OC\URLGenerator;
use OCA\DAV\CalDAV\EventReader;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Config\IUserConfig;
use OCP\IAppConfig;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\IL10N;
use OCP\IUserManager;
@@ -26,7 +25,6 @@ use Sabre\VObject\ITip\Message;
use Sabre\VObject\Parameter;
use Sabre\VObject\Property;
use Sabre\VObject\Recur\EventIterator;
use function htmlspecialchars;
class IMipService {
@@ -42,13 +40,12 @@ class IMipService {
public function __construct(
private URLGenerator $urlGenerator,
private IConfig $config,
private IDBConnection $db,
private ISecureRandom $random,
private L10NFactory $l10nFactory,
private ITimeFactory $timeFactory,
private readonly IUserManager $userManager,
private readonly IUserConfig $userConfig,
private readonly IAppConfig $appConfig,
) {
$language = $this->l10nFactory->findGenericLanguage();
$locale = $this->l10nFactory->findLocale($language);
@@ -83,11 +80,10 @@ class IMipService {
if (!isset($vevent->$property)) {
return $default;
}
$value = $vevent->$property->getValue();
$newstring = $value === null ? null : htmlspecialchars($value);
$newstring = $vevent->$property->getValue();
if (isset($oldVEvent->$property) && $oldVEvent->$property->getValue() !== $newstring) {
$oldstring = $oldVEvent->$property->getValue();
return sprintf($strikethrough, htmlspecialchars($oldstring), $newstring);
return sprintf($strikethrough, $oldstring, $newstring);
}
return $newstring;
}
@@ -100,8 +96,8 @@ class IMipService {
return $default;
}
/** @var string|null $newString */
$newString = htmlspecialchars($vevent->$property->getValue());
$oldString = isset($oldVEvent->$property) ? htmlspecialchars($oldVEvent->$property->getValue()) : null;
$newString = $vevent->$property->getValue();
$oldString = isset($oldVEvent->$property) ? $oldVEvent->$property->getValue() : null;
if ($oldString !== $newString) {
return sprintf(
"<span style='text-decoration: line-through'>%s</span><br />%s",
@@ -801,10 +797,10 @@ class IMipService {
$strikethrough = "<span style='text-decoration: line-through'>%s</span>";
$newMeetingWhen = $this->generateWhenString($eventReaderCurrent);
$newSummary = htmlspecialchars(isset($vEvent->SUMMARY) && (string)$vEvent->SUMMARY !== '' ? (string)$vEvent->SUMMARY : $this->l10n->t('Untitled event'));
$newDescription = htmlspecialchars(isset($vEvent->DESCRIPTION) && (string)$vEvent->DESCRIPTION !== '' ? (string)$vEvent->DESCRIPTION : $defaultVal);
$newSummary = isset($vEvent->SUMMARY) && (string)$vEvent->SUMMARY !== '' ? (string)$vEvent->SUMMARY : $this->l10n->t('Untitled event');
$newDescription = isset($vEvent->DESCRIPTION) && (string)$vEvent->DESCRIPTION !== '' ? (string)$vEvent->DESCRIPTION : $defaultVal;
$newUrl = isset($vEvent->URL) && (string)$vEvent->URL !== '' ? sprintf('<a href="%1$s">%1$s</a>', $vEvent->URL) : $defaultVal;
$newLocation = htmlspecialchars(isset($vEvent->LOCATION) && (string)$vEvent->LOCATION !== '' ? (string)$vEvent->LOCATION : $defaultVal);
$newLocation = isset($vEvent->LOCATION) && (string)$vEvent->LOCATION !== '' ? (string)$vEvent->LOCATION : $defaultVal;
$newLocationHtml = $this->linkify($newLocation) ?? $newLocation;
$data = [];
@@ -889,8 +885,8 @@ class IMipService {
$users = $this->userManager->getByEmail($userAddress);
if ($users !== []) {
$user = array_shift($users);
$language = $this->userConfig->getValueString($user->getUID(), 'core', 'lang', '') ?: null;
$locale = $this->userConfig->getValueString($user->getUID(), 'core', 'locale', '') ?: null;
$language = $this->config->getUserValue($user->getUID(), 'core', 'lang', null);
$locale = $this->config->getUserValue($user->getUID(), 'core', 'locale', null);
}
// fallback to attendee LANGUAGE parameter if language not set
if ($language === null && isset($attendee['LANGUAGE']) && $attendee['LANGUAGE'] instanceof Parameter) {
@@ -998,7 +994,7 @@ class IMipService {
* The default is 'no', which matches old behavior, and is privacy preserving.
*
* To enable including attendees in invitation emails:
* % php occ config:app:set dav invitation_list_attendees --value yes --type bool
* % php occ config:app:set dav invitation_list_attendees --value yes
*
* @param IEMailTemplate $template
* @param IL10N $this->l10n
@@ -1006,12 +1002,12 @@ class IMipService {
* @author brad2014 on github.com
*/
public function addAttendees(IEMailTemplate $template, VEvent $vevent) {
if (!$this->appConfig->getValueBool('dav', 'invitation_list_attendees')) {
if ($this->config->getAppValue('dav', 'invitation_list_attendees', 'no') === 'no') {
return;
}
if (isset($vevent->ORGANIZER)) {
/** @var Property&Property\ICalendar\CalAddress $organizer */
/** @var Property | Property\ICalendar\CalAddress $organizer */
$organizer = $vevent->ORGANIZER;
$organizerEmail = substr($organizer->getNormalizedValue(), 7);
/** @var string|null $organizerName */
@@ -1041,14 +1037,8 @@ class IMipService {
$attendeesHTML = [];
$attendeesText = [];
foreach ($attendees as $attendee) {
/** @var Property&Property\ICalendar\CalAddress $attendee */
$attendeeEmail = substr($attendee->getNormalizedValue(), 7);
$attendeeName = null;
if (isset($attendee['CN'])) {
/** @var Parameter $cn */
$cn = $attendee['CN'];
$attendeeName = $cn->getValue();
}
$attendeeName = isset($attendee['CN']) ? $attendee['CN']->getValue() : null;
$attendeeHTML = sprintf('<a href="%s">%s</a>',
htmlspecialchars($attendee->getNormalizedValue()),
htmlspecialchars($attendeeName ?: $attendeeEmail));
@@ -1077,22 +1067,22 @@ class IMipService {
*/
public function addBulletList(IEMailTemplate $template, VEvent $vevent, $data) {
$template->addBodyListItem(
$data['meeting_title_html'] ?? htmlspecialchars($data['meeting_title']), $this->l10n->t('Title:'),
$data['meeting_title_html'] ?? $data['meeting_title'], $this->l10n->t('Title:'),
$this->getAbsoluteImagePath('caldav/title.png'), $data['meeting_title'], '', IMipPlugin::IMIP_INDENT);
if ($data['meeting_when'] !== '') {
$template->addBodyListItem($data['meeting_when_html'] ?? htmlspecialchars($data['meeting_when']), $this->l10n->t('When:'),
$template->addBodyListItem($data['meeting_when_html'] ?? $data['meeting_when'], $this->l10n->t('When:'),
$this->getAbsoluteImagePath('caldav/time.png'), $data['meeting_when'], '', IMipPlugin::IMIP_INDENT);
}
if ($data['meeting_location'] !== '') {
$template->addBodyListItem($data['meeting_location_html'] ?? htmlspecialchars($data['meeting_location']), $this->l10n->t('Location:'),
$template->addBodyListItem($data['meeting_location_html'] ?? $data['meeting_location'], $this->l10n->t('Location:'),
$this->getAbsoluteImagePath('caldav/location.png'), $data['meeting_location'], '', IMipPlugin::IMIP_INDENT);
}
if ($data['meeting_url'] !== '') {
$template->addBodyListItem($data['meeting_url_html'] ?? htmlspecialchars($data['meeting_url']), $this->l10n->t('Link:'),
$template->addBodyListItem($data['meeting_url_html'] ?? $data['meeting_url'], $this->l10n->t('Link:'),
$this->getAbsoluteImagePath('caldav/link.png'), $data['meeting_url'], '', IMipPlugin::IMIP_INDENT);
}
if (isset($data['meeting_occurring'])) {
$template->addBodyListItem($data['meeting_occurring_html'] ?? htmlspecialchars($data['meeting_occurring']), $this->l10n->t('Occurring:'),
$template->addBodyListItem($data['meeting_occurring_html'] ?? $data['meeting_occurring'], $this->l10n->t('Occurring:'),
$this->getAbsoluteImagePath('caldav/time.png'), $data['meeting_occurring'], '', IMipPlugin::IMIP_INDENT);
}
@@ -1100,7 +1090,7 @@ class IMipService {
/* Put description last, like an email body, since it can be arbitrarily long */
if ($data['meeting_description']) {
$template->addBodyListItem($data['meeting_description_html'] ?? htmlspecialchars($data['meeting_description']), $this->l10n->t('Description:'),
$template->addBodyListItem($data['meeting_description_html'] ?? $data['meeting_description'], $this->l10n->t('Description:'),
$this->getAbsoluteImagePath('caldav/description.png'), $data['meeting_description'], '', IMipPlugin::IMIP_INDENT);
}
}
+2 -4
View File
@@ -12,7 +12,6 @@ namespace OCA\DAV\CalDAV;
use OCA\DAV\Db\PropertyMapper;
use OCP\Calendar\ICalendar;
use OCP\Calendar\IManager;
use OCP\Config\IUserConfig;
use OCP\IConfig;
use Sabre\VObject\Component\VCalendar;
use Sabre\VObject\Component\VTimeZone;
@@ -23,14 +22,13 @@ class TimezoneService {
public function __construct(
private IConfig $config,
private IUserConfig $userConfig,
private PropertyMapper $propertyMapper,
private IManager $calendarManager,
) {
}
public function getUserTimezone(string $userId): ?string {
$fromConfig = $this->userConfig->getValueString(
$fromConfig = $this->config->getUserValue(
$userId,
'core',
'timezone',
@@ -53,7 +51,7 @@ class TimezoneService {
}
$principal = 'principals/users/' . $userId;
$uri = $this->userConfig->getValueString($userId, 'dav', 'defaultCalendar', CalDavBackend::PERSONAL_CALENDAR_URI);
$uri = $this->config->getUserValue($userId, 'dav', 'defaultCalendar', CalDavBackend::PERSONAL_CALENDAR_URI);
$calendars = $this->calendarManager->getCalendarsForPrincipal($principal);
/** @var ?VTimeZone $personalCalendarTimezone */
@@ -14,6 +14,7 @@ use OCP\Http\Client\IClientService;
use OCP\Http\Client\LocalServerException;
use OCP\IAppConfig;
use Psr\Log\LoggerInterface;
use Sabre\VObject\Reader;
class Connection {
public function __construct(
@@ -25,10 +26,8 @@ class Connection {
/**
* gets webcal feed from remote server
*
* @return array{data: resource, format: string}|null
*/
public function queryWebcalFeed(array $subscription): ?array {
public function queryWebcalFeed(array $subscription): ?string {
$subscriptionId = $subscription['id'];
$url = $this->cleanURL($subscription['source']);
if ($url === null) {
@@ -55,7 +54,6 @@ class Connection {
'User-Agent' => $uaString,
'Accept' => 'text/calendar, application/calendar+json, application/calendar+xml',
],
'stream' => true,
];
$user = parse_url($subscription['source'], PHP_URL_USER);
@@ -79,22 +77,42 @@ class Connection {
return null;
}
$body = $response->getBody();
$contentType = $response->getHeader('Content-Type');
$contentType = explode(';', $contentType, 2)[0];
switch ($contentType) {
case 'application/calendar+json':
try {
$jCalendar = Reader::readJson($body, Reader::OPTION_FORGIVING);
} catch (Exception $ex) {
// In case of a parsing error return null
$this->logger->warning("Subscription $subscriptionId could not be parsed", ['exception' => $ex]);
return null;
}
return $jCalendar->serialize();
$format = match ($contentType) {
'application/calendar+json' => 'jcal',
'application/calendar+xml' => 'xcal',
default => 'ical',
};
case 'application/calendar+xml':
try {
$xCalendar = Reader::readXML($body);
} catch (Exception $ex) {
// In case of a parsing error return null
$this->logger->warning("Subscription $subscriptionId could not be parsed", ['exception' => $ex]);
return null;
}
return $xCalendar->serialize();
// With 'stream' => true, getBody() returns the underlying stream resource
$stream = $response->getBody();
if (!is_resource($stream)) {
return null;
case 'text/calendar':
default:
try {
$vCalendar = Reader::read($body);
} catch (Exception $ex) {
// In case of a parsing error return null
$this->logger->warning("Subscription $subscriptionId could not be parsed", ['exception' => $ex]);
return null;
}
return $vCalendar->serialize();
}
return ['data' => $stream, 'format' => $format];
}
/**
@@ -9,14 +9,18 @@ declare(strict_types=1);
namespace OCA\DAV\CalDAV\WebcalCaching;
use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\CalDAV\Import\ImportService;
use OCP\AppFramework\Utility\ITimeFactory;
use Psr\Log\LoggerInterface;
use Sabre\DAV\Exception\BadRequest;
use Sabre\DAV\Exception\Forbidden;
use Sabre\DAV\PropPatch;
use Sabre\VObject\Component;
use Sabre\VObject\DateTimeParser;
use Sabre\VObject\InvalidDataException;
use Sabre\VObject\ParseException;
use Sabre\VObject\Reader;
use Sabre\VObject\Recur\NoInstancesException;
use Sabre\VObject\Splitter\ICalendar;
use Sabre\VObject\UUIDUtil;
use function count;
@@ -32,20 +36,20 @@ class RefreshWebcalService {
private LoggerInterface $logger,
private Connection $connection,
private ITimeFactory $time,
private ImportService $importService,
) {
}
public function refreshSubscription(string $principalUri, string $uri) {
$subscription = $this->getSubscription($principalUri, $uri);
$mutations = [];
if (!$subscription) {
return;
}
// Check the refresh rate if there is any
if (!empty($subscription[self::REFRESH_RATE])) {
// add the refresh interval to the last modified timestamp
$refreshInterval = new \DateInterval($subscription[self::REFRESH_RATE]);
if (!empty($subscription['{http://apple.com/ns/ical/}refreshrate'])) {
// add the refresh interval to the lastmodified timestamp
$refreshInterval = new \DateInterval($subscription['{http://apple.com/ns/ical/}refreshrate']);
$updateTime = $this->time->getDateTime();
$updateTime->setTimestamp($subscription['lastmodified'])->add($refreshInterval);
if ($updateTime->getTimestamp() > $this->time->getTime()) {
@@ -53,116 +57,109 @@ class RefreshWebcalService {
}
}
$result = $this->connection->queryWebcalFeed($subscription);
if (!$result) {
$webcalData = $this->connection->queryWebcalFeed($subscription);
if (!$webcalData) {
return;
}
$data = $result['data'];
$format = $result['format'];
$localData = $this->calDavBackend->getLimitedCalendarObjects((int)$subscription['id'], CalDavBackend::CALENDAR_TYPE_SUBSCRIPTION);
$stripTodos = ($subscription[self::STRIP_TODOS] ?? 1) === 1;
$stripAlarms = ($subscription[self::STRIP_ALARMS] ?? 1) === 1;
$stripAttachments = ($subscription[self::STRIP_ATTACHMENTS] ?? 1) === 1;
try {
$existingObjects = $this->calDavBackend->getLimitedCalendarObjects((int)$subscription['id'], CalDavBackend::CALENDAR_TYPE_SUBSCRIPTION, ['id', 'uid', 'etag', 'uri']);
$splitter = new ICalendar($webcalData, Reader::OPTION_FORGIVING);
$generator = match ($format) {
'xcal' => $this->importService->importXml(...),
'jcal' => $this->importService->importJson(...),
default => $this->importService->importText(...)
};
while ($vObject = $splitter->getNext()) {
/** @var Component $vObject */
$compName = null;
$uid = null;
foreach ($generator($data) as $vObject) {
/** @var Component\VCalendar $vObject */
$vBase = $vObject->getBaseComponent();
if (!$vBase->UID) {
continue;
}
// Some calendar providers (e.g. Google, MS) use very long UIDs
if (strlen($vBase->UID->getValue()) > 512) {
$this->logger->warning('Skipping calendar object with overly long UID from subscription "{subscriptionId}"', [
'subscriptionId' => $subscription['id'],
'uid' => $vBase->UID->getValue(),
]);
continue;
}
if ($stripTodos && $vBase->name === 'VTODO') {
continue;
}
if ($stripAlarms || $stripAttachments) {
foreach ($vObject->getComponents() as $component) {
if ($component->name === 'VTIMEZONE') {
continue;
}
if ($stripAlarms) {
$component->remove('VALARM');
}
if ($stripAttachments) {
$component->remove('ATTACH');
}
foreach ($vObject->getComponents() as $component) {
if ($component->name === 'VTIMEZONE') {
continue;
}
}
$sObject = $vObject->serialize();
$uid = $vBase->UID->getValue();
$etag = md5($sObject);
$compName = $component->name;
// No existing object with this UID, create it
if (!isset($existingObjects[$uid])) {
try {
$this->calDavBackend->createCalendarObject(
$subscription['id'],
UUIDUtil::getUUID() . '.ics',
$sObject,
CalDavBackend::CALENDAR_TYPE_SUBSCRIPTION
);
} catch (\Exception $ex) {
$this->logger->warning('Unable to create calendar object from subscription {subscriptionId}', [
'exception' => $ex,
'subscriptionId' => $subscription['id'],
'source' => $subscription['source'],
]);
if ($stripAlarms) {
unset($component->{'VALARM'});
}
} elseif ($existingObjects[$uid]['etag'] !== $etag) {
// Existing object with this UID but different etag, update it
$this->calDavBackend->updateCalendarObject(
$subscription['id'],
$existingObjects[$uid]['uri'],
$sObject,
CalDavBackend::CALENDAR_TYPE_SUBSCRIPTION
);
unset($existingObjects[$uid]);
} else {
// Existing object with same etag, just remove from tracking
unset($existingObjects[$uid]);
if ($stripAttachments) {
unset($component->{'ATTACH'});
}
$uid = $component->{ 'UID' }->getValue();
}
if ($stripTodos && $compName === 'VTODO') {
continue;
}
if (!isset($uid)) {
continue;
}
try {
$denormalized = $this->calDavBackend->getDenormalizedData($vObject->serialize());
} catch (InvalidDataException|Forbidden $ex) {
$this->logger->warning('Unable to denormalize calendar object from subscription {subscriptionId}', ['exception' => $ex, 'subscriptionId' => $subscription['id'], 'source' => $subscription['source']]);
continue;
}
// Find all identical sets and remove them from the update
if (isset($localData[$uid]) && $denormalized['etag'] === $localData[$uid]['etag']) {
unset($localData[$uid]);
continue;
}
$vObjectCopy = clone $vObject;
$identical = isset($localData[$uid]) && $this->compareWithoutDtstamp($vObjectCopy, $localData[$uid]);
if ($identical) {
unset($localData[$uid]);
continue;
}
// Find all modified sets and update them
if (isset($localData[$uid]) && $denormalized['etag'] !== $localData[$uid]['etag']) {
$this->calDavBackend->updateCalendarObject($subscription['id'], $localData[$uid]['uri'], $vObject->serialize(), CalDavBackend::CALENDAR_TYPE_SUBSCRIPTION);
unset($localData[$uid]);
continue;
}
// Only entirely new events get created here
try {
$objectUri = $this->getRandomCalendarObjectUri();
$this->calDavBackend->createCalendarObject($subscription['id'], $objectUri, $vObject->serialize(), CalDavBackend::CALENDAR_TYPE_SUBSCRIPTION);
} catch (NoInstancesException|BadRequest $ex) {
$this->logger->warning('Unable to create calendar object from subscription {subscriptionId}', ['exception' => $ex, 'subscriptionId' => $subscription['id'], 'source' => $subscription['source']]);
}
}
// Clean up objects that no longer exist in the remote feed
// The only events left over should be those not found upstream
if (!empty($existingObjects)) {
$ids = array_map('intval', array_column($existingObjects, 'id'));
$uris = array_column($existingObjects, 'uri');
$this->calDavBackend->purgeCachedEventsForSubscription((int)$subscription['id'], $ids, $uris);
$ids = array_map(static function ($dataSet): int {
return (int)$dataSet['id'];
}, $localData);
$uris = array_map(static function ($dataSet): string {
return $dataSet['uri'];
}, $localData);
if (!empty($ids) && !empty($uris)) {
// Clean up on aisle 5
// The only events left over in the $localData array should be those that don't exist upstream
// All deleted VObjects from upstream are removed
$this->calDavBackend->purgeCachedEventsForSubscription($subscription['id'], $ids, $uris);
}
// Update refresh rate from the last processed object
if (isset($vObject)) {
$this->updateRefreshRate($subscription, $vObject);
$newRefreshRate = $this->checkWebcalDataForRefreshRate($subscription, $webcalData);
if ($newRefreshRate) {
$mutations[self::REFRESH_RATE] = $newRefreshRate;
}
$this->updateSubscription($subscription, $mutations);
} catch (ParseException $ex) {
$this->logger->error('Subscription {subscriptionId} could not be refreshed due to a parsing error', ['exception' => $ex, 'subscriptionId' => $subscription['id']]);
} finally {
// Close the data stream to free resources
if (is_resource($data)) {
fclose($data);
}
}
}
@@ -184,34 +181,84 @@ class RefreshWebcalService {
return $subscriptions[0];
}
/**
* Update refresh rate from calendar object if:
* - current subscription does not store a refreshrate
* - the webcal feed suggests a valid refreshrate
* check if:
* - current subscription stores a refreshrate
* - the webcal feed suggests a refreshrate
* - return suggested refreshrate if user didn't set a custom one
*
*/
private function updateRefreshRate(array $subscription, Component\VCalendar $vCalendar): void {
// if there is already a refreshrate stored in the database, don't override it
if (!empty($subscription[self::REFRESH_RATE])) {
return;
private function checkWebcalDataForRefreshRate(array $subscription, string $webcalData): ?string {
// if there is no refreshrate stored in the database, check the webcal feed
// whether it suggests any refresh rate and store that in the database
if (isset($subscription[self::REFRESH_RATE]) && $subscription[self::REFRESH_RATE] !== null) {
return null;
}
$refreshRate = $vCalendar->{'REFRESH-INTERVAL'}?->getValue()
?? $vCalendar->{'X-PUBLISHED-TTL'}?->getValue();
/** @var Component\VCalendar $vCalendar */
$vCalendar = Reader::read($webcalData);
if ($refreshRate === null) {
return;
$newRefreshRate = null;
if (isset($vCalendar->{'X-PUBLISHED-TTL'})) {
$newRefreshRate = $vCalendar->{'X-PUBLISHED-TTL'}->getValue();
}
if (isset($vCalendar->{'REFRESH-INTERVAL'})) {
$newRefreshRate = $vCalendar->{'REFRESH-INTERVAL'}->getValue();
}
// check if refresh rate is valid
if (!$newRefreshRate) {
return null;
}
// check if new refresh rate is even valid
try {
DateTimeParser::parseDuration($refreshRate);
} catch (InvalidDataException) {
DateTimeParser::parseDuration($newRefreshRate);
} catch (InvalidDataException $ex) {
return null;
}
return $newRefreshRate;
}
/**
* update subscription stored in database
* used to set:
* - refreshrate
* - source
*
* @param array $subscription
* @param array $mutations
*/
private function updateSubscription(array $subscription, array $mutations) {
if (empty($mutations)) {
return;
}
$propPatch = new PropPatch([self::REFRESH_RATE => $refreshRate]);
$propPatch = new PropPatch($mutations);
$this->calDavBackend->updateSubscription($subscription['id'], $propPatch);
$propPatch->commit();
}
/**
* Returns a random uri for a calendar-object
*
* @return string
*/
public function getRandomCalendarObjectUri():string {
return UUIDUtil::getUUID() . '.ics';
}
private function compareWithoutDtstamp(Component $vObject, array $calendarObject): bool {
foreach ($vObject->getComponents() as $component) {
unset($component->{'DTSTAMP'});
}
$localVobject = Reader::read($calendarObject['calendardata']);
foreach ($localVobject->getComponents() as $component) {
unset($component->{'DTSTAMP'});
}
return strcasecmp($localVobject->serialize(), $vObject->serialize()) === 0;
}
}
+2 -7
View File
@@ -77,10 +77,6 @@ class EntityCollection extends RootCollection implements IProperties {
public function getChild($name) {
try {
$comment = $this->commentsManager->get($name);
if ($comment->getObjectType() !== $this->name
|| $comment->getObjectId() !== $this->id) {
throw new NotFound();
}
return new CommentNode(
$this->commentsManager,
$comment,
@@ -134,9 +130,8 @@ class EntityCollection extends RootCollection implements IProperties {
*/
public function childExists($name) {
try {
$comment = $this->commentsManager->get($name);
return $comment->getObjectType() === $this->name
&& $comment->getObjectId() === $this->id;
$this->commentsManager->get($name);
return true;
} catch (NotFoundException $e) {
return false;
}
@@ -49,7 +49,7 @@ class BlockLegacyClientPlugin extends ServerPlugin {
return;
}
$minimumSupportedDesktopVersion = $this->config->getSystemValueString('minimum.supported.desktop.version', '3.0.82');
$minimumSupportedDesktopVersion = $this->config->getSystemValueString('minimum.supported.desktop.version', '3.1.0');
$maximumSupportedDesktopVersion = $this->config->getSystemValueString('maximum.supported.desktop.version', '99.99.99');
// Check if the client is a desktop client
+2 -84
View File
@@ -8,7 +8,6 @@
namespace OCA\DAV\Connector\Sabre;
use OC\Files\Mount\MoveableMount;
use OC\Files\Utils\PathHelper;
use OC\Files\View;
use OCA\DAV\AppInfo\Application;
use OCA\DAV\Connector\Sabre\Exception\FileLocked;
@@ -39,14 +38,8 @@ use Sabre\DAV\Exception\NotFound;
use Sabre\DAV\Exception\ServiceUnavailable;
use Sabre\DAV\IFile;
use Sabre\DAV\INode;
use Sabre\DAV\INodeByPath;
class Directory extends Node implements
\Sabre\DAV\ICollection,
\Sabre\DAV\IQuota,
\Sabre\DAV\IMoveTarget,
\Sabre\DAV\ICopyTarget,
INodeByPath {
class Directory extends Node implements \Sabre\DAV\ICollection, \Sabre\DAV\IQuota, \Sabre\DAV\IMoveTarget, \Sabre\DAV\ICopyTarget {
/**
* Cached directory content
* @var FileInfo[]
@@ -188,7 +181,7 @@ class Directory extends Node implements
// If we are, then only PUT and MKCOL are allowed (see plugin)
// so we are safe to return the directory without a risk of
// leaking files and folders structure.
if ($storage->instanceOfStorage(PublicShareWrapper::class)) {
if ($storage instanceof PublicShareWrapper) {
$share = $storage->getShare();
$allowDirectory = ($share->getPermissions() & Constants::PERMISSION_READ) !== Constants::PERMISSION_READ;
}
@@ -497,79 +490,4 @@ class Directory extends Node implements
public function getNode(): Folder {
return $this->node;
}
public function getNodeForPath($path): INode {
$storage = $this->info->getStorage();
$allowDirectory = false;
// Checking if we're in a file drop
// If we are, then only PUT and MKCOL are allowed (see plugin)
// so we are safe to return the directory without a risk of
// leaking files and folders structure.
if ($storage->instanceOfStorage(PublicShareWrapper::class)) {
$share = $storage->getShare();
$allowDirectory = ($share->getPermissions() & Constants::PERMISSION_READ) !== Constants::PERMISSION_READ;
}
// For file drop we need to be allowed to read the directory with the nickname
if (!$allowDirectory && !$this->info->isReadable()) {
// avoid detecting files through this way
throw new NotFound();
}
$destinationPath = PathHelper::normalizePath($this->getPath() . '/' . $path);
$destinationDir = dirname($destinationPath);
try {
$info = $this->getNode()->get($path);
} catch (NotFoundException $e) {
throw new \Sabre\DAV\Exception\NotFound('File with name ' . $destinationPath
. ' could not be located');
} catch (StorageNotAvailableException $e) {
throw new \Sabre\DAV\Exception\ServiceUnavailable($e->getMessage(), 0, $e);
} catch (NotPermittedException $ex) {
throw new InvalidPath($ex->getMessage(), false, $ex);
}
// if not in a public share with no read permissions, throw Forbidden
if (!$allowDirectory && !$info->isReadable()) {
if (Server::get(IAppManager::class)->isEnabledForAnyone('files_accesscontrol')) {
throw new Forbidden('No read permissions. This might be caused by files_accesscontrol, check your configured rules');
}
throw new Forbidden('No read permissions');
}
if ($info->getMimeType() === FileInfo::MIMETYPE_FOLDER) {
$node = new \OCA\DAV\Connector\Sabre\Directory($this->fileView, $info, $this->tree, $this->shareManager);
} else {
// In case reading a directory was allowed but it turns out the node was a not a directory, reject it now.
if (!$this->info->isReadable()) {
throw new NotFound();
}
$node = new File($this->fileView, $info, $this->shareManager);
}
$this->tree?->cacheNode($node);
// recurse upwards until the root and check for read permissions to keep
// ACL checks working in files_accesscontrol
if (!$allowDirectory && $destinationDir !== '') {
$scanPath = $destinationPath;
while (($scanPath = dirname($scanPath)) !== '/') {
// fileView can get the parent info in a cheaper way compared
// to the node API
/** @psalm-suppress InternalMethod */
$info = $this->fileView->getFileInfo($scanPath, false);
$directory = new Directory($this->fileView, $info, $this->tree, $this->shareManager);
$readable = $directory->getNode()->isReadable();
if (!$readable) {
throw new \Sabre\DAV\Exception\NotFound('File with name ' . $destinationPath
. ' could not be located');
}
}
}
return $node;
}
}
+7 -14
View File
@@ -18,7 +18,6 @@ use OCA\DAV\Connector\Sabre\Exception\FileLocked;
use OCA\DAV\Connector\Sabre\Exception\Forbidden as DAVForbiddenException;
use OCA\DAV\Connector\Sabre\Exception\UnsupportedMediaType;
use OCP\App\IAppManager;
use OCP\Constants;
use OCP\Encryption\Exceptions\GenericEncryptionException;
use OCP\Files;
use OCP\Files\EntityTooLargeException;
@@ -540,24 +539,18 @@ class File extends Node implements IFile {
}
/**
* @throws NotFoundException
* @throws NotPermittedException
* @return array|bool
*/
public function getDirectDownload(): array|false {
public function getDirectDownload() {
if (Server::get(IAppManager::class)->isEnabledForUser('encryption')) {
return false;
return [];
}
$node = $this->getNode();
$storage = $node->getStorage();
if (!$storage) {
return false;
[$storage, $internalPath] = $this->fileView->resolvePath($this->path);
if (is_null($storage)) {
return [];
}
if (!($node->getPermissions() & Constants::PERMISSION_READ)) {
return false;
}
return $storage->getDirectDownloadById((string)$node->getId());
return $storage->getDirectDownload($internalPath);
}
/**
+7 -20
View File
@@ -50,7 +50,6 @@ class FilesPlugin extends ServerPlugin {
public const OCM_SHARE_PERMISSIONS_PROPERTYNAME = '{http://open-cloud-mesh.org/ns}share-permissions';
public const SHARE_ATTRIBUTES_PROPERTYNAME = '{http://nextcloud.org/ns}share-attributes';
public const DOWNLOADURL_PROPERTYNAME = '{http://owncloud.org/ns}downloadURL';
public const DOWNLOADURL_EXPIRATION_PROPERTYNAME = '{http://nextcloud.org/ns}download-url-expiration';
public const SIZE_PROPERTYNAME = '{http://owncloud.org/ns}size';
public const GETETAG_PROPERTYNAME = '{DAV:}getetag';
public const LASTMODIFIED_PROPERTYNAME = '{DAV:}lastmodified';
@@ -121,7 +120,6 @@ class FilesPlugin extends ServerPlugin {
$server->protectedProperties[] = self::SHARE_ATTRIBUTES_PROPERTYNAME;
$server->protectedProperties[] = self::SIZE_PROPERTYNAME;
$server->protectedProperties[] = self::DOWNLOADURL_PROPERTYNAME;
$server->protectedProperties[] = self::DOWNLOADURL_EXPIRATION_PROPERTYNAME;
$server->protectedProperties[] = self::OWNER_ID_PROPERTYNAME;
$server->protectedProperties[] = self::OWNER_DISPLAY_NAME_PROPERTYNAME;
$server->protectedProperties[] = self::CHECKSUMS_PROPERTYNAME;
@@ -473,30 +471,19 @@ class FilesPlugin extends ServerPlugin {
}
if ($node instanceof File) {
$requestProperties = $propFind->getRequestedProperties();
if (in_array(self::DOWNLOADURL_PROPERTYNAME, $requestProperties, true)
|| in_array(self::DOWNLOADURL_EXPIRATION_PROPERTYNAME, $requestProperties, true)) {
$propFind->handle(self::DOWNLOADURL_PROPERTYNAME, function () use ($node) {
try {
$directDownloadUrl = $node->getDirectDownload();
} catch (StorageNotAvailableException|ForbiddenException) {
$directDownloadUrl = null;
}
$propFind->handle(self::DOWNLOADURL_PROPERTYNAME, function () use ($node, $directDownloadUrl) {
if ($directDownloadUrl && isset($directDownloadUrl['url'])) {
if (isset($directDownloadUrl['url'])) {
return $directDownloadUrl['url'];
}
} catch (StorageNotAvailableException $e) {
return false;
});
$propFind->handle(self::DOWNLOADURL_EXPIRATION_PROPERTYNAME, function () use ($node, $directDownloadUrl) {
if ($directDownloadUrl && isset($directDownloadUrl['expiration'])) {
return $directDownloadUrl['expiration'];
}
} catch (ForbiddenException $e) {
return false;
});
}
}
return false;
});
$propFind->handle(self::CHECKSUMS_PROPERTYNAME, function () use ($node) {
$checksum = $node->getChecksum();
+2 -2
View File
@@ -9,11 +9,11 @@
namespace OCA\DAV\Connector\Sabre;
use OC\Files\View;
use OCA\DAV\Upload\FutureFile;
use OCA\DAV\Upload\UploadFolder;
use OCP\Files\StorageNotAvailableException;
use Sabre\DAV\Exception\InsufficientStorage;
use Sabre\DAV\Exception\ServiceUnavailable;
use Sabre\DAV\IFile;
use Sabre\DAV\INode;
use Sabre\HTTP\RequestInterface;
use Sabre\HTTP\ResponseInterface;
@@ -138,7 +138,7 @@ class QuotaPlugin extends \Sabre\DAV\ServerPlugin {
*/
public function beforeMove(string $sourcePath, string $destinationPath): bool {
$sourceNode = $this->server->tree->getNodeForPath($sourcePath);
if (!$sourceNode instanceof IFile) {
if (!$sourceNode instanceof FutureFile) {
return true;
}
+42 -69
View File
@@ -37,7 +37,6 @@ use OCP\IRequest;
use OCP\ITagManager;
use OCP\IUserManager;
use OCP\IUserSession;
use OCP\L10N\IFactory;
use OCP\SabrePluginEvent;
use OCP\SystemTag\ISystemTagManager;
use OCP\SystemTag\ISystemTagObjectMapper;
@@ -71,13 +70,15 @@ class ServerFactory {
Plugin $authPlugin,
callable $viewCallBack,
): Server {
// /public.php/webdav/ shows the files in the share in the root itself
// and not under /public.php/webdav/files/{token} so we should keep
// compatibility for that.
$needsSharesInRoot = $baseUri === '/public.php/webdav/';
$useCollection = $isPublicShare && !$needsSharesInRoot;
$debugEnabled = $this->config->getSystemValue('debug', false);
[$tree, $rootCollection] = $this->getTree($useCollection);
// Fire up server
if ($isPublicShare) {
$rootCollection = new SimpleCollection('root');
$tree = new CachingTree($rootCollection);
} else {
$rootCollection = null;
$tree = new ObjectTree();
}
$server = new Server($tree);
// Set URL explicitly due to reverse-proxy situations
$server->httpRequest->setUrl($requestUri);
@@ -126,8 +127,8 @@ class ServerFactory {
}
// wait with registering these until auth is handled and the filesystem is setup
$server->on('beforeMethod:*', function () use ($server,
$tree, $viewCallBack, $isPublicShare, $rootCollection, $debugEnabled): void {
$server->on('beforeMethod:*', function () use ($server, $tree,
$viewCallBack, $isPublicShare, $rootCollection, $debugEnabled): void {
// ensure the skeleton is copied
$userFolder = \OC::$server->getUserFolder();
@@ -146,8 +147,36 @@ class ServerFactory {
$root = new File($view, $rootInfo);
}
if ($rootCollection !== null) {
$this->initRootCollection($rootCollection, $root);
if ($isPublicShare) {
$userPrincipalBackend = new Principal(
\OCP\Server::get(IUserManager::class),
\OCP\Server::get(IGroupManager::class),
\OCP\Server::get(IAccountManager::class),
\OCP\Server::get(\OCP\Share\IManager::class),
\OCP\Server::get(IUserSession::class),
\OCP\Server::get(IAppManager::class),
\OCP\Server::get(ProxyMapper::class),
\OCP\Server::get(KnownUserService::class),
\OCP\Server::get(IConfig::class),
\OC::$server->getL10NFactory(),
);
// Mount the share collection at /public.php/dav/shares/<share token>
$rootCollection->addChild(new RootCollection(
$root,
$userPrincipalBackend,
'principals/shares',
));
// Mount the upload collection at /public.php/dav/uploads/<share token>
$rootCollection->addChild(new \OCA\DAV\Upload\RootCollection(
$userPrincipalBackend,
'principals/shares',
\OCP\Server::get(CleanupService::class),
\OCP\Server::get(IRootFolder::class),
\OCP\Server::get(IUserSession::class),
\OCP\Server::get(\OCP\Share\IManager::class),
));
} else {
/** @var ObjectTree $tree */
$tree->init($root, $view, $this->mountManager);
@@ -162,7 +191,8 @@ class ServerFactory {
$this->userSession,
\OCP\Server::get(IFilenameValidator::class),
\OCP\Server::get(IAccountManager::class),
$isPublicShare
false,
!$debugEnabled
)
);
$server->addPlugin(new QuotaPlugin($view));
@@ -222,61 +252,4 @@ class ServerFactory {
}, 30); // priority 30: after auth (10) and acl(20), before lock(50) and handling the request
return $server;
}
/**
* Returns a Tree object and, if $useCollection is true, the collection used
* as root.
*
* @param bool $useCollection Whether to use a collection or the legacy
* ObjectTree, which doesn't use collections.
* @return array{0: CachingTree|ObjectTree, 1: SimpleCollection|null}
*/
public function getTree(bool $useCollection): array {
if ($useCollection) {
$rootCollection = new SimpleCollection('root');
$tree = new CachingTree($rootCollection);
return [$tree, $rootCollection];
}
return [new ObjectTree(), null];
}
/**
* Adds the user's principal backend to $rootCollection.
*/
private function initRootCollection(SimpleCollection $rootCollection, Directory|File $root): void {
$userPrincipalBackend = new Principal(
\OCP\Server::get(IUserManager::class),
\OCP\Server::get(IGroupManager::class),
\OCP\Server::get(IAccountManager::class),
\OCP\Server::get(\OCP\Share\IManager::class),
\OCP\Server::get(IUserSession::class),
\OCP\Server::get(IAppManager::class),
\OCP\Server::get(ProxyMapper::class),
\OCP\Server::get(KnownUserService::class),
\OCP\Server::get(IConfig::class),
\OCP\Server::get(IFactory::class),
);
// Mount the share collection at /public.php/dav/files/<share token>
$rootCollection->addChild(
new RootCollection(
$root,
$userPrincipalBackend,
'principals/shares',
)
);
// Mount the upload collection at /public.php/dav/uploads/<share token>
$rootCollection->addChild(
new \OCA\DAV\Upload\RootCollection(
$userPrincipalBackend,
'principals/shares',
\OCP\Server::get(CleanupService::class),
\OCP\Server::get(IRootFolder::class),
\OCP\Server::get(IUserSession::class),
\OCP\Server::get(\OCP\Share\IManager::class),
)
);
}
}
@@ -1,56 +0,0 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\DAV\Migration;
use Closure;
use OCP\DB\ISchemaWrapper;
use OCP\Migration\Attributes\ColumnType;
use OCP\Migration\Attributes\ModifyColumn;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;
use Override;
#[ModifyColumn(table: 'calendarobjects', name: 'uid', type: ColumnType::STRING, description: 'Increase uid length to 512 characters')]
#[ModifyColumn(table: 'calendar_reminders', name: 'uid', type: ColumnType::STRING, description: 'Increase uid length to 512 characters')]
#[ModifyColumn(table: 'calendar_invitations', name: 'uid', type: ColumnType::STRING, description: 'Increase uid length to 512 characters')]
class Version1036Date20251202000000 extends SimpleMigrationStep {
/**
* @param Closure(): ISchemaWrapper $schemaClosure
*/
#[Override]
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
$modified = false;
$table = $schema->getTable('calendarobjects');
$column = $table->getColumn('uid');
if ($column->getLength() < 512) {
$column->setLength(512);
$modified = true;
}
$table = $schema->getTable('calendar_reminders');
$column = $table->getColumn('uid');
if ($column->getLength() < 512) {
$column->setLength(512);
$modified = true;
}
$table = $schema->getTable('calendar_invitations');
$column = $table->getColumn('uid');
if ($column->getLength() < 512) {
$column->setLength(512);
$modified = true;
}
return $modified ? $schema : null;
}
}
+1
View File
@@ -301,6 +301,7 @@ class Server {
\OCP\Server::get(IFilenameValidator::class),
\OCP\Server::get(IAccountManager::class),
false,
$config->getSystemValueBool('debug', false) === false,
)
);
$this->server->addPlugin(new ChecksumUpdatePlugin());
+1 -7
View File
@@ -14,7 +14,6 @@ use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;
use OCP\IUserSession;
use Sabre\DAV\Exception\Forbidden;
use Sabre\DAV\Exception\NotFound;
use Sabre\DAV\ICollection;
class UploadHome implements ICollection {
@@ -73,12 +72,7 @@ class UploadHome implements ICollection {
}
public function childExists($name): bool {
try {
$this->getChild($name);
return true;
} catch (NotFound $e) {
return false;
}
return !is_null($this->getChild($name));
}
public function delete() {
@@ -45,7 +45,7 @@ class RefreshWebcalJobTest extends TestCase {
#[\PHPUnit\Framework\Attributes\DataProvider('runDataProvider')]
public function testRun(int $lastRun, int $time, bool $process): void {
$backgroundJob = new RefreshWebcalJob($this->refreshWebcalService, $this->config, $this->logger, $this->timeFactory);
$backgroundJob->setId('42');
$backgroundJob->setId(42);
$backgroundJob->setArgument([
'principaluri' => 'principals/users/testuser',
@@ -14,9 +14,9 @@ use OCA\DAV\CalDAV\EventComparisonService;
use OCA\DAV\CalDAV\Schedule\IMipPlugin;
use OCA\DAV\CalDAV\Schedule\IMipService;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Config\IUserConfig;
use OCP\Defaults;
use OCP\IAppConfig;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\IURLGenerator;
use OCP\IUser;
@@ -46,7 +46,7 @@ class IMipPluginCharsetTest extends TestCase {
// Dependencies
private Defaults&MockObject $defaults;
private IAppConfig&MockObject $appConfig;
private IUserConfig&MockObject $userConfig;
private IConfig&MockObject $config;
private IDBConnection&MockObject $db;
private IFactory $l10nFactory;
private IManager&MockObject $mailManager;
@@ -77,8 +77,7 @@ class IMipPluginCharsetTest extends TestCase {
// IMipService
$this->urlGenerator = $this->createMock(URLGenerator::class);
$this->userConfig = $this->createMock(IUserConfig::class);
$this->appConfig = $this->createMock(IAppConfig::class);
$this->config = $this->createMock(IConfig::class);
$this->db = $this->createMock(IDBConnection::class);
$this->random = $this->createMock(ISecureRandom::class);
$l10n = $this->createMock(L10N::class);
@@ -93,19 +92,19 @@ class IMipPluginCharsetTest extends TestCase {
$this->userManager->method('getByEmail')->willReturn([]);
$this->imipService = new IMipService(
$this->urlGenerator,
$this->config,
$this->db,
$this->random,
$this->l10nFactory,
$this->timeFactory,
$this->userManager,
$this->userConfig,
$this->appConfig,
$this->userManager
);
// EventComparisonService
$this->eventComparisonService = new EventComparisonService();
// IMipPlugin
$this->appConfig = $this->createMock(IAppConfig::class);
$message = new \OC\Mail\Message(new Email(), false);
$this->mailer = $this->createMock(IMailer::class);
$this->mailer->method('createMessage')
@@ -178,13 +177,8 @@ class IMipPluginCharsetTest extends TestCase {
public function testCharsetMailProvider(): void {
// Arrange
$this->appConfig->method('getValueBool')
->willReturnCallback(function ($app, $key, $default) {
if ($app === 'core') {
$this->assertEquals($key, 'mail_providers_enabled');
return true;
}
return $default;
});
->with('core', 'mail_providers_enabled', true)
->willReturn(true);
$mailMessage = new MailProviderMessage();
$mailService = $this->createMockForIntersectionOfInterfaces([IService::class, IMessageSend::class]);
$mailService->method('initiateMessage')
@@ -13,8 +13,7 @@ use OC\URLGenerator;
use OCA\DAV\CalDAV\EventReader;
use OCA\DAV\CalDAV\Schedule\IMipService;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Config\IUserConfig;
use OCP\IAppConfig;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\IL10N;
use OCP\IUserManager;
@@ -27,8 +26,7 @@ use Test\TestCase;
class IMipServiceTest extends TestCase {
private URLGenerator&MockObject $urlGenerator;
private IUserConfig&MockObject $userConfig;
private IAppConfig&MockObject $appConfig;
private IConfig&MockObject $config;
private IDBConnection&MockObject $db;
private ISecureRandom&MockObject $random;
private IFactory&MockObject $l10nFactory;
@@ -49,8 +47,7 @@ class IMipServiceTest extends TestCase {
parent::setUp();
$this->urlGenerator = $this->createMock(URLGenerator::class);
$this->userConfig = $this->createMock(IUserConfig::class);
$this->appConfig = $this->createMock(IAppConfig::class);
$this->config = $this->createMock(IConfig::class);
$this->db = $this->createMock(IDBConnection::class);
$this->random = $this->createMock(ISecureRandom::class);
$this->l10nFactory = $this->createMock(IFactory::class);
@@ -66,13 +63,12 @@ class IMipServiceTest extends TestCase {
->willReturn($this->l10n);
$this->service = new IMipService(
$this->urlGenerator,
$this->config,
$this->db,
$this->random,
$this->l10nFactory,
$this->timeFactory,
$this->userManager,
$this->userConfig,
$this->appConfig,
$this->userManager
);
// construct calendar with a 1 hour event and same start/end time zones
@@ -9,14 +9,12 @@ declare(strict_types=1);
namespace OCA\DAV\Tests\unit\CalDAV;
use DateTimeZone;
use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\CalDAV\CalendarImpl;
use OCA\DAV\CalDAV\TimezoneService;
use OCA\DAV\Db\Property;
use OCA\DAV\Db\PropertyMapper;
use OCP\Calendar\ICalendar;
use OCP\Calendar\IManager;
use OCP\Config\IUserConfig;
use OCP\IConfig;
use PHPUnit\Framework\MockObject\MockObject;
use Sabre\VObject\Component\VTimeZone;
@@ -24,7 +22,6 @@ use Test\TestCase;
class TimezoneServiceTest extends TestCase {
private IConfig&MockObject $config;
private IUserConfig&MockObject $userConfig;
private PropertyMapper&MockObject $propertyMapper;
private IManager&MockObject $calendarManager;
private TimezoneService $service;
@@ -33,21 +30,19 @@ class TimezoneServiceTest extends TestCase {
parent::setUp();
$this->config = $this->createMock(IConfig::class);
$this->userConfig = $this->createMock(IUserConfig::class);
$this->propertyMapper = $this->createMock(PropertyMapper::class);
$this->calendarManager = $this->createMock(IManager::class);
$this->service = new TimezoneService(
$this->config,
$this->userConfig,
$this->propertyMapper,
$this->calendarManager,
);
}
public function testGetUserTimezoneFromSettings(): void {
$this->userConfig->expects(self::once())
->method('getValueString')
$this->config->expects(self::once())
->method('getUserValue')
->with('test123', 'core', 'timezone', '')
->willReturn('Europe/Warsaw');
@@ -57,8 +52,8 @@ class TimezoneServiceTest extends TestCase {
}
public function testGetUserTimezoneFromAvailability(): void {
$this->userConfig->expects(self::once())
->method('getValueString')
$this->config->expects(self::once())
->method('getUserValue')
->with('test123', 'core', 'timezone', '')
->willReturn('');
$property = new Property();
@@ -81,11 +76,11 @@ END:VCALENDAR');
}
public function testGetUserTimezoneFromPersonalCalendar(): void {
$this->userConfig->expects(self::exactly(2))
->method('getValueString')
$this->config->expects(self::exactly(2))
->method('getUserValue')
->willReturnMap([
['test123', 'core', 'timezone', '', false, ''],
['test123', 'dav', 'defaultCalendar', CalDavBackend::PERSONAL_CALENDAR_URI, false, 'personal-1'],
['test123', 'core', 'timezone', '', ''],
['test123', 'dav', 'defaultCalendar', '', 'personal-1'],
]);
$other = $this->createMock(ICalendar::class);
$other->method('getUri')->willReturn('other');
@@ -110,11 +105,11 @@ END:VCALENDAR');
}
public function testGetUserTimezoneFromAny(): void {
$this->userConfig->expects(self::exactly(2))
->method('getValueString')
$this->config->expects(self::exactly(2))
->method('getUserValue')
->willReturnMap([
['test123', 'core', 'timezone', '', false, ''],
['test123', 'dav', 'defaultCalendar', CalDavBackend::PERSONAL_CALENDAR_URI, false, 'personal-1'],
['test123', 'core', 'timezone', '', ''],
['test123', 'dav', 'defaultCalendar', '', 'personal-1'],
]);
$other = $this->createMock(ICalendar::class);
$other->method('getUri')->willReturn('other');
@@ -89,8 +89,12 @@ class ConnectionTest extends TestCase {
}
/**
* @param string $result
* @param string $contentType
*/
#[\PHPUnit\Framework\Attributes\DataProvider('urlDataProvider')]
public function testConnection(string $url, string $contentType, string $expectedFormat): void {
public function testConnection(string $url, string $result, string $contentType): void {
$client = $this->createMock(IClient::class);
$response = $this->createMock(IResponse::class);
$subscription = [
@@ -119,76 +123,16 @@ class ConnectionTest extends TestCase {
->with('https://foo.bar/bla2')
->willReturn($response);
$response->expects($this->once())
->method('getBody')
->with()
->willReturn($result);
$response->expects($this->once())
->method('getHeader')
->with('Content-Type')
->willReturn($contentType);
// Create a stream resource to simulate streaming response
$stream = fopen('php://temp', 'r+');
fwrite($stream, 'test calendar data');
rewind($stream);
$response->expects($this->once())
->method('getBody')
->willReturn($stream);
$output = $this->connection->queryWebcalFeed($subscription);
$this->assertIsArray($output);
$this->assertArrayHasKey('data', $output);
$this->assertArrayHasKey('format', $output);
$this->assertIsResource($output['data']);
$this->assertEquals($expectedFormat, $output['format']);
// Cleanup
if (is_resource($output['data'])) {
fclose($output['data']);
}
}
public function testConnectionReturnsNullWhenBodyIsNotResource(): void {
$client = $this->createMock(IClient::class);
$response = $this->createMock(IResponse::class);
$subscription = [
'id' => 42,
'uri' => 'sub123',
'refreshreate' => 'P1H',
'striptodos' => 1,
'stripalarms' => 1,
'stripattachments' => 1,
'source' => 'https://foo.bar/bla2',
'lastmodified' => 0,
];
$this->clientService->expects($this->once())
->method('newClient')
->with()
->willReturn($client);
$this->config->expects($this->once())
->method('getValueString')
->with('dav', 'webcalAllowLocalAccess', 'no')
->willReturn('no');
$client->expects($this->once())
->method('get')
->with('https://foo.bar/bla2')
->willReturn($response);
$response->expects($this->once())
->method('getHeader')
->with('Content-Type')
->willReturn('text/calendar');
// Return a string instead of a resource
$response->expects($this->once())
->method('getBody')
->willReturn('not a resource');
$output = $this->connection->queryWebcalFeed($subscription);
$this->assertNull($output);
$this->connection->queryWebcalFeed($subscription);
}
public static function runLocalURLDataProvider(): array {
@@ -212,9 +156,21 @@ class ConnectionTest extends TestCase {
public static function urlDataProvider(): array {
return [
['https://foo.bar/bla2', 'text/calendar;charset=utf8', 'ical'],
['https://foo.bar/bla2', 'application/calendar+json', 'jcal'],
['https://foo.bar/bla2', 'application/calendar+xml', 'xcal'],
[
'https://foo.bar/bla2',
"BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nCALSCALE:GREGORIAN\r\nBEGIN:VEVENT\r\nUID:12345\r\nDTSTAMP:20160218T133704Z\r\nDTSTART;VALUE=DATE:19000101\r\nDTEND;VALUE=DATE:19000102\r\nRRULE:FREQ=YEARLY\r\nSUMMARY:12345's Birthday (1900)\r\nTRANSP:TRANSPARENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n",
'text/calendar;charset=utf8',
],
[
'https://foo.bar/bla2',
'["vcalendar",[["prodid",{},"text","-//Example Corp.//Example Client//EN"],["version",{},"text","2.0"]],[["vtimezone",[["last-modified",{},"date-time","2004-01-10T03:28:45Z"],["tzid",{},"text","US/Eastern"]],[["daylight",[["dtstart",{},"date-time","2000-04-04T02:00:00"],["rrule",{},"recur",{"freq":"YEARLY","byday":"1SU","bymonth":4}],["tzname",{},"text","EDT"],["tzoffsetfrom",{},"utc-offset","-05:00"],["tzoffsetto",{},"utc-offset","-04:00"]],[]],["standard",[["dtstart",{},"date-time","2000-10-26T02:00:00"],["rrule",{},"recur",{"freq":"YEARLY","byday":"1SU","bymonth":10}],["tzname",{},"text","EST"],["tzoffsetfrom",{},"utc-offset","-04:00"],["tzoffsetto",{},"utc-offset","-05:00"]],[]]]],["vevent",[["dtstamp",{},"date-time","2006-02-06T00:11:21Z"],["dtstart",{"tzid":"US/Eastern"},"date-time","2006-01-02T14:00:00"],["duration",{},"duration","PT1H"],["recurrence-id",{"tzid":"US/Eastern"},"date-time","2006-01-04T12:00:00"],["summary",{},"text","Event #2"],["uid",{},"text","12345"]],[]]]]',
'application/calendar+json',
],
[
'https://foo.bar/bla2',
'<?xml version="1.0" encoding="utf-8" ?><icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"><vcalendar><properties><prodid><text>-//Example Inc.//Example Client//EN</text></prodid><version><text>2.0</text></version></properties><components><vevent><properties><dtstamp><date-time>2006-02-06T00:11:21Z</date-time></dtstamp><dtstart><parameters><tzid><text>US/Eastern</text></tzid></parameters><date-time>2006-01-04T14:00:00</date-time></dtstart><duration><duration>PT1H</duration></duration><recurrence-id><parameters><tzid><text>US/Eastern</text></tzid></parameters><date-time>2006-01-04T12:00:00</date-time></recurrence-id><summary><text>Event #2 bis</text></summary><uid><text>12345</text></uid></properties></vevent></components></vcalendar></icalendar>',
'application/calendar+xml',
],
];
}
}
@@ -8,7 +8,6 @@ declare(strict_types=1);
namespace OCA\DAV\Tests\unit\CalDAV\WebcalCaching;
use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\CalDAV\Import\ImportService;
use OCA\DAV\CalDAV\WebcalCaching\Connection;
use OCA\DAV\CalDAV\WebcalCaching\RefreshWebcalService;
use OCP\AppFramework\Utility\ITimeFactory;
@@ -24,8 +23,7 @@ class RefreshWebcalServiceTest extends TestCase {
private CalDavBackend&MockObject $caldavBackend;
private Connection&MockObject $connection;
private LoggerInterface&MockObject $logger;
private ImportService&MockObject $importService;
private ITimeFactory&MockObject $timeFactory;
private ITimeFactory&MockObject $time;
protected function setUp(): void {
parent::setUp();
@@ -33,32 +31,19 @@ class RefreshWebcalServiceTest extends TestCase {
$this->caldavBackend = $this->createMock(CalDavBackend::class);
$this->connection = $this->createMock(Connection::class);
$this->logger = $this->createMock(LoggerInterface::class);
$this->importService = $this->createMock(ImportService::class);
$this->timeFactory = $this->createMock(ITimeFactory::class);
// Default time factory behavior: current time is far in the future so refresh always happens
$this->timeFactory->method('getTime')->willReturn(PHP_INT_MAX);
$this->timeFactory->method('getDateTime')->willReturn(new \DateTime());
}
/**
* Helper to create a resource stream from string content
*/
private function createStreamFromString(string $content) {
$stream = fopen('php://temp', 'r+');
fwrite($stream, $content);
rewind($stream);
return $stream;
$this->time = $this->createMock(ITimeFactory::class);
}
#[\PHPUnit\Framework\Attributes\DataProvider('runDataProvider')]
public function testRun(string $body, string $format, string $result): void {
$refreshWebcalService = new RefreshWebcalService(
$this->caldavBackend,
$this->logger,
$this->connection,
$this->timeFactory,
$this->importService
);
public function testRun(string $body, string $contentType, string $result): void {
$refreshWebcalService = $this->getMockBuilder(RefreshWebcalService::class)
->onlyMethods(['getRandomCalendarObjectUri'])
->setConstructorArgs([$this->caldavBackend, $this->logger, $this->connection, $this->time])
->getMock();
$refreshWebcalService
->method('getRandomCalendarObjectUri')
->willReturn('uri-1.ics');
$this->caldavBackend->expects(self::once())
->method('getSubscriptionsForUser')
@@ -86,48 +71,26 @@ class RefreshWebcalServiceTest extends TestCase {
],
]);
$stream = $this->createStreamFromString($body);
$this->connection->expects(self::once())
->method('queryWebcalFeed')
->willReturn(['data' => $stream, 'format' => $format]);
$this->caldavBackend->expects(self::once())
->method('getLimitedCalendarObjects')
->willReturn([]);
// Create a VCalendar object that will be yielded by the import service
$vCalendar = VObject\Reader::read($result);
$generator = function () use ($vCalendar) {
yield $vCalendar;
};
$this->importService->expects(self::once())
->method('importText')
->willReturn($generator());
->willReturn($result);
$this->caldavBackend->expects(self::once())
->method('createCalendarObject')
->with(
'42',
self::matchesRegularExpression('/^[a-f0-9-]+\.ics$/'),
$result,
CalDavBackend::CALENDAR_TYPE_SUBSCRIPTION
);
->with(42, 'uri-1.ics', $result, 1);
$refreshWebcalService->refreshSubscription('principals/users/testuser', 'sub123');
}
#[\PHPUnit\Framework\Attributes\DataProvider('identicalDataProvider')]
public function testRunIdentical(string $uid, array $calendarObject, string $body, string $format, string $result): void {
$refreshWebcalService = new RefreshWebcalService(
$this->caldavBackend,
$this->logger,
$this->connection,
$this->timeFactory,
$this->importService
);
public function testRunIdentical(string $uid, array $calendarObject, string $body, string $contentType, string $result): void {
$refreshWebcalService = $this->getMockBuilder(RefreshWebcalService::class)
->onlyMethods(['getRandomCalendarObjectUri'])
->setConstructorArgs([$this->caldavBackend, $this->logger, $this->connection, $this->time])
->getMock();
$refreshWebcalService
->method('getRandomCalendarObjectUri')
->willReturn('uri-1.ics');
$this->caldavBackend->expects(self::once())
->method('getSubscriptionsForUser')
@@ -155,199 +118,78 @@ class RefreshWebcalServiceTest extends TestCase {
],
]);
$stream = $this->createStreamFromString($body);
$this->connection->expects(self::once())
->method('queryWebcalFeed')
->willReturn(['data' => $stream, 'format' => $format]);
->willReturn($result);
$this->caldavBackend->expects(self::once())
->method('getLimitedCalendarObjects')
->willReturn($calendarObject);
// Create a VCalendar object that will be yielded by the import service
$vCalendar = VObject\Reader::read($result);
$denormalised = [
'etag' => 100,
'size' => strlen($calendarObject[$uid]['calendardata']),
'uid' => 'sub456'
];
$generator = function () use ($vCalendar) {
yield $vCalendar;
};
$this->importService->expects(self::once())
->method('importText')
->willReturn($generator());
$this->caldavBackend->expects(self::once())
->method('getDenormalizedData')
->willReturn($denormalised);
$this->caldavBackend->expects(self::never())
->method('createCalendarObject');
$refreshWebcalService->refreshSubscription('principals/users/testuser', 'sub123');
$refreshWebcalService->refreshSubscription('principals/users/testuser', 'sub456');
}
public function testSubscriptionNotFound(): void {
$refreshWebcalService = new RefreshWebcalService(
$this->caldavBackend,
$this->logger,
$this->connection,
$this->timeFactory,
$this->importService
);
public function testRunJustUpdated(): void {
$refreshWebcalService = $this->getMockBuilder(RefreshWebcalService::class)
->onlyMethods(['getRandomCalendarObjectUri'])
->setConstructorArgs([$this->caldavBackend, $this->logger, $this->connection, $this->time])
->getMock();
$refreshWebcalService
->method('getRandomCalendarObjectUri')
->willReturn('uri-1.ics');
$this->caldavBackend->expects(self::once())
->method('getSubscriptionsForUser')
->with('principals/users/testuser')
->willReturn([]);
->willReturn([
[
'id' => '99',
'uri' => 'sub456',
RefreshWebcalService::REFRESH_RATE => 'P1D',
RefreshWebcalService::STRIP_TODOS => '1',
RefreshWebcalService::STRIP_ALARMS => '1',
RefreshWebcalService::STRIP_ATTACHMENTS => '1',
'source' => 'webcal://foo.bar/bla',
'lastmodified' => time(),
],
[
'id' => '42',
'uri' => 'sub123',
RefreshWebcalService::REFRESH_RATE => 'PT1H',
RefreshWebcalService::STRIP_TODOS => '1',
RefreshWebcalService::STRIP_ALARMS => '1',
RefreshWebcalService::STRIP_ATTACHMENTS => '1',
'source' => 'webcal://foo.bar/bla2',
'lastmodified' => time(),
],
]);
$timeMock = $this->createMock(\DateTime::class);
$this->time->expects(self::once())
->method('getDateTime')
->willReturn($timeMock);
$timeMock->expects(self::once())
->method('getTimestamp')
->willReturn(2101724667);
$this->time->expects(self::once())
->method('getTime')
->willReturn(time());
$this->connection->expects(self::never())
->method('queryWebcalFeed');
$refreshWebcalService->refreshSubscription('principals/users/testuser', 'sub123');
}
public function testConnectionReturnsNull(): void {
$refreshWebcalService = new RefreshWebcalService(
$this->caldavBackend,
$this->logger,
$this->connection,
$this->timeFactory,
$this->importService
);
$this->caldavBackend->expects(self::once())
->method('getSubscriptionsForUser')
->with('principals/users/testuser')
->willReturn([
[
'id' => '42',
'uri' => 'sub123',
RefreshWebcalService::STRIP_TODOS => '1',
RefreshWebcalService::STRIP_ALARMS => '1',
RefreshWebcalService::STRIP_ATTACHMENTS => '1',
'source' => 'webcal://foo.bar/bla2',
'lastmodified' => 0,
],
]);
$this->connection->expects(self::once())
->method('queryWebcalFeed')
->willReturn(null);
$this->importService->expects(self::never())
->method('importText');
$this->caldavBackend->expects(self::never())
->method('createCalendarObject');
$refreshWebcalService->refreshSubscription('principals/users/testuser', 'sub123');
}
public function testDeletedObjectsArePurged(): void {
$refreshWebcalService = new RefreshWebcalService(
$this->caldavBackend,
$this->logger,
$this->connection,
$this->timeFactory,
$this->importService
);
$this->caldavBackend->expects(self::once())
->method('getSubscriptionsForUser')
->with('principals/users/testuser')
->willReturn([
[
'id' => '42',
'uri' => 'sub123',
RefreshWebcalService::STRIP_TODOS => '1',
RefreshWebcalService::STRIP_ALARMS => '1',
RefreshWebcalService::STRIP_ATTACHMENTS => '1',
'source' => 'webcal://foo.bar/bla2',
'lastmodified' => 0,
],
]);
$body = "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Test//Test//EN\r\nBEGIN:VEVENT\r\nUID:new-event\r\nDTSTAMP:20160218T133704Z\r\nDTSTART:20160218T133704Z\r\nSUMMARY:New Event\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n";
$stream = $this->createStreamFromString($body);
$this->connection->expects(self::once())
->method('queryWebcalFeed')
->willReturn(['data' => $stream, 'format' => 'ical']);
// Existing objects include one that won't be in the feed
$this->caldavBackend->expects(self::once())
->method('getLimitedCalendarObjects')
->willReturn([
'old-deleted-event' => [
'id' => 99,
'uid' => 'old-deleted-event',
'etag' => 'old-etag',
'uri' => 'old-event.ics',
],
]);
$vCalendar = VObject\Reader::read($body);
$generator = function () use ($vCalendar) {
yield $vCalendar;
};
$this->importService->expects(self::once())
->method('importText')
->willReturn($generator());
$this->caldavBackend->expects(self::once())
->method('createCalendarObject');
$this->caldavBackend->expects(self::once())
->method('purgeCachedEventsForSubscription')
->with(42, [99], ['old-event.ics']);
$refreshWebcalService->refreshSubscription('principals/users/testuser', 'sub123');
}
public function testLongUidIsSkipped(): void {
$refreshWebcalService = new RefreshWebcalService(
$this->caldavBackend,
$this->logger,
$this->connection,
$this->timeFactory,
$this->importService
);
$this->caldavBackend->expects(self::once())
->method('getSubscriptionsForUser')
->with('principals/users/testuser')
->willReturn([
[
'id' => '42',
'uri' => 'sub123',
RefreshWebcalService::STRIP_TODOS => '1',
RefreshWebcalService::STRIP_ALARMS => '1',
RefreshWebcalService::STRIP_ATTACHMENTS => '1',
'source' => 'webcal://foo.bar/bla2',
'lastmodified' => 0,
],
]);
// Create a UID that is longer than 512 characters
$longUid = str_repeat('a', 513);
$body = "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Test//Test//EN\r\nBEGIN:VEVENT\r\nUID:$longUid\r\nDTSTAMP:20160218T133704Z\r\nDTSTART:20160218T133704Z\r\nSUMMARY:Event with long UID\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n";
$stream = $this->createStreamFromString($body);
$this->connection->expects(self::once())
->method('queryWebcalFeed')
->willReturn(['data' => $stream, 'format' => 'ical']);
$this->caldavBackend->expects(self::once())
->method('getLimitedCalendarObjects')
->willReturn([]);
$vCalendar = VObject\Reader::read($body);
$generator = function () use ($vCalendar) {
yield $vCalendar;
};
$this->importService->expects(self::once())
->method('importText')
->willReturn($generator());
// Event with long UID should be skipped, so createCalendarObject should never be called
$this->caldavBackend->expects(self::never())
->method('createCalendarObject');
@@ -355,12 +197,16 @@ class RefreshWebcalServiceTest extends TestCase {
}
#[\PHPUnit\Framework\Attributes\DataProvider('runDataProvider')]
public function testRunCreateCalendarNoException(string $body, string $format, string $result): void {
public function testRunCreateCalendarNoException(string $body, string $contentType, string $result): void {
$refreshWebcalService = $this->getMockBuilder(RefreshWebcalService::class)
->onlyMethods(['getSubscription'])
->setConstructorArgs([$this->caldavBackend, $this->logger, $this->connection, $this->timeFactory, $this->importService])
->onlyMethods(['getRandomCalendarObjectUri', 'getSubscription',])
->setConstructorArgs([$this->caldavBackend, $this->logger, $this->connection, $this->time])
->getMock();
$refreshWebcalService
->method('getRandomCalendarObjectUri')
->willReturn('uri-1.ics');
$refreshWebcalService
->method('getSubscription')
->willReturn([
@@ -374,26 +220,13 @@ class RefreshWebcalServiceTest extends TestCase {
'lastmodified' => 0,
]);
$stream = $this->createStreamFromString($body);
$this->connection->expects(self::once())
->method('queryWebcalFeed')
->willReturn(['data' => $stream, 'format' => $format]);
->willReturn($result);
$this->caldavBackend->expects(self::once())
->method('getLimitedCalendarObjects')
->willReturn([]);
// Create a VCalendar object that will be yielded by the import service
$vCalendar = VObject\Reader::read($result);
$generator = function () use ($vCalendar) {
yield $vCalendar;
};
$this->importService->expects(self::once())
->method('importText')
->willReturn($generator());
->method('createCalendarObject')
->with(42, 'uri-1.ics', $result, 1);
$noInstanceException = new NoInstancesException("can't add calendar object");
$this->caldavBackend->expects(self::once())
@@ -408,12 +241,16 @@ class RefreshWebcalServiceTest extends TestCase {
}
#[\PHPUnit\Framework\Attributes\DataProvider('runDataProvider')]
public function testRunCreateCalendarBadRequest(string $body, string $format, string $result): void {
public function testRunCreateCalendarBadRequest(string $body, string $contentType, string $result): void {
$refreshWebcalService = $this->getMockBuilder(RefreshWebcalService::class)
->onlyMethods(['getSubscription'])
->setConstructorArgs([$this->caldavBackend, $this->logger, $this->connection, $this->timeFactory, $this->importService])
->onlyMethods(['getRandomCalendarObjectUri', 'getSubscription'])
->setConstructorArgs([$this->caldavBackend, $this->logger, $this->connection, $this->time])
->getMock();
$refreshWebcalService
->method('getRandomCalendarObjectUri')
->willReturn('uri-1.ics');
$refreshWebcalService
->method('getSubscription')
->willReturn([
@@ -427,26 +264,13 @@ class RefreshWebcalServiceTest extends TestCase {
'lastmodified' => 0,
]);
$stream = $this->createStreamFromString($body);
$this->connection->expects(self::once())
->method('queryWebcalFeed')
->willReturn(['data' => $stream, 'format' => $format]);
->willReturn($result);
$this->caldavBackend->expects(self::once())
->method('getLimitedCalendarObjects')
->willReturn([]);
// Create a VCalendar object that will be yielded by the import service
$vCalendar = VObject\Reader::read($result);
$generator = function () use ($vCalendar) {
yield $vCalendar;
};
$this->importService->expects(self::once())
->method('importText')
->willReturn($generator());
->method('createCalendarObject')
->with(42, 'uri-1.ics', $result, 1);
$badRequestException = new BadRequest("can't add reach calendar url");
$this->caldavBackend->expects(self::once())
@@ -461,22 +285,20 @@ class RefreshWebcalServiceTest extends TestCase {
}
public static function identicalDataProvider(): array {
$icalBody = "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Sabre//Sabre VObject " . VObject\Version::VERSION . "//EN\r\nCALSCALE:GREGORIAN\r\nBEGIN:VEVENT\r\nUID:12345\r\nDTSTAMP:20160218T133704Z\r\nDTSTART;VALUE=DATE:19000101\r\nDTEND;VALUE=DATE:19000102\r\nRRULE:FREQ=YEARLY\r\nSUMMARY:12345's Birthday (1900)\r\nTRANSP:TRANSPARENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n";
$etag = md5($icalBody);
return [
[
'12345',
[
'12345' => [
'id' => 42,
'etag' => $etag,
'uri' => 'sub456.ics',
'etag' => 100,
'uri' => 'sub456',
'calendardata' => "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nCALSCALE:GREGORIAN\r\nBEGIN:VEVENT\r\nUID:12345\r\nDTSTAMP:20160218T133704Z\r\nDTSTART;VALUE=DATE:19000101\r\nDTEND;VALUE=DATE:19000102\r\nRRULE:FREQ=YEARLY\r\nSUMMARY:12345's Birthday (1900)\r\nTRANSP:TRANSPARENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n",
],
],
"BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nCALSCALE:GREGORIAN\r\nBEGIN:VEVENT\r\nUID:12345\r\nDTSTAMP:20160218T133704Z\r\nDTSTART;VALUE=DATE:19000101\r\nDTEND;VALUE=DATE:19000102\r\nRRULE:FREQ=YEARLY\r\nSUMMARY:12345's Birthday (1900)\r\nTRANSP:TRANSPARENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n",
'ical',
$icalBody,
'text/calendar;charset=utf8',
"BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nCALSCALE:GREGORIAN\r\nBEGIN:VEVENT\r\nUID:12345\r\nDTSTAMP:20180218T133704Z\r\nDTSTART;VALUE=DATE:19000101\r\nDTEND;VALUE=DATE:19000102\r\nRRULE:FREQ=YEARLY\r\nSUMMARY:12345's Birthday (1900)\r\nTRANSP:TRANSPARENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n",
],
];
}
@@ -485,9 +307,19 @@ class RefreshWebcalServiceTest extends TestCase {
return [
[
"BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nCALSCALE:GREGORIAN\r\nBEGIN:VEVENT\r\nUID:12345\r\nDTSTAMP:20160218T133704Z\r\nDTSTART;VALUE=DATE:19000101\r\nDTEND;VALUE=DATE:19000102\r\nRRULE:FREQ=YEARLY\r\nSUMMARY:12345's Birthday (1900)\r\nTRANSP:TRANSPARENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n",
'ical',
'text/calendar;charset=utf8',
"BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Sabre//Sabre VObject " . VObject\Version::VERSION . "//EN\r\nCALSCALE:GREGORIAN\r\nBEGIN:VEVENT\r\nUID:12345\r\nDTSTAMP:20160218T133704Z\r\nDTSTART;VALUE=DATE:19000101\r\nDTEND;VALUE=DATE:19000102\r\nRRULE:FREQ=YEARLY\r\nSUMMARY:12345's Birthday (1900)\r\nTRANSP:TRANSPARENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n",
],
[
'["vcalendar",[["prodid",{},"text","-//Example Corp.//Example Client//EN"],["version",{},"text","2.0"]],[["vtimezone",[["last-modified",{},"date-time","2004-01-10T03:28:45Z"],["tzid",{},"text","US/Eastern"]],[["daylight",[["dtstart",{},"date-time","2000-04-04T02:00:00"],["rrule",{},"recur",{"freq":"YEARLY","byday":"1SU","bymonth":4}],["tzname",{},"text","EDT"],["tzoffsetfrom",{},"utc-offset","-05:00"],["tzoffsetto",{},"utc-offset","-04:00"]],[]],["standard",[["dtstart",{},"date-time","2000-10-26T02:00:00"],["rrule",{},"recur",{"freq":"YEARLY","byday":"1SU","bymonth":10}],["tzname",{},"text","EST"],["tzoffsetfrom",{},"utc-offset","-04:00"],["tzoffsetto",{},"utc-offset","-05:00"]],[]]]],["vevent",[["dtstamp",{},"date-time","2006-02-06T00:11:21Z"],["dtstart",{"tzid":"US/Eastern"},"date-time","2006-01-02T14:00:00"],["duration",{},"duration","PT1H"],["recurrence-id",{"tzid":"US/Eastern"},"date-time","2006-01-04T12:00:00"],["summary",{},"text","Event #2"],["uid",{},"text","12345"]],[]]]]',
'application/calendar+json',
"BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Sabre//Sabre VObject " . VObject\Version::VERSION . "//EN\r\nCALSCALE:GREGORIAN\r\nBEGIN:VTIMEZONE\r\nLAST-MODIFIED:20040110T032845Z\r\nTZID:US/Eastern\r\nBEGIN:DAYLIGHT\r\nDTSTART:20000404T020000\r\nRRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4\r\nTZNAME:EDT\r\nTZOFFSETFROM:-0500\r\nTZOFFSETTO:-0400\r\nEND:DAYLIGHT\r\nBEGIN:STANDARD\r\nDTSTART:20001026T020000\r\nRRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=10\r\nTZNAME:EST\r\nTZOFFSETFROM:-0400\r\nTZOFFSETTO:-0500\r\nEND:STANDARD\r\nEND:VTIMEZONE\r\nBEGIN:VEVENT\r\nDTSTAMP:20060206T001121Z\r\nDTSTART;TZID=US/Eastern:20060102T140000\r\nDURATION:PT1H\r\nRECURRENCE-ID;TZID=US/Eastern:20060104T120000\r\nSUMMARY:Event #2\r\nUID:12345\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"
],
[
'<?xml version="1.0" encoding="utf-8" ?><icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"><vcalendar><properties><prodid><text>-//Example Inc.//Example Client//EN</text></prodid><version><text>2.0</text></version></properties><components><vevent><properties><dtstamp><date-time>2006-02-06T00:11:21Z</date-time></dtstamp><dtstart><parameters><tzid><text>US/Eastern</text></tzid></parameters><date-time>2006-01-04T14:00:00</date-time></dtstart><duration><duration>PT1H</duration></duration><recurrence-id><parameters><tzid><text>US/Eastern</text></tzid></parameters><date-time>2006-01-04T12:00:00</date-time></recurrence-id><summary><text>Event #2 bis</text></summary><uid><text>12345</text></uid></properties></vevent></components></vcalendar></icalendar>',
'application/calendar+xml',
"BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Sabre//Sabre VObject " . VObject\Version::VERSION . "//EN\r\nCALSCALE:GREGORIAN\r\nBEGIN:VEVENT\r\nDTSTAMP:20060206T001121Z\r\nDTSTART;TZID=US/Eastern:20060104T140000\r\nDURATION:PT1H\r\nRECURRENCE-ID;TZID=US/Eastern:20060104T120000\r\nSUMMARY:Event #2 bis\r\nUID:12345\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"
]
];
}
}
@@ -403,31 +403,31 @@ class BirthdayServiceTest extends TestCase {
[null, null, null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nEND:VCARD\r\n", 'BDAY', '', true, null],
[null, null, null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:\r\nEND:VCARD\r\n", 'BDAY', '', true, null],
[null, null, null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:someday\r\nEND:VCARD\r\n", 'BDAY', '', true, null],
['🎂 12345 (1900)', '19000101', 'FREQ=YEARLY', 'BDAY', '0', '1900', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19000101\r\nEND:VCARD\r\n", 'BDAY', '', true, null],
['🎂 12345 (1900)', '19001231', 'FREQ=YEARLY', 'BDAY', '0', '1900', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19001231\r\nEND:VCARD\r\n", 'BDAY', '', true, null],
['Death of 12345 (1900)', '19001231', 'FREQ=YEARLY', 'DEATHDATE', '0', '1900', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nDEATHDATE:19001231\r\nEND:VCARD\r\n", 'DEATHDATE', '-death', true, null],
['Death of 12345 (1900)', '19001231', 'FREQ=YEARLY', 'DEATHDATE', '0', '1900', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nDEATHDATE:19001231\r\nEND:VCARD\r\n", 'DEATHDATE', '-death', false, null],
['💍 12345 (1900)', '19001231', 'FREQ=YEARLY', 'ANNIVERSARY', '0', '1900', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nANNIVERSARY:19001231\r\nEND:VCARD\r\n", 'ANNIVERSARY', '-anniversary', true, null],
['12345 (⚭1900)', '19001231', 'FREQ=YEARLY', 'ANNIVERSARY', '0', '1900', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nANNIVERSARY:19001231\r\nEND:VCARD\r\n", 'ANNIVERSARY', '-anniversary', false, null],
['🎂 12345 (1900)', '19700101', 'FREQ=YEARLY', 'BDAY', '0', '1900', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19000101\r\nEND:VCARD\r\n", 'BDAY', '', true, null],
['🎂 12345 (1900)', '19701231', 'FREQ=YEARLY', 'BDAY', '0', '1900', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19001231\r\nEND:VCARD\r\n", 'BDAY', '', true, null],
['Death of 12345 (1900)', '19701231', 'FREQ=YEARLY', 'DEATHDATE', '0', '1900', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nDEATHDATE:19001231\r\nEND:VCARD\r\n", 'DEATHDATE', '-death', true, null],
['Death of 12345 (1900)', '19701231', 'FREQ=YEARLY', 'DEATHDATE', '0', '1900', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nDEATHDATE:19001231\r\nEND:VCARD\r\n", 'DEATHDATE', '-death', false, null],
['💍 12345 (1900)', '19701231', 'FREQ=YEARLY', 'ANNIVERSARY', '0', '1900', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nANNIVERSARY:19001231\r\nEND:VCARD\r\n", 'ANNIVERSARY', '-anniversary', true, null],
['12345 (⚭1900)', '19701231', 'FREQ=YEARLY', 'ANNIVERSARY', '0', '1900', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nANNIVERSARY:19001231\r\nEND:VCARD\r\n", 'ANNIVERSARY', '-anniversary', false, null],
['🎂 12345', '19701231', 'FREQ=YEARLY', 'BDAY', '1', null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:--1231\r\nEND:VCARD\r\n", 'BDAY', '', true, null],
['🎂 12345', '19701231', 'FREQ=YEARLY', 'BDAY', '1', null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY;X-APPLE-OMIT-YEAR=1604:16041231\r\nEND:VCARD\r\n", 'BDAY', '', true, null],
[null, null, null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:;VALUE=text:circa 1800\r\nEND:VCARD\r\n", 'BDAY', '', true, null],
[null, null, null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nN:12345;;;;\r\nBDAY:20031231\r\nEND:VCARD\r\n", 'BDAY', '', true, null],
['🎂 12345 (900)', '09001231', 'FREQ=YEARLY', 'BDAY', '0', '900', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:09001231\r\nEND:VCARD\r\n", 'BDAY', '', true, null],
['12345 (*1900)', '19000101', 'FREQ=YEARLY', 'BDAY', '0', '1900', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19000101\r\nEND:VCARD\r\n", 'BDAY', '', false, null],
['12345 (*1900)', '19001231', 'FREQ=YEARLY', 'BDAY', '0', '1900', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19001231\r\nEND:VCARD\r\n", 'BDAY', '', false, null],
['🎂 12345 (900)', '19701231', 'FREQ=YEARLY', 'BDAY', '0', '900', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:09001231\r\nEND:VCARD\r\n", 'BDAY', '', true, null],
['12345 (*1900)', '19700101', 'FREQ=YEARLY', 'BDAY', '0', '1900', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19000101\r\nEND:VCARD\r\n", 'BDAY', '', false, null],
['12345 (*1900)', '19701231', 'FREQ=YEARLY', 'BDAY', '0', '1900', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19001231\r\nEND:VCARD\r\n", 'BDAY', '', false, null],
['12345 *', '19701231', 'FREQ=YEARLY', 'BDAY', '1', null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:--1231\r\nEND:VCARD\r\n", 'BDAY', '', false, null],
['12345 *', '19701231', 'FREQ=YEARLY', 'BDAY', '1', null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY;X-APPLE-OMIT-YEAR=1604:16041231\r\nEND:VCARD\r\n", 'BDAY', '', false, null],
[null, null, null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:;VALUE=text:circa 1800\r\nEND:VCARD\r\n", 'BDAY', '', false, null],
[null, null, null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nN:12345;;;;\r\nBDAY:20031231\r\nEND:VCARD\r\n", 'BDAY', '', false, null],
['12345 (*900)', '09001231', 'FREQ=YEARLY', 'BDAY', '0', '900', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:09001231\r\nEND:VCARD\r\n", 'BDAY', '', false, null],
['12345 (*1900)', '19001231', 'FREQ=YEARLY', 'BDAY', '0', '1900', 'PT9H', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19001231\r\nEND:VCARD\r\n", 'BDAY', '', false, 'PT9H'],
['12345 (*1900)', '19001231', 'FREQ=YEARLY', 'BDAY', '0', '1900', '-PT15H', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19001231\r\nEND:VCARD\r\n", 'BDAY', '', false, '-PT15H'],
['12345 (*1900)', '19001231', 'FREQ=YEARLY', 'BDAY', '0', '1900', '-P6DT15H', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19001231\r\nEND:VCARD\r\n", 'BDAY', '', false, '-P6DT15H'],
['12345 (*900)', '19701231', 'FREQ=YEARLY', 'BDAY', '0', '900', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:09001231\r\nEND:VCARD\r\n", 'BDAY', '', false, null],
['12345 (*1900)', '19701231', 'FREQ=YEARLY', 'BDAY', '0', '1900', 'PT9H', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19001231\r\nEND:VCARD\r\n", 'BDAY', '', false, 'PT9H'],
['12345 (*1900)', '19701231', 'FREQ=YEARLY', 'BDAY', '0', '1900', '-PT15H', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19001231\r\nEND:VCARD\r\n", 'BDAY', '', false, '-PT15H'],
['12345 (*1900)', '19701231', 'FREQ=YEARLY', 'BDAY', '0', '1900', '-P6DT15H', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19001231\r\nEND:VCARD\r\n", 'BDAY', '', false, '-P6DT15H'],
[null, null, null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19000101\r\nX-NC-EXCLUDE-FROM-BIRTHDAY-CALENDAR;TYPE=boolean:true\r\nEND:VCARD\r\n", 'BDAY', '', true, null],
[null, null, null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nX-NC-EXCLUDE-FROM-BIRTHDAY-CALENDAR;TYPE=boolean:true\r\nDEATHDATE:19001231\r\nEND:VCARD\r\n", 'DEATHDATE', '-death', true, null],
[null, null, null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nANNIVERSARY:19001231\r\nX-NC-EXCLUDE-FROM-BIRTHDAY-CALENDAR;TYPE=boolean:true\r\nEND:VCARD\r\n", 'ANNIVERSARY', '-anniversary', true, null],
['🎂 12345 (1904)', '19040229', 'FREQ=YEARLY;BYMONTH=2;BYMONTHDAY=-1', 'BDAY', '0', '1904', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19040229\r\nEND:VCARD\r\n", 'BDAY', '', true, null],
['🎂 12345 (1902)', '19720229', 'FREQ=YEARLY;BYMONTH=2;BYMONTHDAY=-1', 'BDAY', '0', null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19020229\r\nEND:VCARD\r\n", 'BDAY', '', true, null],
];
}
}
@@ -48,16 +48,14 @@ class EntityCollectionTest extends \Test\TestCase {
}
public function testGetChild(): void {
$comment = $this->createMock(IComment::class);
$comment->method('getObjectType')
->willReturn('files');
$comment->method('getObjectId')
->willReturn('19');
$this->commentsManager->expects($this->once())
->method('get')
->with('55')
->willReturn($comment);
->willReturn(
$this->getMockBuilder(IComment::class)
->disableOriginalConstructor()
->getMock()
);
$node = $this->collection->getChild('55');
$this->assertInstanceOf(CommentNode::class, $node);
@@ -109,17 +107,6 @@ class EntityCollectionTest extends \Test\TestCase {
}
public function testChildExistsTrue(): void {
$comment = $this->createMock(IComment::class);
$comment->method('getObjectType')
->willReturn('files');
$comment->method('getObjectId')
->willReturn('19');
$this->commentsManager->expects($this->once())
->method('get')
->with('44')
->willReturn($comment);
$this->assertTrue($this->collection->childExists('44'));
}
@@ -10,7 +10,6 @@ namespace OCA\DAV\Tests\unit\Connector\Sabre;
use OC\Files\FileInfo;
use OC\Files\Filesystem;
use OC\Files\Node\Folder;
use OC\Files\Node\Node;
use OC\Files\Storage\Wrapper\Quota;
use OC\Files\View;
@@ -22,10 +21,8 @@ use OCP\Constants;
use OCP\Files\ForbiddenException;
use OCP\Files\InvalidPathException;
use OCP\Files\Mount\IMountPoint;
use OCP\Files\Storage\IStorage;
use OCP\Files\StorageNotAvailableException;
use PHPUnit\Framework\MockObject\MockObject;
use Sabre\DAV\Exception\NotFound;
use Test\Traits\UserTrait;
class TestViewDirectory extends View {
@@ -64,16 +61,12 @@ class DirectoryTest extends \Test\TestCase {
private View&MockObject $view;
private FileInfo&MockObject $info;
private IStorage&MockObject $storage;
protected function setUp(): void {
parent::setUp();
$this->view = $this->createMock(View::class);
$this->info = $this->createMock(FileInfo::class);
$this->storage = $this->createMock(IStorage::class);
$this->info->method('getStorage')
->willReturn($this->storage);
$this->info->method('isReadable')
->willReturn(true);
$this->info->method('getType')
@@ -273,146 +266,6 @@ class DirectoryTest extends \Test\TestCase {
$dir->getChild('.');
}
public function testGetNodeForPath(): void {
$directoryNode = $this->createMock(Folder::class);
$pathNode = $this->createMock(Folder::class);
$pathParentNode = $this->createMock(Folder::class);
$storage = $this->createMock(IStorage::class);
$directoryNode->expects($this->once())
->method('getStorage')
->willReturn($storage);
$storage->expects($this->once())
->method('instanceOfStorage')
->willReturn(false);
$directoryNode->expects($this->once())
->method('isReadable')
->willReturn(true);
$directoryNode->expects($this->once())
->method('getPath')
->willReturn('/admin/files/');
$directoryNode->expects($this->once())
->method('get')
->willReturn($pathNode);
$pathNode->expects($this->once())
->method('getPath')
->willReturn('/admin/files/my/deep/folder/');
$pathNode->expects($this->once())
->method('isReadable')
->willReturn(true);
$pathNode->expects($this->once())
->method('getMimetype')
->willReturn(FileInfo::MIMETYPE_FOLDER);
$this->view->method('getRelativePath')
->willReturnCallback(function ($path) {
return str_replace('/admin/files/', '', $path);
});
$this->view->expects($this->exactly(2))
->method('getFileInfo')
->willReturn($pathParentNode);
$pathParentNode->expects($this->exactly(2))
->method('getPath')
->willReturnOnConsecutiveCalls('/my/deep', '/my');
$pathParentNode->expects($this->exactly(2))
->method('isReadable')
->willReturn(true);
$dir = new Directory($this->view, $directoryNode);
$dir->getNodeForPath('/my/deep/folder/');
}
public function testGetNodeForPathFailsWithNoReadPermissionsForParent(): void {
$directoryNode = $this->createMock(Folder::class);
$pathNode = $this->createMock(Folder::class);
$pathParentNode = $this->createMock(Folder::class);
$storage = $this->createMock(IStorage::class);
$directoryNode->expects($this->once())
->method('getStorage')
->willReturn($storage);
$storage->expects($this->once())
->method('instanceOfStorage')
->willReturn(false);
$directoryNode->expects($this->once())
->method('isReadable')
->willReturn(true);
$directoryNode->expects($this->once())
->method('getPath')
->willReturn('/admin/files/');
$directoryNode->expects($this->once())
->method('get')
->willReturn($pathNode);
$pathNode->expects($this->once())
->method('getPath')
->willReturn('/admin/files/my/deep/folder/');
$pathNode->expects($this->once())
->method('isReadable')
->willReturn(true);
$pathNode->expects($this->once())
->method('getMimetype')
->willReturn(FileInfo::MIMETYPE_FOLDER);
$this->view->method('getRelativePath')
->willReturnCallback(function ($path) {
return str_replace('/admin/files/', '', $path);
});
$this->view->expects($this->exactly(2))
->method('getFileInfo')
->willReturn($pathParentNode);
$pathParentNode->expects($this->exactly(2))
->method('getPath')
->willReturnOnConsecutiveCalls('/my/deep', '/my');
$pathParentNode->expects($this->exactly(2))
->method('isReadable')
->willReturnOnConsecutiveCalls(true, false);
$this->expectException(NotFound::class);
$dir = new Directory($this->view, $directoryNode);
$dir->getNodeForPath('/my/deep/folder/');
}
public function testGetNodeForPathFailsWithNoReadPermissionsForPath(): void {
$directoryNode = $this->createMock(Folder::class);
$pathNode = $this->createMock(Folder::class);
$storage = $this->createMock(IStorage::class);
$directoryNode->expects($this->once())
->method('getStorage')
->willReturn($storage);
$storage->expects($this->once())
->method('instanceOfStorage')
->willReturn(false);
$directoryNode->expects($this->once())
->method('isReadable')
->willReturn(true);
$directoryNode->expects($this->once())
->method('getPath')
->willReturn('/admin/files/');
$directoryNode->expects($this->once())
->method('get')
->willReturn($pathNode);
$pathNode->expects($this->once())
->method('isReadable')
->willReturn(false);
$this->expectException(\Sabre\DAV\Exception\Forbidden::class);
$dir = new Directory($this->view, $directoryNode);
$dir->getNodeForPath('/my/deep/folder/');
}
public function testGetQuotaInfoUnlimited(): void {
$this->createUser('user', 'password');
self::loginAsUser('user');
@@ -7,14 +7,14 @@ namespace Composer\Autoload;
class ComposerStaticInitEncryption
{
public static $prefixLengthsPsr4 = array (
'O' =>
'O' =>
array (
'OCA\\Encryption\\' => 15,
),
);
public static $prefixDirsPsr4 = array (
'OCA\\Encryption\\' =>
'OCA\\Encryption\\' =>
array (
0 => __DIR__ . '/..' . '/../lib',
),
@@ -7,14 +7,14 @@ namespace Composer\Autoload;
class ComposerStaticInitFederatedFileSharing
{
public static $prefixLengthsPsr4 = array (
'O' =>
'O' =>
array (
'OCA\\FederatedFileSharing\\' => 25,
),
);
public static $prefixDirsPsr4 = array (
'OCA\\FederatedFileSharing\\' =>
'OCA\\FederatedFileSharing\\' =>
array (
0 => __DIR__ . '/..' . '/../lib',
),
+5 -5
View File
@@ -25,14 +25,14 @@ OC.L10N.register(
"Provide federated file sharing across servers" : "توفير مشاركة الملفات عبر خوادم السحابة الاتحادية",
"Confirm data upload to lookup server" : "تأكيد تحميل البيانات لخادوم البحث",
"When enabled, all account properties (e.g. email address) with scope visibility set to \"published\", will be automatically synced and transmitted to an external system and made available in a public, global address book." : "عند تفعيل هذا الخيار، ستتم تلقائياً مزامنة جميع خصائص الحساب (على سبيل المثال عنوان البريد الإلكتروني) التي تم تعيين \"نطاق الرؤية\" visibility scope فيها على \"منشور published\"، وإرسالها إلى نظام خارجي وإتاحتها في دفتر عناوين عمومي شامل.",
"Enable data upload" : "تمكين رفع البيانات",
"Disable upload" : "تعطيل الرفع",
"Enable data upload" : "تمكين رفع البيانات",
"Confirm querying lookup server" : "تأكيد استعلام خادوم البحث",
"When enabled, the search input when creating shares will be sent to an external system that provides a public and global address book." : "عند تمكين هذا الخيار، سيتم إرسال إدخال البحث عند إنشاء المشاركات إلى نظام خارجي يوفر دفتر عناوين شامل وعمومي.",
"This is used to retrieve the federated cloud ID to make federated sharing easier." : "يستعمل هذا لاسترجاع معرف السحابة الاتحادية وذلك لتسهيل المشاركات الاتحادية",
"Moreover, email addresses of users might be sent to that system in order to verify them." : "علاوة على ذلك، قد يتم إرسال عناوين البريد الإلكتروني للمستخدِمين إلى ذلك النظام للتحقق منها.",
"Enable querying" : "تمكين الاستعلام",
"Disable querying" : "تعطيل الاستعلام",
"Enable querying" : "تمكين الاستعلام",
"Unable to update federated files sharing config" : "تعذر تحديث تهيئة مشاركة الملفات عبر السحابة الاتحادية",
"Adjust how people can share between servers. This includes shares between people on this server as well if they are using federated sharing." : "اضبط كيفية مشاركة الأشخاص بين الخوادم. هذا يشمل المشاركات بين الأشخاص على هذا الخادوم أيضاً إذا كانوا يستعملون المشاركة عبر السحابة الاتحادية.",
"Allow people on this server to send shares to other servers (this option also allows WebDAV access to public shares)" : "السماح للأشخاص على هذا الخادوم بإرسال مشاركات إلى خوادم أخرى (هذا الخيار يسمح أيضاً بالوصول عبر WebDAV إلى المشاركات العمومية)",
@@ -54,6 +54,8 @@ OC.L10N.register(
"Your Federated Cloud ID" : "مُعرِّف شبكتك الاتحادية",
"Share it so your friends can share files with you:" : "شاركه مع أصدقائك بحيث يمكنهم مُشاركة الملفات معك:",
"Facebook" : "فيسبوك",
"X (formerly Twitter)" : "منصة X (تويتر سابقاً)",
"formerly Twitter" : "تويتر سابقاً",
"Mastodon" : "برنامج ماستودون Mastodon",
"Add to your website" : "أضِف إلى موقعك على الويب",
"HTML Code:" : "كود HTML: ",
@@ -65,8 +67,6 @@ OC.L10N.register(
"Incoming share could not be processed" : "لا يمكن معالجة المشاركة الواردة",
"Cloud ID copied to the clipboard" : "تمّ نسخ مُعرِّف السحابة إلى الحافظة",
"Copy to clipboard" : "نسخ الرابط",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "يُمكنك المشاركة مع أي شخص يستخدم خادم نكست كلاود أو خوادم وخدمات أخرى متوافقة مع بروتوكول Open Cloud Mesh (OCM)! فقط ضع مُعرّف السحابة الاتحادية في مربع حوار المُشاركة؛ و الذي سيكون شكله على هذا النسق: person@cloud.example.com",
"X (formerly Twitter)" : "منصة X (تويتر سابقاً)",
"formerly Twitter" : "تويتر سابقاً"
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "يُمكنك المشاركة مع أي شخص يستخدم خادم نكست كلاود أو خوادم وخدمات أخرى متوافقة مع بروتوكول Open Cloud Mesh (OCM)! فقط ضع مُعرّف السحابة الاتحادية في مربع حوار المُشاركة؛ و الذي سيكون شكله على هذا النسق: person@cloud.example.com"
},
"nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;");
+5 -5
View File
@@ -23,14 +23,14 @@
"Provide federated file sharing across servers" : "توفير مشاركة الملفات عبر خوادم السحابة الاتحادية",
"Confirm data upload to lookup server" : "تأكيد تحميل البيانات لخادوم البحث",
"When enabled, all account properties (e.g. email address) with scope visibility set to \"published\", will be automatically synced and transmitted to an external system and made available in a public, global address book." : "عند تفعيل هذا الخيار، ستتم تلقائياً مزامنة جميع خصائص الحساب (على سبيل المثال عنوان البريد الإلكتروني) التي تم تعيين \"نطاق الرؤية\" visibility scope فيها على \"منشور published\"، وإرسالها إلى نظام خارجي وإتاحتها في دفتر عناوين عمومي شامل.",
"Enable data upload" : "تمكين رفع البيانات",
"Disable upload" : "تعطيل الرفع",
"Enable data upload" : "تمكين رفع البيانات",
"Confirm querying lookup server" : "تأكيد استعلام خادوم البحث",
"When enabled, the search input when creating shares will be sent to an external system that provides a public and global address book." : "عند تمكين هذا الخيار، سيتم إرسال إدخال البحث عند إنشاء المشاركات إلى نظام خارجي يوفر دفتر عناوين شامل وعمومي.",
"This is used to retrieve the federated cloud ID to make federated sharing easier." : "يستعمل هذا لاسترجاع معرف السحابة الاتحادية وذلك لتسهيل المشاركات الاتحادية",
"Moreover, email addresses of users might be sent to that system in order to verify them." : "علاوة على ذلك، قد يتم إرسال عناوين البريد الإلكتروني للمستخدِمين إلى ذلك النظام للتحقق منها.",
"Enable querying" : "تمكين الاستعلام",
"Disable querying" : "تعطيل الاستعلام",
"Enable querying" : "تمكين الاستعلام",
"Unable to update federated files sharing config" : "تعذر تحديث تهيئة مشاركة الملفات عبر السحابة الاتحادية",
"Adjust how people can share between servers. This includes shares between people on this server as well if they are using federated sharing." : "اضبط كيفية مشاركة الأشخاص بين الخوادم. هذا يشمل المشاركات بين الأشخاص على هذا الخادوم أيضاً إذا كانوا يستعملون المشاركة عبر السحابة الاتحادية.",
"Allow people on this server to send shares to other servers (this option also allows WebDAV access to public shares)" : "السماح للأشخاص على هذا الخادوم بإرسال مشاركات إلى خوادم أخرى (هذا الخيار يسمح أيضاً بالوصول عبر WebDAV إلى المشاركات العمومية)",
@@ -52,6 +52,8 @@
"Your Federated Cloud ID" : "مُعرِّف شبكتك الاتحادية",
"Share it so your friends can share files with you:" : "شاركه مع أصدقائك بحيث يمكنهم مُشاركة الملفات معك:",
"Facebook" : "فيسبوك",
"X (formerly Twitter)" : "منصة X (تويتر سابقاً)",
"formerly Twitter" : "تويتر سابقاً",
"Mastodon" : "برنامج ماستودون Mastodon",
"Add to your website" : "أضِف إلى موقعك على الويب",
"HTML Code:" : "كود HTML: ",
@@ -63,8 +65,6 @@
"Incoming share could not be processed" : "لا يمكن معالجة المشاركة الواردة",
"Cloud ID copied to the clipboard" : "تمّ نسخ مُعرِّف السحابة إلى الحافظة",
"Copy to clipboard" : "نسخ الرابط",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "يُمكنك المشاركة مع أي شخص يستخدم خادم نكست كلاود أو خوادم وخدمات أخرى متوافقة مع بروتوكول Open Cloud Mesh (OCM)! فقط ضع مُعرّف السحابة الاتحادية في مربع حوار المُشاركة؛ و الذي سيكون شكله على هذا النسق: person@cloud.example.com",
"X (formerly Twitter)" : "منصة X (تويتر سابقاً)",
"formerly Twitter" : "تويتر سابقاً"
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "يُمكنك المشاركة مع أي شخص يستخدم خادم نكست كلاود أو خوادم وخدمات أخرى متوافقة مع بروتوكول Open Cloud Mesh (OCM)! فقط ضع مُعرّف السحابة الاتحادية في مربع حوار المُشاركة؛ و الذي سيكون شكله على هذا النسق: person@cloud.example.com"
},"pluralForm" :"nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;"
}
+2 -2
View File
@@ -39,6 +39,7 @@ OC.L10N.register(
"Federated Cloud" : "Nube federada",
"Share it so your friends can share files with you:" : "Compártilu pa que los tos amigos puedan compartir ficheros contigo:",
"Facebook" : "Facebook",
"X (formerly Twitter)" : "X (anteriormente Twitter)",
"Mastodon" : "Mastodon",
"Add to your website" : "Amestar al to sitiu web",
"HTML Code:" : "Códigu HTML:",
@@ -49,7 +50,6 @@ OC.L10N.register(
"Remote share password" : "Contraseña de la compartición remota",
"Cloud ID copied to the clipboard" : "La ID de la nube copióse nel cartafueyu",
"Copy to clipboard" : "Copiar nel cartafueyu",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Pues compartir conteníu con cualesquier persona qu'use un sirvidor de Nextcloud o otros sirvidores y servicios compatibles con Open Cloud Mesh (OCM). Namás indica la ID de na nube federada nel cuadru de diálogu d'usu compartíu. Aseméyase a persona@nube.exemplu.com",
"X (formerly Twitter)" : "X (anteriormente Twitter)"
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Pues compartir conteníu con cualesquier persona qu'use un sirvidor de Nextcloud o otros sirvidores y servicios compatibles con Open Cloud Mesh (OCM). Namás indica la ID de na nube federada nel cuadru de diálogu d'usu compartíu. Aseméyase a persona@nube.exemplu.com"
},
"nplurals=2; plural=(n != 1);");
+2 -2
View File
@@ -37,6 +37,7 @@
"Federated Cloud" : "Nube federada",
"Share it so your friends can share files with you:" : "Compártilu pa que los tos amigos puedan compartir ficheros contigo:",
"Facebook" : "Facebook",
"X (formerly Twitter)" : "X (anteriormente Twitter)",
"Mastodon" : "Mastodon",
"Add to your website" : "Amestar al to sitiu web",
"HTML Code:" : "Códigu HTML:",
@@ -47,7 +48,6 @@
"Remote share password" : "Contraseña de la compartición remota",
"Cloud ID copied to the clipboard" : "La ID de la nube copióse nel cartafueyu",
"Copy to clipboard" : "Copiar nel cartafueyu",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Pues compartir conteníu con cualesquier persona qu'use un sirvidor de Nextcloud o otros sirvidores y servicios compatibles con Open Cloud Mesh (OCM). Namás indica la ID de na nube federada nel cuadru de diálogu d'usu compartíu. Aseméyase a persona@nube.exemplu.com",
"X (formerly Twitter)" : "X (anteriormente Twitter)"
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Pues compartir conteníu con cualesquier persona qu'use un sirvidor de Nextcloud o otros sirvidores y servicios compatibles con Open Cloud Mesh (OCM). Namás indica la ID de na nube federada nel cuadru de diálogu d'usu compartíu. Aseméyase a persona@nube.exemplu.com"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}
+3 -3
View File
@@ -30,9 +30,10 @@ OC.L10N.register(
"Copied!" : "Копирано!",
"Federated Cloud" : "Федериран облак",
"Share it so your friends can share files with you:" : "Споделете, за да могат приятелите ви да споделят файлове, с вас:",
"Bluesky" : "Bluesky",
"Facebook" : "Фейсбук",
"X (formerly Twitter)" : "X (преди Twitter)",
"Mastodon" : "Mastodon",
"Bluesky" : "Bluesky",
"Add to your website" : "Добавете към вашия уеб сайт",
"HTML Code:" : "HTML код:",
"Cancel" : "Отказ",
@@ -42,7 +43,6 @@ OC.L10N.register(
"Remote share password" : "Парола за отдалечено споделяне",
"Cloud ID copied to the clipboard" : "Cloud идентификатора е копиран в клипборда",
"Copy to clipboard" : "Копиране в клипборда",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Можете да споделяте с всеки, който използва сървър Nextcloud или други сървъри и услуги, съвместими с Open Cloud Mesh (OCM)! Просто поставете техния идентификатор за Федериран облак в диалоговия прозорец за споделяне. Изглежда като person@cloud.example.com",
"X (formerly Twitter)" : "X (преди Twitter)"
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Можете да споделяте с всеки, който използва сървър Nextcloud или други сървъри и услуги, съвместими с Open Cloud Mesh (OCM)! Просто поставете техния идентификатор за Федериран облак в диалоговия прозорец за споделяне. Изглежда като person@cloud.example.com"
},
"nplurals=2; plural=(n != 1);");
+3 -3
View File
@@ -28,9 +28,10 @@
"Copied!" : "Копирано!",
"Federated Cloud" : "Федериран облак",
"Share it so your friends can share files with you:" : "Споделете, за да могат приятелите ви да споделят файлове, с вас:",
"Bluesky" : "Bluesky",
"Facebook" : "Фейсбук",
"X (formerly Twitter)" : "X (преди Twitter)",
"Mastodon" : "Mastodon",
"Bluesky" : "Bluesky",
"Add to your website" : "Добавете към вашия уеб сайт",
"HTML Code:" : "HTML код:",
"Cancel" : "Отказ",
@@ -40,7 +41,6 @@
"Remote share password" : "Парола за отдалечено споделяне",
"Cloud ID copied to the clipboard" : "Cloud идентификатора е копиран в клипборда",
"Copy to clipboard" : "Копиране в клипборда",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Можете да споделяте с всеки, който използва сървър Nextcloud или други сървъри и услуги, съвместими с Open Cloud Mesh (OCM)! Просто поставете техния идентификатор за Федериран облак в диалоговия прозорец за споделяне. Изглежда като person@cloud.example.com",
"X (formerly Twitter)" : "X (преди Twitter)"
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Можете да споделяте с всеки, който използва сървър Nextcloud или други сървъри и услуги, съвместими с Open Cloud Mesh (OCM)! Просто поставете техния идентификатор за Федериран облак в диалоговия прозорец за споделяне. Изглежда като person@cloud.example.com"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}
+3 -3
View File
@@ -44,6 +44,8 @@ OC.L10N.register(
"Your Federated Cloud ID" : "El vostre ID de núvol federat",
"Share it so your friends can share files with you:" : "Compartiu-lo perquè els vostres amics puguin enviar-vos fitxers compartits:",
"Facebook" : "Facebook",
"X (formerly Twitter)" : "X (abans Twitter)",
"formerly Twitter" : "abans Twitter",
"Mastodon" : "Mastodon",
"Add to your website" : "Afegiu-lo al vostre lloc web",
"HTML Code:" : "Codi HTML:",
@@ -55,8 +57,6 @@ OC.L10N.register(
"Incoming share could not be processed" : "No s'ha pogut processar la compartició entrant",
"Cloud ID copied to the clipboard" : "S'ha copiat l'ID del núvol al porta-retalls",
"Copy to clipboard" : "Copia-ho al porta-retalls",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Podeu compartir contingut amb qualsevol persona que utilitzi un servidor del Nextcloud o altres servidors i serveis compatibles amb Open Cloud Mesh (OCM)! Només cal que n'indiqueu l'ID de núvol federat al quadre de diàleg d'ús compatit. És semblant a persona@nuvol.exemple.com",
"X (formerly Twitter)" : "X (abans Twitter)",
"formerly Twitter" : "abans Twitter"
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Podeu compartir contingut amb qualsevol persona que utilitzi un servidor del Nextcloud o altres servidors i serveis compatibles amb Open Cloud Mesh (OCM)! Només cal que n'indiqueu l'ID de núvol federat al quadre de diàleg d'ús compatit. És semblant a persona@nuvol.exemple.com"
},
"nplurals=2; plural=(n != 1);");
+3 -3
View File
@@ -42,6 +42,8 @@
"Your Federated Cloud ID" : "El vostre ID de núvol federat",
"Share it so your friends can share files with you:" : "Compartiu-lo perquè els vostres amics puguin enviar-vos fitxers compartits:",
"Facebook" : "Facebook",
"X (formerly Twitter)" : "X (abans Twitter)",
"formerly Twitter" : "abans Twitter",
"Mastodon" : "Mastodon",
"Add to your website" : "Afegiu-lo al vostre lloc web",
"HTML Code:" : "Codi HTML:",
@@ -53,8 +55,6 @@
"Incoming share could not be processed" : "No s'ha pogut processar la compartició entrant",
"Cloud ID copied to the clipboard" : "S'ha copiat l'ID del núvol al porta-retalls",
"Copy to clipboard" : "Copia-ho al porta-retalls",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Podeu compartir contingut amb qualsevol persona que utilitzi un servidor del Nextcloud o altres servidors i serveis compatibles amb Open Cloud Mesh (OCM)! Només cal que n'indiqueu l'ID de núvol federat al quadre de diàleg d'ús compatit. És semblant a persona@nuvol.exemple.com",
"X (formerly Twitter)" : "X (abans Twitter)",
"formerly Twitter" : "abans Twitter"
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Podeu compartir contingut amb qualsevol persona que utilitzi un servidor del Nextcloud o altres servidors i serveis compatibles amb Open Cloud Mesh (OCM)! Només cal que n'indiqueu l'ID de núvol federat al quadre de diàleg d'ús compatit. És semblant a persona@nuvol.exemple.com"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}
+6 -6
View File
@@ -25,14 +25,14 @@ OC.L10N.register(
"Provide federated file sharing across servers" : "Poskytnout federované sdílení souborů napříč servery",
"Confirm data upload to lookup server" : "Potvrdit nahrávání dat na vyhledávací server",
"When enabled, all account properties (e.g. email address) with scope visibility set to \"published\", will be automatically synced and transmitted to an external system and made available in a public, global address book." : "Pokud zapnuto, veškeré vlastnosti účtu (např. e-mailová adresa) s rozsahem viditelnosti nastavené na „zveřejněné“ budou automaticky synchronizovány a přenášeny do externího systému a veřejně k dispozici v globální adresáři kontaktů.",
"Enable data upload" : "Zapnout nahrávání dat",
"Disable upload" : "Vypnout nahrávání",
"Enable data upload" : "Zapnout nahrávání dat",
"Confirm querying lookup server" : "Potvrdit dotazování vyhledávacího serveru",
"When enabled, the search input when creating shares will be sent to an external system that provides a public and global address book." : "Pokud zapnuto, obsah hledání při vytváření sdílení bude odeslán na externí systém, který poskytuje veřejný a globální adresář kontaktů.",
"This is used to retrieve the federated cloud ID to make federated sharing easier." : "Toto slouží k získávání identifikátorů v rámci federovaného cloudu pro usnadnění federovaného sdílení.",
"Moreover, email addresses of users might be sent to that system in order to verify them." : "Dále mohou být pro ověření odeslány na server e-mailové adresy uživatelů.",
"Enable querying" : "Zapnout dotazování",
"Disable querying" : "Vypnout dotazování",
"Enable querying" : "Zapnout dotazování",
"Unable to update federated files sharing config" : "Nedaří se aktualizovat nastavení federovaného sdílení souborů",
"Adjust how people can share between servers. This includes shares between people on this server as well if they are using federated sharing." : "Upravte, jak mohou lidé sdílet mezi servery. Týká se sdílení mezi lidmi na tomto serveru a stejně tak uživatelů federovaného sdílení.",
"Allow people on this server to send shares to other servers (this option also allows WebDAV access to public shares)" : "Umožnit lidem na tomto serveru posílat sdílení na ostatní servery (tato volba také umožňuje WebDAV přístup k veřejným sdílením)",
@@ -55,9 +55,11 @@ OC.L10N.register(
"You can share with anyone who uses a {productName} server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Můžete sdílet s kýmkoliv, kdo používá {productName} nebo jiný server či služby, kompatibilní se standardem Open Cloud Mesh (OCM)! Stačí do dialogu pro sdílení zadat jejich jejich identif. v rámci sdruženého cloudu. Má podobu person@cloud.example.com",
"Your Federated Cloud ID" : "Váš identifikátor v rámci fedorovaného cloudu",
"Share it so your friends can share files with you:" : "Podělte se o to, aby mohli vaši přátelé s vámi mohli sdílet soubory:",
"Bluesky" : "Bluesky",
"Facebook" : "Facebook",
"X (formerly Twitter)" : "X (dříve Twitter)",
"formerly Twitter" : "dříve Twitter",
"Mastodon" : "Mastodon",
"Bluesky" : "Bluesky",
"Add to your website" : "Přidat na svou webovou stránku",
"Share with me via {productName}" : "Nasdíleno mě prostřednictvím {productName}",
"HTML Code:" : "HTML kód:",
@@ -69,8 +71,6 @@ OC.L10N.register(
"Incoming share could not be processed" : "Příchozí sdílení se nepodařilo zpracovat",
"Cloud ID copied to the clipboard" : "Cloudový identifikátor zkopírován do schránky",
"Copy to clipboard" : "Zkopírovat do schránky",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Můžete sdílet s kýmkoliv, kdo používá Nextcloud nebo jiný server či služby, kompatibilní se standardem Open Cloud Mesh (OCM)! Stačí do dialogu pro sdílení zadat jejich jejich identif. v rámci sdruženého cloudu. Má podobu person@cloud.example.com",
"X (formerly Twitter)" : "X (dříve Twitter)",
"formerly Twitter" : "dříve Twitter"
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Můžete sdílet s kýmkoliv, kdo používá Nextcloud nebo jiný server či služby, kompatibilní se standardem Open Cloud Mesh (OCM)! Stačí do dialogu pro sdílení zadat jejich jejich identif. v rámci sdruženého cloudu. Má podobu person@cloud.example.com"
},
"nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;");
+6 -6
View File
@@ -23,14 +23,14 @@
"Provide federated file sharing across servers" : "Poskytnout federované sdílení souborů napříč servery",
"Confirm data upload to lookup server" : "Potvrdit nahrávání dat na vyhledávací server",
"When enabled, all account properties (e.g. email address) with scope visibility set to \"published\", will be automatically synced and transmitted to an external system and made available in a public, global address book." : "Pokud zapnuto, veškeré vlastnosti účtu (např. e-mailová adresa) s rozsahem viditelnosti nastavené na „zveřejněné“ budou automaticky synchronizovány a přenášeny do externího systému a veřejně k dispozici v globální adresáři kontaktů.",
"Enable data upload" : "Zapnout nahrávání dat",
"Disable upload" : "Vypnout nahrávání",
"Enable data upload" : "Zapnout nahrávání dat",
"Confirm querying lookup server" : "Potvrdit dotazování vyhledávacího serveru",
"When enabled, the search input when creating shares will be sent to an external system that provides a public and global address book." : "Pokud zapnuto, obsah hledání při vytváření sdílení bude odeslán na externí systém, který poskytuje veřejný a globální adresář kontaktů.",
"This is used to retrieve the federated cloud ID to make federated sharing easier." : "Toto slouží k získávání identifikátorů v rámci federovaného cloudu pro usnadnění federovaného sdílení.",
"Moreover, email addresses of users might be sent to that system in order to verify them." : "Dále mohou být pro ověření odeslány na server e-mailové adresy uživatelů.",
"Enable querying" : "Zapnout dotazování",
"Disable querying" : "Vypnout dotazování",
"Enable querying" : "Zapnout dotazování",
"Unable to update federated files sharing config" : "Nedaří se aktualizovat nastavení federovaného sdílení souborů",
"Adjust how people can share between servers. This includes shares between people on this server as well if they are using federated sharing." : "Upravte, jak mohou lidé sdílet mezi servery. Týká se sdílení mezi lidmi na tomto serveru a stejně tak uživatelů federovaného sdílení.",
"Allow people on this server to send shares to other servers (this option also allows WebDAV access to public shares)" : "Umožnit lidem na tomto serveru posílat sdílení na ostatní servery (tato volba také umožňuje WebDAV přístup k veřejným sdílením)",
@@ -53,9 +53,11 @@
"You can share with anyone who uses a {productName} server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Můžete sdílet s kýmkoliv, kdo používá {productName} nebo jiný server či služby, kompatibilní se standardem Open Cloud Mesh (OCM)! Stačí do dialogu pro sdílení zadat jejich jejich identif. v rámci sdruženého cloudu. Má podobu person@cloud.example.com",
"Your Federated Cloud ID" : "Váš identifikátor v rámci fedorovaného cloudu",
"Share it so your friends can share files with you:" : "Podělte se o to, aby mohli vaši přátelé s vámi mohli sdílet soubory:",
"Bluesky" : "Bluesky",
"Facebook" : "Facebook",
"X (formerly Twitter)" : "X (dříve Twitter)",
"formerly Twitter" : "dříve Twitter",
"Mastodon" : "Mastodon",
"Bluesky" : "Bluesky",
"Add to your website" : "Přidat na svou webovou stránku",
"Share with me via {productName}" : "Nasdíleno mě prostřednictvím {productName}",
"HTML Code:" : "HTML kód:",
@@ -67,8 +69,6 @@
"Incoming share could not be processed" : "Příchozí sdílení se nepodařilo zpracovat",
"Cloud ID copied to the clipboard" : "Cloudový identifikátor zkopírován do schránky",
"Copy to clipboard" : "Zkopírovat do schránky",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Můžete sdílet s kýmkoliv, kdo používá Nextcloud nebo jiný server či služby, kompatibilní se standardem Open Cloud Mesh (OCM)! Stačí do dialogu pro sdílení zadat jejich jejich identif. v rámci sdruženého cloudu. Má podobu person@cloud.example.com",
"X (formerly Twitter)" : "X (dříve Twitter)",
"formerly Twitter" : "dříve Twitter"
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Můžete sdílet s kýmkoliv, kdo používá Nextcloud nebo jiný server či služby, kompatibilní se standardem Open Cloud Mesh (OCM)! Stačí do dialogu pro sdílení zadat jejich jejich identif. v rámci sdruženého cloudu. Má podobu person@cloud.example.com"
},"pluralForm" :"nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;"
}
+7 -7
View File
@@ -25,14 +25,14 @@ OC.L10N.register(
"Provide federated file sharing across servers" : "Leverer fødereret fildeling på tværs af servere",
"Confirm data upload to lookup server" : "Bekræft dataoverførsel til opslagsserver",
"When enabled, all account properties (e.g. email address) with scope visibility set to \"published\", will be automatically synced and transmitted to an external system and made available in a public, global address book." : "Når det er aktiveret, så vil alle kontoegenskaber (f.eks. e-mailadresse) med omfangssynlighed indstillet til \"publiceret\", automatisk blive synkroniseret og transmitteret til et eksternt system og gjort tilgængelige i en offentlig, global adressebog.",
"Enable data upload" : "Aktivér data upload",
"Disable upload" : "Deaktivér upload",
"Enable data upload" : "Aktivér data upload",
"Confirm querying lookup server" : "Bekræft forespørgsel til opslagsserver",
"When enabled, the search input when creating shares will be sent to an external system that provides a public and global address book." : "Når det er aktiveret, vil søgeinputtet, når der oprettes delinger, blive sendt til et eksternt system, der leverer en offentlig og global adressebog.",
"This is used to retrieve the federated cloud ID to make federated sharing easier." : "Dette bruges til at hente det fødererede cloud ID for at gøre fødereret deling nemmere.",
"Moreover, email addresses of users might be sent to that system in order to verify them." : "Desuden kan e-mailadresser på brugere blive sendt til dette system for at verificere dem.",
"Enable querying" : "Aktivér forespørgsler",
"Disable querying" : "Deaktivér forespørgsler",
"Enable querying" : "Aktivér forespørgsler",
"Unable to update federated files sharing config" : "Kan ikke opdatere fødereret fildelingskonfiguration",
"Adjust how people can share between servers. This includes shares between people on this server as well if they are using federated sharing." : "Juster, hvordan brugere kan dele mellem servere. Dette inkluderer også delinger mellem brugere på denne server, hvis de bruger fødereret deling.",
"Allow people on this server to send shares to other servers (this option also allows WebDAV access to public shares)" : "Tillad brugere på denne server at sende shares til andre servere (denne mulighed giver også WebDAV adgang til offentlige shares)",
@@ -55,13 +55,15 @@ OC.L10N.register(
"You can share with anyone who uses a {productName} server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Du kan dele med enhver der anvender en {productName} server eller andre Open Cloud Mesh (OCM) kompatible serverer og services! Angiv blot deres fødererede Cloud ID i delings dialogen. Det ser ud som følger: person@cloud.example.com",
"Your Federated Cloud ID" : "Dit sammenkoblings Cloud ID",
"Share it so your friends can share files with you:" : "Del så dine venner kan dele filer med dig:",
"Bluesky" : "Bluesky",
"Facebook" : "Facebook",
"X (formerly Twitter)" : "X (tidligere Twitter)",
"formerly Twitter" : "tidligere Twitter",
"Mastodon" : "Mastodont",
"Bluesky" : "Bluesky",
"Add to your website" : "Tilføj til dit websted",
"Share with me via {productName}" : "Del med mig via {productName}",
"HTML Code:" : "HTML kode:",
"Cancel" : "Annullér",
"Cancel" : "Annuller",
"Add remote share" : "Tilføj ekstern deling",
"Remote share" : "Eksterne drev",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "Ønsker du at tilføje det eksterne drev {name} fra {owner}@{remote}?",
@@ -69,8 +71,6 @@ OC.L10N.register(
"Incoming share could not be processed" : "Indgående deling kunne ikke behandles",
"Cloud ID copied to the clipboard" : "Cloud ID er kopieret til udklipsholderen.",
"Copy to clipboard" : "Kopier til udklipsholder",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Du kan dele med alle, der bruger en Nextcloud-server eller andre Open Cloud Mesh (OCM)-kompatible servere og tjenester! Indsæt blot deres Sammenkoblings Cloud ID i delingsdialogen. Det ligner person@cloud.example.com",
"X (formerly Twitter)" : "X (tidligere Twitter)",
"formerly Twitter" : "tidligere Twitter"
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Du kan dele med alle, der bruger en Nextcloud-server eller andre Open Cloud Mesh (OCM)-kompatible servere og tjenester! Indsæt blot deres Sammenkoblings Cloud ID i delingsdialogen. Det ligner person@cloud.example.com"
},
"nplurals=2; plural=(n != 1);");
+7 -7
View File
@@ -23,14 +23,14 @@
"Provide federated file sharing across servers" : "Leverer fødereret fildeling på tværs af servere",
"Confirm data upload to lookup server" : "Bekræft dataoverførsel til opslagsserver",
"When enabled, all account properties (e.g. email address) with scope visibility set to \"published\", will be automatically synced and transmitted to an external system and made available in a public, global address book." : "Når det er aktiveret, så vil alle kontoegenskaber (f.eks. e-mailadresse) med omfangssynlighed indstillet til \"publiceret\", automatisk blive synkroniseret og transmitteret til et eksternt system og gjort tilgængelige i en offentlig, global adressebog.",
"Enable data upload" : "Aktivér data upload",
"Disable upload" : "Deaktivér upload",
"Enable data upload" : "Aktivér data upload",
"Confirm querying lookup server" : "Bekræft forespørgsel til opslagsserver",
"When enabled, the search input when creating shares will be sent to an external system that provides a public and global address book." : "Når det er aktiveret, vil søgeinputtet, når der oprettes delinger, blive sendt til et eksternt system, der leverer en offentlig og global adressebog.",
"This is used to retrieve the federated cloud ID to make federated sharing easier." : "Dette bruges til at hente det fødererede cloud ID for at gøre fødereret deling nemmere.",
"Moreover, email addresses of users might be sent to that system in order to verify them." : "Desuden kan e-mailadresser på brugere blive sendt til dette system for at verificere dem.",
"Enable querying" : "Aktivér forespørgsler",
"Disable querying" : "Deaktivér forespørgsler",
"Enable querying" : "Aktivér forespørgsler",
"Unable to update federated files sharing config" : "Kan ikke opdatere fødereret fildelingskonfiguration",
"Adjust how people can share between servers. This includes shares between people on this server as well if they are using federated sharing." : "Juster, hvordan brugere kan dele mellem servere. Dette inkluderer også delinger mellem brugere på denne server, hvis de bruger fødereret deling.",
"Allow people on this server to send shares to other servers (this option also allows WebDAV access to public shares)" : "Tillad brugere på denne server at sende shares til andre servere (denne mulighed giver også WebDAV adgang til offentlige shares)",
@@ -53,13 +53,15 @@
"You can share with anyone who uses a {productName} server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Du kan dele med enhver der anvender en {productName} server eller andre Open Cloud Mesh (OCM) kompatible serverer og services! Angiv blot deres fødererede Cloud ID i delings dialogen. Det ser ud som følger: person@cloud.example.com",
"Your Federated Cloud ID" : "Dit sammenkoblings Cloud ID",
"Share it so your friends can share files with you:" : "Del så dine venner kan dele filer med dig:",
"Bluesky" : "Bluesky",
"Facebook" : "Facebook",
"X (formerly Twitter)" : "X (tidligere Twitter)",
"formerly Twitter" : "tidligere Twitter",
"Mastodon" : "Mastodont",
"Bluesky" : "Bluesky",
"Add to your website" : "Tilføj til dit websted",
"Share with me via {productName}" : "Del med mig via {productName}",
"HTML Code:" : "HTML kode:",
"Cancel" : "Annullér",
"Cancel" : "Annuller",
"Add remote share" : "Tilføj ekstern deling",
"Remote share" : "Eksterne drev",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "Ønsker du at tilføje det eksterne drev {name} fra {owner}@{remote}?",
@@ -67,8 +69,6 @@
"Incoming share could not be processed" : "Indgående deling kunne ikke behandles",
"Cloud ID copied to the clipboard" : "Cloud ID er kopieret til udklipsholderen.",
"Copy to clipboard" : "Kopier til udklipsholder",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Du kan dele med alle, der bruger en Nextcloud-server eller andre Open Cloud Mesh (OCM)-kompatible servere og tjenester! Indsæt blot deres Sammenkoblings Cloud ID i delingsdialogen. Det ligner person@cloud.example.com",
"X (formerly Twitter)" : "X (tidligere Twitter)",
"formerly Twitter" : "tidligere Twitter"
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Du kan dele med alle, der bruger en Nextcloud-server eller andre Open Cloud Mesh (OCM)-kompatible servere og tjenester! Indsæt blot deres Sammenkoblings Cloud ID i delingsdialogen. Det ligner person@cloud.example.com"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}
+6 -6
View File
@@ -25,14 +25,14 @@ OC.L10N.register(
"Provide federated file sharing across servers" : "Bietet Federated Datei-Freigaben über Servergrenzen hinweg",
"Confirm data upload to lookup server" : "Hochladen der Daten zum Lookup-Server bestätigen",
"When enabled, all account properties (e.g. email address) with scope visibility set to \"published\", will be automatically synced and transmitted to an external system and made available in a public, global address book." : "Wenn diese Option aktiviert ist, werden alle Kontoeigenschaften (z. B. E-Mail-Adresse) mit der auf \"veröffentlicht\" eingestellten Bereichssichtbarkeit automatisch synchronisiert und an ein externes System übertragen sowie in einem öffentlichen, globalen Adressbuch verfügbar gemacht.",
"Enable data upload" : "Hochladen von Daten aktivieren",
"Disable upload" : "Hochladen deaktivieren",
"Enable data upload" : "Hochladen von Daten aktivieren",
"Confirm querying lookup server" : "Abfrage des Lookup-Servers bestätigen",
"When enabled, the search input when creating shares will be sent to an external system that provides a public and global address book." : "Wenn aktiviert, wird die Sucheingabe beim Erstellen von Freigaben an ein externes System gesendet, das ein öffentliches und globales Adressbuch bereitstellt.",
"This is used to retrieve the federated cloud ID to make federated sharing easier." : "Dies wird verwendet, um die federierte Cloud-ID abzurufen und so die federierte Freigabe zu vereinfachen.",
"Moreover, email addresses of users might be sent to that system in order to verify them." : "Darüber hinaus können E-Mail-Adressen von Benutzern an dieses System gesendet werden, um sie zu überprüfen.",
"Enable querying" : "Abfragen aktivieren",
"Disable querying" : "Abfragen deaktivieren",
"Enable querying" : "Abfragen aktivieren",
"Unable to update federated files sharing config" : "Einstellungen zum federierten Teilen konnten nicht aktualisiert werden",
"Adjust how people can share between servers. This includes shares between people on this server as well if they are using federated sharing." : "Legt fest, wie Personen zwischen Servern teilen können. Dies gilt auch für Freigaben zwischen Personen auf diesem Server, wenn sie Federated-Sharing verwenden.",
"Allow people on this server to send shares to other servers (this option also allows WebDAV access to public shares)" : "Personen auf diesem Server das Senden von Freigaben an andere Server erlauben (Diese Option ermöglicht auch den WebDAV-Zugriff auf öffentliche Freigaben)",
@@ -55,9 +55,11 @@ OC.L10N.register(
"You can share with anyone who uses a {productName} server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Du kannst mit jedem teilen, der einen {productName}-Server oder andere Open Cloud Mesh (OCM) kompatible Server und Dienste verwendet! Gebe einfach deren Federated-Cloud-ID in den Teilen-Dialog ein. Diese sieht wie folgt aus: person@cloud.example.com",
"Your Federated Cloud ID" : "Deine Federated-Cloud-ID",
"Share it so your friends can share files with you:" : "Teile es, damit deine Freunde Dateien mit dir teilen können:",
"Bluesky" : "Bluesky",
"Facebook" : "Facebook",
"X (formerly Twitter)" : "X (früher Twitter)",
"formerly Twitter" : "früher Twitter",
"Mastodon" : "Mastodon",
"Bluesky" : "Bluesky",
"Add to your website" : "Zu deiner Webseite hinzufügen",
"Share with me via {productName}" : "Mit mir über {productName} teilen",
"HTML Code:" : "HTML-Code:",
@@ -69,8 +71,6 @@ OC.L10N.register(
"Incoming share could not be processed" : "Eingehende Freigabe konnte nicht verarbeitet werden",
"Cloud ID copied to the clipboard" : "Cloud-ID in die Zwischenablage kopiert",
"Copy to clipboard" : "In die Zwischenablage kopieren",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Du kannst mit jedem teilen, der einen Nextcloud-Server oder andere Open Cloud Mesh (OCM) kompatible Server und Dienste verwendet! Gib einfach deren Federated-Cloud-ID in den Teilen-Dialog ein. Diese sieht wie folgt aus: person@cloud.example.com",
"X (formerly Twitter)" : "X (früher Twitter)",
"formerly Twitter" : "früher Twitter"
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Du kannst mit jedem teilen, der einen Nextcloud-Server oder andere Open Cloud Mesh (OCM) kompatible Server und Dienste verwendet! Gib einfach deren Federated-Cloud-ID in den Teilen-Dialog ein. Diese sieht wie folgt aus: person@cloud.example.com"
},
"nplurals=2; plural=(n != 1);");
+6 -6
View File
@@ -23,14 +23,14 @@
"Provide federated file sharing across servers" : "Bietet Federated Datei-Freigaben über Servergrenzen hinweg",
"Confirm data upload to lookup server" : "Hochladen der Daten zum Lookup-Server bestätigen",
"When enabled, all account properties (e.g. email address) with scope visibility set to \"published\", will be automatically synced and transmitted to an external system and made available in a public, global address book." : "Wenn diese Option aktiviert ist, werden alle Kontoeigenschaften (z. B. E-Mail-Adresse) mit der auf \"veröffentlicht\" eingestellten Bereichssichtbarkeit automatisch synchronisiert und an ein externes System übertragen sowie in einem öffentlichen, globalen Adressbuch verfügbar gemacht.",
"Enable data upload" : "Hochladen von Daten aktivieren",
"Disable upload" : "Hochladen deaktivieren",
"Enable data upload" : "Hochladen von Daten aktivieren",
"Confirm querying lookup server" : "Abfrage des Lookup-Servers bestätigen",
"When enabled, the search input when creating shares will be sent to an external system that provides a public and global address book." : "Wenn aktiviert, wird die Sucheingabe beim Erstellen von Freigaben an ein externes System gesendet, das ein öffentliches und globales Adressbuch bereitstellt.",
"This is used to retrieve the federated cloud ID to make federated sharing easier." : "Dies wird verwendet, um die federierte Cloud-ID abzurufen und so die federierte Freigabe zu vereinfachen.",
"Moreover, email addresses of users might be sent to that system in order to verify them." : "Darüber hinaus können E-Mail-Adressen von Benutzern an dieses System gesendet werden, um sie zu überprüfen.",
"Enable querying" : "Abfragen aktivieren",
"Disable querying" : "Abfragen deaktivieren",
"Enable querying" : "Abfragen aktivieren",
"Unable to update federated files sharing config" : "Einstellungen zum federierten Teilen konnten nicht aktualisiert werden",
"Adjust how people can share between servers. This includes shares between people on this server as well if they are using federated sharing." : "Legt fest, wie Personen zwischen Servern teilen können. Dies gilt auch für Freigaben zwischen Personen auf diesem Server, wenn sie Federated-Sharing verwenden.",
"Allow people on this server to send shares to other servers (this option also allows WebDAV access to public shares)" : "Personen auf diesem Server das Senden von Freigaben an andere Server erlauben (Diese Option ermöglicht auch den WebDAV-Zugriff auf öffentliche Freigaben)",
@@ -53,9 +53,11 @@
"You can share with anyone who uses a {productName} server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Du kannst mit jedem teilen, der einen {productName}-Server oder andere Open Cloud Mesh (OCM) kompatible Server und Dienste verwendet! Gebe einfach deren Federated-Cloud-ID in den Teilen-Dialog ein. Diese sieht wie folgt aus: person@cloud.example.com",
"Your Federated Cloud ID" : "Deine Federated-Cloud-ID",
"Share it so your friends can share files with you:" : "Teile es, damit deine Freunde Dateien mit dir teilen können:",
"Bluesky" : "Bluesky",
"Facebook" : "Facebook",
"X (formerly Twitter)" : "X (früher Twitter)",
"formerly Twitter" : "früher Twitter",
"Mastodon" : "Mastodon",
"Bluesky" : "Bluesky",
"Add to your website" : "Zu deiner Webseite hinzufügen",
"Share with me via {productName}" : "Mit mir über {productName} teilen",
"HTML Code:" : "HTML-Code:",
@@ -67,8 +69,6 @@
"Incoming share could not be processed" : "Eingehende Freigabe konnte nicht verarbeitet werden",
"Cloud ID copied to the clipboard" : "Cloud-ID in die Zwischenablage kopiert",
"Copy to clipboard" : "In die Zwischenablage kopieren",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Du kannst mit jedem teilen, der einen Nextcloud-Server oder andere Open Cloud Mesh (OCM) kompatible Server und Dienste verwendet! Gib einfach deren Federated-Cloud-ID in den Teilen-Dialog ein. Diese sieht wie folgt aus: person@cloud.example.com",
"X (formerly Twitter)" : "X (früher Twitter)",
"formerly Twitter" : "früher Twitter"
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Du kannst mit jedem teilen, der einen Nextcloud-Server oder andere Open Cloud Mesh (OCM) kompatible Server und Dienste verwendet! Gib einfach deren Federated-Cloud-ID in den Teilen-Dialog ein. Diese sieht wie folgt aus: person@cloud.example.com"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}
+6 -6
View File
@@ -25,14 +25,14 @@ OC.L10N.register(
"Provide federated file sharing across servers" : "Bietet Cloud-übergreifende Datei-Freigaben",
"Confirm data upload to lookup server" : "Datenupload zum Lookup-Server bestätigen",
"When enabled, all account properties (e.g. email address) with scope visibility set to \"published\", will be automatically synced and transmitted to an external system and made available in a public, global address book." : "Wenn diese Option aktiviert ist, werden alle Kontoeigenschaften (z. B. E-Mail-Adresse) mit der auf \"veröffentlicht\" eingestellten Bereichssichtbarkeit automatisch synchronisiert und an ein externes System übertragen sowie in einem öffentlichen, globalen Adressbuch verfügbar gemacht.",
"Enable data upload" : "Datenhochladen aktivieren",
"Disable upload" : "Hochladen deaktivieren",
"Enable data upload" : "Datenhochladen aktivieren",
"Confirm querying lookup server" : "Abfrage des Lookup-Servers bestätigen",
"When enabled, the search input when creating shares will be sent to an external system that provides a public and global address book." : "Wenn aktiviert, wird die Sucheingabe beim Erstellen von Freigaben an ein externes System gesendet, das ein öffentliches und globales Adressbuch bereitstellt.",
"This is used to retrieve the federated cloud ID to make federated sharing easier." : "Dies wird verwendet, um die Federated-Cloud-ID abzurufen und so die federierte Freigabe zu vereinfachen.",
"Moreover, email addresses of users might be sent to that system in order to verify them." : "Darüber hinaus können E-Mail-Adressen von Benutzern an dieses System gesendet werden, um sie zu überprüfen.",
"Enable querying" : "Abfragen aktivieren",
"Disable querying" : "Abfragen deaktivieren",
"Enable querying" : "Abfragen aktivieren",
"Unable to update federated files sharing config" : "Einstellungen zum Federated-Teilen konnten nicht aktualisiert werden",
"Adjust how people can share between servers. This includes shares between people on this server as well if they are using federated sharing." : "Legt fest, wie Personen zwischen Servern teilen können. Dies gilt auch für Freigaben zwischen Personen auf diesem Server, wenn sie Federated-Sharing verwenden.",
"Allow people on this server to send shares to other servers (this option also allows WebDAV access to public shares)" : "Personen auf diesem Server das Senden von Freigaben an andere Server erlauben (Diese Option ermöglicht auch den WebDAV-Zugriff auf öffentliche Freigaben)",
@@ -55,9 +55,11 @@ OC.L10N.register(
"You can share with anyone who uses a {productName} server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Sie können mit jedem teilen, der einen {productName}-Server oder andere Open Cloud Mesh (OCM) kompatible Server und Dienste verwendet! Geben Sie einfach deren Federated-Cloud-ID in den Teilen-Dialog ein. Diese sieht wie folgt aus: person@cloud.example.com",
"Your Federated Cloud ID" : "Ihre Federated-Cloud-ID",
"Share it so your friends can share files with you:" : "Teilen Sie es, damit Ihre Freunde Dateien mit Ihnen teilen können:",
"Bluesky" : "Bluesky",
"Facebook" : "Facebook",
"X (formerly Twitter)" : "X (früher Twitter)",
"formerly Twitter" : "früher Twitter",
"Mastodon" : "Mastodon",
"Bluesky" : "Bluesky",
"Add to your website" : "Zu Ihrer Webseite hinzufügen",
"Share with me via {productName}" : "Mit mir über {productName} teilen",
"HTML Code:" : "HTML-Code:",
@@ -69,8 +71,6 @@ OC.L10N.register(
"Incoming share could not be processed" : "Eingehende Freigabe konnte nicht verarbeitet werden",
"Cloud ID copied to the clipboard" : "Cloud-ID wurde in die Zwischenablage kopiert",
"Copy to clipboard" : "In die Zwischenablage kopieren",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Sie können mit jedem teilen, der einen Nextcloud-Server oder andere Open Cloud Mesh (OCM) kompatible Server und Dienste verwendet! Geben Sie einfach deren Federated-Cloud-ID in den Teilen-Dialog ein. Diese sieht wie folgt aus: person@cloud.example.com",
"X (formerly Twitter)" : "X (früher Twitter)",
"formerly Twitter" : "früher Twitter"
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Sie können mit jedem teilen, der einen Nextcloud-Server oder andere Open Cloud Mesh (OCM) kompatible Server und Dienste verwendet! Geben Sie einfach deren Federated-Cloud-ID in den Teilen-Dialog ein. Diese sieht wie folgt aus: person@cloud.example.com"
},
"nplurals=2; plural=(n != 1);");
+6 -6
View File
@@ -23,14 +23,14 @@
"Provide federated file sharing across servers" : "Bietet Cloud-übergreifende Datei-Freigaben",
"Confirm data upload to lookup server" : "Datenupload zum Lookup-Server bestätigen",
"When enabled, all account properties (e.g. email address) with scope visibility set to \"published\", will be automatically synced and transmitted to an external system and made available in a public, global address book." : "Wenn diese Option aktiviert ist, werden alle Kontoeigenschaften (z. B. E-Mail-Adresse) mit der auf \"veröffentlicht\" eingestellten Bereichssichtbarkeit automatisch synchronisiert und an ein externes System übertragen sowie in einem öffentlichen, globalen Adressbuch verfügbar gemacht.",
"Enable data upload" : "Datenhochladen aktivieren",
"Disable upload" : "Hochladen deaktivieren",
"Enable data upload" : "Datenhochladen aktivieren",
"Confirm querying lookup server" : "Abfrage des Lookup-Servers bestätigen",
"When enabled, the search input when creating shares will be sent to an external system that provides a public and global address book." : "Wenn aktiviert, wird die Sucheingabe beim Erstellen von Freigaben an ein externes System gesendet, das ein öffentliches und globales Adressbuch bereitstellt.",
"This is used to retrieve the federated cloud ID to make federated sharing easier." : "Dies wird verwendet, um die Federated-Cloud-ID abzurufen und so die federierte Freigabe zu vereinfachen.",
"Moreover, email addresses of users might be sent to that system in order to verify them." : "Darüber hinaus können E-Mail-Adressen von Benutzern an dieses System gesendet werden, um sie zu überprüfen.",
"Enable querying" : "Abfragen aktivieren",
"Disable querying" : "Abfragen deaktivieren",
"Enable querying" : "Abfragen aktivieren",
"Unable to update federated files sharing config" : "Einstellungen zum Federated-Teilen konnten nicht aktualisiert werden",
"Adjust how people can share between servers. This includes shares between people on this server as well if they are using federated sharing." : "Legt fest, wie Personen zwischen Servern teilen können. Dies gilt auch für Freigaben zwischen Personen auf diesem Server, wenn sie Federated-Sharing verwenden.",
"Allow people on this server to send shares to other servers (this option also allows WebDAV access to public shares)" : "Personen auf diesem Server das Senden von Freigaben an andere Server erlauben (Diese Option ermöglicht auch den WebDAV-Zugriff auf öffentliche Freigaben)",
@@ -53,9 +53,11 @@
"You can share with anyone who uses a {productName} server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Sie können mit jedem teilen, der einen {productName}-Server oder andere Open Cloud Mesh (OCM) kompatible Server und Dienste verwendet! Geben Sie einfach deren Federated-Cloud-ID in den Teilen-Dialog ein. Diese sieht wie folgt aus: person@cloud.example.com",
"Your Federated Cloud ID" : "Ihre Federated-Cloud-ID",
"Share it so your friends can share files with you:" : "Teilen Sie es, damit Ihre Freunde Dateien mit Ihnen teilen können:",
"Bluesky" : "Bluesky",
"Facebook" : "Facebook",
"X (formerly Twitter)" : "X (früher Twitter)",
"formerly Twitter" : "früher Twitter",
"Mastodon" : "Mastodon",
"Bluesky" : "Bluesky",
"Add to your website" : "Zu Ihrer Webseite hinzufügen",
"Share with me via {productName}" : "Mit mir über {productName} teilen",
"HTML Code:" : "HTML-Code:",
@@ -67,8 +69,6 @@
"Incoming share could not be processed" : "Eingehende Freigabe konnte nicht verarbeitet werden",
"Cloud ID copied to the clipboard" : "Cloud-ID wurde in die Zwischenablage kopiert",
"Copy to clipboard" : "In die Zwischenablage kopieren",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Sie können mit jedem teilen, der einen Nextcloud-Server oder andere Open Cloud Mesh (OCM) kompatible Server und Dienste verwendet! Geben Sie einfach deren Federated-Cloud-ID in den Teilen-Dialog ein. Diese sieht wie folgt aus: person@cloud.example.com",
"X (formerly Twitter)" : "X (früher Twitter)",
"formerly Twitter" : "früher Twitter"
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Sie können mit jedem teilen, der einen Nextcloud-Server oder andere Open Cloud Mesh (OCM) kompatible Server und Dienste verwendet! Geben Sie einfach deren Federated-Cloud-ID in den Teilen-Dialog ein. Diese sieht wie folgt aus: person@cloud.example.com"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}
+6 -6
View File
@@ -25,14 +25,14 @@ OC.L10N.register(
"Provide federated file sharing across servers" : "Παρέχει κοινής χρήσης αρχεία μεταξύ διακομιστών",
"Confirm data upload to lookup server" : "Επιβεβαίωση μεταφόρτωσης δεδομένων στον διακομιστή αναζήτησης",
"When enabled, all account properties (e.g. email address) with scope visibility set to \"published\", will be automatically synced and transmitted to an external system and made available in a public, global address book." : "Όταν είναι ενεργοποιημένο, όλες οι ιδιότητες λογαριασμού (π.χ. διεύθυνση ηλεκτρονικού ταχυδρομείου) με ορατότητα ορισμένη ως «δημοσιευμένη», θα συγχρονίζονται και μεταφέρονται αυτόματα σε ένα εξωτερικό σύστημα, όπου θα είναι διαθέσιμες σε έναν δημόσιο, παγκόσμιο κατάλογο διευθύνσεων.",
"Enable data upload" : "Ενεργοποίηση μεταφόρτωσης δεδομένων",
"Disable upload" : "Απενεργοποίηση μεταφόρτωσης",
"Enable data upload" : "Ενεργοποίηση μεταφόρτωσης δεδομένων",
"Confirm querying lookup server" : "Επιβεβαίωση ερώτησης στον διακομιστή αναζήτησης",
"When enabled, the search input when creating shares will be sent to an external system that provides a public and global address book." : "Όταν είναι ενεργοποιημένο, η είσοδος αναζήτησης κατά τη δημιουργία κοινών χρήσεων θα αποστέλλεται σε ένα εξωτερικό σύστημα που παρέχει έναν δημόσιο και παγκόσμιο κατάλογο διευθύνσεων.",
"This is used to retrieve the federated cloud ID to make federated sharing easier." : "Αυτό χρησιμοποιείται για την ανάκτηση του συνενωμένου αναγνωριστικού νέφους για να διευκολύνεται η συνενωμένη κοινή χρήση.",
"Moreover, email addresses of users might be sent to that system in order to verify them." : "Επιπλέον, οι διευθύνσεις ηλεκτρονικού ταχυδρομείου των χρηστών ενδέχεται να αποστέλλονται σε αυτό το σύστημα για επαλήθευση.",
"Enable querying" : "Ενεργοποίηση ερώτησης",
"Disable querying" : "Απενεργοποίηση ερώτησης",
"Enable querying" : "Ενεργοποίηση ερώτησης",
"Unable to update federated files sharing config" : "Αδυναμία ενημέρωσης ρυθμίσεων συνενωμένης κοινής χρήσης αρχείων",
"Adjust how people can share between servers. This includes shares between people on this server as well if they are using federated sharing." : "Ρυθμίστε τον τρόπο με τον οποίο οι χρήστες μπορούν να μοιράζονται μεταξύ διακομιστών. Αυτό περιλαμβάνει και κοινές χρήσεις μεταξύ χρηστών σε αυτόν τον διακομιστή, εφόσον χρησιμοποιούν συνενωμένη κοινή χρήση.",
"Allow people on this server to send shares to other servers (this option also allows WebDAV access to public shares)" : "Επιτρέψτε στους χρήστες αυτού του διακομιστή να αποστέλλουν κοινές χρήσεις σε άλλους διακομιστές (αυτή η επιλογή επιτρέπει επίσης πρόσβαση μέσω WebDAV σε δημόσιες κοινές χρήσεις)",
@@ -55,9 +55,11 @@ OC.L10N.register(
"You can share with anyone who uses a {productName} server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Μπορείτε να μοιραστείτε με οποιονδήποτε χρησιμοποιεί έναν διακομιστή {productName} ή άλλους συμβατούς διακομιστές και υπηρεσίες Open Cloud Mesh (OCM)! Απλά εισάγετε το αναγνωριστικό του ομοσπονδοποιημένου υπολογιστικού νέφους τους στο παράθυρο κοινής χρήσης. Μοιάζει με person@cloud.example.com",
"Your Federated Cloud ID" : "Το αναγνωριστικό του ομοσπονδοποιημένου υπολογιστικού νέφους σας",
"Share it so your friends can share files with you:" : "Διαμοιραστείτε το ώστε οι φίλοι σας να μπορούν να διαμοιράζονται αρχεία με εσάς:",
"Bluesky" : "Bluesky",
"Facebook" : "Facebook",
"X (formerly Twitter)" : "X (πρώην Twitter)",
"formerly Twitter" : "πρώην Twitter",
"Mastodon" : "Mastodon",
"Bluesky" : "Bluesky",
"Add to your website" : "Προσθήκη στην ιστοσελίδα σας",
"Share with me via {productName}" : "Μοιραστείτε μαζί μου μέσω {productName}",
"HTML Code:" : "Κώδικας HTML:",
@@ -69,8 +71,6 @@ OC.L10N.register(
"Incoming share could not be processed" : "Η εισερχόμενη κοινή χρήση δεν μπορούσε να επεξεργαστεί",
"Cloud ID copied to the clipboard" : "Το αναγνωριστικό νέφους αντιγράφηκε στο πρόχειρο",
"Copy to clipboard" : "Αντιγραφή στο πρόχειρο",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Μπορείτε να διαμοιράζεστε με οποιονδήποτε χρησιμοποιεί Nextcloud ή άλλο συμβατό διακομιστή και υπηρεσιών Open Cloud Mesh (OCM)! Απλά προσθέστε το Federated Cloud ID στο πλαίσιο διαλόγου διαμοιρασμού. Θα μοιάζει με person@cloud.example.com",
"X (formerly Twitter)" : "X (πρώην Twitter)",
"formerly Twitter" : "πρώην Twitter"
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Μπορείτε να διαμοιράζεστε με οποιονδήποτε χρησιμοποιεί Nextcloud ή άλλο συμβατό διακομιστή και υπηρεσιών Open Cloud Mesh (OCM)! Απλά προσθέστε το Federated Cloud ID στο πλαίσιο διαλόγου διαμοιρασμού. Θα μοιάζει με person@cloud.example.com"
},
"nplurals=2; plural=(n != 1);");
+6 -6
View File
@@ -23,14 +23,14 @@
"Provide federated file sharing across servers" : "Παρέχει κοινής χρήσης αρχεία μεταξύ διακομιστών",
"Confirm data upload to lookup server" : "Επιβεβαίωση μεταφόρτωσης δεδομένων στον διακομιστή αναζήτησης",
"When enabled, all account properties (e.g. email address) with scope visibility set to \"published\", will be automatically synced and transmitted to an external system and made available in a public, global address book." : "Όταν είναι ενεργοποιημένο, όλες οι ιδιότητες λογαριασμού (π.χ. διεύθυνση ηλεκτρονικού ταχυδρομείου) με ορατότητα ορισμένη ως «δημοσιευμένη», θα συγχρονίζονται και μεταφέρονται αυτόματα σε ένα εξωτερικό σύστημα, όπου θα είναι διαθέσιμες σε έναν δημόσιο, παγκόσμιο κατάλογο διευθύνσεων.",
"Enable data upload" : "Ενεργοποίηση μεταφόρτωσης δεδομένων",
"Disable upload" : "Απενεργοποίηση μεταφόρτωσης",
"Enable data upload" : "Ενεργοποίηση μεταφόρτωσης δεδομένων",
"Confirm querying lookup server" : "Επιβεβαίωση ερώτησης στον διακομιστή αναζήτησης",
"When enabled, the search input when creating shares will be sent to an external system that provides a public and global address book." : "Όταν είναι ενεργοποιημένο, η είσοδος αναζήτησης κατά τη δημιουργία κοινών χρήσεων θα αποστέλλεται σε ένα εξωτερικό σύστημα που παρέχει έναν δημόσιο και παγκόσμιο κατάλογο διευθύνσεων.",
"This is used to retrieve the federated cloud ID to make federated sharing easier." : "Αυτό χρησιμοποιείται για την ανάκτηση του συνενωμένου αναγνωριστικού νέφους για να διευκολύνεται η συνενωμένη κοινή χρήση.",
"Moreover, email addresses of users might be sent to that system in order to verify them." : "Επιπλέον, οι διευθύνσεις ηλεκτρονικού ταχυδρομείου των χρηστών ενδέχεται να αποστέλλονται σε αυτό το σύστημα για επαλήθευση.",
"Enable querying" : "Ενεργοποίηση ερώτησης",
"Disable querying" : "Απενεργοποίηση ερώτησης",
"Enable querying" : "Ενεργοποίηση ερώτησης",
"Unable to update federated files sharing config" : "Αδυναμία ενημέρωσης ρυθμίσεων συνενωμένης κοινής χρήσης αρχείων",
"Adjust how people can share between servers. This includes shares between people on this server as well if they are using federated sharing." : "Ρυθμίστε τον τρόπο με τον οποίο οι χρήστες μπορούν να μοιράζονται μεταξύ διακομιστών. Αυτό περιλαμβάνει και κοινές χρήσεις μεταξύ χρηστών σε αυτόν τον διακομιστή, εφόσον χρησιμοποιούν συνενωμένη κοινή χρήση.",
"Allow people on this server to send shares to other servers (this option also allows WebDAV access to public shares)" : "Επιτρέψτε στους χρήστες αυτού του διακομιστή να αποστέλλουν κοινές χρήσεις σε άλλους διακομιστές (αυτή η επιλογή επιτρέπει επίσης πρόσβαση μέσω WebDAV σε δημόσιες κοινές χρήσεις)",
@@ -53,9 +53,11 @@
"You can share with anyone who uses a {productName} server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Μπορείτε να μοιραστείτε με οποιονδήποτε χρησιμοποιεί έναν διακομιστή {productName} ή άλλους συμβατούς διακομιστές και υπηρεσίες Open Cloud Mesh (OCM)! Απλά εισάγετε το αναγνωριστικό του ομοσπονδοποιημένου υπολογιστικού νέφους τους στο παράθυρο κοινής χρήσης. Μοιάζει με person@cloud.example.com",
"Your Federated Cloud ID" : "Το αναγνωριστικό του ομοσπονδοποιημένου υπολογιστικού νέφους σας",
"Share it so your friends can share files with you:" : "Διαμοιραστείτε το ώστε οι φίλοι σας να μπορούν να διαμοιράζονται αρχεία με εσάς:",
"Bluesky" : "Bluesky",
"Facebook" : "Facebook",
"X (formerly Twitter)" : "X (πρώην Twitter)",
"formerly Twitter" : "πρώην Twitter",
"Mastodon" : "Mastodon",
"Bluesky" : "Bluesky",
"Add to your website" : "Προσθήκη στην ιστοσελίδα σας",
"Share with me via {productName}" : "Μοιραστείτε μαζί μου μέσω {productName}",
"HTML Code:" : "Κώδικας HTML:",
@@ -67,8 +69,6 @@
"Incoming share could not be processed" : "Η εισερχόμενη κοινή χρήση δεν μπορούσε να επεξεργαστεί",
"Cloud ID copied to the clipboard" : "Το αναγνωριστικό νέφους αντιγράφηκε στο πρόχειρο",
"Copy to clipboard" : "Αντιγραφή στο πρόχειρο",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Μπορείτε να διαμοιράζεστε με οποιονδήποτε χρησιμοποιεί Nextcloud ή άλλο συμβατό διακομιστή και υπηρεσιών Open Cloud Mesh (OCM)! Απλά προσθέστε το Federated Cloud ID στο πλαίσιο διαλόγου διαμοιρασμού. Θα μοιάζει με person@cloud.example.com",
"X (formerly Twitter)" : "X (πρώην Twitter)",
"formerly Twitter" : "πρώην Twitter"
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Μπορείτε να διαμοιράζεστε με οποιονδήποτε χρησιμοποιεί Nextcloud ή άλλο συμβατό διακομιστή και υπηρεσιών Open Cloud Mesh (OCM)! Απλά προσθέστε το Federated Cloud ID στο πλαίσιο διαλόγου διαμοιρασμού. Θα μοιάζει με person@cloud.example.com"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}
+6 -6
View File
@@ -25,14 +25,14 @@ OC.L10N.register(
"Provide federated file sharing across servers" : "Provide federated file sharing across servers",
"Confirm data upload to lookup server" : "Confirm data upload to lookup server",
"When enabled, all account properties (e.g. email address) with scope visibility set to \"published\", will be automatically synced and transmitted to an external system and made available in a public, global address book." : "When enabled, all account properties (e.g. email address) with scope visibility set to \"published\", will be automatically synced and transmitted to an external system and made available in a public, global address book.",
"Enable data upload" : "Enable data upload",
"Disable upload" : "Disable upload",
"Enable data upload" : "Enable data upload",
"Confirm querying lookup server" : "Confirm querying lookup server",
"When enabled, the search input when creating shares will be sent to an external system that provides a public and global address book." : "When enabled, the search input when creating shares will be sent to an external system that provides a public and global address book.",
"This is used to retrieve the federated cloud ID to make federated sharing easier." : "This is used to retrieve the federated cloud ID to make federated sharing easier.",
"Moreover, email addresses of users might be sent to that system in order to verify them." : "Moreover, email addresses of users might be sent to that system in order to verify them.",
"Enable querying" : "Enable querying",
"Disable querying" : "Disable querying",
"Enable querying" : "Enable querying",
"Unable to update federated files sharing config" : "Unable to update federated files sharing config",
"Adjust how people can share between servers. This includes shares between people on this server as well if they are using federated sharing." : "Adjust how people can share between servers. This includes shares between people on this server as well if they are using federated sharing.",
"Allow people on this server to send shares to other servers (this option also allows WebDAV access to public shares)" : "Allow people on this server to send shares to other servers (this option also allows WebDAV access to public shares)",
@@ -55,9 +55,11 @@ OC.L10N.register(
"You can share with anyone who uses a {productName} server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "You can share with anyone who uses a {productName} server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com",
"Your Federated Cloud ID" : "Your Federated Cloud ID",
"Share it so your friends can share files with you:" : "Share it so your friends can share files with you:",
"Bluesky" : "Bluesky",
"Facebook" : "Facebook",
"X (formerly Twitter)" : "X (formerly Twitter)",
"formerly Twitter" : "formerly Twitter",
"Mastodon" : "Mastodon",
"Bluesky" : "Bluesky",
"Add to your website" : "Add to your website",
"Share with me via {productName}" : "Share with me via {productName}",
"HTML Code:" : "HTML Code:",
@@ -69,8 +71,6 @@ OC.L10N.register(
"Incoming share could not be processed" : "Incoming share could not be processed",
"Cloud ID copied to the clipboard" : "Cloud ID copied to the clipboard",
"Copy to clipboard" : "Copy to clipboard",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialogue. It looks like person@cloud.example.com",
"X (formerly Twitter)" : "X (formerly Twitter)",
"formerly Twitter" : "formerly Twitter"
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialogue. It looks like person@cloud.example.com"
},
"nplurals=2; plural=(n != 1);");
+6 -6
View File
@@ -23,14 +23,14 @@
"Provide federated file sharing across servers" : "Provide federated file sharing across servers",
"Confirm data upload to lookup server" : "Confirm data upload to lookup server",
"When enabled, all account properties (e.g. email address) with scope visibility set to \"published\", will be automatically synced and transmitted to an external system and made available in a public, global address book." : "When enabled, all account properties (e.g. email address) with scope visibility set to \"published\", will be automatically synced and transmitted to an external system and made available in a public, global address book.",
"Enable data upload" : "Enable data upload",
"Disable upload" : "Disable upload",
"Enable data upload" : "Enable data upload",
"Confirm querying lookup server" : "Confirm querying lookup server",
"When enabled, the search input when creating shares will be sent to an external system that provides a public and global address book." : "When enabled, the search input when creating shares will be sent to an external system that provides a public and global address book.",
"This is used to retrieve the federated cloud ID to make federated sharing easier." : "This is used to retrieve the federated cloud ID to make federated sharing easier.",
"Moreover, email addresses of users might be sent to that system in order to verify them." : "Moreover, email addresses of users might be sent to that system in order to verify them.",
"Enable querying" : "Enable querying",
"Disable querying" : "Disable querying",
"Enable querying" : "Enable querying",
"Unable to update federated files sharing config" : "Unable to update federated files sharing config",
"Adjust how people can share between servers. This includes shares between people on this server as well if they are using federated sharing." : "Adjust how people can share between servers. This includes shares between people on this server as well if they are using federated sharing.",
"Allow people on this server to send shares to other servers (this option also allows WebDAV access to public shares)" : "Allow people on this server to send shares to other servers (this option also allows WebDAV access to public shares)",
@@ -53,9 +53,11 @@
"You can share with anyone who uses a {productName} server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "You can share with anyone who uses a {productName} server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com",
"Your Federated Cloud ID" : "Your Federated Cloud ID",
"Share it so your friends can share files with you:" : "Share it so your friends can share files with you:",
"Bluesky" : "Bluesky",
"Facebook" : "Facebook",
"X (formerly Twitter)" : "X (formerly Twitter)",
"formerly Twitter" : "formerly Twitter",
"Mastodon" : "Mastodon",
"Bluesky" : "Bluesky",
"Add to your website" : "Add to your website",
"Share with me via {productName}" : "Share with me via {productName}",
"HTML Code:" : "HTML Code:",
@@ -67,8 +69,6 @@
"Incoming share could not be processed" : "Incoming share could not be processed",
"Cloud ID copied to the clipboard" : "Cloud ID copied to the clipboard",
"Copy to clipboard" : "Copy to clipboard",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialogue. It looks like person@cloud.example.com",
"X (formerly Twitter)" : "X (formerly Twitter)",
"formerly Twitter" : "formerly Twitter"
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialogue. It looks like person@cloud.example.com"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}
+6 -6
View File
@@ -25,14 +25,14 @@ OC.L10N.register(
"Provide federated file sharing across servers" : "Provee compartición federada de archivos entre servidores",
"Confirm data upload to lookup server" : "Confirmar la carga de datos para el servidor de búsqueda",
"When enabled, all account properties (e.g. email address) with scope visibility set to \"published\", will be automatically synced and transmitted to an external system and made available in a public, global address book." : "Cuando está habilitado, todas las propiedades de las cuentas (p.ej. direcciones de correo electrónico) que se posean el ámbito de visibilidad establecido a \"publicada\" serán automáticamente sincronizadas y transmitidas a un sistema externo y se harán disponible en una libreta de direcciones pública global.",
"Enable data upload" : "Habilitar la subida de datos",
"Disable upload" : "Deshabilitar subidas",
"Enable data upload" : "Habilitar la subida de datos",
"Confirm querying lookup server" : "Confirmar la consulta al servidor de búsqueda",
"When enabled, the search input when creating shares will be sent to an external system that provides a public and global address book." : "Cuando está habilitado, cualquier entrada en la búsqueda cuando se crean recursos compartidos será enviada a un sistema externo que provee una libreta de direcciones pública global.",
"This is used to retrieve the federated cloud ID to make federated sharing easier." : "Esto se utiliza para obtener el ID de nube federada para hacer más sencilla la compartición federada. ",
"Moreover, email addresses of users might be sent to that system in order to verify them." : "Además, las direcciones de correo electrónico de los usuarios podrían ser enviadas a este sistema de manera de verificarlas.",
"Enable querying" : "Habilitar consultas",
"Disable querying" : "Deshabilitar consultas",
"Enable querying" : "Habilitar consultas",
"Unable to update federated files sharing config" : "No fue posible actualizar la configuración de los recursos compartidos federados de archivos",
"Adjust how people can share between servers. This includes shares between people on this server as well if they are using federated sharing." : "Ajustar la forma en que los usuarios pueden compartir entre servidores. Esto incluye a los recursos compartidos entre usuarios en este servidor tanto como a aquellos que usan compartición federada",
"Allow people on this server to send shares to other servers (this option also allows WebDAV access to public shares)" : "Permitir a los usuarios en este servidor enviar recursos compartidos a otros servidores (esta opción también permite el acceso público vía WebDAV a los recursos compartidos públicos)",
@@ -55,9 +55,11 @@ OC.L10N.register(
"You can share with anyone who uses a {productName} server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "¡Puede compartir con cualquier persona que use un servidor {productName} u otros servicios compatibles con Open Cloud Mesh (OCM)!. Solo coloque el ID de Nube Federada de esta(s) persona(s) en el diálogo de compartir. Se verán así: persona@nube.ejemplo.com",
"Your Federated Cloud ID" : "Su ID de Nube Federada",
"Share it so your friends can share files with you:" : "Compártalo para que sus amigos puedan compartir archivos con Ud.:",
"Bluesky" : "Bluesky",
"Facebook" : "Facebook",
"X (formerly Twitter)" : "X (anteriormente Twitter)",
"formerly Twitter" : "anteriormente Twitter",
"Mastodon" : "Mastodon",
"Bluesky" : "Bluesky",
"Add to your website" : "Añadir a su sitio web",
"Share with me via {productName}" : "Compartir conmigo a través de {productName}",
"HTML Code:" : "Código HTML:",
@@ -69,8 +71,6 @@ OC.L10N.register(
"Incoming share could not be processed" : "Elemento compartido entrante no pudo ser procesado",
"Cloud ID copied to the clipboard" : "ID de nube copiado al portapapeles",
"Copy to clipboard" : "Copiar al portapapeles",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "¡Puede compartir con cualquier persona que use un servidor Nextcloud u otros servidores y servicios compatibles con Open Cloud Mesh (OCM)!. Solo coloque el ID de Nube Federada de esta(s) persona(s) en el diálogo de compartir. Se verán así: persona@nube.ejemplo.com",
"X (formerly Twitter)" : "X (anteriormente Twitter)",
"formerly Twitter" : "anteriormente Twitter"
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "¡Puede compartir con cualquier persona que use un servidor Nextcloud u otros servidores y servicios compatibles con Open Cloud Mesh (OCM)!. Solo coloque el ID de Nube Federada de esta(s) persona(s) en el diálogo de compartir. Se verán así: persona@nube.ejemplo.com"
},
"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
+6 -6
View File
@@ -23,14 +23,14 @@
"Provide federated file sharing across servers" : "Provee compartición federada de archivos entre servidores",
"Confirm data upload to lookup server" : "Confirmar la carga de datos para el servidor de búsqueda",
"When enabled, all account properties (e.g. email address) with scope visibility set to \"published\", will be automatically synced and transmitted to an external system and made available in a public, global address book." : "Cuando está habilitado, todas las propiedades de las cuentas (p.ej. direcciones de correo electrónico) que se posean el ámbito de visibilidad establecido a \"publicada\" serán automáticamente sincronizadas y transmitidas a un sistema externo y se harán disponible en una libreta de direcciones pública global.",
"Enable data upload" : "Habilitar la subida de datos",
"Disable upload" : "Deshabilitar subidas",
"Enable data upload" : "Habilitar la subida de datos",
"Confirm querying lookup server" : "Confirmar la consulta al servidor de búsqueda",
"When enabled, the search input when creating shares will be sent to an external system that provides a public and global address book." : "Cuando está habilitado, cualquier entrada en la búsqueda cuando se crean recursos compartidos será enviada a un sistema externo que provee una libreta de direcciones pública global.",
"This is used to retrieve the federated cloud ID to make federated sharing easier." : "Esto se utiliza para obtener el ID de nube federada para hacer más sencilla la compartición federada. ",
"Moreover, email addresses of users might be sent to that system in order to verify them." : "Además, las direcciones de correo electrónico de los usuarios podrían ser enviadas a este sistema de manera de verificarlas.",
"Enable querying" : "Habilitar consultas",
"Disable querying" : "Deshabilitar consultas",
"Enable querying" : "Habilitar consultas",
"Unable to update federated files sharing config" : "No fue posible actualizar la configuración de los recursos compartidos federados de archivos",
"Adjust how people can share between servers. This includes shares between people on this server as well if they are using federated sharing." : "Ajustar la forma en que los usuarios pueden compartir entre servidores. Esto incluye a los recursos compartidos entre usuarios en este servidor tanto como a aquellos que usan compartición federada",
"Allow people on this server to send shares to other servers (this option also allows WebDAV access to public shares)" : "Permitir a los usuarios en este servidor enviar recursos compartidos a otros servidores (esta opción también permite el acceso público vía WebDAV a los recursos compartidos públicos)",
@@ -53,9 +53,11 @@
"You can share with anyone who uses a {productName} server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "¡Puede compartir con cualquier persona que use un servidor {productName} u otros servicios compatibles con Open Cloud Mesh (OCM)!. Solo coloque el ID de Nube Federada de esta(s) persona(s) en el diálogo de compartir. Se verán así: persona@nube.ejemplo.com",
"Your Federated Cloud ID" : "Su ID de Nube Federada",
"Share it so your friends can share files with you:" : "Compártalo para que sus amigos puedan compartir archivos con Ud.:",
"Bluesky" : "Bluesky",
"Facebook" : "Facebook",
"X (formerly Twitter)" : "X (anteriormente Twitter)",
"formerly Twitter" : "anteriormente Twitter",
"Mastodon" : "Mastodon",
"Bluesky" : "Bluesky",
"Add to your website" : "Añadir a su sitio web",
"Share with me via {productName}" : "Compartir conmigo a través de {productName}",
"HTML Code:" : "Código HTML:",
@@ -67,8 +69,6 @@
"Incoming share could not be processed" : "Elemento compartido entrante no pudo ser procesado",
"Cloud ID copied to the clipboard" : "ID de nube copiado al portapapeles",
"Copy to clipboard" : "Copiar al portapapeles",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "¡Puede compartir con cualquier persona que use un servidor Nextcloud u otros servidores y servicios compatibles con Open Cloud Mesh (OCM)!. Solo coloque el ID de Nube Federada de esta(s) persona(s) en el diálogo de compartir. Se verán así: persona@nube.ejemplo.com",
"X (formerly Twitter)" : "X (anteriormente Twitter)",
"formerly Twitter" : "anteriormente Twitter"
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "¡Puede compartir con cualquier persona que use un servidor Nextcloud u otros servidores y servicios compatibles con Open Cloud Mesh (OCM)!. Solo coloque el ID de Nube Federada de esta(s) persona(s) en el diálogo de compartir. Se verán así: persona@nube.ejemplo.com"
},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
}
+2 -2
View File
@@ -39,6 +39,7 @@ OC.L10N.register(
"Federated Cloud" : "Nube Federada",
"Share it so your friends can share files with you:" : "Compártelo para que tus amigos puedan compartir archivos contigo:",
"Facebook" : "Facebook",
"X (formerly Twitter)" : "X (anteriormente Twitter)",
"Add to your website" : "Agregar a tu sitio web",
"HTML Code:" : "Código HTML:",
"Cancel" : "Cancelar",
@@ -48,7 +49,6 @@ OC.L10N.register(
"Remote share password" : "Contraseña del elemento compartido remoto",
"Cloud ID copied to the clipboard" : "Identificador de nube copiado al portapapeles",
"Copy to clipboard" : "Copiar al portapapeles",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "¡Puede compartir con cualquier persona que use un servidor Nextcloud u otros servidores y servicios compatibles con Open Cloud Mesh (OCM)!. Sólo ponga el identificador de nube federada en el diálogo de compartir. Tiene la forma: persona@nube.ejemplo.com",
"X (formerly Twitter)" : "X (anteriormente Twitter)"
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "¡Puede compartir con cualquier persona que use un servidor Nextcloud u otros servidores y servicios compatibles con Open Cloud Mesh (OCM)!. Sólo ponga el identificador de nube federada en el diálogo de compartir. Tiene la forma: persona@nube.ejemplo.com"
},
"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
+2 -2
View File
@@ -37,6 +37,7 @@
"Federated Cloud" : "Nube Federada",
"Share it so your friends can share files with you:" : "Compártelo para que tus amigos puedan compartir archivos contigo:",
"Facebook" : "Facebook",
"X (formerly Twitter)" : "X (anteriormente Twitter)",
"Add to your website" : "Agregar a tu sitio web",
"HTML Code:" : "Código HTML:",
"Cancel" : "Cancelar",
@@ -46,7 +47,6 @@
"Remote share password" : "Contraseña del elemento compartido remoto",
"Cloud ID copied to the clipboard" : "Identificador de nube copiado al portapapeles",
"Copy to clipboard" : "Copiar al portapapeles",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "¡Puede compartir con cualquier persona que use un servidor Nextcloud u otros servidores y servicios compatibles con Open Cloud Mesh (OCM)!. Sólo ponga el identificador de nube federada en el diálogo de compartir. Tiene la forma: persona@nube.ejemplo.com",
"X (formerly Twitter)" : "X (anteriormente Twitter)"
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "¡Puede compartir con cualquier persona que use un servidor Nextcloud u otros servidores y servicios compatibles con Open Cloud Mesh (OCM)!. Sólo ponga el identificador de nube federada en el diálogo de compartir. Tiene la forma: persona@nube.ejemplo.com"
},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
}
+6 -6
View File
@@ -25,14 +25,14 @@ OC.L10N.register(
"Provide federated file sharing across servers" : "Toeta failide jagamist liitpilves üle serverite",
"Confirm data upload to lookup server" : "Kinnita andmete üleslaadimine tuvastusserverisse",
"When enabled, all account properties (e.g. email address) with scope visibility set to \"published\", will be automatically synced and transmitted to an external system and made available in a public, global address book." : "Kui see eelistus on kasutusel, siis kõik kasutajakonto andmed, mille nähtavuseks on märgitud „avaldatud“, sünkroniseeritakse automaatselt välistesse serveritesse ning avaldatakse liitpilve üldises aadressiraamatus.",
"Enable data upload" : "Luba andmete üleslaadimine",
"Disable upload" : "Keela üleslaadimine",
"Enable data upload" : "Luba andmete üleslaadimine",
"Confirm querying lookup server" : "Kinnita andmete pärimine tuvastusserverist",
"When enabled, the search input when creating shares will be sent to an external system that provides a public and global address book." : "Kui see eelistus on kasutusel, siis jaosmeedia loomisel kasutatud otsingusisend saadetakse automaatselt välistesse serveritesse, mis tagavad liitpilve üldise ja avaliku aadressiraamatu toimimise.",
"This is used to retrieve the federated cloud ID to make federated sharing easier." : "See on kasutusel liitpilve tunnuse laadimiseks ning see teeb jagamise liitpilves lihtsamaks.",
"Moreover, email addresses of users might be sent to that system in order to verify them." : "Lisaks võidakse sellesse serverisse saata verifitseerimise jaoks kasutajate e-posti aadresse.",
"Enable querying" : "Luba päringute tegemine",
"Disable querying" : "Keela päringute tegemine",
"Enable querying" : "Luba päringute tegemine",
"Unable to update federated files sharing config" : "Failide liitpilves jagamise seadistusi ei saa uuendada",
"Adjust how people can share between servers. This includes shares between people on this server as well if they are using federated sharing." : "Reguleeri, kuidas inimesed saavad serverite vahel jagada. See hõlmab ka selles serveris olevate inimeste vahelisi jagamisi, kui nad kasutavad liitjagamist.",
"Allow people on this server to send shares to other servers (this option also allows WebDAV access to public shares)" : "Luba selles serveris olevatel inimestel saata kaustu teistele serveritele (see valik võimaldab ka WebDAV-i juurdepääsu avalikele jagamistele)",
@@ -55,9 +55,11 @@ OC.L10N.register(
"You can share with anyone who uses a {productName} server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Saad jagada kõigiga, kes kasutavad {productName}i serverit või muid Open Cloud Meshi (OCM) ühilduvaid servereid ja teenuseid! Lihtsalt sisesta jagamise vaates nende kasutajatunnus liitpilves. See näeb välja nagu kasutajanimi@pilv.toreserver.com",
"Your Federated Cloud ID" : "Sinu kasutajatunnus liitpilves",
"Share it so your friends can share files with you:" : "Jaga seda, et su sõbrad saaksid sinuga faile jagada:",
"Bluesky" : "Bluesky",
"Facebook" : "Facebook",
"X (formerly Twitter)" : "X (varasemalt Twitter)",
"formerly Twitter" : "varasemalt Twitter",
"Mastodon" : "Mastodon",
"Bluesky" : "Bluesky",
"Add to your website" : "Lisa oma veebisaidile",
"Share with me via {productName}" : "Jaga minuga {productName}i vahendusel",
"HTML Code:" : "HTML kood:",
@@ -69,8 +71,6 @@ OC.L10N.register(
"Incoming share could not be processed" : "Sissetulevat kausta ei saanud töödelda",
"Cloud ID copied to the clipboard" : "Kasutajatunnus liitpilves on kopeeritud lõikelauale",
"Copy to clipboard" : "Kopeeri lõikelauale",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Saad jagada kõigiga, kes kasutavad Nextcloudi serverit või muid Open Cloud Meshi (OCM) ühilduvaid servereid ja teenuseid! Lihtsalt sisesta nende kasutajatunnus liitpilves jagamise vaates. See näeb välja nagu isiksus@pilv.toreserver.com",
"X (formerly Twitter)" : "X (varasemalt Twitter)",
"formerly Twitter" : "varasemalt Twitter"
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Saad jagada kõigiga, kes kasutavad Nextcloudi serverit või muid Open Cloud Meshi (OCM) ühilduvaid servereid ja teenuseid! Lihtsalt sisesta nende kasutajatunnus liitpilves jagamise vaates. See näeb välja nagu isiksus@pilv.toreserver.com"
},
"nplurals=2; plural=(n != 1);");
+6 -6
View File
@@ -23,14 +23,14 @@
"Provide federated file sharing across servers" : "Toeta failide jagamist liitpilves üle serverite",
"Confirm data upload to lookup server" : "Kinnita andmete üleslaadimine tuvastusserverisse",
"When enabled, all account properties (e.g. email address) with scope visibility set to \"published\", will be automatically synced and transmitted to an external system and made available in a public, global address book." : "Kui see eelistus on kasutusel, siis kõik kasutajakonto andmed, mille nähtavuseks on märgitud „avaldatud“, sünkroniseeritakse automaatselt välistesse serveritesse ning avaldatakse liitpilve üldises aadressiraamatus.",
"Enable data upload" : "Luba andmete üleslaadimine",
"Disable upload" : "Keela üleslaadimine",
"Enable data upload" : "Luba andmete üleslaadimine",
"Confirm querying lookup server" : "Kinnita andmete pärimine tuvastusserverist",
"When enabled, the search input when creating shares will be sent to an external system that provides a public and global address book." : "Kui see eelistus on kasutusel, siis jaosmeedia loomisel kasutatud otsingusisend saadetakse automaatselt välistesse serveritesse, mis tagavad liitpilve üldise ja avaliku aadressiraamatu toimimise.",
"This is used to retrieve the federated cloud ID to make federated sharing easier." : "See on kasutusel liitpilve tunnuse laadimiseks ning see teeb jagamise liitpilves lihtsamaks.",
"Moreover, email addresses of users might be sent to that system in order to verify them." : "Lisaks võidakse sellesse serverisse saata verifitseerimise jaoks kasutajate e-posti aadresse.",
"Enable querying" : "Luba päringute tegemine",
"Disable querying" : "Keela päringute tegemine",
"Enable querying" : "Luba päringute tegemine",
"Unable to update federated files sharing config" : "Failide liitpilves jagamise seadistusi ei saa uuendada",
"Adjust how people can share between servers. This includes shares between people on this server as well if they are using federated sharing." : "Reguleeri, kuidas inimesed saavad serverite vahel jagada. See hõlmab ka selles serveris olevate inimeste vahelisi jagamisi, kui nad kasutavad liitjagamist.",
"Allow people on this server to send shares to other servers (this option also allows WebDAV access to public shares)" : "Luba selles serveris olevatel inimestel saata kaustu teistele serveritele (see valik võimaldab ka WebDAV-i juurdepääsu avalikele jagamistele)",
@@ -53,9 +53,11 @@
"You can share with anyone who uses a {productName} server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Saad jagada kõigiga, kes kasutavad {productName}i serverit või muid Open Cloud Meshi (OCM) ühilduvaid servereid ja teenuseid! Lihtsalt sisesta jagamise vaates nende kasutajatunnus liitpilves. See näeb välja nagu kasutajanimi@pilv.toreserver.com",
"Your Federated Cloud ID" : "Sinu kasutajatunnus liitpilves",
"Share it so your friends can share files with you:" : "Jaga seda, et su sõbrad saaksid sinuga faile jagada:",
"Bluesky" : "Bluesky",
"Facebook" : "Facebook",
"X (formerly Twitter)" : "X (varasemalt Twitter)",
"formerly Twitter" : "varasemalt Twitter",
"Mastodon" : "Mastodon",
"Bluesky" : "Bluesky",
"Add to your website" : "Lisa oma veebisaidile",
"Share with me via {productName}" : "Jaga minuga {productName}i vahendusel",
"HTML Code:" : "HTML kood:",
@@ -67,8 +69,6 @@
"Incoming share could not be processed" : "Sissetulevat kausta ei saanud töödelda",
"Cloud ID copied to the clipboard" : "Kasutajatunnus liitpilves on kopeeritud lõikelauale",
"Copy to clipboard" : "Kopeeri lõikelauale",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Saad jagada kõigiga, kes kasutavad Nextcloudi serverit või muid Open Cloud Meshi (OCM) ühilduvaid servereid ja teenuseid! Lihtsalt sisesta nende kasutajatunnus liitpilves jagamise vaates. See näeb välja nagu isiksus@pilv.toreserver.com",
"X (formerly Twitter)" : "X (varasemalt Twitter)",
"formerly Twitter" : "varasemalt Twitter"
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Saad jagada kõigiga, kes kasutavad Nextcloudi serverit või muid Open Cloud Meshi (OCM) ühilduvaid servereid ja teenuseid! Lihtsalt sisesta nende kasutajatunnus liitpilves jagamise vaates. See näeb välja nagu isiksus@pilv.toreserver.com"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}
+6 -6
View File
@@ -25,14 +25,14 @@ OC.L10N.register(
"Provide federated file sharing across servers" : "Eskaini zerbitzarien arteko federatutako partekatzea",
"Confirm data upload to lookup server" : "Berretsi datuak bilaketa-zerbitzarira kargatzea",
"When enabled, all account properties (e.g. email address) with scope visibility set to \"published\", will be automatically synced and transmitted to an external system and made available in a public, global address book." : "Gaituta dagoenean, kontuaren propietate guztiak (adib. helbide elektronikoa) ikusgarritasun eremua \"argitaratuta\" ezarrita dutenak, automatikoki sinkronizatuko dira eta kanpoko sistema batera igorriko da, eta helbide-liburu publiko eta global batean eskuragarri jarriko da.",
"Enable data upload" : "Aktibatu datuen kargatzea",
"Disable upload" : "Desgaitu kargatzea",
"Enable data upload" : "Aktibatu datuen kargatzea",
"Confirm querying lookup server" : "Berretsi bilaketa-zerbitzaria kontsultatzea",
"When enabled, the search input when creating shares will be sent to an external system that provides a public and global address book." : "Aktibatuta dagoenean, partekatzeak sortzean egiten den bilaketa-sarrera helbide-liburu publiko eta global bat ematen duen kanpoko sistema batera bidaliko da.",
"This is used to retrieve the federated cloud ID to make federated sharing easier." : "Hau federatutako hodeiaren identifikazioa berreskuratzeko erabiltzen da, federatutako partekatzea errazagoa izan dadin.",
"Moreover, email addresses of users might be sent to that system in order to verify them." : "Gainera, erabiltzaileen posta elektronikoko helbideak bidal daitezke sistema horretara, horiek egiaztatzeko.",
"Enable querying" : "Aktibatu kontsultatzea",
"Disable querying" : "Desaktibatu kontsultatzea",
"Enable querying" : "Aktibatu kontsultatzea",
"Unable to update federated files sharing config" : "Ezin da eguneratu fitxategi partekatze federatuaren konfigurazioa",
"Adjust how people can share between servers. This includes shares between people on this server as well if they are using federated sharing." : "Doitu nola parteka dezakeen jendeak zerbitzarien artean. Horrek zerbitzari honetako pertsonen arteko partekatzeak barne hartzen ditu, partekatze federatua erabiltzen ari badira.",
"Allow people on this server to send shares to other servers (this option also allows WebDAV access to public shares)" : "Baimendu zerbitzari honetako pertsonei beste zerbitzari batzuetarako partekatzeak bidaltzea (aukera honek partekatze publikoetara WebDAV sarbidea ere baimentzen du)",
@@ -55,9 +55,11 @@ OC.L10N.register(
"You can share with anyone who uses a {productName} server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "{productName} zerbitzari bat edo Open Cloud Mesh (OCM) zerbitzari eta zerbitzu bateragarriak erabiltzen dituen edonorekin parteka dezakezu! Jarri Hodei Federatuaren identifikazioa elkarrizketa partekatuan. Horrelako itxura du: pertsona@cloud.example.com",
"Your Federated Cloud ID" : "Zure federatutako hodei IDa",
"Share it so your friends can share files with you:" : "Bidali lagunei, zurekin fitxategiak parteka ditzaten:",
"Bluesky" : "Bluesky",
"Facebook" : "Facebook",
"X (formerly Twitter)" : "X (lehen Twitter)",
"formerly Twitter" : "lehen Twitter",
"Mastodon" : "Mastodon",
"Bluesky" : "Bluesky",
"Add to your website" : "Gehitu zure webgunera",
"Share with me via {productName}" : "Partekatu nirekin {productName}zerbitzariaren bidez",
"HTML Code:" : "HTML kodea:",
@@ -69,8 +71,6 @@ OC.L10N.register(
"Incoming share could not be processed" : "Sarrerako partekatzea ezin izan da prozesatu",
"Cloud ID copied to the clipboard" : "Hodei IDa arbelean kopiatu da",
"Copy to clipboard" : "Kopiatu arbelera",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Nextcloud zerbitzaria darabilen edo Open Cloud Mesh (OCM) zerbitzuarekin bateragarri den zerbitzua erabiltzen duen edonorekin partekatu dezakezu! Ipini beren Federatutako Hodei IDa partekatze leihoan. Horrelako zerbait izan ohi da: erabiltzailea@nextcloud.zerbitzaria.com",
"X (formerly Twitter)" : "X (lehen Twitter)",
"formerly Twitter" : "lehen Twitter"
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Nextcloud zerbitzaria darabilen edo Open Cloud Mesh (OCM) zerbitzuarekin bateragarri den zerbitzua erabiltzen duen edonorekin partekatu dezakezu! Ipini beren Federatutako Hodei IDa partekatze leihoan. Horrelako zerbait izan ohi da: erabiltzailea@nextcloud.zerbitzaria.com"
},
"nplurals=2; plural=(n != 1);");
+6 -6
View File
@@ -23,14 +23,14 @@
"Provide federated file sharing across servers" : "Eskaini zerbitzarien arteko federatutako partekatzea",
"Confirm data upload to lookup server" : "Berretsi datuak bilaketa-zerbitzarira kargatzea",
"When enabled, all account properties (e.g. email address) with scope visibility set to \"published\", will be automatically synced and transmitted to an external system and made available in a public, global address book." : "Gaituta dagoenean, kontuaren propietate guztiak (adib. helbide elektronikoa) ikusgarritasun eremua \"argitaratuta\" ezarrita dutenak, automatikoki sinkronizatuko dira eta kanpoko sistema batera igorriko da, eta helbide-liburu publiko eta global batean eskuragarri jarriko da.",
"Enable data upload" : "Aktibatu datuen kargatzea",
"Disable upload" : "Desgaitu kargatzea",
"Enable data upload" : "Aktibatu datuen kargatzea",
"Confirm querying lookup server" : "Berretsi bilaketa-zerbitzaria kontsultatzea",
"When enabled, the search input when creating shares will be sent to an external system that provides a public and global address book." : "Aktibatuta dagoenean, partekatzeak sortzean egiten den bilaketa-sarrera helbide-liburu publiko eta global bat ematen duen kanpoko sistema batera bidaliko da.",
"This is used to retrieve the federated cloud ID to make federated sharing easier." : "Hau federatutako hodeiaren identifikazioa berreskuratzeko erabiltzen da, federatutako partekatzea errazagoa izan dadin.",
"Moreover, email addresses of users might be sent to that system in order to verify them." : "Gainera, erabiltzaileen posta elektronikoko helbideak bidal daitezke sistema horretara, horiek egiaztatzeko.",
"Enable querying" : "Aktibatu kontsultatzea",
"Disable querying" : "Desaktibatu kontsultatzea",
"Enable querying" : "Aktibatu kontsultatzea",
"Unable to update federated files sharing config" : "Ezin da eguneratu fitxategi partekatze federatuaren konfigurazioa",
"Adjust how people can share between servers. This includes shares between people on this server as well if they are using federated sharing." : "Doitu nola parteka dezakeen jendeak zerbitzarien artean. Horrek zerbitzari honetako pertsonen arteko partekatzeak barne hartzen ditu, partekatze federatua erabiltzen ari badira.",
"Allow people on this server to send shares to other servers (this option also allows WebDAV access to public shares)" : "Baimendu zerbitzari honetako pertsonei beste zerbitzari batzuetarako partekatzeak bidaltzea (aukera honek partekatze publikoetara WebDAV sarbidea ere baimentzen du)",
@@ -53,9 +53,11 @@
"You can share with anyone who uses a {productName} server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "{productName} zerbitzari bat edo Open Cloud Mesh (OCM) zerbitzari eta zerbitzu bateragarriak erabiltzen dituen edonorekin parteka dezakezu! Jarri Hodei Federatuaren identifikazioa elkarrizketa partekatuan. Horrelako itxura du: pertsona@cloud.example.com",
"Your Federated Cloud ID" : "Zure federatutako hodei IDa",
"Share it so your friends can share files with you:" : "Bidali lagunei, zurekin fitxategiak parteka ditzaten:",
"Bluesky" : "Bluesky",
"Facebook" : "Facebook",
"X (formerly Twitter)" : "X (lehen Twitter)",
"formerly Twitter" : "lehen Twitter",
"Mastodon" : "Mastodon",
"Bluesky" : "Bluesky",
"Add to your website" : "Gehitu zure webgunera",
"Share with me via {productName}" : "Partekatu nirekin {productName}zerbitzariaren bidez",
"HTML Code:" : "HTML kodea:",
@@ -67,8 +69,6 @@
"Incoming share could not be processed" : "Sarrerako partekatzea ezin izan da prozesatu",
"Cloud ID copied to the clipboard" : "Hodei IDa arbelean kopiatu da",
"Copy to clipboard" : "Kopiatu arbelera",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Nextcloud zerbitzaria darabilen edo Open Cloud Mesh (OCM) zerbitzuarekin bateragarri den zerbitzua erabiltzen duen edonorekin partekatu dezakezu! Ipini beren Federatutako Hodei IDa partekatze leihoan. Horrelako zerbait izan ohi da: erabiltzailea@nextcloud.zerbitzaria.com",
"X (formerly Twitter)" : "X (lehen Twitter)",
"formerly Twitter" : "lehen Twitter"
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Nextcloud zerbitzaria darabilen edo Open Cloud Mesh (OCM) zerbitzuarekin bateragarri den zerbitzua erabiltzen duen edonorekin partekatu dezakezu! Ipini beren Federatutako Hodei IDa partekatze leihoan. Horrelako zerbait izan ohi da: erabiltzailea@nextcloud.zerbitzaria.com"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}
+2 -3
View File
@@ -26,8 +26,8 @@ OC.L10N.register(
"Copied!" : "Kopioitu!",
"Federated Cloud" : "Federoitu pilvi",
"Share it so your friends can share files with you:" : "Jaa se, jotta ystäväsi voivat jakaa tiedostoja kanssasi:",
"Bluesky" : "Bluesky",
"Facebook" : "Facebook",
"X (formerly Twitter)" : "X (aiemmin Twitter)",
"Mastodon" : "Mastodon",
"Add to your website" : "Lisää verkkosivuillesi",
"HTML Code:" : "HTML-koodi:",
@@ -37,7 +37,6 @@ OC.L10N.register(
"Do you want to add the remote share {name} from {owner}@{remote}?" : "Haluatko lisätä etäjaon {name} kohteesta {owner}@{remote}?",
"Remote share password" : "Etäjaon salasana",
"Copy to clipboard" : "Kopioi leikepöydälle",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Voit jakaa kenelle tahansa, joka käyttää Nextcloud-palvelinta tai muuta Open Cloud Mesh (OCM) -yhteensopivaa palvelinta tai palvelua! Kirjoita heidän federoidun pilven tunniste jaon kohteeksi. Se on muodossa henkilö@cloud.example.com",
"X (formerly Twitter)" : "X (aiemmin Twitter)"
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Voit jakaa kenelle tahansa, joka käyttää Nextcloud-palvelinta tai muuta Open Cloud Mesh (OCM) -yhteensopivaa palvelinta tai palvelua! Kirjoita heidän federoidun pilven tunniste jaon kohteeksi. Se on muodossa henkilö@cloud.example.com"
},
"nplurals=2; plural=(n != 1);");

Some files were not shown because too many files have changed in this diff Show More