Compare commits
95 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 62cfd3b4c9 | |||
| 2ea7537ca6 | |||
| e480563a54 | |||
| 2db99590b9 | |||
| c6f424bddd | |||
| 164021dee4 | |||
| bdf17e5252 | |||
| 5ece6f29c3 | |||
| a6e89e0410 | |||
| 335b871810 | |||
| 106fb14183 | |||
| 17a29c865b | |||
| 0b4cc1b83f | |||
| 45ecf37248 | |||
| 221bf33245 | |||
| 6b54bfb7b2 | |||
| c2bdb02144 | |||
| ce8ec36184 | |||
| 3094e70350 | |||
| 5ac1b3533b | |||
| 4bcb38c0b2 | |||
| 78e591d0d4 | |||
| 4c161a70a4 | |||
| ae34be566e | |||
| dc3c44a84f | |||
| d6a3fe7e83 | |||
| a086db37cd | |||
| d62643b43e | |||
| 826766d3bd | |||
| dd3f6516c5 | |||
| 259530ed5a | |||
| 15d7fb35f4 | |||
| 333d6bcc45 | |||
| bcc9ee4011 | |||
| 6f8d75bd24 | |||
| a9cf423321 | |||
| 3d6a864210 | |||
| e7c6ff1709 | |||
| ee38128f3d | |||
| 05b6ff5e7b | |||
| bddab7d302 | |||
| 958f9c9147 | |||
| ac2a53a4bd | |||
| 0b2ad6cefc | |||
| 71b74992e5 | |||
| c18e5fe8a9 | |||
| 226c601d1d | |||
| cf73f6e8bf | |||
| 5f6579fb6a | |||
| 6a120bfda1 | |||
| df8b19aea3 | |||
| 703043a024 | |||
| 865fb8092b | |||
| 51c76884bc | |||
| e3c7bcc61d | |||
| 92b8a5ef1e | |||
| 72cf710ec8 | |||
| 2b404e2a04 | |||
| ae911a21d2 | |||
| a7e5e7e4a1 | |||
| 609badf6a2 | |||
| 2664036b57 | |||
| 442bce3e0b | |||
| 5943d0a715 | |||
| f5ab3123df | |||
| d29551a46d | |||
| ffb1a39bba | |||
| 0446703202 | |||
| 89b396da95 | |||
| c4c02afb62 | |||
| b2c8be171d | |||
| b9c4112f47 | |||
| 82a3b6a311 | |||
| 62866bb097 | |||
| f30c7834d8 | |||
| 69ca730bd1 | |||
| f7fa2fd974 | |||
| 4edfeab4a7 | |||
| a5607754cf | |||
| 0cd9ca5be2 | |||
| bc759977d2 | |||
| 54a93e9252 | |||
| 4ca6c3af7a | |||
| cab4f8eca3 | |||
| 8477b1e408 | |||
| 0a518bd5ff | |||
| f1ced06cd5 | |||
| b21ef4881f | |||
| 5323b8caf1 | |||
| 1ca288f0a4 | |||
| 126db69108 | |||
| 3d7ae18660 | |||
| 0d51e1d3b5 | |||
| 7b7909f767 | |||
| b846f10e7b |
+2
-2
@@ -1238,9 +1238,9 @@ steps:
|
||||
- name: install-talk
|
||||
image: ghcr.io/nextcloud/continuous-integration-php8.0:latest
|
||||
commands:
|
||||
# JavaScript files are not used in integration tests so it is not needed to
|
||||
# JavaScript files are not used in integration tests, so it is not needed to
|
||||
# build them.
|
||||
- git clone --depth 1 https://github.com/nextcloud/spreed apps/spreed
|
||||
- git clone --depth 1 --branch stable26 https://github.com/nextcloud/spreed apps/spreed
|
||||
- cd apps/spreed
|
||||
- composer --version
|
||||
- composer self-update --2
|
||||
|
||||
@@ -16,6 +16,8 @@ jobs:
|
||||
init:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
if: ${{ github.repository_owner != 'nextcloud-gmbh' }}
|
||||
|
||||
steps:
|
||||
- name: Checkout server
|
||||
uses: actions/checkout@v3
|
||||
|
||||
@@ -17,6 +17,8 @@ jobs:
|
||||
ftp-tests:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
if: ${{ github.repository_owner != 'nextcloud-gmbh' }}
|
||||
|
||||
strategy:
|
||||
# do not stop on another job's failure
|
||||
fail-fast: false
|
||||
|
||||
@@ -10,6 +10,9 @@ on:
|
||||
jobs:
|
||||
versions:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
if: ${{ github.repository_owner != 'nextcloud-gmbh' }}
|
||||
|
||||
outputs:
|
||||
nodeVersion: ${{ steps.versions.outputs.nodeVersion }}
|
||||
npmVersion: ${{ steps.versions.outputs.npmVersion }}
|
||||
|
||||
@@ -13,6 +13,8 @@ jobs:
|
||||
phpunit-oci:
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
if: ${{ github.repository_owner != 'nextcloud-gmbh' }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php-versions: ['8.0', '8.1', '8.2']
|
||||
|
||||
@@ -6,6 +6,8 @@ jobs:
|
||||
performance-testing:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
if: ${{ github.repository_owner != 'nextcloud-gmbh' }}
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -50,7 +52,7 @@ jobs:
|
||||
curl -s -u test:test -T README.md http://localhost:8080/remote.php/dav/files/test/new_file.txt
|
||||
curl -s -u test:test -X DELETE http://localhost:8080/remote.php/dav/files/test/new_file.txt
|
||||
output: before.json
|
||||
profiler-branch: master
|
||||
profiler-branch: stable26
|
||||
|
||||
- name: Apply PR
|
||||
run: |
|
||||
|
||||
@@ -15,6 +15,9 @@ concurrency:
|
||||
jobs:
|
||||
phpunit-32bits:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
if: ${{ github.repository_owner != 'nextcloud-gmbh' }}
|
||||
|
||||
container: shivammathur/node:latest-i386
|
||||
|
||||
strategy:
|
||||
|
||||
@@ -17,6 +17,8 @@ jobs:
|
||||
s3-external-tests-minio:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
if: ${{ github.repository_owner != 'nextcloud-gmbh' }}
|
||||
|
||||
strategy:
|
||||
# do not stop on another job's failure
|
||||
fail-fast: false
|
||||
@@ -68,6 +70,8 @@ jobs:
|
||||
s3-external-tests-localstack:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
if: ${{ github.repository_owner != 'nextcloud-gmbh' }}
|
||||
|
||||
strategy:
|
||||
# do not stop on another job's failure
|
||||
fail-fast: false
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
name: S3 primary storage integration tests
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- stable*
|
||||
|
||||
jobs:
|
||||
s3-primary-integration-tests-minio:
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
strategy:
|
||||
# do not stop on another job's failure
|
||||
fail-fast: false
|
||||
matrix:
|
||||
php-versions: ['8.0']
|
||||
key: ['objectstore', 'objectstore_multibucket']
|
||||
|
||||
name: php${{ matrix.php-versions }}-${{ matrix.key }}-minio
|
||||
|
||||
services:
|
||||
redis:
|
||||
image: redis
|
||||
ports:
|
||||
- "6379:6379"
|
||||
minio:
|
||||
env:
|
||||
MINIO_ACCESS_KEY: minio
|
||||
MINIO_SECRET_KEY: minio123
|
||||
image: bitnami/minio:2021.12.29
|
||||
ports:
|
||||
- "9000:9000"
|
||||
|
||||
steps:
|
||||
- name: Checkout server
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Set up php ${{ matrix.php-versions }}
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-versions }}
|
||||
tools: phpunit:9
|
||||
extensions: mbstring, fileinfo, intl, sqlite, pdo_sqlite, zip, gd, redis
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Wait for S3
|
||||
run: |
|
||||
sleep 10
|
||||
curl -f -m 1 --retry-connrefused --retry 10 --retry-delay 10 http://localhost:9000/minio/health/ready
|
||||
|
||||
- name: Set up Nextcloud
|
||||
run: |
|
||||
mkdir data
|
||||
echo '<?php $CONFIG=["${{ matrix.key }}" => ["class" => "OC\Files\ObjectStore\S3", "arguments" => ["bucket" => "nextcloud", "autocreate" => true, "key" => "minio", "secret" => "minio123", "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
|
||||
./occ maintenance:install --verbose --database=sqlite --database-name=nextcloud --database-host=127.0.0.1 --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass admin
|
||||
php -f index.php
|
||||
|
||||
- name: Integration
|
||||
run: |
|
||||
cd build/integration
|
||||
bash run.sh --tags "~@failure-s3" features/webdav-related.feature
|
||||
|
||||
- name: S3 logs
|
||||
if: always()
|
||||
run: |
|
||||
cat data/nextcloud.log
|
||||
docker ps -a
|
||||
docker ps -aq | while read container ; do IMAGE=$(docker inspect --format='{{.Config.Image}}' $container); echo $IMAGE; docker logs $container; echo "\n\n" ; done
|
||||
|
||||
|
||||
s3-primary-integration-summary:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [s3-primary-integration-tests-minio]
|
||||
|
||||
if: always()
|
||||
|
||||
steps:
|
||||
- name: Summary status
|
||||
run: if ${{ needs.s3-primary-integration-tests-minio.result != 'success' }}; then exit 1; fi
|
||||
@@ -10,6 +10,8 @@ jobs:
|
||||
s3-primary-tests-minio:
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
if: ${{ github.repository_owner != 'nextcloud-gmbh' }}
|
||||
|
||||
strategy:
|
||||
# do not stop on another job's failure
|
||||
fail-fast: false
|
||||
|
||||
@@ -16,6 +16,8 @@ jobs:
|
||||
smb-kerberos-tests:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
if: ${{ github.repository_owner != 'nextcloud-gmbh' }}
|
||||
|
||||
name: smb-kerberos-sso
|
||||
|
||||
steps:
|
||||
|
||||
@@ -7,12 +7,13 @@ on:
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
|
||||
if: ${{ github.repository_owner != 'nextcloud-gmbh' }}
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
|
||||
|
||||
steps:
|
||||
- uses: actions/stale@v5
|
||||
with:
|
||||
@@ -29,4 +30,4 @@ jobs:
|
||||
days-before-stale: 30
|
||||
days-before-close: 14
|
||||
# debug-only: true
|
||||
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ jobs:
|
||||
static-code-analysis:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
if: ${{ github.repository_owner != 'nextcloud-gmbh' }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
@@ -9,6 +9,8 @@ jobs:
|
||||
update-psalm-baseline:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
if: ${{ github.repository_owner != 'nextcloud-gmbh' }}
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
|
||||
@@ -41,11 +41,12 @@
|
||||
|
||||
# Add cache control for static resources
|
||||
<FilesMatch "\.(css|js|svg|gif|png|jpg|ico|wasm|tflite)$">
|
||||
Header set Cache-Control "max-age=15778463"
|
||||
</FilesMatch>
|
||||
|
||||
<FilesMatch "\.(css|js|svg|gif|png|jpg|ico|wasm|tflite)(\?v=.*)?$">
|
||||
Header set Cache-Control "max-age=15778463, immutable"
|
||||
<If "%{QUERY_STRING} =~ /(^|&)v=/">
|
||||
Header set Cache-Control "max-age=15778463, immutable"
|
||||
</If>
|
||||
<Else>
|
||||
Header set Cache-Control "max-age=15778463"
|
||||
</Else>
|
||||
</FilesMatch>
|
||||
|
||||
# Let browsers cache WOFF files for a week
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
OC.L10N.register(
|
||||
"admin_audit",
|
||||
{
|
||||
"Auditing / Logging" : "Աուդիտ / Տեղեկագրում",
|
||||
"Provides logging abilities for Nextcloud such as logging file accesses or otherwise sensitive actions." : "Nextcloud ֊ի համար տրամադրում է տեղեկագրման հնարավորություն՝ նիշքերի հասանելիության և զգայուն գործողություինների պահպանումը տեղեկամատյանում։"
|
||||
},
|
||||
"nplurals=2; plural=(n != 1);");
|
||||
@@ -0,0 +1,5 @@
|
||||
{ "translations": {
|
||||
"Auditing / Logging" : "Աուդիտ / Տեղեկագրում",
|
||||
"Provides logging abilities for Nextcloud such as logging file accesses or otherwise sensitive actions." : "Nextcloud ֊ի համար տրամադրում է տեղեկագրման հնարավորություն՝ նիշքերի հասանելիության և զգայուն գործողություինների պահպանումը տեղեկամատյանում։"
|
||||
},"pluralForm" :"nplurals=2; plural=(n != 1);"
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
OC.L10N.register(
|
||||
"admin_audit",
|
||||
{
|
||||
"Auditing / Logging" : "Ревизија / Евиденција"
|
||||
"Auditing / Logging" : "Ревизија / Евиденција",
|
||||
"Provides logging abilities for Nextcloud such as logging file accesses or otherwise sensitive actions." : "Обезбедува можности за евидентирање на Nextcloud, како што се евидентирање пристапи до датотеки или на друг начин чувствителни дејства."
|
||||
},
|
||||
"nplurals=2; plural=(n % 10 == 1 && n % 100 != 11) ? 0 : 1;");
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{ "translations": {
|
||||
"Auditing / Logging" : "Ревизија / Евиденција"
|
||||
"Auditing / Logging" : "Ревизија / Евиденција",
|
||||
"Provides logging abilities for Nextcloud such as logging file accesses or otherwise sensitive actions." : "Обезбедува можности за евидентирање на Nextcloud, како што се евидентирање пристапи до датотеки или на друг начин чувствителни дејства."
|
||||
},"pluralForm" :"nplurals=2; plural=(n % 10 == 1 && n % 100 != 11) ? 0 : 1;"
|
||||
}
|
||||
@@ -160,6 +160,19 @@ class Sharing extends Action {
|
||||
'id',
|
||||
]
|
||||
);
|
||||
} elseif ($params['shareType'] === IShare::TYPE_SCIENCEMESH) {
|
||||
$this->log(
|
||||
'The %s "%s" with ID "%s" has been shared to the sciencemesh user "%s" with permissions "%s" (Share ID: %s)',
|
||||
$params,
|
||||
[
|
||||
'itemType',
|
||||
'path',
|
||||
'itemSource',
|
||||
'shareWith',
|
||||
'permissions',
|
||||
'id',
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,6 +289,18 @@ class Sharing extends Action {
|
||||
'id',
|
||||
]
|
||||
);
|
||||
} elseif ($params['shareType'] === IShare::TYPE_SCIENCEMESH) {
|
||||
$this->log(
|
||||
'The %s "%s" with ID "%s" has been unshared from the sciencemesh user "%s" (Share ID: %s)',
|
||||
$params,
|
||||
[
|
||||
'itemType',
|
||||
'fileTarget',
|
||||
'itemSource',
|
||||
'shareWith',
|
||||
'id',
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ OC.L10N.register(
|
||||
"cloud_federation_api",
|
||||
{
|
||||
"Cloud Federation API" : "API pro federovaný cloud",
|
||||
"Enable clouds to communicate with each other and exchange data" : "Umožňuje cloudům navzájem komunikovat a vyměňovat si data",
|
||||
"Enable clouds to communicate with each other and exchange data" : "Umožňuje cloudům vzájemně komunikovat a vyměňovat si data",
|
||||
"The Cloud Federation API enables various Nextcloud instances to communicate with each other and to exchange data." : "API pro federovaný cloud umožňuje různým instancím Nextcloud vzájemně komunikovat a vyměňovat si data."
|
||||
},
|
||||
"nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{ "translations": {
|
||||
"Cloud Federation API" : "API pro federovaný cloud",
|
||||
"Enable clouds to communicate with each other and exchange data" : "Umožňuje cloudům navzájem komunikovat a vyměňovat si data",
|
||||
"Enable clouds to communicate with each other and exchange data" : "Umožňuje cloudům vzájemně komunikovat a vyměňovat si data",
|
||||
"The Cloud Federation API enables various Nextcloud instances to communicate with each other and to exchange data." : "API pro federovaný cloud umožňuje různým instancím Nextcloud vzájemně komunikovat a vyměňovat si data."
|
||||
},"pluralForm" :"nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;"
|
||||
}
|
||||
@@ -9,6 +9,7 @@ OC.L10N.register(
|
||||
"%1$s commented on %2$s" : "%1$s كتب تعليق على %2$s",
|
||||
"{author} commented on {file}" : "{author} علّق على {file}",
|
||||
"<strong>Comments</strong> for files" : "<strong>تعليقات</strong> على الملفات",
|
||||
"You were mentioned on \"{file}\", in a comment by a user that has since been deleted" : "تمت الإشارة إليك في \"{file}\" في تعليق لمستخدم. لكن هذا المستخدم تم حذف حسابه بعدها",
|
||||
"{user} mentioned you in a comment on \"{file}\"" : "أشار إليك {user} في تعليق على {file}",
|
||||
"Files app plugin to add comments to files" : "المكوِّن الإضافي لتطبيق الملفات لإضافة تعليقات إلى الملفات",
|
||||
"Edit comment" : "تعديل التعليق",
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"%1$s commented on %2$s" : "%1$s كتب تعليق على %2$s",
|
||||
"{author} commented on {file}" : "{author} علّق على {file}",
|
||||
"<strong>Comments</strong> for files" : "<strong>تعليقات</strong> على الملفات",
|
||||
"You were mentioned on \"{file}\", in a comment by a user that has since been deleted" : "تمت الإشارة إليك في \"{file}\" في تعليق لمستخدم. لكن هذا المستخدم تم حذف حسابه بعدها",
|
||||
"{user} mentioned you in a comment on \"{file}\"" : "أشار إليك {user} في تعليق على {file}",
|
||||
"Files app plugin to add comments to files" : "المكوِّن الإضافي لتطبيق الملفات لإضافة تعليقات إلى الملفات",
|
||||
"Edit comment" : "تعديل التعليق",
|
||||
|
||||
@@ -1,33 +1,3 @@
|
||||
# Dashboard
|
||||
|
||||
|
||||
## Background picture credits
|
||||
|
||||
- Default background: [Clouds (Kamil Porembiński, CC BY-SA)](https://www.flickr.com/photos/paszczak000/8715851521/) – original 4k, color modified and sky color changed to Nextcloud blue.
|
||||
- Default dark mode background: [Pedra azul milky way (Eduardo Neves, CC BY-SA)](https://commons.wikimedia.org/wiki/File:Pedra_Azul_Milky_Way.jpg) – original 5k.
|
||||
- [Butterfly wing scale (Anatoly Mikhaltsov, CC BY-SA)](https://commons.wikimedia.org/wiki/File:%D0%A7%D0%B5%D1%88%D1%83%D0%B9%D0%BA%D0%B8_%D0%BA%D1%80%D1%8B%D0%BB%D0%B0_%D0%B1%D0%B0%D0%B1%D0%BE%D1%87%D0%BA%D0%B8.jpg) – original 5k, cropped to use top right and retouched away a bright spot, now 4k.
|
||||
- [Cetonia aurata take off composition (Bernie, Public Domain)](https://commons.wikimedia.org/wiki/File:Cetonia_aurata_take_off_composition_05172009.jpg) – original 8k.
|
||||
- [Ribbed red metal (Dejan Krsmanovic, CC BY)](https://www.flickr.com/photos/dejankrsmanovic/42971456774/) – original 5k.
|
||||
- [Barents bloom (European Space Agency, CC BY-SA)](https://www.esa.int/ESA_Multimedia/Images/2016/08/Barents_bloom) – original 2k (it’s fine since the motive is blurry anyway), rotated 90° right.
|
||||
- [Flippity floppity (Hannes Fritz, CC BY-SA)](http://hannes.photos/flippity-floppity) – original 4k, cropped to top left (2k) so the sharp parts are not part of the photo, looks better.
|
||||
- [Roulette (Hannes Fritz, CC BY-SA)](http://hannes.photos/roulette) – original 4k.
|
||||
- [Sea spray (Hannes Fritz, CC BY-SA)](http://hannes.photos/sea-spray) – original 6k.
|
||||
- [New zealand fern (Bernard Spragg, CC0)](https://commons.wikimedia.org/wiki/File:NZ_Fern.(Blechnum_chambersii)_(11263534936).jpg) – original 2.5k.
|
||||
- [Pink tapioca bubbles (Rawpixel, CC BY)](https://www.flickr.com/photos/byrawpixel/27665140298/in/photostream/) – original 6k.
|
||||
- [Waxing crescent moon (NASA, Public Domain)](https://www.nasa.gov/image-feature/a-waxing-crescent-moon)
|
||||
- [Cityscape (Tommy Chau, CC BY)](https://www.flickr.com/photos/90975693@N05/16910999368) – original 6k.
|
||||
- [Lion rock hill (Tommy Chau, CC BY)](https://www.flickr.com/photos/90975693@N05/17136440246) – original 6k.
|
||||
- [Yellow bricks (Lali Masriera, CC BY)](https://www.flickr.com/photos/visualpanic/3982464447) – original 4k, color modified for visibility of icons, and slightly cropped on the left so motive is centered.
|
||||
|
||||
|
||||
## Background picture requirements
|
||||
|
||||
A reference to why it was very difficult to actually find good background pictures – there are quite some requirements when it comes to picking:
|
||||
|
||||
- It needs to be an exceptionally good photo of course – since when chosen, people will see it every day.
|
||||
- We need to have a good balance of different motives, e.g. not too many landscape pics.
|
||||
- Same for a good balance of different colors.
|
||||
- The photo needs to work as a background. Photos with objects focused in the middle don’t really work as they will be overlapped by the widgets anyway.
|
||||
- Especially the top part cannot have too much differing contrast, as then it’s not possible to see the navigation icons.
|
||||
- We serve the pictures at 4k resolution and most of the selected images are also available in 6k or higher so it is future-proof.
|
||||
- For the search of course we had to limit to CC0, CC By and CC By-Sa. Only CC0 would have been practically impossible cause there’s just not so many good ones which fit.
|
||||
Background image documentation moved to theming folder at `apps/theming/README.md`.
|
||||
@@ -3,7 +3,7 @@ OC.L10N.register(
|
||||
{
|
||||
"Dashboard" : "Dasbor",
|
||||
"Dashboard app" : "Aplikasi dasbor",
|
||||
"Start your day informed\n\nThe Nextcloud Dashboard is your starting point of the day, giving you an\noverview of your upcoming appointments, urgent emails, chat messages,\nincoming tickets, latest tweets and much more! Users can add the widgets\nthey like and change the background to their liking." : "Mulai harimu sama berita\n\nDasbor Nextcloud memberi kamu ikhtisar janji temumu yang akan datang, email mendesak, pesan obrolan, tiket masuk, tweet terbaru, dan lebih banyak! Pengguna bisa tambahkan widget-widget yang mereka suka dan berubah mereka dapat mengubah latar belakang sesuai keinginan mereka.",
|
||||
"Start your day informed\n\nThe Nextcloud Dashboard is your starting point of the day, giving you an\noverview of your upcoming appointments, urgent emails, chat messages,\nincoming tickets, latest tweets and much more! Users can add the widgets\nthey like and change the background to their liking." : "Mulai hari Anda sama berita\n\nDasbor Nextcloud memberi Anda ikhtisar janji temu Anda yang akan datang, surel mendesak, pesan obrolan, tiket masuk, tweet terbaru, dan lebih banyak! Pengguna bisa tambahkan widget-widget yang mereka suka dan berubah mereka dapat mengubah latar belakang sesuai keinginan mereka.",
|
||||
"Customize" : "Sesuaikan",
|
||||
"Edit widgets" : "Edit widget",
|
||||
"Get more widgets from the App Store" : "Dapatkan lebih banyak widget dari App Store",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{ "translations": {
|
||||
"Dashboard" : "Dasbor",
|
||||
"Dashboard app" : "Aplikasi dasbor",
|
||||
"Start your day informed\n\nThe Nextcloud Dashboard is your starting point of the day, giving you an\noverview of your upcoming appointments, urgent emails, chat messages,\nincoming tickets, latest tweets and much more! Users can add the widgets\nthey like and change the background to their liking." : "Mulai harimu sama berita\n\nDasbor Nextcloud memberi kamu ikhtisar janji temumu yang akan datang, email mendesak, pesan obrolan, tiket masuk, tweet terbaru, dan lebih banyak! Pengguna bisa tambahkan widget-widget yang mereka suka dan berubah mereka dapat mengubah latar belakang sesuai keinginan mereka.",
|
||||
"Start your day informed\n\nThe Nextcloud Dashboard is your starting point of the day, giving you an\noverview of your upcoming appointments, urgent emails, chat messages,\nincoming tickets, latest tweets and much more! Users can add the widgets\nthey like and change the background to their liking." : "Mulai hari Anda sama berita\n\nDasbor Nextcloud memberi Anda ikhtisar janji temu Anda yang akan datang, surel mendesak, pesan obrolan, tiket masuk, tweet terbaru, dan lebih banyak! Pengguna bisa tambahkan widget-widget yang mereka suka dan berubah mereka dapat mengubah latar belakang sesuai keinginan mereka.",
|
||||
"Customize" : "Sesuaikan",
|
||||
"Edit widgets" : "Edit widget",
|
||||
"Get more widgets from the App Store" : "Dapatkan lebih banyak widget dari App Store",
|
||||
|
||||
@@ -310,8 +310,10 @@ return array(
|
||||
'OCA\\DAV\\Traits\\PrincipalProxyTrait' => $baseDir . '/../lib/Traits/PrincipalProxyTrait.php',
|
||||
'OCA\\DAV\\Upload\\AssemblyStream' => $baseDir . '/../lib/Upload/AssemblyStream.php',
|
||||
'OCA\\DAV\\Upload\\ChunkingPlugin' => $baseDir . '/../lib/Upload/ChunkingPlugin.php',
|
||||
'OCA\\DAV\\Upload\\ChunkingV2Plugin' => $baseDir . '/../lib/Upload/ChunkingV2Plugin.php',
|
||||
'OCA\\DAV\\Upload\\CleanupService' => $baseDir . '/../lib/Upload/CleanupService.php',
|
||||
'OCA\\DAV\\Upload\\FutureFile' => $baseDir . '/../lib/Upload/FutureFile.php',
|
||||
'OCA\\DAV\\Upload\\PartFile' => $baseDir . '/../lib/Upload/PartFile.php',
|
||||
'OCA\\DAV\\Upload\\RootCollection' => $baseDir . '/../lib/Upload/RootCollection.php',
|
||||
'OCA\\DAV\\Upload\\UploadFile' => $baseDir . '/../lib/Upload/UploadFile.php',
|
||||
'OCA\\DAV\\Upload\\UploadFolder' => $baseDir . '/../lib/Upload/UploadFolder.php',
|
||||
|
||||
@@ -325,8 +325,10 @@ class ComposerStaticInitDAV
|
||||
'OCA\\DAV\\Traits\\PrincipalProxyTrait' => __DIR__ . '/..' . '/../lib/Traits/PrincipalProxyTrait.php',
|
||||
'OCA\\DAV\\Upload\\AssemblyStream' => __DIR__ . '/..' . '/../lib/Upload/AssemblyStream.php',
|
||||
'OCA\\DAV\\Upload\\ChunkingPlugin' => __DIR__ . '/..' . '/../lib/Upload/ChunkingPlugin.php',
|
||||
'OCA\\DAV\\Upload\\ChunkingV2Plugin' => __DIR__ . '/..' . '/../lib/Upload/ChunkingV2Plugin.php',
|
||||
'OCA\\DAV\\Upload\\CleanupService' => __DIR__ . '/..' . '/../lib/Upload/CleanupService.php',
|
||||
'OCA\\DAV\\Upload\\FutureFile' => __DIR__ . '/..' . '/../lib/Upload/FutureFile.php',
|
||||
'OCA\\DAV\\Upload\\PartFile' => __DIR__ . '/..' . '/../lib/Upload/PartFile.php',
|
||||
'OCA\\DAV\\Upload\\RootCollection' => __DIR__ . '/..' . '/../lib/Upload/RootCollection.php',
|
||||
'OCA\\DAV\\Upload\\UploadFile' => __DIR__ . '/..' . '/../lib/Upload/UploadFile.php',
|
||||
'OCA\\DAV\\Upload\\UploadFolder' => __DIR__ . '/..' . '/../lib/Upload/UploadFolder.php',
|
||||
|
||||
@@ -72,8 +72,11 @@ OC.L10N.register(
|
||||
"Where: %s" : "On: %s",
|
||||
"%1$s via %2$s" : "%1$s mitjançant %2$s",
|
||||
"Cancelled: %1$s" : "Cancel·lat: %1$s",
|
||||
"\"%1$s\" has been canceled" : "\"%1$s\" s'ha cancel·lat",
|
||||
"Re: %1$s" : "Re: %1$s",
|
||||
"%1$s has responded to your invitation" : "%1$s ha respost a la teva invitació",
|
||||
"Invitation: %1$s" : "Invitació: %1$s",
|
||||
"%1$s would like to invite you to \"%2$s\"" : "%1$s vol convidar-vos a \"%2$s\"",
|
||||
"Organizer:" : "Organitzador:",
|
||||
"Attendees:" : "Assistents:",
|
||||
"Title:" : "Títol:",
|
||||
|
||||
@@ -70,8 +70,11 @@
|
||||
"Where: %s" : "On: %s",
|
||||
"%1$s via %2$s" : "%1$s mitjançant %2$s",
|
||||
"Cancelled: %1$s" : "Cancel·lat: %1$s",
|
||||
"\"%1$s\" has been canceled" : "\"%1$s\" s'ha cancel·lat",
|
||||
"Re: %1$s" : "Re: %1$s",
|
||||
"%1$s has responded to your invitation" : "%1$s ha respost a la teva invitació",
|
||||
"Invitation: %1$s" : "Invitació: %1$s",
|
||||
"%1$s would like to invite you to \"%2$s\"" : "%1$s vol convidar-vos a \"%2$s\"",
|
||||
"Organizer:" : "Organitzador:",
|
||||
"Attendees:" : "Assistents:",
|
||||
"Title:" : "Títol:",
|
||||
|
||||
+3
-1
@@ -75,6 +75,8 @@ OC.L10N.register(
|
||||
"\"%1$s\" has been canceled" : "„%1$s“ bylo zrušeno",
|
||||
"Re: %1$s" : "Odp.: %1$s",
|
||||
"%1$s has responded to your invitation" : "%1$s odpověděl(a) na vaši pozvánku",
|
||||
"Invitation updated: %1$s" : "Pozvánka aktualizována: %1$s",
|
||||
"%1$s updated the event \"%2$s\"" : "%1$s zaktualizoval(a) událost „%2$s",
|
||||
"Invitation: %1$s" : "Pozvánka: %1$s",
|
||||
"%1$s would like to invite you to \"%2$s\"" : "%1$s by vás ráda pozval(a) na „%2$s“",
|
||||
"Organizer:" : "Organizátor:",
|
||||
@@ -129,7 +131,7 @@ OC.L10N.register(
|
||||
"Could not rename part file assembled from chunks" : "Nedaří se přejmenovat částečný soubor složený ze shluků",
|
||||
"Failed to write file contents: %1$s" : "Nepodařilo se zapsat obsahy souborů: %1$s",
|
||||
"File not found: %1$s" : "Soubor nenalezen: %1$s",
|
||||
"System is in maintenance mode." : "Na systému právě probíhá údržba.",
|
||||
"System is in maintenance mode." : "Systém se právě nachází v režimu údržby.",
|
||||
"Upgrade needed" : "Je třeba přejít na novější verzi",
|
||||
"Your %s needs to be configured to use HTTPS in order to use CalDAV and CardDAV with iOS/macOS." : "Váš %s potřebuje být nastavený aby používal HTTPS, aby bylo možné používat CalDAV a CardDAV s iOS/macOS.",
|
||||
"Configures a CalDAV account" : "Nastaví CalDAV účet",
|
||||
|
||||
@@ -73,6 +73,8 @@
|
||||
"\"%1$s\" has been canceled" : "„%1$s“ bylo zrušeno",
|
||||
"Re: %1$s" : "Odp.: %1$s",
|
||||
"%1$s has responded to your invitation" : "%1$s odpověděl(a) na vaši pozvánku",
|
||||
"Invitation updated: %1$s" : "Pozvánka aktualizována: %1$s",
|
||||
"%1$s updated the event \"%2$s\"" : "%1$s zaktualizoval(a) událost „%2$s",
|
||||
"Invitation: %1$s" : "Pozvánka: %1$s",
|
||||
"%1$s would like to invite you to \"%2$s\"" : "%1$s by vás ráda pozval(a) na „%2$s“",
|
||||
"Organizer:" : "Organizátor:",
|
||||
@@ -127,7 +129,7 @@
|
||||
"Could not rename part file assembled from chunks" : "Nedaří se přejmenovat částečný soubor složený ze shluků",
|
||||
"Failed to write file contents: %1$s" : "Nepodařilo se zapsat obsahy souborů: %1$s",
|
||||
"File not found: %1$s" : "Soubor nenalezen: %1$s",
|
||||
"System is in maintenance mode." : "Na systému právě probíhá údržba.",
|
||||
"System is in maintenance mode." : "Systém se právě nachází v režimu údržby.",
|
||||
"Upgrade needed" : "Je třeba přejít na novější verzi",
|
||||
"Your %s needs to be configured to use HTTPS in order to use CalDAV and CardDAV with iOS/macOS." : "Váš %s potřebuje být nastavený aby používal HTTPS, aby bylo možné používat CalDAV a CardDAV s iOS/macOS.",
|
||||
"Configures a CalDAV account" : "Nastaví CalDAV účet",
|
||||
|
||||
@@ -72,9 +72,11 @@ OC.L10N.register(
|
||||
"Where: %s" : "Ort: %s",
|
||||
"%1$s via %2$s" : "%1$s über %2$s",
|
||||
"Cancelled: %1$s" : "Abgesagt: %1$s",
|
||||
"\"%1$s\" has been canceled" : "\"%1$s\" wurde abgebrochen",
|
||||
"\"%1$s\" has been canceled" : "\"%1$s“ wurde abgesagt.",
|
||||
"Re: %1$s" : "Re: %1$s",
|
||||
"%1$s has responded to your invitation" : "%1$s hat auf Ihre Einladung geantwortet",
|
||||
"Invitation updated: %1$s" : "Einladung aktualisiert: %1$s",
|
||||
"%1$s updated the event \"%2$s\"" : "%1$s hat die Veranstaltung \"%2$s\" aktualisiert",
|
||||
"Invitation: %1$s" : "Einladung: %1$s",
|
||||
"%1$s would like to invite you to \"%2$s\"" : "%1$s möchte Sie zu \"%2$s\" einladen",
|
||||
"Organizer:" : "Organisator:",
|
||||
|
||||
@@ -70,9 +70,11 @@
|
||||
"Where: %s" : "Ort: %s",
|
||||
"%1$s via %2$s" : "%1$s über %2$s",
|
||||
"Cancelled: %1$s" : "Abgesagt: %1$s",
|
||||
"\"%1$s\" has been canceled" : "\"%1$s\" wurde abgebrochen",
|
||||
"\"%1$s\" has been canceled" : "\"%1$s“ wurde abgesagt.",
|
||||
"Re: %1$s" : "Re: %1$s",
|
||||
"%1$s has responded to your invitation" : "%1$s hat auf Ihre Einladung geantwortet",
|
||||
"Invitation updated: %1$s" : "Einladung aktualisiert: %1$s",
|
||||
"%1$s updated the event \"%2$s\"" : "%1$s hat die Veranstaltung \"%2$s\" aktualisiert",
|
||||
"Invitation: %1$s" : "Einladung: %1$s",
|
||||
"%1$s would like to invite you to \"%2$s\"" : "%1$s möchte Sie zu \"%2$s\" einladen",
|
||||
"Organizer:" : "Organisator:",
|
||||
|
||||
@@ -75,6 +75,8 @@ OC.L10N.register(
|
||||
"\"%1$s\" has been canceled" : "\"%1$s\" has been cancelled",
|
||||
"Re: %1$s" : "Re: %1$s",
|
||||
"%1$s has responded to your invitation" : "%1$s has responded to your invitation",
|
||||
"Invitation updated: %1$s" : "Invitation updated: %1$s",
|
||||
"%1$s updated the event \"%2$s\"" : "%1$s updated the event \"%2$s\"",
|
||||
"Invitation: %1$s" : "Invitation: %1$s",
|
||||
"%1$s would like to invite you to \"%2$s\"" : "%1$s would like to invite you to \"%2$s\"",
|
||||
"Organizer:" : "Organiser:",
|
||||
|
||||
@@ -73,6 +73,8 @@
|
||||
"\"%1$s\" has been canceled" : "\"%1$s\" has been cancelled",
|
||||
"Re: %1$s" : "Re: %1$s",
|
||||
"%1$s has responded to your invitation" : "%1$s has responded to your invitation",
|
||||
"Invitation updated: %1$s" : "Invitation updated: %1$s",
|
||||
"%1$s updated the event \"%2$s\"" : "%1$s updated the event \"%2$s\"",
|
||||
"Invitation: %1$s" : "Invitation: %1$s",
|
||||
"%1$s would like to invite you to \"%2$s\"" : "%1$s would like to invite you to \"%2$s\"",
|
||||
"Organizer:" : "Organiser:",
|
||||
|
||||
+3
-1
@@ -75,6 +75,8 @@ OC.L10N.register(
|
||||
"\"%1$s\" has been canceled" : "\"%1$s\" a été annulé(e)",
|
||||
"Re: %1$s" : "Re : %1$s",
|
||||
"%1$s has responded to your invitation" : "%1$s a répondu à votre invitation",
|
||||
"Invitation updated: %1$s" : "Invitation mise à jour : %1$s",
|
||||
"%1$s updated the event \"%2$s\"" : "%1$s a mis à jour l'évènement %2$s",
|
||||
"Invitation: %1$s" : "Invitation : %1$s",
|
||||
"%1$s would like to invite you to \"%2$s\"" : "%1$s souhaite vous inviter à \"%2$s\"",
|
||||
"Organizer:" : "Organisateur :",
|
||||
@@ -85,7 +87,7 @@ OC.L10N.register(
|
||||
"Link:" : "Lien :",
|
||||
"Accept" : "Accepter",
|
||||
"Decline" : "Refuser",
|
||||
"More options …" : "Plus d'options …",
|
||||
"More options …" : "Plus d'options…",
|
||||
"More options at %s" : "Plus d'options à %s",
|
||||
"Contacts" : "Contacts",
|
||||
"{actor} created address book {addressbook}" : "{actor} a créé le carnet d'adresses {addressbook}",
|
||||
|
||||
@@ -73,6 +73,8 @@
|
||||
"\"%1$s\" has been canceled" : "\"%1$s\" a été annulé(e)",
|
||||
"Re: %1$s" : "Re : %1$s",
|
||||
"%1$s has responded to your invitation" : "%1$s a répondu à votre invitation",
|
||||
"Invitation updated: %1$s" : "Invitation mise à jour : %1$s",
|
||||
"%1$s updated the event \"%2$s\"" : "%1$s a mis à jour l'évènement %2$s",
|
||||
"Invitation: %1$s" : "Invitation : %1$s",
|
||||
"%1$s would like to invite you to \"%2$s\"" : "%1$s souhaite vous inviter à \"%2$s\"",
|
||||
"Organizer:" : "Organisateur :",
|
||||
@@ -83,7 +85,7 @@
|
||||
"Link:" : "Lien :",
|
||||
"Accept" : "Accepter",
|
||||
"Decline" : "Refuser",
|
||||
"More options …" : "Plus d'options …",
|
||||
"More options …" : "Plus d'options…",
|
||||
"More options at %s" : "Plus d'options à %s",
|
||||
"Contacts" : "Contacts",
|
||||
"{actor} created address book {addressbook}" : "{actor} a créé le carnet d'adresses {addressbook}",
|
||||
|
||||
+1
-1
@@ -173,7 +173,7 @@ OC.L10N.register(
|
||||
"Notifications are sent via background jobs, so these must occur often enough." : "As notificacións enviaranse mediante procesos en segundo plano, polo que estes teñen que suceder con frecuencia.",
|
||||
"Send reminder notifications to calendar sharees as well" : "Envía notificacións de recordatorio tamén aos que comparten calendario",
|
||||
"Reminders are always sent to organizers and attendees." : "Os recordatorios sempre se envían aos organizadores e aos asistentes.",
|
||||
"Enable notifications for events via push" : "Activar o envío de notificacións do servidor para eventos",
|
||||
"Enable notifications for events via push" : "Activar o envío de notificacións do automáticas para eventos",
|
||||
"Also install the {calendarappstoreopen}Calendar app{linkclose}, or {calendardocopen}connect your desktop & mobile for syncing ↗{linkclose}." : "Instale tamén a {calendarappstoreopen}aplicación do Calendario{linkclose} ou {calendardocopen}conecte os seus escritorio e móbil para sincronizalos ↗{linkclose}.",
|
||||
"Please make sure to properly set up {emailopen}the email server{linkclose}." : "Asegúrese de ter configurado correctamente {emailopen}o servidor de correo-e{linkclose}.",
|
||||
"There was an error updating your attendance status." : "Produciuse un erro ao actualizar o seu estado de asistencia.",
|
||||
|
||||
@@ -171,7 +171,7 @@
|
||||
"Notifications are sent via background jobs, so these must occur often enough." : "As notificacións enviaranse mediante procesos en segundo plano, polo que estes teñen que suceder con frecuencia.",
|
||||
"Send reminder notifications to calendar sharees as well" : "Envía notificacións de recordatorio tamén aos que comparten calendario",
|
||||
"Reminders are always sent to organizers and attendees." : "Os recordatorios sempre se envían aos organizadores e aos asistentes.",
|
||||
"Enable notifications for events via push" : "Activar o envío de notificacións do servidor para eventos",
|
||||
"Enable notifications for events via push" : "Activar o envío de notificacións do automáticas para eventos",
|
||||
"Also install the {calendarappstoreopen}Calendar app{linkclose}, or {calendardocopen}connect your desktop & mobile for syncing ↗{linkclose}." : "Instale tamén a {calendarappstoreopen}aplicación do Calendario{linkclose} ou {calendardocopen}conecte os seus escritorio e móbil para sincronizalos ↗{linkclose}.",
|
||||
"Please make sure to properly set up {emailopen}the email server{linkclose}." : "Asegúrese de ter configurado correctamente {emailopen}o servidor de correo-e{linkclose}.",
|
||||
"There was an error updating your attendance status." : "Produciuse un erro ao actualizar o seu estado de asistencia.",
|
||||
|
||||
@@ -72,8 +72,13 @@ OC.L10N.register(
|
||||
"Where: %s" : "Где: %s",
|
||||
"%1$s via %2$s" : "%1$s через %2$s",
|
||||
"Cancelled: %1$s" : "Событие отменено: %1$s",
|
||||
"\"%1$s\" has been canceled" : "Событие «%1$s» отменено",
|
||||
"Re: %1$s" : "Re: %1$s",
|
||||
"%1$s has responded to your invitation" : "%1$s ответил(а) на ваше приглашение",
|
||||
"Invitation updated: %1$s" : "Изменение приглашения: %1$s",
|
||||
"%1$s updated the event \"%2$s\"" : "%1$s изменил(а) событие «%2$s»",
|
||||
"Invitation: %1$s" : "Приглашение: %1$s",
|
||||
"%1$s would like to invite you to \"%2$s\"" : "%1$s приглашает вас принять участие в событии «%2$s»",
|
||||
"Organizer:" : "Организатор:",
|
||||
"Attendees:" : "Участники:",
|
||||
"Title:" : "Название:",
|
||||
@@ -110,11 +115,20 @@ OC.L10N.register(
|
||||
"You updated contact {card} in address book {addressbook}" : "Вы изменили запись {card} в адресной книге {addressbook}",
|
||||
"A <strong>contact</strong> or <strong>address book</strong> was modified" : "Изменение <strong>контакта</strong> или <strong>адресной книги</strong>",
|
||||
"File is not updatable: %1$s" : "Файл не подлежит обновлению: %1$s",
|
||||
"Could not write to final file, canceled by hook" : "Не удалось записать результирующий файл, запись отменена вызовом обработчика",
|
||||
"Could not write file contents" : "Не удалось записать содержимое файла",
|
||||
"_%n byte_::_%n bytes_" : ["%n байт","%n байта","%n байт","%n байта"],
|
||||
"Error while copying file to target location (copied: %1$s, expected filesize: %2$s)" : "Ошибка при копировании в целевое расположение, скопировано: %1$s, ожидаемый размер файла: %2$s",
|
||||
"Expected filesize of %1$s but read (from Nextcloud client) and wrote (to Nextcloud storage) %2$s. Could either be a network problem on the sending side or a problem writing to the storage on the server side." : "Ожидаемый размер файла составляет %1$s, но из клиента приложения Nextcloud было прочитано и записано в хранилище %2$s. К этому могла привести ошибка при передаче данных на стороне отправителя либо проблема в подсистеме хранения данных на стороне сервера.",
|
||||
"Could not rename part file to final file, canceled by hook" : "Не удалось переименовать временный файл в результирующий, операция отменена вызовом обработчика",
|
||||
"Could not rename part file to final file" : "Не удалось переименовать временный файл в результирующий",
|
||||
"Failed to check file size: %1$s" : "Не удалось проверить размер файла: %1$s",
|
||||
"Could not open file" : "Не удалось открыть файл",
|
||||
"Encryption not ready: %1$s" : "Подсистема шифрования не готова: %1$s",
|
||||
"Failed to open file: %1$s" : "Не удалось открыть файл: %1$s",
|
||||
"Failed to unlink: %1$s" : "Не удалось разорвать связь: %1$s",
|
||||
"Invalid chunk name" : "Недопустимое имя сегмента",
|
||||
"Could not rename part file assembled from chunks" : "Не удалось переименовать временный файл, сформированный из сегментов",
|
||||
"Failed to write file contents: %1$s" : "Не удалось записать содержимое файла: %1$s",
|
||||
"File not found: %1$s" : "Файл не найден: %1$s",
|
||||
"System is in maintenance mode." : "Сервер находится в режиме обслуживания.",
|
||||
@@ -128,6 +142,7 @@ OC.L10N.register(
|
||||
"Completed on %s" : "Завершено %s",
|
||||
"Due on %s by %s" : "До %s %s",
|
||||
"Due on %s" : "До %s",
|
||||
"Migrated calendar (%1$s)" : "Перенос календаря (%1$s)",
|
||||
"Calendars including events, details and attendees" : "Календари, в том числе события, подробные сведения и участники",
|
||||
"Contacts and groups" : "Контакты и группы",
|
||||
"WebDAV" : "WebDAV",
|
||||
@@ -146,8 +161,11 @@ OC.L10N.register(
|
||||
"Friday" : "Пятница",
|
||||
"Saturday" : "Суббота",
|
||||
"Sunday" : "Воскресенье",
|
||||
"Automatically set user status to \"Do not disturb\" outside of availability to mute all notifications." : "Автоматически изменять статус на «Не беспокоить» вне интервала доступности для отключения уведомлений.",
|
||||
"Save" : "Сохранить",
|
||||
"Failed to load availability" : "Не удалось получить сведения о доступности",
|
||||
"Saved availability" : "Сведения о доступности сохранены",
|
||||
"Failed to save availability" : "Не удалось сохранить сведения о доступности",
|
||||
"Calendar server" : "Сервер календаря",
|
||||
"Send invitations to attendees" : "Отправить приглашения",
|
||||
"Automatically generate a birthday calendar" : "Создавать календарь дней рождения автоматически",
|
||||
@@ -155,6 +173,8 @@ OC.L10N.register(
|
||||
"Hence they will not be available immediately after enabling but will show up after some time." : "И поэтому они станут доступны не моментально, а через некоторое время.",
|
||||
"Send notifications for events" : "Отправлять уведомления о событиях",
|
||||
"Notifications are sent via background jobs, so these must occur often enough." : "Уведомления будут отправляться через фоновые задания, поэтому они должны выполняться достаточно часто.",
|
||||
"Send reminder notifications to calendar sharees as well" : "Отправлять напоминания всем пользователям, имеющим доступ к календарю",
|
||||
"Reminders are always sent to organizers and attendees." : "Организаторам и участникам уведомления отправляются во всех случаях.",
|
||||
"Enable notifications for events via push" : "Включить уведомления о событиях с помощью push",
|
||||
"Also install the {calendarappstoreopen}Calendar app{linkclose}, or {calendardocopen}connect your desktop & mobile for syncing ↗{linkclose}." : "Также установите {calendarappstoreopen}приложение Calendar{linkclose}, или {calendardocopen}подключите ваш ПК и мобильное устройство для синхронизации ↗{linkclose}.",
|
||||
"Please make sure to properly set up {emailopen}the email server{linkclose}." : "Проверьте правильность настройки {emailopen}почтового сервера{linkclose}.",
|
||||
|
||||
@@ -70,8 +70,13 @@
|
||||
"Where: %s" : "Где: %s",
|
||||
"%1$s via %2$s" : "%1$s через %2$s",
|
||||
"Cancelled: %1$s" : "Событие отменено: %1$s",
|
||||
"\"%1$s\" has been canceled" : "Событие «%1$s» отменено",
|
||||
"Re: %1$s" : "Re: %1$s",
|
||||
"%1$s has responded to your invitation" : "%1$s ответил(а) на ваше приглашение",
|
||||
"Invitation updated: %1$s" : "Изменение приглашения: %1$s",
|
||||
"%1$s updated the event \"%2$s\"" : "%1$s изменил(а) событие «%2$s»",
|
||||
"Invitation: %1$s" : "Приглашение: %1$s",
|
||||
"%1$s would like to invite you to \"%2$s\"" : "%1$s приглашает вас принять участие в событии «%2$s»",
|
||||
"Organizer:" : "Организатор:",
|
||||
"Attendees:" : "Участники:",
|
||||
"Title:" : "Название:",
|
||||
@@ -108,11 +113,20 @@
|
||||
"You updated contact {card} in address book {addressbook}" : "Вы изменили запись {card} в адресной книге {addressbook}",
|
||||
"A <strong>contact</strong> or <strong>address book</strong> was modified" : "Изменение <strong>контакта</strong> или <strong>адресной книги</strong>",
|
||||
"File is not updatable: %1$s" : "Файл не подлежит обновлению: %1$s",
|
||||
"Could not write to final file, canceled by hook" : "Не удалось записать результирующий файл, запись отменена вызовом обработчика",
|
||||
"Could not write file contents" : "Не удалось записать содержимое файла",
|
||||
"_%n byte_::_%n bytes_" : ["%n байт","%n байта","%n байт","%n байта"],
|
||||
"Error while copying file to target location (copied: %1$s, expected filesize: %2$s)" : "Ошибка при копировании в целевое расположение, скопировано: %1$s, ожидаемый размер файла: %2$s",
|
||||
"Expected filesize of %1$s but read (from Nextcloud client) and wrote (to Nextcloud storage) %2$s. Could either be a network problem on the sending side or a problem writing to the storage on the server side." : "Ожидаемый размер файла составляет %1$s, но из клиента приложения Nextcloud было прочитано и записано в хранилище %2$s. К этому могла привести ошибка при передаче данных на стороне отправителя либо проблема в подсистеме хранения данных на стороне сервера.",
|
||||
"Could not rename part file to final file, canceled by hook" : "Не удалось переименовать временный файл в результирующий, операция отменена вызовом обработчика",
|
||||
"Could not rename part file to final file" : "Не удалось переименовать временный файл в результирующий",
|
||||
"Failed to check file size: %1$s" : "Не удалось проверить размер файла: %1$s",
|
||||
"Could not open file" : "Не удалось открыть файл",
|
||||
"Encryption not ready: %1$s" : "Подсистема шифрования не готова: %1$s",
|
||||
"Failed to open file: %1$s" : "Не удалось открыть файл: %1$s",
|
||||
"Failed to unlink: %1$s" : "Не удалось разорвать связь: %1$s",
|
||||
"Invalid chunk name" : "Недопустимое имя сегмента",
|
||||
"Could not rename part file assembled from chunks" : "Не удалось переименовать временный файл, сформированный из сегментов",
|
||||
"Failed to write file contents: %1$s" : "Не удалось записать содержимое файла: %1$s",
|
||||
"File not found: %1$s" : "Файл не найден: %1$s",
|
||||
"System is in maintenance mode." : "Сервер находится в режиме обслуживания.",
|
||||
@@ -126,6 +140,7 @@
|
||||
"Completed on %s" : "Завершено %s",
|
||||
"Due on %s by %s" : "До %s %s",
|
||||
"Due on %s" : "До %s",
|
||||
"Migrated calendar (%1$s)" : "Перенос календаря (%1$s)",
|
||||
"Calendars including events, details and attendees" : "Календари, в том числе события, подробные сведения и участники",
|
||||
"Contacts and groups" : "Контакты и группы",
|
||||
"WebDAV" : "WebDAV",
|
||||
@@ -144,8 +159,11 @@
|
||||
"Friday" : "Пятница",
|
||||
"Saturday" : "Суббота",
|
||||
"Sunday" : "Воскресенье",
|
||||
"Automatically set user status to \"Do not disturb\" outside of availability to mute all notifications." : "Автоматически изменять статус на «Не беспокоить» вне интервала доступности для отключения уведомлений.",
|
||||
"Save" : "Сохранить",
|
||||
"Failed to load availability" : "Не удалось получить сведения о доступности",
|
||||
"Saved availability" : "Сведения о доступности сохранены",
|
||||
"Failed to save availability" : "Не удалось сохранить сведения о доступности",
|
||||
"Calendar server" : "Сервер календаря",
|
||||
"Send invitations to attendees" : "Отправить приглашения",
|
||||
"Automatically generate a birthday calendar" : "Создавать календарь дней рождения автоматически",
|
||||
@@ -153,6 +171,8 @@
|
||||
"Hence they will not be available immediately after enabling but will show up after some time." : "И поэтому они станут доступны не моментально, а через некоторое время.",
|
||||
"Send notifications for events" : "Отправлять уведомления о событиях",
|
||||
"Notifications are sent via background jobs, so these must occur often enough." : "Уведомления будут отправляться через фоновые задания, поэтому они должны выполняться достаточно часто.",
|
||||
"Send reminder notifications to calendar sharees as well" : "Отправлять напоминания всем пользователям, имеющим доступ к календарю",
|
||||
"Reminders are always sent to organizers and attendees." : "Организаторам и участникам уведомления отправляются во всех случаях.",
|
||||
"Enable notifications for events via push" : "Включить уведомления о событиях с помощью push",
|
||||
"Also install the {calendarappstoreopen}Calendar app{linkclose}, or {calendardocopen}connect your desktop & mobile for syncing ↗{linkclose}." : "Также установите {calendarappstoreopen}приложение Calendar{linkclose}, или {calendardocopen}подключите ваш ПК и мобильное устройство для синхронизации ↗{linkclose}.",
|
||||
"Please make sure to properly set up {emailopen}the email server{linkclose}." : "Проверьте правильность настройки {emailopen}почтового сервера{linkclose}.",
|
||||
|
||||
@@ -75,6 +75,8 @@ OC.L10N.register(
|
||||
"\"%1$s\" has been canceled" : "„%1$s” је отказано",
|
||||
"Re: %1$s" : "Одг: %1$s",
|
||||
"%1$s has responded to your invitation" : "%1$s је одговорио на вашу позивницу",
|
||||
"Invitation updated: %1$s" : "Позивница је ажурирана: %1$s",
|
||||
"%1$s updated the event \"%2$s\"" : "%1$s је ажурирао догађај „%2$s”",
|
||||
"Invitation: %1$s" : "Позивница: %1$s",
|
||||
"%1$s would like to invite you to \"%2$s\"" : "%1$s жели да вас позове на „%2$s",
|
||||
"Organizer:" : "Организатор:",
|
||||
|
||||
@@ -73,6 +73,8 @@
|
||||
"\"%1$s\" has been canceled" : "„%1$s” је отказано",
|
||||
"Re: %1$s" : "Одг: %1$s",
|
||||
"%1$s has responded to your invitation" : "%1$s је одговорио на вашу позивницу",
|
||||
"Invitation updated: %1$s" : "Позивница је ажурирана: %1$s",
|
||||
"%1$s updated the event \"%2$s\"" : "%1$s је ажурирао догађај „%2$s”",
|
||||
"Invitation: %1$s" : "Позивница: %1$s",
|
||||
"%1$s would like to invite you to \"%2$s\"" : "%1$s жели да вас позове на „%2$s",
|
||||
"Organizer:" : "Организатор:",
|
||||
|
||||
@@ -75,6 +75,8 @@ OC.L10N.register(
|
||||
"\"%1$s\" has been canceled" : "\"%1$s\" har avbrutits",
|
||||
"Re: %1$s" : "Sv: %1$s",
|
||||
"%1$s has responded to your invitation" : "%1$s har svarat på din inbjudan",
|
||||
"Invitation updated: %1$s" : "Inbjudan uppdaterad: %1$s",
|
||||
"%1$s updated the event \"%2$s\"" : "%1$s uppdaterade händelse \"%2$s\"",
|
||||
"Invitation: %1$s" : "Inbjudan: %1$s",
|
||||
"%1$s would like to invite you to \"%2$s\"" : "%1$s skulle vilja bjuda in dig till \"%2$s\"",
|
||||
"Organizer:" : "Arrangör:",
|
||||
|
||||
@@ -73,6 +73,8 @@
|
||||
"\"%1$s\" has been canceled" : "\"%1$s\" har avbrutits",
|
||||
"Re: %1$s" : "Sv: %1$s",
|
||||
"%1$s has responded to your invitation" : "%1$s har svarat på din inbjudan",
|
||||
"Invitation updated: %1$s" : "Inbjudan uppdaterad: %1$s",
|
||||
"%1$s updated the event \"%2$s\"" : "%1$s uppdaterade händelse \"%2$s\"",
|
||||
"Invitation: %1$s" : "Inbjudan: %1$s",
|
||||
"%1$s would like to invite you to \"%2$s\"" : "%1$s skulle vilja bjuda in dig till \"%2$s\"",
|
||||
"Organizer:" : "Arrangör:",
|
||||
|
||||
@@ -75,6 +75,8 @@ OC.L10N.register(
|
||||
"\"%1$s\" has been canceled" : "\"%1$s\" iptal edildi",
|
||||
"Re: %1$s" : "Ynt: %1$s",
|
||||
"%1$s has responded to your invitation" : "%1$s çağrınızı yanıtladı",
|
||||
"Invitation updated: %1$s" : "Çağrı güncellendi: %1$s",
|
||||
"%1$s updated the event \"%2$s\"" : "%1$s, \"%2$s\" etkinliğini güncelledi",
|
||||
"Invitation: %1$s" : "Çağrı: %1$s",
|
||||
"%1$s would like to invite you to \"%2$s\"" : "%1$s, size \"%2$s\" için çağrı gönderdi",
|
||||
"Organizer:" : "Düzenleyen:",
|
||||
|
||||
@@ -73,6 +73,8 @@
|
||||
"\"%1$s\" has been canceled" : "\"%1$s\" iptal edildi",
|
||||
"Re: %1$s" : "Ynt: %1$s",
|
||||
"%1$s has responded to your invitation" : "%1$s çağrınızı yanıtladı",
|
||||
"Invitation updated: %1$s" : "Çağrı güncellendi: %1$s",
|
||||
"%1$s updated the event \"%2$s\"" : "%1$s, \"%2$s\" etkinliğini güncelledi",
|
||||
"Invitation: %1$s" : "Çağrı: %1$s",
|
||||
"%1$s would like to invite you to \"%2$s\"" : "%1$s, size \"%2$s\" için çağrı gönderdi",
|
||||
"Organizer:" : "Düzenleyen:",
|
||||
|
||||
@@ -75,6 +75,8 @@ OC.L10N.register(
|
||||
"\"%1$s\" has been canceled" : "\"%1$s\" 已被取消",
|
||||
"Re: %1$s" : "關於: %1$s",
|
||||
"%1$s has responded to your invitation" : "%1$s 已回應您的邀請",
|
||||
"Invitation updated: %1$s" : "邀請已更新︰%1$s",
|
||||
"%1$s updated the event \"%2$s\"" : "%1$s 已更新了活動 \"%2$s\"",
|
||||
"Invitation: %1$s" : "邀請:%1$s",
|
||||
"%1$s would like to invite you to \"%2$s\"" : "%1$s 想邀請您加入“%2$s”",
|
||||
"Organizer:" : "主辦單位:",
|
||||
|
||||
@@ -73,6 +73,8 @@
|
||||
"\"%1$s\" has been canceled" : "\"%1$s\" 已被取消",
|
||||
"Re: %1$s" : "關於: %1$s",
|
||||
"%1$s has responded to your invitation" : "%1$s 已回應您的邀請",
|
||||
"Invitation updated: %1$s" : "邀請已更新︰%1$s",
|
||||
"%1$s updated the event \"%2$s\"" : "%1$s 已更新了活動 \"%2$s\"",
|
||||
"Invitation: %1$s" : "邀請:%1$s",
|
||||
"%1$s would like to invite you to \"%2$s\"" : "%1$s 想邀請您加入“%2$s”",
|
||||
"Organizer:" : "主辦單位:",
|
||||
|
||||
@@ -75,6 +75,8 @@ OC.L10N.register(
|
||||
"\"%1$s\" has been canceled" : "「%1$s」已取消",
|
||||
"Re: %1$s" : "回覆:%1$s",
|
||||
"%1$s has responded to your invitation" : "%1$s 已回應您的邀請",
|
||||
"Invitation updated: %1$s" : "邀請已更新:%1$s",
|
||||
"%1$s updated the event \"%2$s\"" : "%1$s 已更新事件:「%2$s」",
|
||||
"Invitation: %1$s" : "邀請:%1$s",
|
||||
"%1$s would like to invite you to \"%2$s\"" : "%1$s 想邀請您加入「%2$s」",
|
||||
"Organizer:" : "組織者:",
|
||||
|
||||
@@ -73,6 +73,8 @@
|
||||
"\"%1$s\" has been canceled" : "「%1$s」已取消",
|
||||
"Re: %1$s" : "回覆:%1$s",
|
||||
"%1$s has responded to your invitation" : "%1$s 已回應您的邀請",
|
||||
"Invitation updated: %1$s" : "邀請已更新:%1$s",
|
||||
"%1$s updated the event \"%2$s\"" : "%1$s 已更新事件:「%2$s」",
|
||||
"Invitation: %1$s" : "邀請:%1$s",
|
||||
"%1$s would like to invite you to \"%2$s\"" : "%1$s 想邀請您加入「%2$s」",
|
||||
"Organizer:" : "組織者:",
|
||||
|
||||
@@ -92,7 +92,7 @@ class UserStatusAutomation extends TimedJob {
|
||||
$isCurrentlyAvailable = false;
|
||||
$nextPotentialToggles = [];
|
||||
|
||||
$now = new \DateTime('now');
|
||||
$now = $this->time->getDateTime();
|
||||
$lastMidnight = (clone $now)->setTime(0, 0);
|
||||
|
||||
$vObject = Reader::read($property);
|
||||
@@ -105,9 +105,16 @@ class UserStatusAutomation extends TimedJob {
|
||||
foreach ($availables as $available) {
|
||||
/** @var Available $available */
|
||||
if ($available->name === 'AVAILABLE') {
|
||||
/** @var \DateTimeInterface $effectiveStart */
|
||||
/** @var \DateTimeInterface $effectiveEnd */
|
||||
[$effectiveStart, $effectiveEnd] = $available->getEffectiveStartEnd();
|
||||
/** @var \DateTimeImmutable $originalStart */
|
||||
/** @var \DateTimeImmutable $originalEnd */
|
||||
[$originalStart, $originalEnd] = $available->getEffectiveStartEnd();
|
||||
|
||||
// Little shenanigans to fix the automation on the day the rules were adjusted
|
||||
// Otherwise the $originalStart would match rules for Thursdays on a Friday, etc.
|
||||
// So we simply wind back a week and then fastForward to the next occurrence
|
||||
// since today's midnight, which then also accounts for the week days.
|
||||
$effectiveStart = \DateTime::createFromImmutable($originalStart)->sub(new \DateInterval('P7D'));
|
||||
$effectiveEnd = \DateTime::createFromImmutable($originalEnd)->sub(new \DateInterval('P7D'));
|
||||
|
||||
try {
|
||||
$it = new RRuleIterator((string) $available->RRULE, $effectiveStart);
|
||||
@@ -139,12 +146,21 @@ class UserStatusAutomation extends TimedJob {
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($nextPotentialToggles)) {
|
||||
$this->logger->info('Removing ' . self::class . ' background job for user "' . $userId . '" because the user has no valid availability rules set');
|
||||
$this->jobList->remove(self::class, $argument);
|
||||
$this->manager->revertUserStatus($userId, IUserStatus::MESSAGE_AVAILABILITY, IUserStatus::DND);
|
||||
return;
|
||||
}
|
||||
|
||||
$nextAutomaticToggle = min($nextPotentialToggles);
|
||||
$this->setLastRunToNextToggleTime($userId, $nextAutomaticToggle - 1);
|
||||
|
||||
if ($isCurrentlyAvailable) {
|
||||
$this->logger->debug('User is currently available, reverting DND status if applicable');
|
||||
$this->manager->revertUserStatus($userId, IUserStatus::MESSAGE_AVAILABILITY, IUserStatus::DND);
|
||||
} else {
|
||||
$this->logger->debug('User is currently NOT available, reverting call status if applicable and then setting DND');
|
||||
// The DND status automation is more important than the "Away - In call" so we also restore that one if it exists.
|
||||
$this->manager->revertUserStatus($userId, IUserStatus::MESSAGE_CALL, IUserStatus::AWAY);
|
||||
$this->manager->setUserStatus($userId, IUserStatus::MESSAGE_AVAILABILITY, IUserStatus::DND, true);
|
||||
|
||||
@@ -35,10 +35,10 @@ namespace OCA\DAV\Connector\Sabre;
|
||||
use OC\Files\Mount\MoveableMount;
|
||||
use OC\Files\View;
|
||||
use OC\Metadata\FileMetadata;
|
||||
use OC\Metadata\MetadataGroup;
|
||||
use OCA\DAV\Connector\Sabre\Exception\FileLocked;
|
||||
use OCA\DAV\Connector\Sabre\Exception\Forbidden;
|
||||
use OCA\DAV\Connector\Sabre\Exception\InvalidPath;
|
||||
use OCA\DAV\Upload\FutureFile;
|
||||
use OCP\Files\FileInfo;
|
||||
use OCP\Files\Folder;
|
||||
use OCP\Files\ForbiddenException;
|
||||
@@ -57,7 +57,6 @@ use Sabre\DAV\INode;
|
||||
use OCP\Share\IManager as IShareManager;
|
||||
|
||||
class Directory extends \OCA\DAV\Connector\Sabre\Node implements \Sabre\DAV\ICollection, \Sabre\DAV\IQuota, \Sabre\DAV\IMoveTarget, \Sabre\DAV\ICopyTarget {
|
||||
|
||||
/**
|
||||
* Cached directory content
|
||||
* @var \OCP\Files\FileInfo[]
|
||||
@@ -116,7 +115,6 @@ class Directory extends \OCA\DAV\Connector\Sabre\Node implements \Sabre\DAV\ICol
|
||||
// for chunked upload also updating a existing file is a "createFile"
|
||||
// because we create all the chunks before re-assemble them to the existing file.
|
||||
if (isset($_SERVER['HTTP_OC_CHUNKED'])) {
|
||||
|
||||
// exit if we can't create a new file and we don't updatable existing file
|
||||
$chunkInfo = \OC_FileChunking::decodeName($name);
|
||||
if (!$this->fileView->isCreatable($this->path) &&
|
||||
@@ -328,8 +326,14 @@ class Directory extends \OCA\DAV\Connector\Sabre\Node implements \Sabre\DAV\ICol
|
||||
if ($this->quotaInfo) {
|
||||
return $this->quotaInfo;
|
||||
}
|
||||
$relativePath = $this->fileView->getRelativePath($this->info->getPath());
|
||||
if ($relativePath === null) {
|
||||
$logger->warning("error while getting quota as the relative path cannot be found");
|
||||
return [0, 0];
|
||||
}
|
||||
|
||||
try {
|
||||
$storageInfo = \OC_Helper::getStorageInfo($this->info->getPath(), $this->info, false);
|
||||
$storageInfo = \OC_Helper::getStorageInfo($relativePath, $this->info, false);
|
||||
if ($storageInfo['quota'] === \OCP\Files\FileInfo::SPACE_UNLIMITED) {
|
||||
$free = \OCP\Files\FileInfo::SPACE_UNLIMITED;
|
||||
} else {
|
||||
|
||||
@@ -261,6 +261,10 @@ abstract class Node implements \Sabre\DAV\INode {
|
||||
return $this->info->getId();
|
||||
}
|
||||
|
||||
public function getInternalPath(): string {
|
||||
return $this->info->getInternalPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $user
|
||||
* @return int
|
||||
|
||||
@@ -110,6 +110,7 @@ class SharesPlugin extends \Sabre\DAV\ServerPlugin {
|
||||
IShare::TYPE_ROOM,
|
||||
IShare::TYPE_CIRCLE,
|
||||
IShare::TYPE_DECK,
|
||||
IShare::TYPE_SCIENCEMESH,
|
||||
];
|
||||
foreach ($requestedShareTypes as $requestedShareType) {
|
||||
$shares = $this->shareManager->getSharesBy(
|
||||
|
||||
@@ -71,9 +71,11 @@ use OCA\DAV\Profiler\ProfilerPlugin;
|
||||
use OCA\DAV\Provisioning\Apple\AppleProvisioningPlugin;
|
||||
use OCA\DAV\SystemTag\SystemTagPlugin;
|
||||
use OCA\DAV\Upload\ChunkingPlugin;
|
||||
use OCA\DAV\Upload\ChunkingV2Plugin;
|
||||
use OCP\AppFramework\Http\Response;
|
||||
use OCP\Diagnostics\IEventLogger;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\ICacheFactory;
|
||||
use OCP\IRequest;
|
||||
use OCP\Profiler\IProfiler;
|
||||
use OCP\SabrePluginEvent;
|
||||
@@ -218,6 +220,7 @@ class Server {
|
||||
|
||||
$this->server->addPlugin(new CopyEtagHeaderPlugin());
|
||||
$this->server->addPlugin(new RequestIdHeaderPlugin(\OC::$server->get(IRequest::class)));
|
||||
$this->server->addPlugin(new ChunkingV2Plugin(\OCP\Server::get(ICacheFactory::class)));
|
||||
$this->server->addPlugin(new ChunkingPlugin());
|
||||
|
||||
// allow setup of additional plugins
|
||||
|
||||
@@ -0,0 +1,392 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* @copyright Copyright (c) 2021 Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @author Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\DAV\Upload;
|
||||
|
||||
use Exception;
|
||||
use InvalidArgumentException;
|
||||
use OC\Files\Filesystem;
|
||||
use OC\Files\ObjectStore\ObjectStoreStorage;
|
||||
use OC\Files\View;
|
||||
use OC_Hook;
|
||||
use OCA\DAV\Connector\Sabre\Directory;
|
||||
use OCA\DAV\Connector\Sabre\File;
|
||||
use OCP\Files\IMimeTypeDetector;
|
||||
use OCP\Files\IRootFolder;
|
||||
use OCP\Files\ObjectStore\IObjectStoreMultiPartUpload;
|
||||
use OCP\Files\Storage\IChunkedFileWrite;
|
||||
use OCP\Files\StorageInvalidException;
|
||||
use OCP\ICache;
|
||||
use OCP\ICacheFactory;
|
||||
use OCP\Lock\ILockingProvider;
|
||||
use Sabre\DAV\Exception\BadRequest;
|
||||
use Sabre\DAV\Exception\InsufficientStorage;
|
||||
use Sabre\DAV\Exception\NotFound;
|
||||
use Sabre\DAV\Exception\PreconditionFailed;
|
||||
use Sabre\DAV\ICollection;
|
||||
use Sabre\DAV\INode;
|
||||
use Sabre\DAV\Server;
|
||||
use Sabre\DAV\ServerPlugin;
|
||||
use Sabre\HTTP\RequestInterface;
|
||||
use Sabre\HTTP\ResponseInterface;
|
||||
use Sabre\Uri;
|
||||
|
||||
class ChunkingV2Plugin extends ServerPlugin {
|
||||
/** @var Server */
|
||||
private $server;
|
||||
/** @var UploadFolder */
|
||||
private $uploadFolder;
|
||||
/** @var ICache */
|
||||
private $cache;
|
||||
|
||||
private ?string $uploadId = null;
|
||||
private ?string $uploadPath = null;
|
||||
|
||||
private const TEMP_TARGET = '.target';
|
||||
|
||||
public const CACHE_KEY = 'chunking-v2';
|
||||
public const UPLOAD_TARGET_PATH = 'upload-target-path';
|
||||
public const UPLOAD_TARGET_ID = 'upload-target-id';
|
||||
public const UPLOAD_ID = 'upload-id';
|
||||
|
||||
private const DESTINATION_HEADER = 'Destination';
|
||||
|
||||
public function __construct(ICacheFactory $cacheFactory) {
|
||||
$this->cache = $cacheFactory->createDistributed(self::CACHE_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function initialize(Server $server) {
|
||||
$server->on('afterMethod:MKCOL', [$this, 'afterMkcol']);
|
||||
$server->on('beforeMethod:PUT', [$this, 'beforePut']);
|
||||
$server->on('beforeMethod:DELETE', [$this, 'beforeDelete']);
|
||||
$server->on('beforeMove', [$this, 'beforeMove'], 90);
|
||||
|
||||
$this->server = $server;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @param bool $createIfNotExists
|
||||
* @return FutureFile|UploadFile|ICollection|INode
|
||||
*/
|
||||
private function getUploadFile(string $path, bool $createIfNotExists = false) {
|
||||
try {
|
||||
$actualFile = $this->server->tree->getNodeForPath($path);
|
||||
// Only directly upload to the target file if it is on the same storage
|
||||
// There may be further potential to optimize here by also uploading
|
||||
// to other storages directly. This would require to also carefully pick
|
||||
// the storage/path used in getStorage()
|
||||
if ($actualFile instanceof File && $this->uploadFolder->getStorage()->getId() === $actualFile->getNode()->getStorage()->getId()) {
|
||||
return $actualFile;
|
||||
}
|
||||
} catch (NotFound $e) {
|
||||
// If there is no target file we upload to the upload folder first
|
||||
}
|
||||
|
||||
// Use file in the upload directory that will be copied or moved afterwards
|
||||
if ($createIfNotExists) {
|
||||
$this->uploadFolder->createFile(self::TEMP_TARGET);
|
||||
}
|
||||
|
||||
/** @var UploadFile $uploadFile */
|
||||
$uploadFile = $this->uploadFolder->getChild(self::TEMP_TARGET);
|
||||
return $uploadFile->getFile();
|
||||
}
|
||||
|
||||
public function afterMkcol(RequestInterface $request, ResponseInterface $response): bool {
|
||||
try {
|
||||
$this->prepareUpload($request->getPath());
|
||||
$this->checkPrerequisites(false);
|
||||
} catch (BadRequest|StorageInvalidException|NotFound $e) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->uploadPath = $this->server->calculateUri($this->server->httpRequest->getHeader(self::DESTINATION_HEADER));
|
||||
$targetFile = $this->getUploadFile($this->uploadPath, true);
|
||||
[$storage, $storagePath] = $this->getUploadStorage($this->uploadPath);
|
||||
|
||||
$this->uploadId = $storage->startChunkedWrite($storagePath);
|
||||
|
||||
$this->cache->set($this->uploadFolder->getName(), [
|
||||
self::UPLOAD_ID => $this->uploadId,
|
||||
self::UPLOAD_TARGET_PATH => $this->uploadPath,
|
||||
self::UPLOAD_TARGET_ID => $targetFile->getId(),
|
||||
], 86400);
|
||||
|
||||
$response->setStatus(201);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function beforePut(RequestInterface $request, ResponseInterface $response): bool {
|
||||
try {
|
||||
$this->prepareUpload(dirname($request->getPath()));
|
||||
$this->checkPrerequisites();
|
||||
} catch (StorageInvalidException|BadRequest|NotFound $e) {
|
||||
return true;
|
||||
}
|
||||
|
||||
[$storage, $storagePath] = $this->getUploadStorage($this->uploadPath);
|
||||
|
||||
$chunkName = basename($request->getPath());
|
||||
$partId = is_numeric($chunkName) ? (int)$chunkName : -1;
|
||||
if (!($partId >= 1 && $partId <= 10000)) {
|
||||
throw new BadRequest('Invalid chunk name, must be numeric between 1 and 10000');
|
||||
}
|
||||
|
||||
$uploadFile = $this->getUploadFile($this->uploadPath);
|
||||
$tempTargetFile = null;
|
||||
|
||||
$additionalSize = (int)$request->getHeader('Content-Length');
|
||||
if ($this->uploadFolder->childExists(self::TEMP_TARGET) && $this->uploadPath) {
|
||||
/** @var UploadFile $tempTargetFile */
|
||||
$tempTargetFile = $this->uploadFolder->getChild(self::TEMP_TARGET);
|
||||
[$destinationDir, $destinationName] = Uri\split($this->uploadPath);
|
||||
/** @var Directory $destinationParent */
|
||||
$destinationParent = $this->server->tree->getNodeForPath($destinationDir);
|
||||
$free = $storage->free_space($destinationParent->getInternalPath());
|
||||
$newSize = $tempTargetFile->getSize() + $additionalSize;
|
||||
if ($free >= 0 && ($tempTargetFile->getSize() > $free || $newSize > $free)) {
|
||||
throw new InsufficientStorage("Insufficient space in $this->uploadPath");
|
||||
}
|
||||
}
|
||||
|
||||
$stream = $request->getBodyAsStream();
|
||||
$storage->putChunkedWritePart($storagePath, $this->uploadId, (string)$partId, $stream, $additionalSize);
|
||||
|
||||
$storage->getCache()->update($uploadFile->getId(), ['size' => $uploadFile->getSize() + $additionalSize]);
|
||||
if ($tempTargetFile) {
|
||||
$storage->getPropagator()->propagateChange($tempTargetFile->getInternalPath(), time(), $additionalSize);
|
||||
}
|
||||
|
||||
$response->setStatus(201);
|
||||
return false;
|
||||
}
|
||||
|
||||
public function beforeMove($sourcePath, $destination): bool {
|
||||
try {
|
||||
$this->prepareUpload(dirname($sourcePath));
|
||||
$this->checkPrerequisites();
|
||||
} catch (StorageInvalidException|BadRequest|NotFound|PreconditionFailed $e) {
|
||||
return true;
|
||||
}
|
||||
[$storage, $storagePath] = $this->getUploadStorage($this->uploadPath);
|
||||
|
||||
$targetFile = $this->getUploadFile($this->uploadPath);
|
||||
|
||||
[$destinationDir, $destinationName] = Uri\split($destination);
|
||||
/** @var Directory $destinationParent */
|
||||
$destinationParent = $this->server->tree->getNodeForPath($destinationDir);
|
||||
$destinationExists = $destinationParent->childExists($destinationName);
|
||||
|
||||
|
||||
// allow sync clients to send the modification and creation time along in a header
|
||||
$updateFileInfo = [];
|
||||
if ($this->server->httpRequest->getHeader('X-OC-MTime') !== null) {
|
||||
$updateFileInfo['mtime'] = $this->sanitizeMtime($this->server->httpRequest->getHeader('X-OC-MTime'));
|
||||
$this->server->httpResponse->setHeader('X-OC-MTime', 'accepted');
|
||||
}
|
||||
if ($this->server->httpRequest->getHeader('X-OC-CTime') !== null) {
|
||||
$updateFileInfo['creation_time'] = $this->sanitizeMtime($this->server->httpRequest->getHeader('X-OC-CTime'));
|
||||
$this->server->httpResponse->setHeader('X-OC-CTime', 'accepted');
|
||||
}
|
||||
$updateFileInfo['mimetype'] = \OCP\Server::get(IMimeTypeDetector::class)->detectPath($destinationName);
|
||||
|
||||
if ($storage->instanceOfStorage(ObjectStoreStorage::class) && $storage->getObjectStore() instanceof IObjectStoreMultiPartUpload) {
|
||||
/** @var ObjectStoreStorage $storage */
|
||||
/** @var IObjectStoreMultiPartUpload $objectStore */
|
||||
$objectStore = $storage->getObjectStore();
|
||||
$parts = $objectStore->getMultipartUploads($storage->getURN($targetFile->getId()), $this->uploadId);
|
||||
$size = 0;
|
||||
foreach ($parts as $part) {
|
||||
$size += $part['Size'];
|
||||
}
|
||||
$free = $storage->free_space($destinationParent->getInternalPath());
|
||||
if ($free >= 0 && ($size > $free)) {
|
||||
throw new InsufficientStorage("Insufficient space in $this->uploadPath");
|
||||
}
|
||||
}
|
||||
|
||||
$destinationInView = $destinationParent->getFileInfo()->getPath() . '/' . $destinationName;
|
||||
$this->completeChunkedWrite($destinationInView);
|
||||
|
||||
$rootView = new View();
|
||||
$rootView->putFileInfo($destinationInView, $updateFileInfo);
|
||||
|
||||
$sourceNode = $this->server->tree->getNodeForPath($sourcePath);
|
||||
if ($sourceNode instanceof FutureFile) {
|
||||
$this->uploadFolder->delete();
|
||||
}
|
||||
|
||||
$this->server->emit('afterMove', [$sourcePath, $destination]);
|
||||
$this->server->emit('afterUnbind', [$sourcePath]);
|
||||
$this->server->emit('afterBind', [$destination]);
|
||||
|
||||
$response = $this->server->httpResponse;
|
||||
$response->setHeader('Content-Type', 'application/xml; charset=utf-8');
|
||||
$response->setHeader('Content-Length', '0');
|
||||
$response->setStatus($destinationExists ? 204 : 201);
|
||||
return false;
|
||||
}
|
||||
|
||||
public function beforeDelete(RequestInterface $request, ResponseInterface $response) {
|
||||
try {
|
||||
$this->prepareUpload($request->getPath());
|
||||
if (!$this->uploadFolder instanceof UploadFolder) {
|
||||
return true;
|
||||
}
|
||||
|
||||
[$storage, $storagePath] = $this->getUploadStorage($this->uploadPath);
|
||||
$storage->cancelChunkedWrite($storagePath, $this->uploadId);
|
||||
return true;
|
||||
} catch (NotFound $e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws BadRequest
|
||||
* @throws PreconditionFailed
|
||||
* @throws StorageInvalidException
|
||||
*/
|
||||
private function checkPrerequisites(bool $checkUploadMetadata = true): void {
|
||||
if (!$this->uploadFolder instanceof UploadFolder || empty($this->server->httpRequest->getHeader(self::DESTINATION_HEADER))) {
|
||||
throw new BadRequest('Skipping chunked file writing as the destination header was not passed');
|
||||
}
|
||||
if (!$this->uploadFolder->getStorage()->instanceOfStorage(IChunkedFileWrite::class)) {
|
||||
throw new StorageInvalidException('Storage does not support chunked file writing');
|
||||
}
|
||||
|
||||
if ($checkUploadMetadata) {
|
||||
if ($this->uploadId === null || $this->uploadPath === null) {
|
||||
throw new PreconditionFailed('Missing metadata for chunked upload');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array [IStorage, string]
|
||||
*/
|
||||
private function getUploadStorage(string $targetPath): array {
|
||||
$storage = $this->uploadFolder->getStorage();
|
||||
$targetFile = $this->getUploadFile($targetPath);
|
||||
return [$storage, $targetFile->getInternalPath()];
|
||||
}
|
||||
|
||||
protected function sanitizeMtime(string $mtimeFromRequest): int {
|
||||
if (!is_numeric($mtimeFromRequest)) {
|
||||
throw new InvalidArgumentException('X-OC-MTime header must be an integer (unix timestamp).');
|
||||
}
|
||||
|
||||
return (int)$mtimeFromRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws NotFound
|
||||
*/
|
||||
public function prepareUpload($path): void {
|
||||
$this->uploadFolder = $this->server->tree->getNodeForPath($path);
|
||||
$uploadMetadata = $this->cache->get($this->uploadFolder->getName());
|
||||
$this->uploadId = $uploadMetadata[self::UPLOAD_ID] ?? null;
|
||||
$this->uploadPath = $uploadMetadata[self::UPLOAD_TARGET_PATH] ?? null;
|
||||
}
|
||||
|
||||
private function completeChunkedWrite(string $targetAbsolutePath): void {
|
||||
$uploadFile = $this->getUploadFile($this->uploadPath)->getNode();
|
||||
[$storage, $storagePath] = $this->getUploadStorage($this->uploadPath);
|
||||
|
||||
$rootFolder = \OCP\Server::get(IRootFolder::class);
|
||||
$exists = $rootFolder->nodeExists($targetAbsolutePath);
|
||||
|
||||
$uploadFile->lock(ILockingProvider::LOCK_SHARED);
|
||||
$this->emitPreHooks($targetAbsolutePath, $exists);
|
||||
try {
|
||||
$uploadFile->changeLock(ILockingProvider::LOCK_EXCLUSIVE);
|
||||
$storage->completeChunkedWrite($storagePath, $this->uploadId);
|
||||
$uploadFile->changeLock(ILockingProvider::LOCK_SHARED);
|
||||
} catch (Exception $e) {
|
||||
$uploadFile->unlock(ILockingProvider::LOCK_EXCLUSIVE);
|
||||
throw $e;
|
||||
}
|
||||
|
||||
// If the file was not uploaded to the user storage directly we need to copy/move it
|
||||
try {
|
||||
$uploadFileAbsolutePath = Filesystem::getRoot() . $uploadFile->getPath();
|
||||
if ($uploadFileAbsolutePath !== $targetAbsolutePath) {
|
||||
$uploadFile = $rootFolder->get($uploadFile->getFileInfo()->getPath());
|
||||
if ($exists) {
|
||||
$uploadFile->copy($targetAbsolutePath);
|
||||
} else {
|
||||
$uploadFile->move($targetAbsolutePath);
|
||||
}
|
||||
}
|
||||
$this->emitPostHooks($targetAbsolutePath, $exists);
|
||||
} catch (Exception $e) {
|
||||
$uploadFile->unlock(ILockingProvider::LOCK_SHARED);
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
private function emitPreHooks(string $target, bool $exists): void {
|
||||
$hookPath = $this->getHookPath($target);
|
||||
if (!$exists) {
|
||||
OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_create, [
|
||||
Filesystem::signal_param_path => $hookPath,
|
||||
]);
|
||||
} else {
|
||||
OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_update, [
|
||||
Filesystem::signal_param_path => $hookPath,
|
||||
]);
|
||||
}
|
||||
OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_write, [
|
||||
Filesystem::signal_param_path => $hookPath,
|
||||
]);
|
||||
}
|
||||
|
||||
private function emitPostHooks(string $target, bool $exists): void {
|
||||
$hookPath = $this->getHookPath($target);
|
||||
if (!$exists) {
|
||||
OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_create, [
|
||||
Filesystem::signal_param_path => $hookPath,
|
||||
]);
|
||||
} else {
|
||||
OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_update, [
|
||||
Filesystem::signal_param_path => $hookPath,
|
||||
]);
|
||||
}
|
||||
OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_write, [
|
||||
Filesystem::signal_param_path => $hookPath,
|
||||
]);
|
||||
}
|
||||
|
||||
private function getHookPath(string $path): ?string {
|
||||
if (!Filesystem::getView()) {
|
||||
return $path;
|
||||
}
|
||||
return Filesystem::getView()->getRelativePath($path);
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,6 @@ use Sabre\DAV\IFile;
|
||||
* @package OCA\DAV\Upload
|
||||
*/
|
||||
class FutureFile implements \Sabre\DAV\IFile {
|
||||
|
||||
/** @var Directory */
|
||||
private $root;
|
||||
/** @var string */
|
||||
@@ -66,6 +65,10 @@ class FutureFile implements \Sabre\DAV\IFile {
|
||||
return AssemblyStream::wrap($nodes);
|
||||
}
|
||||
|
||||
public function getPath() {
|
||||
return $this->root->getFileInfo()->getInternalPath() . '/.file';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2016, ownCloud, Inc.
|
||||
*
|
||||
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
* @author Lukas Reschke <lukas@statuscode.ch>
|
||||
* @author Thomas Müller <thomas.mueller@tmit.eu>
|
||||
*
|
||||
* @license AGPL-3.0
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License, version 3,
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
namespace OCA\DAV\Upload;
|
||||
|
||||
use OCA\DAV\Connector\Sabre\Directory;
|
||||
use Sabre\DAV\Exception\Forbidden;
|
||||
use Sabre\DAV\IFile;
|
||||
|
||||
/**
|
||||
* This class represents an Upload part which is not present on the storage itself
|
||||
* but handled directly by external storage services like S3 with Multipart Upload
|
||||
*/
|
||||
class PartFile implements IFile {
|
||||
/** @var Directory */
|
||||
private $root;
|
||||
/** @var array */
|
||||
private $partInfo;
|
||||
|
||||
public function __construct(Directory $root, array $partInfo) {
|
||||
$this->root = $root;
|
||||
$this->partInfo = $partInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function put($data) {
|
||||
throw new Forbidden('Permission denied to put into this file');
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function get() {
|
||||
throw new Forbidden('Permission denied to get this file');
|
||||
}
|
||||
|
||||
public function getPath() {
|
||||
return $this->root->getFileInfo()->getInternalPath() . '/' . $this->partInfo['PartNumber'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getContentType() {
|
||||
return 'application/octet-stream';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getETag() {
|
||||
return $this->partInfo['ETag'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getSize() {
|
||||
return $this->partInfo['Size'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function delete() {
|
||||
$this->root->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getName() {
|
||||
return $this->partInfo['PartNumber'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function setName($name) {
|
||||
throw new Forbidden('Permission denied to rename this file');
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getLastModified() {
|
||||
return $this->partInfo['LastModified'];
|
||||
}
|
||||
}
|
||||
@@ -44,6 +44,10 @@ class UploadFile implements IFile {
|
||||
return $this->file->get();
|
||||
}
|
||||
|
||||
public function getId() {
|
||||
return $this->file->getId();
|
||||
}
|
||||
|
||||
public function getContentType() {
|
||||
return $this->file->getContentType();
|
||||
}
|
||||
@@ -75,4 +79,16 @@ class UploadFile implements IFile {
|
||||
public function getLastModified() {
|
||||
return $this->file->getLastModified();
|
||||
}
|
||||
|
||||
public function getInternalPath(): string {
|
||||
return $this->file->getInternalPath();
|
||||
}
|
||||
|
||||
public function getFile(): File {
|
||||
return $this->file;
|
||||
}
|
||||
|
||||
public function getNode() {
|
||||
return $this->file->getNode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,20 +24,25 @@
|
||||
*/
|
||||
namespace OCA\DAV\Upload;
|
||||
|
||||
use OC\Files\ObjectStore\ObjectStoreStorage;
|
||||
use OCA\DAV\Connector\Sabre\Directory;
|
||||
use OCP\Files\ObjectStore\IObjectStoreMultiPartUpload;
|
||||
use OCP\Files\Storage\IStorage;
|
||||
use Sabre\DAV\Exception\Forbidden;
|
||||
use Sabre\DAV\ICollection;
|
||||
|
||||
class UploadFolder implements ICollection {
|
||||
|
||||
/** @var Directory */
|
||||
private $node;
|
||||
/** @var CleanupService */
|
||||
private $cleanupService;
|
||||
/** @var IStorage */
|
||||
private $storage;
|
||||
|
||||
public function __construct(Directory $node, CleanupService $cleanupService) {
|
||||
public function __construct(Directory $node, CleanupService $cleanupService, IStorage $storage) {
|
||||
$this->node = $node;
|
||||
$this->cleanupService = $cleanupService;
|
||||
$this->storage = $storage;
|
||||
}
|
||||
|
||||
public function createFile($name, $data = null) {
|
||||
@@ -66,6 +71,23 @@ class UploadFolder implements ICollection {
|
||||
$children[] = new UploadFile($child);
|
||||
}
|
||||
|
||||
if ($this->storage->instanceOfStorage(ObjectStoreStorage::class)) {
|
||||
/** @var ObjectStoreStorage $storage */
|
||||
$objectStore = $this->storage->getObjectStore();
|
||||
if ($objectStore instanceof IObjectStoreMultiPartUpload) {
|
||||
$cache = \OC::$server->getMemCacheFactory()->createDistributed(ChunkingV2Plugin::CACHE_KEY);
|
||||
$uploadSession = $cache->get($this->getName());
|
||||
if ($uploadSession) {
|
||||
$uploadId = $uploadSession[ChunkingV2Plugin::UPLOAD_ID];
|
||||
$id = $uploadSession[ChunkingV2Plugin::UPLOAD_TARGET_ID];
|
||||
$parts = $objectStore->getMultipartUploads($this->storage->getURN($id), $uploadId);
|
||||
foreach ($parts as $part) {
|
||||
$children[] = new PartFile($this->node, $part);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $children;
|
||||
}
|
||||
|
||||
@@ -94,4 +116,8 @@ class UploadFolder implements ICollection {
|
||||
public function getLastModified() {
|
||||
return $this->node->getLastModified();
|
||||
}
|
||||
|
||||
public function getStorage() {
|
||||
return $this->storage;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ use Sabre\DAV\Exception\Forbidden;
|
||||
use Sabre\DAV\ICollection;
|
||||
|
||||
class UploadHome implements ICollection {
|
||||
|
||||
/** @var array */
|
||||
private $principalInfo;
|
||||
/** @var CleanupService */
|
||||
@@ -55,12 +54,12 @@ class UploadHome implements ICollection {
|
||||
}
|
||||
|
||||
public function getChild($name): UploadFolder {
|
||||
return new UploadFolder($this->impl()->getChild($name), $this->cleanupService);
|
||||
return new UploadFolder($this->impl()->getChild($name), $this->cleanupService, $this->getStorage());
|
||||
}
|
||||
|
||||
public function getChildren(): array {
|
||||
return array_map(function ($node) {
|
||||
return new UploadFolder($node, $this->cleanupService);
|
||||
return new UploadFolder($node, $this->cleanupService, $this->getStorage());
|
||||
}, $this->impl()->getChildren());
|
||||
}
|
||||
|
||||
@@ -89,14 +88,24 @@ class UploadHome implements ICollection {
|
||||
* @return Directory
|
||||
*/
|
||||
private function impl() {
|
||||
$view = $this->getView();
|
||||
$rootInfo = $view->getFileInfo('');
|
||||
return new Directory($view, $rootInfo);
|
||||
}
|
||||
|
||||
private function getView() {
|
||||
$rootView = new View();
|
||||
$user = \OC::$server->getUserSession()->getUser();
|
||||
Filesystem::initMountPoints($user->getUID());
|
||||
if (!$rootView->file_exists('/' . $user->getUID() . '/uploads')) {
|
||||
$rootView->mkdir('/' . $user->getUID() . '/uploads');
|
||||
}
|
||||
$view = new View('/' . $user->getUID() . '/uploads');
|
||||
$rootInfo = $view->getFileInfo('');
|
||||
return new Directory($view, $rootInfo);
|
||||
return new View('/' . $user->getUID() . '/uploads');
|
||||
}
|
||||
|
||||
private function getStorage() {
|
||||
$view = $this->getView();
|
||||
$storage = $view->getFileInfo('')->getStorage();
|
||||
return $storage;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @copyright Copyright (c) 2023 Joas Schilling <coding@schilljs.com>
|
||||
*
|
||||
* @author Joas Schilling <coding@schilljs.com>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\DAV\Tests\unit\BackgroundJob;
|
||||
|
||||
use OCA\DAV\BackgroundJob\UserStatusAutomation;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\BackgroundJob\IJobList;
|
||||
use OCP\IConfig;
|
||||
use OCP\UserStatus\IManager;
|
||||
use OCP\UserStatus\IUserStatus;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Test\TestCase;
|
||||
|
||||
/**
|
||||
* @group DB
|
||||
*/
|
||||
class UserStatusAutomationTest extends TestCase {
|
||||
|
||||
protected MockObject|ITimeFactory $time;
|
||||
protected MockObject|IJobList $jobList;
|
||||
protected MockObject|LoggerInterface $logger;
|
||||
protected MockObject|IManager $statusManager;
|
||||
protected MockObject|IConfig $config;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->time = $this->createMock(ITimeFactory::class);
|
||||
$this->jobList = $this->createMock(IJobList::class);
|
||||
$this->logger = $this->createMock(LoggerInterface::class);
|
||||
$this->statusManager = $this->createMock(IManager::class);
|
||||
$this->config = $this->createMock(IConfig::class);
|
||||
|
||||
}
|
||||
|
||||
protected function getAutomationMock(array $methods): MockObject|UserStatusAutomation {
|
||||
if (empty($methods)) {
|
||||
return new UserStatusAutomation(
|
||||
$this->time,
|
||||
\OC::$server->getDatabaseConnection(),
|
||||
$this->jobList,
|
||||
$this->logger,
|
||||
$this->statusManager,
|
||||
$this->config,
|
||||
);
|
||||
}
|
||||
|
||||
return $this->getMockBuilder(UserStatusAutomation::class)
|
||||
->setConstructorArgs([
|
||||
$this->time,
|
||||
\OC::$server->getDatabaseConnection(),
|
||||
$this->jobList,
|
||||
$this->logger,
|
||||
$this->statusManager,
|
||||
$this->config,
|
||||
])
|
||||
->setMethods($methods)
|
||||
->getMock();
|
||||
}
|
||||
|
||||
public function dataRun(): array {
|
||||
return [
|
||||
['20230217', '2023-02-24 10:49:36.613834', true],
|
||||
['20230224', '2023-02-24 10:49:36.613834', true],
|
||||
['20230217', '2023-02-24 13:58:24.479357', false],
|
||||
['20230224', '2023-02-24 13:58:24.479357', false],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataRun
|
||||
*/
|
||||
public function testRun(string $ruleDay, string $currentTime, bool $isAvailable): void {
|
||||
$this->config->method('getUserValue')
|
||||
->with('user', 'dav', 'user_status_automation', 'no')
|
||||
->willReturn('yes');
|
||||
|
||||
$this->time->method('getDateTime')
|
||||
->willReturn(new \DateTime($currentTime, new \DateTimeZone('UTC')));
|
||||
|
||||
$automation = $this->getAutomationMock(['getAvailabilityFromPropertiesTable']);
|
||||
$automation->method('getAvailabilityFromPropertiesTable')
|
||||
->with('user')
|
||||
->willReturn('BEGIN:VCALENDAR
|
||||
PRODID:Nextcloud DAV app
|
||||
BEGIN:VTIMEZONE
|
||||
TZID:Europe/Berlin
|
||||
BEGIN:STANDARD
|
||||
TZNAME:CET
|
||||
TZOFFSETFROM:+0200
|
||||
TZOFFSETTO:+0100
|
||||
DTSTART:19701025T030000
|
||||
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
|
||||
END:STANDARD
|
||||
BEGIN:DAYLIGHT
|
||||
TZNAME:CEST
|
||||
TZOFFSETFROM:+0100
|
||||
TZOFFSETTO:+0200
|
||||
DTSTART:19700329T020000
|
||||
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
|
||||
END:DAYLIGHT
|
||||
END:VTIMEZONE
|
||||
BEGIN:VAVAILABILITY
|
||||
BEGIN:AVAILABLE
|
||||
DTSTART;TZID=Europe/Berlin:' . $ruleDay . 'T090000
|
||||
DTEND;TZID=Europe/Berlin:' . $ruleDay . 'T170000
|
||||
UID:3e6feeec-8e00-4265-b822-b73174e8b39f
|
||||
RRULE:FREQ=WEEKLY;BYDAY=TH
|
||||
END:AVAILABLE
|
||||
BEGIN:AVAILABLE
|
||||
DTSTART;TZID=Europe/Berlin:' . $ruleDay . 'T090000
|
||||
DTEND;TZID=Europe/Berlin:' . $ruleDay . 'T120000
|
||||
UID:8a634e99-07cf-443b-b480-005a0e1db323
|
||||
RRULE:FREQ=WEEKLY;BYDAY=FR
|
||||
END:AVAILABLE
|
||||
END:VAVAILABILITY
|
||||
END:VCALENDAR');
|
||||
|
||||
if ($isAvailable) {
|
||||
$this->statusManager->expects($this->once())
|
||||
->method('revertUserStatus')
|
||||
->with('user', IUserStatus::MESSAGE_AVAILABILITY, IUserStatus::DND);
|
||||
} else {
|
||||
$this->statusManager->expects($this->once())
|
||||
->method('revertUserStatus')
|
||||
->with('user', IUserStatus::MESSAGE_CALL, IUserStatus::AWAY);
|
||||
$this->statusManager->expects($this->once())
|
||||
->method('setUserStatus')
|
||||
->with('user', IUserStatus::MESSAGE_AVAILABILITY, IUserStatus::DND, true);
|
||||
}
|
||||
|
||||
self::invokePrivate($automation, 'run', [['userId' => 'user']]);
|
||||
}
|
||||
|
||||
public function testRunNoMoreAvailabilityDefined(): void {
|
||||
$this->config->method('getUserValue')
|
||||
->with('user', 'dav', 'user_status_automation', 'no')
|
||||
->willReturn('yes');
|
||||
|
||||
$this->time->method('getDateTime')
|
||||
->willReturn(new \DateTime('2023-02-24 13:58:24.479357', new \DateTimeZone('UTC')));
|
||||
|
||||
$automation = $this->getAutomationMock(['getAvailabilityFromPropertiesTable']);
|
||||
$automation->method('getAvailabilityFromPropertiesTable')
|
||||
->with('user')
|
||||
->willReturn('BEGIN:VCALENDAR
|
||||
PRODID:Nextcloud DAV app
|
||||
BEGIN:VTIMEZONE
|
||||
TZID:Europe/Berlin
|
||||
BEGIN:STANDARD
|
||||
TZNAME:CET
|
||||
TZOFFSETFROM:+0200
|
||||
TZOFFSETTO:+0100
|
||||
DTSTART:19701025T030000
|
||||
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
|
||||
END:STANDARD
|
||||
BEGIN:DAYLIGHT
|
||||
TZNAME:CEST
|
||||
TZOFFSETFROM:+0100
|
||||
TZOFFSETTO:+0200
|
||||
DTSTART:19700329T020000
|
||||
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
|
||||
END:DAYLIGHT
|
||||
END:VTIMEZONE
|
||||
BEGIN:VAVAILABILITY
|
||||
END:VAVAILABILITY
|
||||
END:VCALENDAR');
|
||||
|
||||
$this->statusManager->expects($this->once())
|
||||
->method('revertUserStatus')
|
||||
->with('user', IUserStatus::MESSAGE_AVAILABILITY, IUserStatus::DND);
|
||||
|
||||
$this->jobList->expects($this->once())
|
||||
->method('remove')
|
||||
->with(UserStatusAutomation::class, ['userId' => 'user']);
|
||||
|
||||
self::invokePrivate($automation, 'run', [['userId' => 'user']]);
|
||||
}
|
||||
}
|
||||
@@ -304,6 +304,10 @@ class DirectoryTest extends \Test\TestCase {
|
||||
->method('free_space')
|
||||
->willReturn(800);
|
||||
|
||||
$this->info->expects($this->any())
|
||||
->method('getPath')
|
||||
->willReturn('/admin/files/foo');
|
||||
|
||||
$this->info->expects($this->once())
|
||||
->method('getSize')
|
||||
->willReturn(200);
|
||||
@@ -312,6 +316,10 @@ class DirectoryTest extends \Test\TestCase {
|
||||
->method('getMountPoint')
|
||||
->willReturn($mountPoint);
|
||||
|
||||
$this->view->expects($this->any())
|
||||
->method('getRelativePath')
|
||||
->willReturn('/foo');
|
||||
|
||||
$mountPoint->method('getMountPoint')
|
||||
->willReturn('/user/files/mymountpoint');
|
||||
|
||||
@@ -359,6 +367,10 @@ class DirectoryTest extends \Test\TestCase {
|
||||
$mountPoint->method('getMountPoint')
|
||||
->willReturn('/user/files/mymountpoint');
|
||||
|
||||
$this->view->expects($this->any())
|
||||
->method('getRelativePath')
|
||||
->willReturn('/foo');
|
||||
|
||||
$dir = new Directory($this->view, $this->info);
|
||||
$this->assertEquals([200, 800], $dir->getQuotaInfo()); //200 used, 800 free
|
||||
}
|
||||
|
||||
@@ -278,6 +278,7 @@ class SharesPluginTest extends \Test\TestCase {
|
||||
[[IShare::TYPE_REMOTE]],
|
||||
[[IShare::TYPE_ROOM]],
|
||||
[[IShare::TYPE_DECK]],
|
||||
[[IShare::TYPE_SCIENCEMESH]],
|
||||
[[IShare::TYPE_USER, IShare::TYPE_GROUP]],
|
||||
[[IShare::TYPE_USER, IShare::TYPE_GROUP, IShare::TYPE_LINK]],
|
||||
[[IShare::TYPE_USER, IShare::TYPE_LINK]],
|
||||
|
||||
@@ -2,7 +2,7 @@ OC.L10N.register(
|
||||
"encryption",
|
||||
{
|
||||
"Missing recovery key password" : "Kata sandi kunci pemulihan tidak ada",
|
||||
"Please repeat the recovery key password" : "Silahkan ulangi kata sandi kunci pemulihan",
|
||||
"Please repeat the recovery key password" : "Silakan ulangi kata sandi kunci pemulihan",
|
||||
"Repeated recovery key password does not match the provided recovery key password" : "Kata sandi kunci pemulihan yang diulangi tidak cocok dengan kata sandi kunci pemulihan yang diberikan",
|
||||
"Recovery key successfully enabled" : "Kunci pemulihan berhasil diaktifkan",
|
||||
"Could not enable recovery key. Please check your recovery key password!" : "Tidak dapat mengaktifkan kunci pemulihan. Silakan periksa kata sandi kunci pemulihan Anda!",
|
||||
@@ -21,8 +21,8 @@ OC.L10N.register(
|
||||
"The old password was not correct, please try again." : "Kata sandi lama salah, mohon coba lagi.",
|
||||
"The current log-in password was not correct, please try again." : "Kata sandi masuk saat ini salah, mohon coba lagi.",
|
||||
"Private key password successfully updated." : "Sandi kunci privat berhasil diperbarui.",
|
||||
"Invalid private key for encryption app. Please update your private key password in your personal settings to recover access to your encrypted files." : "Kunci privat tidak sah untuk aplikasi enkripsi. Silakan perbarui kata sandi kunci privat anda pada pengaturan pribadi untuk memulihkan akses ke berkas anda yang dienkripsi.",
|
||||
"Encryption App is enabled, but your keys are not initialized. Please log-out and log-in again." : "Apl Enkripsi telah aktif, tapi kunci anda tidak diinisialisasikan. Harap keluar-log dan masuk-log kembali.",
|
||||
"Invalid private key for encryption app. Please update your private key password in your personal settings to recover access to your encrypted files." : "Kunci privat tidak sah untuk aplikasi enkripsi. Silakan perbarui kata sandi kunci privat Anda pada pengaturan pribadi untuk memulihkan akses ke berkas Anda yang dienkripsi.",
|
||||
"Encryption App is enabled, but your keys are not initialized. Please log-out and log-in again." : "Apl Enkripsi telah aktif, tapi kunci Anda tidak diinisialisasikan. Harap keluar-log dan masuk-log kembali.",
|
||||
"Please enable server side encryption in the admin settings in order to use the encryption module." : "Silakan aktifkan enkripsi sisi-peladen pada setelan admin untuk menggunakan modul enkripsi.",
|
||||
"Encryption app is enabled and ready" : "Apl enkripsi aktif dan siap",
|
||||
"Bad Signature" : "Tanda salah",
|
||||
@@ -40,7 +40,7 @@ OC.L10N.register(
|
||||
"Enabling this option encrypts all files stored on the main storage, otherwise only files on external storage will be encrypted" : "Mengaktifkan opsi ini akan mengenkripsi semua berkas yang disimpan pada penyimpanan utama, jika tidak diaktifkan maka hanya berkas pada penyimpanan eksternal saja yang akan dienkripsi.",
|
||||
"Enable recovery key" : "Aktifkan kunci pemulihan",
|
||||
"Disable recovery key" : "Nonaktifkan kunci pemulihan",
|
||||
"The recovery key is an extra encryption key that is used to encrypt files. It allows recovery of a user's files if the user forgets his or her password." : "Kunci pemulihan adalah kunci enkripsi tambahan yang digunakan untuk mengenkripsi berkas. Kunci pemulihan memungkinkan untuk memulihkan berkas-berkas pengguna ketika pengguna tersebut melupakan kata sandi mereka.",
|
||||
"The recovery key is an extra encryption key that is used to encrypt files. It allows recovery of a user's files if the user forgets his or her password." : "Kunci pemulihan adalah kunci enkripsi tambahan yang digunakan untuk mengenkripsi berkas. Kunci pemulihan memungkinkan untuk memulihkan berkas pengguna ketika pengguna tersebut melupakan kata sandi mereka.",
|
||||
"Recovery key password" : "Kata sandi kunci pemulihan",
|
||||
"Repeat recovery key password" : "Ulangi kata sandi kunci pemulihan",
|
||||
"Change recovery key password:" : "Ubah kata sandi kunci pemulihan:",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{ "translations": {
|
||||
"Missing recovery key password" : "Kata sandi kunci pemulihan tidak ada",
|
||||
"Please repeat the recovery key password" : "Silahkan ulangi kata sandi kunci pemulihan",
|
||||
"Please repeat the recovery key password" : "Silakan ulangi kata sandi kunci pemulihan",
|
||||
"Repeated recovery key password does not match the provided recovery key password" : "Kata sandi kunci pemulihan yang diulangi tidak cocok dengan kata sandi kunci pemulihan yang diberikan",
|
||||
"Recovery key successfully enabled" : "Kunci pemulihan berhasil diaktifkan",
|
||||
"Could not enable recovery key. Please check your recovery key password!" : "Tidak dapat mengaktifkan kunci pemulihan. Silakan periksa kata sandi kunci pemulihan Anda!",
|
||||
@@ -19,8 +19,8 @@
|
||||
"The old password was not correct, please try again." : "Kata sandi lama salah, mohon coba lagi.",
|
||||
"The current log-in password was not correct, please try again." : "Kata sandi masuk saat ini salah, mohon coba lagi.",
|
||||
"Private key password successfully updated." : "Sandi kunci privat berhasil diperbarui.",
|
||||
"Invalid private key for encryption app. Please update your private key password in your personal settings to recover access to your encrypted files." : "Kunci privat tidak sah untuk aplikasi enkripsi. Silakan perbarui kata sandi kunci privat anda pada pengaturan pribadi untuk memulihkan akses ke berkas anda yang dienkripsi.",
|
||||
"Encryption App is enabled, but your keys are not initialized. Please log-out and log-in again." : "Apl Enkripsi telah aktif, tapi kunci anda tidak diinisialisasikan. Harap keluar-log dan masuk-log kembali.",
|
||||
"Invalid private key for encryption app. Please update your private key password in your personal settings to recover access to your encrypted files." : "Kunci privat tidak sah untuk aplikasi enkripsi. Silakan perbarui kata sandi kunci privat Anda pada pengaturan pribadi untuk memulihkan akses ke berkas Anda yang dienkripsi.",
|
||||
"Encryption App is enabled, but your keys are not initialized. Please log-out and log-in again." : "Apl Enkripsi telah aktif, tapi kunci Anda tidak diinisialisasikan. Harap keluar-log dan masuk-log kembali.",
|
||||
"Please enable server side encryption in the admin settings in order to use the encryption module." : "Silakan aktifkan enkripsi sisi-peladen pada setelan admin untuk menggunakan modul enkripsi.",
|
||||
"Encryption app is enabled and ready" : "Apl enkripsi aktif dan siap",
|
||||
"Bad Signature" : "Tanda salah",
|
||||
@@ -38,7 +38,7 @@
|
||||
"Enabling this option encrypts all files stored on the main storage, otherwise only files on external storage will be encrypted" : "Mengaktifkan opsi ini akan mengenkripsi semua berkas yang disimpan pada penyimpanan utama, jika tidak diaktifkan maka hanya berkas pada penyimpanan eksternal saja yang akan dienkripsi.",
|
||||
"Enable recovery key" : "Aktifkan kunci pemulihan",
|
||||
"Disable recovery key" : "Nonaktifkan kunci pemulihan",
|
||||
"The recovery key is an extra encryption key that is used to encrypt files. It allows recovery of a user's files if the user forgets his or her password." : "Kunci pemulihan adalah kunci enkripsi tambahan yang digunakan untuk mengenkripsi berkas. Kunci pemulihan memungkinkan untuk memulihkan berkas-berkas pengguna ketika pengguna tersebut melupakan kata sandi mereka.",
|
||||
"The recovery key is an extra encryption key that is used to encrypt files. It allows recovery of a user's files if the user forgets his or her password." : "Kunci pemulihan adalah kunci enkripsi tambahan yang digunakan untuk mengenkripsi berkas. Kunci pemulihan memungkinkan untuk memulihkan berkas pengguna ketika pengguna tersebut melupakan kata sandi mereka.",
|
||||
"Recovery key password" : "Kata sandi kunci pemulihan",
|
||||
"Repeat recovery key password" : "Ulangi kata sandi kunci pemulihan",
|
||||
"Change recovery key password:" : "Ubah kata sandi kunci pemulihan:",
|
||||
|
||||
@@ -28,11 +28,15 @@ OC.L10N.register(
|
||||
"Bad Signature" : "Лош потпис",
|
||||
"Missing Signature" : "Недостаје потпис",
|
||||
"one-time password for server-side-encryption" : "једнократна лозинка за шифровање на страни сервера",
|
||||
"Cannot decrypt this file, probably this is a shared file. Please ask the file owner to reshare the file with you." : "Овај фајл не може да се дешифрује, то је вероватно дељени фајл. Молимо вас да замолите власника да га поново подели са вама.",
|
||||
"Cannot read this file, probably this is a shared file. Please ask the file owner to reshare the file with you." : "Овај фајл не може да се прочита, то је вероватно дељени фајл. Молимо вас да замолите власника да га поново подели са вама.",
|
||||
"Default encryption module" : "Подразумевани модул за шифровање",
|
||||
"Default encryption module for server-side encryption" : "Подразумевани модул за шифровање на серверској страни",
|
||||
"In order to use this encryption module you need to enable server-side\n\t\tencryption in the admin settings. Once enabled this module will encrypt\n\t\tall your files transparently. The encryption is based on AES 256 keys.\n\t\tThe module won't touch existing files, only new files will be encrypted\n\t\tafter server-side encryption was enabled. It is also not possible to\n\t\tdisable the encryption again and switch back to a unencrypted system.\n\t\tPlease read the documentation to know all implications before you decide\n\t\tto enable server-side encryption." : "Да бисте користили овај модул, морате на серверској страни омогућити\n\t\tшифровање у администраторским поставкама. Једном укључен, овај модул ће шифровати\n\t\tсве фајлове транспарентно. Шифровање је базирано на „AES 256“ кључевима.\n\t\tМодул неће дирати постојеће фајлове, само ће нови фајлови бити шифровани\n\t\tнакон укључења шифровања на серверској страни. Није могуће да\n\t\tсе искључи шифровање и да врати се на нешифровани систем.\n\t\tПрочитајте документацију да сазнате све импликације пре него што се одлучите\n\t\tда укључите шифровање на серверу.",
|
||||
"Hey there,\n\nThe administration enabled server-side-encryption. Your files were encrypted using the password \"%s\".\n\nPlease login to the web interface, go to the section \"Basic encryption module\" of your personal settings and update your encryption password by entering this password into the \"Old log-in password\" field and your current login-password.\n\n" : "Здраво,\n\nАдминистрација је укључила енкрипцију на страни сервера. Ваши фајлови су шифровани коришћењем лозинке „%s”.\n\nМолимо вас да се пријавите веб интерфејсом, одете на одељак „Модул основне енкрипције” у вашим личним подешавањима и ажурирате лозинку шифрирања уношењем ове лозинке у поље „Стара лозинка за пријаву” и вашу текућу лозинку за пријаву.\n\n",
|
||||
"The share will expire on %s." : "Дељење истиче %s.",
|
||||
"Cheers!" : "Здраво!",
|
||||
"Hey there,<br><br>The administration enabled server-side-encryption. Your files were encrypted using the password <strong>%s</strong>.<br><br>Please login to the web interface, go to the section \"Basic encryption module\" of your personal settings and update your encryption password by entering this password into the \"Old log-in password\" field and your current login-password.<br><br>" : "Здраво,<br><br>Администрација је укључила енкрипцију на страни сервера. Ваши фајлови су шифровани коришћењем лозинке <strong>%s</strong>.<br><br>Молимо вас да се пријавите веб интерфејсом, одете на одељак „Модул основне енкрипције” у вашим личним подешавањима и ажурирате лозинку шифрирања уношењем ове лозинке у поље „Стара лозинка за пријаву” и вашу текућу лозинку за пријаву.<br><br>",
|
||||
"Encryption app is enabled but your keys are not initialized, please log-out and log-in again" : "Апликација за шифровање је укључена али кључеви још нису иницијализовани. Одјавите се и поново се пријавите.",
|
||||
"Encrypt the home storage" : "Шифровање главног складишта",
|
||||
"Enabling this option encrypts all files stored on the main storage, otherwise only files on external storage will be encrypted" : "Укључивање ове опције ће шифровати све фајлове на главном складишту. У супротном ће само фајлови на спољашњем складишту бити шифровани",
|
||||
|
||||
@@ -26,11 +26,15 @@
|
||||
"Bad Signature" : "Лош потпис",
|
||||
"Missing Signature" : "Недостаје потпис",
|
||||
"one-time password for server-side-encryption" : "једнократна лозинка за шифровање на страни сервера",
|
||||
"Cannot decrypt this file, probably this is a shared file. Please ask the file owner to reshare the file with you." : "Овај фајл не може да се дешифрује, то је вероватно дељени фајл. Молимо вас да замолите власника да га поново подели са вама.",
|
||||
"Cannot read this file, probably this is a shared file. Please ask the file owner to reshare the file with you." : "Овај фајл не може да се прочита, то је вероватно дељени фајл. Молимо вас да замолите власника да га поново подели са вама.",
|
||||
"Default encryption module" : "Подразумевани модул за шифровање",
|
||||
"Default encryption module for server-side encryption" : "Подразумевани модул за шифровање на серверској страни",
|
||||
"In order to use this encryption module you need to enable server-side\n\t\tencryption in the admin settings. Once enabled this module will encrypt\n\t\tall your files transparently. The encryption is based on AES 256 keys.\n\t\tThe module won't touch existing files, only new files will be encrypted\n\t\tafter server-side encryption was enabled. It is also not possible to\n\t\tdisable the encryption again and switch back to a unencrypted system.\n\t\tPlease read the documentation to know all implications before you decide\n\t\tto enable server-side encryption." : "Да бисте користили овај модул, морате на серверској страни омогућити\n\t\tшифровање у администраторским поставкама. Једном укључен, овај модул ће шифровати\n\t\tсве фајлове транспарентно. Шифровање је базирано на „AES 256“ кључевима.\n\t\tМодул неће дирати постојеће фајлове, само ће нови фајлови бити шифровани\n\t\tнакон укључења шифровања на серверској страни. Није могуће да\n\t\tсе искључи шифровање и да врати се на нешифровани систем.\n\t\tПрочитајте документацију да сазнате све импликације пре него што се одлучите\n\t\tда укључите шифровање на серверу.",
|
||||
"Hey there,\n\nThe administration enabled server-side-encryption. Your files were encrypted using the password \"%s\".\n\nPlease login to the web interface, go to the section \"Basic encryption module\" of your personal settings and update your encryption password by entering this password into the \"Old log-in password\" field and your current login-password.\n\n" : "Здраво,\n\nАдминистрација је укључила енкрипцију на страни сервера. Ваши фајлови су шифровани коришћењем лозинке „%s”.\n\nМолимо вас да се пријавите веб интерфејсом, одете на одељак „Модул основне енкрипције” у вашим личним подешавањима и ажурирате лозинку шифрирања уношењем ове лозинке у поље „Стара лозинка за пријаву” и вашу текућу лозинку за пријаву.\n\n",
|
||||
"The share will expire on %s." : "Дељење истиче %s.",
|
||||
"Cheers!" : "Здраво!",
|
||||
"Hey there,<br><br>The administration enabled server-side-encryption. Your files were encrypted using the password <strong>%s</strong>.<br><br>Please login to the web interface, go to the section \"Basic encryption module\" of your personal settings and update your encryption password by entering this password into the \"Old log-in password\" field and your current login-password.<br><br>" : "Здраво,<br><br>Администрација је укључила енкрипцију на страни сервера. Ваши фајлови су шифровани коришћењем лозинке <strong>%s</strong>.<br><br>Молимо вас да се пријавите веб интерфејсом, одете на одељак „Модул основне енкрипције” у вашим личним подешавањима и ажурирате лозинку шифрирања уношењем ове лозинке у поље „Стара лозинка за пријаву” и вашу текућу лозинку за пријаву.<br><br>",
|
||||
"Encryption app is enabled but your keys are not initialized, please log-out and log-in again" : "Апликација за шифровање је укључена али кључеви још нису иницијализовани. Одјавите се и поново се пријавите.",
|
||||
"Encrypt the home storage" : "Шифровање главног складишта",
|
||||
"Enabling this option encrypts all files stored on the main storage, otherwise only files on external storage will be encrypted" : "Укључивање ове опције ће шифровати све фајлове на главном складишту. У супротном ће само фајлови на спољашњем складишту бити шифровани",
|
||||
|
||||
@@ -29,6 +29,8 @@ OC.L10N.register(
|
||||
"Share with me via Nextcloud" : "Dibagikan pada saya via Nextcloud",
|
||||
"HTML Code:" : "Kode HTML:",
|
||||
"Share with me through my #Nextcloud Federated Cloud ID" : "Dibagikan pada saya melalui #Nextcloud Federated Cloud ID saya",
|
||||
"Copy to clipboard" : "Salin ke papan klip",
|
||||
"Clipboard is not available" : "Papan klip tidak tersedia",
|
||||
"Copied!" : "Tersalin!",
|
||||
"Copy" : "Salin",
|
||||
"Not supported!" : "Tidak didukung!",
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
"Share with me via Nextcloud" : "Dibagikan pada saya via Nextcloud",
|
||||
"HTML Code:" : "Kode HTML:",
|
||||
"Share with me through my #Nextcloud Federated Cloud ID" : "Dibagikan pada saya melalui #Nextcloud Federated Cloud ID saya",
|
||||
"Copy to clipboard" : "Salin ke papan klip",
|
||||
"Clipboard is not available" : "Papan klip tidak tersedia",
|
||||
"Copied!" : "Tersalin!",
|
||||
"Copy" : "Salin",
|
||||
"Not supported!" : "Tidak didukung!",
|
||||
|
||||
@@ -49,7 +49,7 @@ OC.L10N.register(
|
||||
"Share with me through my #Nextcloud Federated Cloud ID, see {url}" : "Поділіться зі мною через мій #Nextcloud Federated Cloud ID, див. {url}",
|
||||
"Share with me through my #Nextcloud Federated Cloud ID" : "Поділіться зі мною через мій #Nextcloud Federated Cloud ID",
|
||||
"Cloud ID copied to the clipboard" : "Cloud ID скопійовано в буфер обміну",
|
||||
"Copy to clipboard" : "Скопіювати в буфер обміну ",
|
||||
"Copy to clipboard" : "Копіювати до буферу обміну",
|
||||
"Clipboard is not available" : "Буфер обміну недоступний",
|
||||
"Copied!" : "Скопійовано!",
|
||||
"Copy" : "Копіювати",
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
"Share with me through my #Nextcloud Federated Cloud ID, see {url}" : "Поділіться зі мною через мій #Nextcloud Federated Cloud ID, див. {url}",
|
||||
"Share with me through my #Nextcloud Federated Cloud ID" : "Поділіться зі мною через мій #Nextcloud Federated Cloud ID",
|
||||
"Cloud ID copied to the clipboard" : "Cloud ID скопійовано в буфер обміну",
|
||||
"Copy to clipboard" : "Скопіювати в буфер обміну ",
|
||||
"Copy to clipboard" : "Копіювати до буферу обміну",
|
||||
"Clipboard is not available" : "Буфер обміну недоступний",
|
||||
"Copied!" : "Скопійовано!",
|
||||
"Copy" : "Копіювати",
|
||||
|
||||
@@ -269,8 +269,12 @@ OC.FileUpload.prototype = {
|
||||
&& this.getFile().size > this.uploader.fileUploadParam.maxChunkSize
|
||||
) {
|
||||
data.isChunked = true;
|
||||
var headers = {
|
||||
Destination: this.uploader.davClient._buildUrl(this.getTargetDestination())
|
||||
};
|
||||
|
||||
chunkFolderPromise = this.uploader.davClient.createDirectory(
|
||||
'uploads/' + OC.getCurrentUser().uid + '/' + this.getId()
|
||||
'uploads/' + OC.getCurrentUser().uid + '/' + this.getId(), headers
|
||||
);
|
||||
// TODO: if fails, it means same id already existed, need to retry
|
||||
} else {
|
||||
@@ -309,17 +313,22 @@ OC.FileUpload.prototype = {
|
||||
}
|
||||
if (size) {
|
||||
headers['OC-Total-Length'] = size;
|
||||
|
||||
}
|
||||
headers['Destination'] = this.uploader.davClient._buildUrl(this.getTargetDestination());
|
||||
|
||||
return this.uploader.davClient.move(
|
||||
'uploads/' + uid + '/' + this.getId() + '/.file',
|
||||
'files/' + uid + '/' + OC.joinPaths(this.getFullPath(), this.getFileName()),
|
||||
this.getTargetDestination(),
|
||||
true,
|
||||
headers
|
||||
);
|
||||
},
|
||||
|
||||
getTargetDestination: function() {
|
||||
var uid = OC.getCurrentUser().uid;
|
||||
return 'files/' + uid + '/' + OC.joinPaths(this.getFullPath(), this.getFileName());
|
||||
},
|
||||
|
||||
_deleteChunkFolder: function() {
|
||||
// delete transfer directory for this upload
|
||||
this.uploader.davClient.remove(
|
||||
@@ -1326,6 +1335,10 @@ OC.Uploader.prototype = _.extend({
|
||||
}
|
||||
var range = data.contentRange.split(' ')[1];
|
||||
var chunkId = range.split('/')[0].split('-')[0];
|
||||
// Use a numeric chunk id and set the Destination header on all request for ChunkingV2
|
||||
chunkId = Math.ceil((data.chunkSize+Number(chunkId)) / upload.uploader.fileUploadParam.maxChunkSize);
|
||||
data.headers['Destination'] = self.davClient._buildUrl(upload.getTargetDestination());
|
||||
|
||||
data.url = OC.getRootPath() +
|
||||
'/remote.php/dav/uploads' +
|
||||
'/' + OC.getCurrentUser().uid +
|
||||
|
||||
Vendored
+6
@@ -733,6 +733,12 @@
|
||||
promise = dfd.promise(),
|
||||
jqXHR,
|
||||
upload;
|
||||
|
||||
// Dynamically adjust the chunk size for Chunking V2 to fit into the 10000 chunk limit
|
||||
if (file.size/mcs > 10000) {
|
||||
mcs = Math.ceil(file.size/10000)
|
||||
}
|
||||
|
||||
if (!(this._isXHRUpload(options) && slice && (ub || mcs < fs)) ||
|
||||
options.data) {
|
||||
return false;
|
||||
|
||||
@@ -185,12 +185,15 @@ OC.L10N.register(
|
||||
"Select file or folder to link to" : "Избор на файл или папка, към които да поставите връзка",
|
||||
"Open the files app settings" : "Отваряне на настройките на приложението за файлове",
|
||||
"Files settings" : "Настройки на файловете",
|
||||
"File cannot be accessed" : "Файлът не е достъпен",
|
||||
"You might not have have permissions to view it, ask the sender to share it" : "Може да нямате права да го видите, помолете подателя да го сподели",
|
||||
"Show hidden files" : "Показвай и скрити файлове",
|
||||
"Crop image previews" : "Изрязване на визуализациите на изображение",
|
||||
"Additional settings" : "Допълнителни настройки",
|
||||
"WebDAV" : "WebDAV",
|
||||
"Copy to clipboard" : "Копиране в клипборда",
|
||||
"Use this address to access your Files via WebDAV" : "Ползвайте този адрес за достъп до файловете си чрез WebDAV",
|
||||
"If you have enabled 2FA, you must create and use a new app password by clicking here." : "Ако сте активирали 2FA, трябва да създадете и използвате нова парола за приложението, като кликнете тук.",
|
||||
"Clipboard is not available" : "Клипбордът не е достъпен",
|
||||
"WebDAV URL copied to clipboard" : "WebDAV URL адрес е копиран в клипборда",
|
||||
"Unable to change the favourite state of the file" : "Не може да се промени състоянието за предпочитане на файла",
|
||||
|
||||
@@ -183,12 +183,15 @@
|
||||
"Select file or folder to link to" : "Избор на файл или папка, към които да поставите връзка",
|
||||
"Open the files app settings" : "Отваряне на настройките на приложението за файлове",
|
||||
"Files settings" : "Настройки на файловете",
|
||||
"File cannot be accessed" : "Файлът не е достъпен",
|
||||
"You might not have have permissions to view it, ask the sender to share it" : "Може да нямате права да го видите, помолете подателя да го сподели",
|
||||
"Show hidden files" : "Показвай и скрити файлове",
|
||||
"Crop image previews" : "Изрязване на визуализациите на изображение",
|
||||
"Additional settings" : "Допълнителни настройки",
|
||||
"WebDAV" : "WebDAV",
|
||||
"Copy to clipboard" : "Копиране в клипборда",
|
||||
"Use this address to access your Files via WebDAV" : "Ползвайте този адрес за достъп до файловете си чрез WebDAV",
|
||||
"If you have enabled 2FA, you must create and use a new app password by clicking here." : "Ако сте активирали 2FA, трябва да създадете и използвате нова парола за приложението, като кликнете тук.",
|
||||
"Clipboard is not available" : "Клипбордът не е достъпен",
|
||||
"WebDAV URL copied to clipboard" : "WebDAV URL адрес е копиран в клипборда",
|
||||
"Unable to change the favourite state of the file" : "Не може да се промени състоянието за предпочитане на файла",
|
||||
|
||||
@@ -98,6 +98,7 @@ OC.L10N.register(
|
||||
"Your storage is almost full ({usedSpacePercent}%)." : "L'emmagatzematge està gairebé ple ({usedSpacePercent}%).",
|
||||
"_matches \"{filter}\"_::_match \"{filter}\"_" : ["coincideix amb «{filter}»","coincideixen amb «{filter}»"],
|
||||
"View in folder" : "Visualitza-ho a la carpeta",
|
||||
"Direct link was copied (only works for users who have access to this file/folder)" : "S'ha copiat l'enllaç directe (només funciona per als usuaris que tenen accés a aquest fitxer/carpeta)",
|
||||
"Path" : "Ruta",
|
||||
"_%n byte_::_%n bytes_" : ["%n byte","%n bytes"],
|
||||
"Favorited" : "S'ha afegit als preferits",
|
||||
@@ -165,6 +166,10 @@ OC.L10N.register(
|
||||
"The ownership transfer of {path} from {user} has completed." : "S'ha completat la transferència de propietat de {path} de {user}.",
|
||||
"in %s" : "%s",
|
||||
"File Management" : "Gestió de fitxers",
|
||||
"Storage informations" : "Informació d'emmagatzematge",
|
||||
"{usedQuotaByte} used" : "{usedQuotaByte} utilitzat",
|
||||
"{relative}% used" : "{relative}% used",
|
||||
"Could not refresh storage stats" : "No s'han pogut actualitzar les estadístiques d'emmagatzematge",
|
||||
"Transfer ownership of a file or folder" : "Transferiu la propietat d'un fitxer o carpeta",
|
||||
"Choose file or folder to transfer" : "Tria el fitxer o carpeta que s'ha de transferir",
|
||||
"Change" : "Canvia",
|
||||
@@ -178,7 +183,10 @@ OC.L10N.register(
|
||||
"Ownership transfer request sent" : "S'ha enviat la sol·licitud de transferència de propietat",
|
||||
"Cannot transfer ownership of a file or folder you do not own" : "No es pot transferir la propietat d'un fitxer o carpeta que no és vostre",
|
||||
"Select file or folder to link to" : "Seleccioneu el fitxer o la carpeta per enllaçar",
|
||||
"Open the files app settings" : "Obriu la configuració de l'aplicació de fitxers",
|
||||
"Files settings" : "Paràmetres dels fitxers",
|
||||
"File cannot be accessed" : "No es pot accedir al fitxer",
|
||||
"You might not have have permissions to view it, ask the sender to share it" : "És possible que no tingueu permisos per veure'l, demaneu al remitent que el comparteixi",
|
||||
"Show hidden files" : "Mostra els fitxers ocults",
|
||||
"Crop image previews" : "Retalla les previsualitzacions de les imatges",
|
||||
"Additional settings" : "Paràmetres addicionals",
|
||||
@@ -186,6 +194,7 @@ OC.L10N.register(
|
||||
"Copy to clipboard" : "Copia-ho al porta-retalls",
|
||||
"Use this address to access your Files via WebDAV" : "Utilitzeu aquesta adreça per a accedir als vostres fitxers mitjançant WebDAV",
|
||||
"Clipboard is not available" : "El porta-retalls no està disponible",
|
||||
"WebDAV URL copied to clipboard" : "URL de WebDAV copiat al porta-retalls",
|
||||
"Unable to change the favourite state of the file" : "No s'ha pogut canviar l'estat de preferit del fitxer",
|
||||
"Error while loading the file data" : "S'ha produït un error en carregar la informació del fitxer",
|
||||
"Pick a template for {name}" : "Trieu una plantilla per a {name}",
|
||||
|
||||
@@ -96,6 +96,7 @@
|
||||
"Your storage is almost full ({usedSpacePercent}%)." : "L'emmagatzematge està gairebé ple ({usedSpacePercent}%).",
|
||||
"_matches \"{filter}\"_::_match \"{filter}\"_" : ["coincideix amb «{filter}»","coincideixen amb «{filter}»"],
|
||||
"View in folder" : "Visualitza-ho a la carpeta",
|
||||
"Direct link was copied (only works for users who have access to this file/folder)" : "S'ha copiat l'enllaç directe (només funciona per als usuaris que tenen accés a aquest fitxer/carpeta)",
|
||||
"Path" : "Ruta",
|
||||
"_%n byte_::_%n bytes_" : ["%n byte","%n bytes"],
|
||||
"Favorited" : "S'ha afegit als preferits",
|
||||
@@ -163,6 +164,10 @@
|
||||
"The ownership transfer of {path} from {user} has completed." : "S'ha completat la transferència de propietat de {path} de {user}.",
|
||||
"in %s" : "%s",
|
||||
"File Management" : "Gestió de fitxers",
|
||||
"Storage informations" : "Informació d'emmagatzematge",
|
||||
"{usedQuotaByte} used" : "{usedQuotaByte} utilitzat",
|
||||
"{relative}% used" : "{relative}% used",
|
||||
"Could not refresh storage stats" : "No s'han pogut actualitzar les estadístiques d'emmagatzematge",
|
||||
"Transfer ownership of a file or folder" : "Transferiu la propietat d'un fitxer o carpeta",
|
||||
"Choose file or folder to transfer" : "Tria el fitxer o carpeta que s'ha de transferir",
|
||||
"Change" : "Canvia",
|
||||
@@ -176,7 +181,10 @@
|
||||
"Ownership transfer request sent" : "S'ha enviat la sol·licitud de transferència de propietat",
|
||||
"Cannot transfer ownership of a file or folder you do not own" : "No es pot transferir la propietat d'un fitxer o carpeta que no és vostre",
|
||||
"Select file or folder to link to" : "Seleccioneu el fitxer o la carpeta per enllaçar",
|
||||
"Open the files app settings" : "Obriu la configuració de l'aplicació de fitxers",
|
||||
"Files settings" : "Paràmetres dels fitxers",
|
||||
"File cannot be accessed" : "No es pot accedir al fitxer",
|
||||
"You might not have have permissions to view it, ask the sender to share it" : "És possible que no tingueu permisos per veure'l, demaneu al remitent que el comparteixi",
|
||||
"Show hidden files" : "Mostra els fitxers ocults",
|
||||
"Crop image previews" : "Retalla les previsualitzacions de les imatges",
|
||||
"Additional settings" : "Paràmetres addicionals",
|
||||
@@ -184,6 +192,7 @@
|
||||
"Copy to clipboard" : "Copia-ho al porta-retalls",
|
||||
"Use this address to access your Files via WebDAV" : "Utilitzeu aquesta adreça per a accedir als vostres fitxers mitjançant WebDAV",
|
||||
"Clipboard is not available" : "El porta-retalls no està disponible",
|
||||
"WebDAV URL copied to clipboard" : "URL de WebDAV copiat al porta-retalls",
|
||||
"Unable to change the favourite state of the file" : "No s'ha pogut canviar l'estat de preferit del fitxer",
|
||||
"Error while loading the file data" : "S'ha produït un error en carregar la informació del fitxer",
|
||||
"Pick a template for {name}" : "Trieu una plantilla per a {name}",
|
||||
|
||||
@@ -185,12 +185,15 @@ OC.L10N.register(
|
||||
"Select file or folder to link to" : "Vyberte soubor nebo složku na kterou odkazovat",
|
||||
"Open the files app settings" : "Otevřít nastavení aplikace soubory",
|
||||
"Files settings" : "Nastavení pro Soubory",
|
||||
"File cannot be accessed" : "K souboru se nedaří přistoupit",
|
||||
"You might not have have permissions to view it, ask the sender to share it" : "Může být, že nemáte oprávnění pro jeho zobrazení – požádejte odesilatele aby vám ho nasdílel",
|
||||
"Show hidden files" : "Zobrazit skryté soubory",
|
||||
"Crop image previews" : "Oříznout náhledy obrázků",
|
||||
"Additional settings" : "Další nastavení",
|
||||
"WebDAV" : "WebDAV",
|
||||
"Copy to clipboard" : "Zkopírovat do schránky",
|
||||
"Use this address to access your Files via WebDAV" : "Tuto adresu použijte pro přístup k vašim souborům prostřednictvím protokolu WebDAV",
|
||||
"If you have enabled 2FA, you must create and use a new app password by clicking here." : "Pokud jste zapnuli 2FA, je třeba kliknutím sem vytvořit a použít nové heslo pro aplikaci.",
|
||||
"Clipboard is not available" : "Schránka není k dispozici",
|
||||
"WebDAV URL copied to clipboard" : "WebDAV URL zkopírována do schránky",
|
||||
"Unable to change the favourite state of the file" : "Nedaří se změnit stav „oblíbené“ souboru",
|
||||
|
||||
@@ -183,12 +183,15 @@
|
||||
"Select file or folder to link to" : "Vyberte soubor nebo složku na kterou odkazovat",
|
||||
"Open the files app settings" : "Otevřít nastavení aplikace soubory",
|
||||
"Files settings" : "Nastavení pro Soubory",
|
||||
"File cannot be accessed" : "K souboru se nedaří přistoupit",
|
||||
"You might not have have permissions to view it, ask the sender to share it" : "Může být, že nemáte oprávnění pro jeho zobrazení – požádejte odesilatele aby vám ho nasdílel",
|
||||
"Show hidden files" : "Zobrazit skryté soubory",
|
||||
"Crop image previews" : "Oříznout náhledy obrázků",
|
||||
"Additional settings" : "Další nastavení",
|
||||
"WebDAV" : "WebDAV",
|
||||
"Copy to clipboard" : "Zkopírovat do schránky",
|
||||
"Use this address to access your Files via WebDAV" : "Tuto adresu použijte pro přístup k vašim souborům prostřednictvím protokolu WebDAV",
|
||||
"If you have enabled 2FA, you must create and use a new app password by clicking here." : "Pokud jste zapnuli 2FA, je třeba kliknutím sem vytvořit a použít nové heslo pro aplikaci.",
|
||||
"Clipboard is not available" : "Schránka není k dispozici",
|
||||
"WebDAV URL copied to clipboard" : "WebDAV URL zkopírována do schránky",
|
||||
"Unable to change the favourite state of the file" : "Nedaří se změnit stav „oblíbené“ souboru",
|
||||
|
||||
@@ -182,8 +182,11 @@ OC.L10N.register(
|
||||
"Unknown error" : "Ukendt fejl",
|
||||
"Ownership transfer request sent" : "Anmodning om ejerskabsoverdragelse sendt",
|
||||
"Cannot transfer ownership of a file or folder you do not own" : "Kan ikke overføre ejerskab af en fil eller mappe, du ikke ejer",
|
||||
"Select file or folder to link to" : "Vælg fil eller mappe at linke til",
|
||||
"Open the files app settings" : "Åbn fil-app indstillinger",
|
||||
"Files settings" : "indstillinger for filer",
|
||||
"File cannot be accessed" : "Filen kan ikke tilgås",
|
||||
"You might not have have permissions to view it, ask the sender to share it" : "Du har muligvis ikke tilladelser til at se.... Bed afsenderen om at dele...",
|
||||
"Show hidden files" : "Vis skjulte filer",
|
||||
"Crop image previews" : "Beskær forhåndsvisninger af billeder",
|
||||
"Additional settings" : "Yderligere indstillinger",
|
||||
@@ -191,6 +194,7 @@ OC.L10N.register(
|
||||
"Copy to clipboard" : "Kopier til udklipsholder",
|
||||
"Use this address to access your Files via WebDAV" : "Brug denne adresse til at få adgang til dine filer via WebDAV",
|
||||
"Clipboard is not available" : "Udklipsholderen er ikke tilgængelig",
|
||||
"WebDAV URL copied to clipboard" : "WebDAV URL kopieret til udklipsholder",
|
||||
"Unable to change the favourite state of the file" : "Kan ikke ændre favorittilstanden for filen",
|
||||
"Error while loading the file data" : "Fejl under indlæsning af fildata",
|
||||
"Pick a template for {name}" : "Vælg en skabelon til {name}",
|
||||
|
||||
@@ -180,8 +180,11 @@
|
||||
"Unknown error" : "Ukendt fejl",
|
||||
"Ownership transfer request sent" : "Anmodning om ejerskabsoverdragelse sendt",
|
||||
"Cannot transfer ownership of a file or folder you do not own" : "Kan ikke overføre ejerskab af en fil eller mappe, du ikke ejer",
|
||||
"Select file or folder to link to" : "Vælg fil eller mappe at linke til",
|
||||
"Open the files app settings" : "Åbn fil-app indstillinger",
|
||||
"Files settings" : "indstillinger for filer",
|
||||
"File cannot be accessed" : "Filen kan ikke tilgås",
|
||||
"You might not have have permissions to view it, ask the sender to share it" : "Du har muligvis ikke tilladelser til at se.... Bed afsenderen om at dele...",
|
||||
"Show hidden files" : "Vis skjulte filer",
|
||||
"Crop image previews" : "Beskær forhåndsvisninger af billeder",
|
||||
"Additional settings" : "Yderligere indstillinger",
|
||||
@@ -189,6 +192,7 @@
|
||||
"Copy to clipboard" : "Kopier til udklipsholder",
|
||||
"Use this address to access your Files via WebDAV" : "Brug denne adresse til at få adgang til dine filer via WebDAV",
|
||||
"Clipboard is not available" : "Udklipsholderen er ikke tilgængelig",
|
||||
"WebDAV URL copied to clipboard" : "WebDAV URL kopieret til udklipsholder",
|
||||
"Unable to change the favourite state of the file" : "Kan ikke ændre favorittilstanden for filen",
|
||||
"Error while loading the file data" : "Fejl under indlæsning af fildata",
|
||||
"Pick a template for {name}" : "Vælg en skabelon til {name}",
|
||||
|
||||
@@ -185,6 +185,8 @@ OC.L10N.register(
|
||||
"Select file or folder to link to" : "Datei oder Ordner zum Verknüpfen auswählen",
|
||||
"Open the files app settings" : "Einstellungen der Dateien-App öffnen",
|
||||
"Files settings" : "Dateien-Einstellungen",
|
||||
"File cannot be accessed" : "Auf die Datei kann nicht zugegriffen werden",
|
||||
"You might not have have permissions to view it, ask the sender to share it" : "Möglicherweise hast du nicht die Berechtigung zur Anzeige. Bitte den Absender, die Datei freizugeben.",
|
||||
"Show hidden files" : "Versteckte Dateien anzeigen",
|
||||
"Crop image previews" : "Bildvorschauen zuschneiden",
|
||||
"Additional settings" : "Zusätzliche Einstellungen",
|
||||
@@ -192,6 +194,7 @@ OC.L10N.register(
|
||||
"Copy to clipboard" : "In die Zwischenablage kopieren",
|
||||
"Use this address to access your Files via WebDAV" : "Diese Adresse benutzen, um über WebDAV auf deine Dateien zuzugreifen",
|
||||
"Clipboard is not available" : "Zwischenablage ist nicht verfügbar",
|
||||
"WebDAV URL copied to clipboard" : "WebDAV-URL in die Zwischenablage kopiert",
|
||||
"Unable to change the favourite state of the file" : "Der favorisierte Status der Datei konnte nicht geändert werden",
|
||||
"Error while loading the file data" : "Fehler beim Laden der Datei-Daten",
|
||||
"Pick a template for {name}" : "Eine Vorlage für {name} wählen",
|
||||
|
||||
@@ -183,6 +183,8 @@
|
||||
"Select file or folder to link to" : "Datei oder Ordner zum Verknüpfen auswählen",
|
||||
"Open the files app settings" : "Einstellungen der Dateien-App öffnen",
|
||||
"Files settings" : "Dateien-Einstellungen",
|
||||
"File cannot be accessed" : "Auf die Datei kann nicht zugegriffen werden",
|
||||
"You might not have have permissions to view it, ask the sender to share it" : "Möglicherweise hast du nicht die Berechtigung zur Anzeige. Bitte den Absender, die Datei freizugeben.",
|
||||
"Show hidden files" : "Versteckte Dateien anzeigen",
|
||||
"Crop image previews" : "Bildvorschauen zuschneiden",
|
||||
"Additional settings" : "Zusätzliche Einstellungen",
|
||||
@@ -190,6 +192,7 @@
|
||||
"Copy to clipboard" : "In die Zwischenablage kopieren",
|
||||
"Use this address to access your Files via WebDAV" : "Diese Adresse benutzen, um über WebDAV auf deine Dateien zuzugreifen",
|
||||
"Clipboard is not available" : "Zwischenablage ist nicht verfügbar",
|
||||
"WebDAV URL copied to clipboard" : "WebDAV-URL in die Zwischenablage kopiert",
|
||||
"Unable to change the favourite state of the file" : "Der favorisierte Status der Datei konnte nicht geändert werden",
|
||||
"Error while loading the file data" : "Fehler beim Laden der Datei-Daten",
|
||||
"Pick a template for {name}" : "Eine Vorlage für {name} wählen",
|
||||
|
||||
@@ -185,12 +185,15 @@ OC.L10N.register(
|
||||
"Select file or folder to link to" : "Datei oder Ordner zum Verknüpfen auswählen",
|
||||
"Open the files app settings" : "Einstellungen der Dateien-App öffnen",
|
||||
"Files settings" : "Dateien-Einstellungen",
|
||||
"File cannot be accessed" : "Auf die Datei kann nicht zugegriffen werden",
|
||||
"You might not have have permissions to view it, ask the sender to share it" : "Möglicherweise haben Sie nicht die Berechtigung zur Anzeige. Bitten Sie den Absender, die Datei freizugeben.",
|
||||
"Show hidden files" : "Versteckte Dateien anzeigen",
|
||||
"Crop image previews" : "Bildvorschauen zuschneiden",
|
||||
"Additional settings" : "Zusätzliche Einstellungen",
|
||||
"WebDAV" : "WebDAV",
|
||||
"Copy to clipboard" : "In die Zwischenablage kopieren",
|
||||
"Use this address to access your Files via WebDAV" : "Benutzen Sie diese Adresse, um via WebDAV auf Ihre Dateien zuzugreifen",
|
||||
"If you have enabled 2FA, you must create and use a new app password by clicking here." : "Wenn Sie 2FA aktiviert haben, müssen Sie ein neues App-Passwort erstellen und verwenden, indem Sie hier klicken.",
|
||||
"Clipboard is not available" : "Zwischenablage ist nicht verfügbar",
|
||||
"WebDAV URL copied to clipboard" : "WebDAV-URL in die Zwischenablage kopiert",
|
||||
"Unable to change the favourite state of the file" : "Der favorisierte Status der Datei kann nicht geändert werden",
|
||||
|
||||
@@ -183,12 +183,15 @@
|
||||
"Select file or folder to link to" : "Datei oder Ordner zum Verknüpfen auswählen",
|
||||
"Open the files app settings" : "Einstellungen der Dateien-App öffnen",
|
||||
"Files settings" : "Dateien-Einstellungen",
|
||||
"File cannot be accessed" : "Auf die Datei kann nicht zugegriffen werden",
|
||||
"You might not have have permissions to view it, ask the sender to share it" : "Möglicherweise haben Sie nicht die Berechtigung zur Anzeige. Bitten Sie den Absender, die Datei freizugeben.",
|
||||
"Show hidden files" : "Versteckte Dateien anzeigen",
|
||||
"Crop image previews" : "Bildvorschauen zuschneiden",
|
||||
"Additional settings" : "Zusätzliche Einstellungen",
|
||||
"WebDAV" : "WebDAV",
|
||||
"Copy to clipboard" : "In die Zwischenablage kopieren",
|
||||
"Use this address to access your Files via WebDAV" : "Benutzen Sie diese Adresse, um via WebDAV auf Ihre Dateien zuzugreifen",
|
||||
"If you have enabled 2FA, you must create and use a new app password by clicking here." : "Wenn Sie 2FA aktiviert haben, müssen Sie ein neues App-Passwort erstellen und verwenden, indem Sie hier klicken.",
|
||||
"Clipboard is not available" : "Zwischenablage ist nicht verfügbar",
|
||||
"WebDAV URL copied to clipboard" : "WebDAV-URL in die Zwischenablage kopiert",
|
||||
"Unable to change the favourite state of the file" : "Der favorisierte Status der Datei kann nicht geändert werden",
|
||||
|
||||
@@ -185,12 +185,15 @@ OC.L10N.register(
|
||||
"Select file or folder to link to" : "Select file or folder to link to",
|
||||
"Open the files app settings" : "Open the files app settings",
|
||||
"Files settings" : "Files settings",
|
||||
"File cannot be accessed" : "File cannot be accessed",
|
||||
"You might not have have permissions to view it, ask the sender to share it" : "You might not have have permissions to view it, ask the sender to share it",
|
||||
"Show hidden files" : "Show hidden files",
|
||||
"Crop image previews" : "Crop image previews",
|
||||
"Additional settings" : "Additional settings",
|
||||
"WebDAV" : "WebDAV",
|
||||
"Copy to clipboard" : "Copy to clipboard",
|
||||
"Use this address to access your Files via WebDAV" : "Use this address to access your Files via WebDAV",
|
||||
"If you have enabled 2FA, you must create and use a new app password by clicking here." : "If you have enabled 2FA, you must create and use a new app password by clicking here.",
|
||||
"Clipboard is not available" : "Clipboard is not available",
|
||||
"WebDAV URL copied to clipboard" : "WebDAV URL copied to clipboard",
|
||||
"Unable to change the favourite state of the file" : "Unable to change the favourite state of the file",
|
||||
|
||||
@@ -183,12 +183,15 @@
|
||||
"Select file or folder to link to" : "Select file or folder to link to",
|
||||
"Open the files app settings" : "Open the files app settings",
|
||||
"Files settings" : "Files settings",
|
||||
"File cannot be accessed" : "File cannot be accessed",
|
||||
"You might not have have permissions to view it, ask the sender to share it" : "You might not have have permissions to view it, ask the sender to share it",
|
||||
"Show hidden files" : "Show hidden files",
|
||||
"Crop image previews" : "Crop image previews",
|
||||
"Additional settings" : "Additional settings",
|
||||
"WebDAV" : "WebDAV",
|
||||
"Copy to clipboard" : "Copy to clipboard",
|
||||
"Use this address to access your Files via WebDAV" : "Use this address to access your Files via WebDAV",
|
||||
"If you have enabled 2FA, you must create and use a new app password by clicking here." : "If you have enabled 2FA, you must create and use a new app password by clicking here.",
|
||||
"Clipboard is not available" : "Clipboard is not available",
|
||||
"WebDAV URL copied to clipboard" : "WebDAV URL copied to clipboard",
|
||||
"Unable to change the favourite state of the file" : "Unable to change the favourite state of the file",
|
||||
|
||||
@@ -185,12 +185,15 @@ OC.L10N.register(
|
||||
"Select file or folder to link to" : "Selecciona archivo o carpeta a enlazar",
|
||||
"Open the files app settings" : "Abrir la configuración de la app Archivos",
|
||||
"Files settings" : "Configuración de archivos",
|
||||
"File cannot be accessed" : "El archivo no puede ser accesado",
|
||||
"You might not have have permissions to view it, ask the sender to share it" : "Puede que no tenga permisos para verlo, solicite al remitente que lo comparta",
|
||||
"Show hidden files" : "Mostrar archivos ocultos",
|
||||
"Crop image previews" : "Recortar la previsualización de las imágenes",
|
||||
"Additional settings" : "Configuración adicional",
|
||||
"WebDAV" : "WebDAV",
|
||||
"Copy to clipboard" : "Copiar al portapapeles",
|
||||
"Use this address to access your Files via WebDAV" : "Use esta dirección para acceder a tus archivos vía WebDAV",
|
||||
"If you have enabled 2FA, you must create and use a new app password by clicking here." : "Si ha habilitado 2FA, debe crear y utilizar una nueva contraseña de aplicación haciendo clic aquí.",
|
||||
"Clipboard is not available" : "El portapapeles no está disponible",
|
||||
"WebDAV URL copied to clipboard" : "El URL Webdav URL fue copiado al portapapeles",
|
||||
"Unable to change the favourite state of the file" : "No se ha podido cambiar el estado de favorito del fichero",
|
||||
|
||||
@@ -183,12 +183,15 @@
|
||||
"Select file or folder to link to" : "Selecciona archivo o carpeta a enlazar",
|
||||
"Open the files app settings" : "Abrir la configuración de la app Archivos",
|
||||
"Files settings" : "Configuración de archivos",
|
||||
"File cannot be accessed" : "El archivo no puede ser accesado",
|
||||
"You might not have have permissions to view it, ask the sender to share it" : "Puede que no tenga permisos para verlo, solicite al remitente que lo comparta",
|
||||
"Show hidden files" : "Mostrar archivos ocultos",
|
||||
"Crop image previews" : "Recortar la previsualización de las imágenes",
|
||||
"Additional settings" : "Configuración adicional",
|
||||
"WebDAV" : "WebDAV",
|
||||
"Copy to clipboard" : "Copiar al portapapeles",
|
||||
"Use this address to access your Files via WebDAV" : "Use esta dirección para acceder a tus archivos vía WebDAV",
|
||||
"If you have enabled 2FA, you must create and use a new app password by clicking here." : "Si ha habilitado 2FA, debe crear y utilizar una nueva contraseña de aplicación haciendo clic aquí.",
|
||||
"Clipboard is not available" : "El portapapeles no está disponible",
|
||||
"WebDAV URL copied to clipboard" : "El URL Webdav URL fue copiado al portapapeles",
|
||||
"Unable to change the favourite state of the file" : "No se ha podido cambiar el estado de favorito del fichero",
|
||||
|
||||
@@ -185,12 +185,15 @@ OC.L10N.register(
|
||||
"Select file or folder to link to" : "Valitse tiedosto tai kansio, johon linkitetään",
|
||||
"Open the files app settings" : "Avaa tiedostosovelluksen asetukset",
|
||||
"Files settings" : "Tiedostojen asetukset",
|
||||
"File cannot be accessed" : "Tiedostoa ei voi käyttää",
|
||||
"You might not have have permissions to view it, ask the sender to share it" : "Oikeutesi eivät kenties riitä sen katseluun, pyydä lähettäjää jakamaan se",
|
||||
"Show hidden files" : "Näytä piilotetut tiedostot",
|
||||
"Crop image previews" : "Rajaa kuvien esikatseluja",
|
||||
"Additional settings" : "Lisäasetukset",
|
||||
"WebDAV" : "WebDAV",
|
||||
"Copy to clipboard" : "Kopioi leikepöydälle",
|
||||
"Use this address to access your Files via WebDAV" : "Käytä tätä osoitetta yhdistääksesi tiedostosi WebDAV:in kautta",
|
||||
"If you have enabled 2FA, you must create and use a new app password by clicking here." : "Jos sinulla on kaksivaiheinen todennus käytössä, sinun täytyy luoda uusi sovellussalasana ja käyttää sitä napsauttamalla tästä.",
|
||||
"Clipboard is not available" : "Leikepöytä ei ole käytettävissä",
|
||||
"WebDAV URL copied to clipboard" : "WebDAV-osoite kopioitu leikepöydälle",
|
||||
"Unable to change the favourite state of the file" : "Suosikki-tilan muuttaminen epäonnistui.",
|
||||
|
||||
@@ -183,12 +183,15 @@
|
||||
"Select file or folder to link to" : "Valitse tiedosto tai kansio, johon linkitetään",
|
||||
"Open the files app settings" : "Avaa tiedostosovelluksen asetukset",
|
||||
"Files settings" : "Tiedostojen asetukset",
|
||||
"File cannot be accessed" : "Tiedostoa ei voi käyttää",
|
||||
"You might not have have permissions to view it, ask the sender to share it" : "Oikeutesi eivät kenties riitä sen katseluun, pyydä lähettäjää jakamaan se",
|
||||
"Show hidden files" : "Näytä piilotetut tiedostot",
|
||||
"Crop image previews" : "Rajaa kuvien esikatseluja",
|
||||
"Additional settings" : "Lisäasetukset",
|
||||
"WebDAV" : "WebDAV",
|
||||
"Copy to clipboard" : "Kopioi leikepöydälle",
|
||||
"Use this address to access your Files via WebDAV" : "Käytä tätä osoitetta yhdistääksesi tiedostosi WebDAV:in kautta",
|
||||
"If you have enabled 2FA, you must create and use a new app password by clicking here." : "Jos sinulla on kaksivaiheinen todennus käytössä, sinun täytyy luoda uusi sovellussalasana ja käyttää sitä napsauttamalla tästä.",
|
||||
"Clipboard is not available" : "Leikepöytä ei ole käytettävissä",
|
||||
"WebDAV URL copied to clipboard" : "WebDAV-osoite kopioitu leikepöydälle",
|
||||
"Unable to change the favourite state of the file" : "Suosikki-tilan muuttaminen epäonnistui.",
|
||||
|
||||
@@ -185,12 +185,15 @@ OC.L10N.register(
|
||||
"Select file or folder to link to" : "Sélectionnez le fichier ou le dossier à lier",
|
||||
"Open the files app settings" : "Ouvrir les paramètres de l'application Fichiers",
|
||||
"Files settings" : "Paramètres des fichiers",
|
||||
"File cannot be accessed" : "Impossible d'accéder au fichier",
|
||||
"You might not have have permissions to view it, ask the sender to share it" : "Vous n’avez peut-être pas les autorisations pour le voir, demandez à l’expéditeur de le partager.",
|
||||
"Show hidden files" : "Afficher les fichiers masqués",
|
||||
"Crop image previews" : "Afficher en miniatures carrées",
|
||||
"Additional settings" : "Paramètres supplémentaires",
|
||||
"WebDAV" : "WebDAV",
|
||||
"Copy to clipboard" : "Copier dans le presse-papiers",
|
||||
"Use this address to access your Files via WebDAV" : "Utilisez cette adresse pour accéder à vos fichiers via WebDAV",
|
||||
"If you have enabled 2FA, you must create and use a new app password by clicking here." : "Si vous avez activé l'A2F, vous devez créer et utiliser un nouveau mot de passe d'application en cliquant ici.",
|
||||
"Clipboard is not available" : "Le presse-papiers n'est pas disponible",
|
||||
"WebDAV URL copied to clipboard" : "URL WebDAV copiée dans le presse-papier",
|
||||
"Unable to change the favourite state of the file" : "Impossible de modifier l'état favori du fichier",
|
||||
|
||||
@@ -183,12 +183,15 @@
|
||||
"Select file or folder to link to" : "Sélectionnez le fichier ou le dossier à lier",
|
||||
"Open the files app settings" : "Ouvrir les paramètres de l'application Fichiers",
|
||||
"Files settings" : "Paramètres des fichiers",
|
||||
"File cannot be accessed" : "Impossible d'accéder au fichier",
|
||||
"You might not have have permissions to view it, ask the sender to share it" : "Vous n’avez peut-être pas les autorisations pour le voir, demandez à l’expéditeur de le partager.",
|
||||
"Show hidden files" : "Afficher les fichiers masqués",
|
||||
"Crop image previews" : "Afficher en miniatures carrées",
|
||||
"Additional settings" : "Paramètres supplémentaires",
|
||||
"WebDAV" : "WebDAV",
|
||||
"Copy to clipboard" : "Copier dans le presse-papiers",
|
||||
"Use this address to access your Files via WebDAV" : "Utilisez cette adresse pour accéder à vos fichiers via WebDAV",
|
||||
"If you have enabled 2FA, you must create and use a new app password by clicking here." : "Si vous avez activé l'A2F, vous devez créer et utiliser un nouveau mot de passe d'application en cliquant ici.",
|
||||
"Clipboard is not available" : "Le presse-papiers n'est pas disponible",
|
||||
"WebDAV URL copied to clipboard" : "URL WebDAV copiée dans le presse-papier",
|
||||
"Unable to change the favourite state of the file" : "Impossible de modifier l'état favori du fichier",
|
||||
|
||||
@@ -182,14 +182,18 @@ OC.L10N.register(
|
||||
"Unknown error" : "Erro descoñecido",
|
||||
"Ownership transfer request sent" : "Enviouse solicitude de transferencia da propiedade",
|
||||
"Cannot transfer ownership of a file or folder you do not own" : "Non se pode transferir a propiedade dun ficheiro ou cartafol do que non eres propietario",
|
||||
"Select file or folder to link to" : "Seleccione o ficheiro ou cartafol ao que quere enlazar",
|
||||
"Open the files app settings" : "Abre a configuración da aplicación de ficheiros",
|
||||
"Files settings" : "Axustes de ficheiros",
|
||||
"File cannot be accessed" : "Non se pode acceder ao ficheiro",
|
||||
"You might not have have permissions to view it, ask the sender to share it" : "É posible que non teñas permisos para velo, pídelle ao remitente que o comparta",
|
||||
"Show hidden files" : "Amosar os ficheiros agochados",
|
||||
"Crop image previews" : "Recortar a vista previa das imaxes",
|
||||
"Additional settings" : "Axustes adicionais",
|
||||
"WebDAV" : "WebDAV",
|
||||
"Copy to clipboard" : "Copiar no portapapeis.",
|
||||
"Use this address to access your Files via WebDAV" : "Empregue este enderezo para acceder ao seu Ficheiros mediante WebDAV",
|
||||
"If you have enabled 2FA, you must create and use a new app password by clicking here." : "Se activaches 2FA, debes crear e utilizar un novo contrasinal da aplicación facendo clic aquí.",
|
||||
"Clipboard is not available" : "O portapapeis non está dispoñible",
|
||||
"WebDAV URL copied to clipboard" : "A URL de WebDAV copiouse no portapapeis",
|
||||
"Unable to change the favourite state of the file" : "Non é posíbel cambiar o estado favorito do ficheiro",
|
||||
|
||||
@@ -180,14 +180,18 @@
|
||||
"Unknown error" : "Erro descoñecido",
|
||||
"Ownership transfer request sent" : "Enviouse solicitude de transferencia da propiedade",
|
||||
"Cannot transfer ownership of a file or folder you do not own" : "Non se pode transferir a propiedade dun ficheiro ou cartafol do que non eres propietario",
|
||||
"Select file or folder to link to" : "Seleccione o ficheiro ou cartafol ao que quere enlazar",
|
||||
"Open the files app settings" : "Abre a configuración da aplicación de ficheiros",
|
||||
"Files settings" : "Axustes de ficheiros",
|
||||
"File cannot be accessed" : "Non se pode acceder ao ficheiro",
|
||||
"You might not have have permissions to view it, ask the sender to share it" : "É posible que non teñas permisos para velo, pídelle ao remitente que o comparta",
|
||||
"Show hidden files" : "Amosar os ficheiros agochados",
|
||||
"Crop image previews" : "Recortar a vista previa das imaxes",
|
||||
"Additional settings" : "Axustes adicionais",
|
||||
"WebDAV" : "WebDAV",
|
||||
"Copy to clipboard" : "Copiar no portapapeis.",
|
||||
"Use this address to access your Files via WebDAV" : "Empregue este enderezo para acceder ao seu Ficheiros mediante WebDAV",
|
||||
"If you have enabled 2FA, you must create and use a new app password by clicking here." : "Se activaches 2FA, debes crear e utilizar un novo contrasinal da aplicación facendo clic aquí.",
|
||||
"Clipboard is not available" : "O portapapeis non está dispoñible",
|
||||
"WebDAV URL copied to clipboard" : "A URL de WebDAV copiouse no portapapeis",
|
||||
"Unable to change the favourite state of the file" : "Non é posíbel cambiar o estado favorito do ficheiro",
|
||||
|
||||
@@ -166,10 +166,10 @@ OC.L10N.register(
|
||||
"The ownership transfer of {path} from {user} has completed." : "A(z) {path} tulajdonjogának átruházása {user} részéről sikeres.",
|
||||
"in %s" : "itt: %s",
|
||||
"File Management" : "Fájlkezelés",
|
||||
"Storage informations" : "Tárhely információk",
|
||||
"Storage informations" : "Tárhely-információk",
|
||||
"{usedQuotaByte} used" : "{usedQuotaByte} felhasználva",
|
||||
"{relative}% used" : "{relative}% felhasználva",
|
||||
"Could not refresh storage stats" : "Nem sikerült frissíteni a tárolói statisztikákat",
|
||||
"Could not refresh storage stats" : "Nem sikerült frissíteni a tárhelystatisztikákat",
|
||||
"Transfer ownership of a file or folder" : "Fájl vagy mappa tulajdonjogának átruházása",
|
||||
"Choose file or folder to transfer" : "Válasszon egy fájlt vagy mappát az átruházáshoz",
|
||||
"Change" : "Módosítás",
|
||||
@@ -183,7 +183,7 @@ OC.L10N.register(
|
||||
"Ownership transfer request sent" : "Tulajdonjog átruházási kérés elküldve",
|
||||
"Cannot transfer ownership of a file or folder you do not own" : "Nem ruházható át olyan fájl vagy mappa tulajdonjoga, amely nem Öné",
|
||||
"Select file or folder to link to" : "Válassza ki a hivatkozandó fájlt vagy mappát",
|
||||
"Open the files app settings" : "Nyissa meg a fájlalkalmazás beállításait",
|
||||
"Open the files app settings" : "Nyissa meg a Fájlok lalkalmazás beállításait",
|
||||
"Files settings" : "Fájlok beállításai",
|
||||
"Show hidden files" : "Rejtett fájlok megjelenítése",
|
||||
"Crop image previews" : "Kép előnézetek vágása",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user