diff --git a/.devcontainer/codespace.config.php b/.devcontainer/codespace.config.php index 95d4e021a9e27..0fa45c16b3e16 100644 --- a/.devcontainer/codespace.config.php +++ b/.devcontainer/codespace.config.php @@ -7,6 +7,19 @@ $codespaceName = getenv('CODESPACE_NAME'); $codespaceDomain = getenv('GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN'); +// When running under Apache, env vars from the shell profile are not inherited. +// Fall back to the Codespaces shared environment file and the well-known domain. +if (empty($codespaceName)) { + $sharedEnvFile = '/workspaces/.codespaces/shared/environment-variables.json'; + if (is_readable($sharedEnvFile)) { + $sharedEnv = json_decode(file_get_contents($sharedEnvFile), true) ?? []; + $codespaceName = $sharedEnv['CODESPACE_NAME'] ?? ''; + } +} +if (!empty($codespaceName) && empty($codespaceDomain)) { + $codespaceDomain = 'app.github.dev'; +} + $CONFIG = [ 'mail_from_address' => 'no-reply', 'mail_smtpmode' => 'smtp', diff --git a/.github/workflows/cypress.yml b/.github/workflows/cypress.yml index f3e85bdf15f1a..cf2ed977b1fbb 100644 --- a/.github/workflows/cypress.yml +++ b/.github/workflows/cypress.yml @@ -105,10 +105,10 @@ jobs: matrix: # Run multiple copies of the current job in parallel # Please increase the number or runners as your tests suite grows (0 based index for e2e tests) - containers: ['setup', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] + containers: ['setup', '0', '1', '2', '3', '4', '5', '6'] # Hack as strategy.job-total includes the "setup" and GitHub does not allow math expressions # Always align this number with the total of e2e runners (max. index + 1) - total-containers: [10] + total-containers: [7] services: mysql: diff --git a/.github/workflows/integration-sqlite.yml b/.github/workflows/integration-sqlite.yml index d60faba595a9b..3a0bbcce85a38 100644 --- a/.github/workflows/integration-sqlite.yml +++ b/.github/workflows/integration-sqlite.yml @@ -4,11 +4,6 @@ name: Integration sqlite on: pull_request: - push: - branches: - - main - - master - - stable* permissions: contents: read diff --git a/.github/workflows/phpunit-mariadb.yml b/.github/workflows/phpunit-mariadb.yml index c7a6010360c28..b8b70029dc31c 100644 --- a/.github/workflows/phpunit-mariadb.yml +++ b/.github/workflows/phpunit-mariadb.yml @@ -59,16 +59,12 @@ jobs: strategy: fail-fast: false matrix: - php-versions: ['8.2'] - mariadb-versions: ['10.6'] include: - - php-versions: '8.3' - mariadb-versions: '10.11' - coverage: ${{ github.event_name != 'pull_request' }} - - php-versions: '8.4' - mariadb-versions: '11.4' + - php-versions: '8.2' + mariadb-versions: '10.6' - php-versions: '8.5' mariadb-versions: '11.8' + coverage: ${{ github.event_name != 'pull_request' }} name: MariaDB ${{ matrix.mariadb-versions }} (PHP ${{ matrix.php-versions }}) - database tests @@ -108,6 +104,7 @@ jobs: ini-file: development ini-values: disable_functions="" env: + fail-fast: true GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Set up dependencies diff --git a/.github/workflows/phpunit-memcached.yml b/.github/workflows/phpunit-memcached.yml index 96729c1e8dd58..7aa42765fe9a8 100644 --- a/.github/workflows/phpunit-memcached.yml +++ b/.github/workflows/phpunit-memcached.yml @@ -88,6 +88,7 @@ jobs: ini-file: development ini-values: disable_functions="" env: + fail-fast: true GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Set up dependencies diff --git a/.github/workflows/phpunit-mysql-sharding.yml b/.github/workflows/phpunit-mysql-sharding.yml index f8c458b8e961a..204b151b92e18 100644 --- a/.github/workflows/phpunit-mysql-sharding.yml +++ b/.github/workflows/phpunit-mysql-sharding.yml @@ -56,8 +56,8 @@ jobs: strategy: fail-fast: false matrix: - php-versions: ['8.2'] - mysql-versions: ['8.4'] + php-versions: ["8.2"] + mysql-versions: ["8.4"] name: Sharding - MySQL ${{ matrix.mysql-versions }} (PHP ${{ matrix.php-versions }}) - database tests @@ -137,6 +137,7 @@ jobs: ini-file: development ini-values: disable_functions="" env: + fail-fast: true GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Set up dependencies diff --git a/.github/workflows/phpunit-mysql.yml b/.github/workflows/phpunit-mysql.yml index 431f53771fdf7..a0f4a3f3136ed 100644 --- a/.github/workflows/phpunit-mysql.yml +++ b/.github/workflows/phpunit-mysql.yml @@ -59,16 +59,12 @@ jobs: strategy: fail-fast: false matrix: - php-versions: ['8.2'] - mysql-versions: ['8.0'] include: - mysql-versions: '8.0' - php-versions: '8.3' - coverage: ${{ github.event_name != 'pull_request' }} - - mysql-versions: '8.4' - php-versions: '8.4' + php-versions: '8.2' - mysql-versions: '8.4' php-versions: '8.5' + coverage: ${{ github.event_name != 'pull_request' }} name: MySQL ${{ matrix.mysql-versions }} (PHP ${{ matrix.php-versions }}) - database tests @@ -108,6 +104,7 @@ jobs: ini-file: development ini-values: disable_functions="" env: + fail-fast: true GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Set up dependencies diff --git a/.github/workflows/phpunit-nodb.yml b/.github/workflows/phpunit-nodb.yml index 3c7af086a4bd1..3504d3568754a 100644 --- a/.github/workflows/phpunit-nodb.yml +++ b/.github/workflows/phpunit-nodb.yml @@ -59,9 +59,9 @@ jobs: strategy: fail-fast: false matrix: - php-versions: ['8.3', '8.4', '8.5'] + php-versions: ["8.3", "8.4", "8.5"] include: - - php-versions: '8.2' + - php-versions: "8.2" coverage: ${{ github.event_name != 'pull_request' }} name: No DB unit tests (PHP ${{ matrix.php-versions }}) @@ -86,12 +86,13 @@ jobs: with: php-version: ${{ matrix.php-versions }} # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation - extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, imagick, intl, json, libxml, mbstring, openssl, pcntl, posix, redis, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite + extensions: apcu, bz2, ctype, curl, dom, fileinfo, gd, iconv, imagick, intl, json, libxml, mbstring, openssl, pcntl, pdo_sqlite, posix, redis, session, simplexml, sqlite, xmlreader, xmlwriter, zip, zlib coverage: ${{ matrix.coverage && 'xdebug' || 'none' }} ini-file: development # Required for tests that use pcntl ini-values: disable_functions="" env: + fail-fast: true GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Set up dependencies @@ -106,7 +107,7 @@ jobs: php -f tests/enable_all.php - name: PHPUnit nodb testsuite - run: composer run test -- --exclude-group DB --exclude-group SLOWDB --log-junit junit.xml ${{ matrix.coverage && '--coverage-clover ./clover.nodb.xml' || '' }} + run: composer run test -- --exclude-group DB --exclude-group SLOWDB --exclude-group Memcached --exclude-group PRIMARY-swift --exclude-group PRIMARY-s3 --exclude-group PRIMARY-azure --log-junit junit.xml ${{ matrix.coverage && '--coverage-clover ./clover.nodb.xml' || '' }} - name: Upload nodb code coverage if: ${{ !cancelled() && matrix.coverage }} diff --git a/.github/workflows/phpunit-object-store-primary.yml b/.github/workflows/phpunit-object-store-primary.yml index b7187d349a0a0..0b1e0106b71c7 100644 --- a/.github/workflows/phpunit-object-store-primary.yml +++ b/.github/workflows/phpunit-object-store-primary.yml @@ -49,8 +49,8 @@ jobs: strategy: fail-fast: false matrix: - php-versions: ['8.2'] - key: ['s3', 's3-multibucket'] + php-versions: ["8.2"] + key: ["s3", "s3-multibucket"] name: php${{ matrix.php-versions }}-${{ matrix.key }}-minio @@ -84,6 +84,7 @@ jobs: php-version: ${{ matrix.php-versions }} extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, redis, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite env: + fail-fast: true GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Set up Nextcloud @@ -113,10 +114,9 @@ jobs: 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 - object-store-primary-summary: runs-on: ubuntu-latest-low - needs: [changes,object-store-primary-tests-minio] + needs: [changes, object-store-primary-tests-minio] if: always() diff --git a/.github/workflows/phpunit-oci.yml b/.github/workflows/phpunit-oci.yml index bf3751175af6d..2e524df428f90 100644 --- a/.github/workflows/phpunit-oci.yml +++ b/.github/workflows/phpunit-oci.yml @@ -62,13 +62,9 @@ jobs: include: - oracle-versions: '18' php-versions: '8.2' - coverage: ${{ github.event_name != 'pull_request' }} - - oracle-versions: '21' - php-versions: '8.3' - - oracle-versions: '23' - php-versions: '8.4' - oracle-versions: '23' php-versions: '8.5' + coverage: ${{ github.event_name != 'pull_request' }} name: Oracle ${{ matrix.oracle-versions }} (PHP ${{ matrix.php-versions }}) - database tests @@ -115,6 +111,7 @@ jobs: ini-file: development ini-values: disable_functions="" env: + fail-fast: true GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Set up dependencies diff --git a/.github/workflows/phpunit-pgsql.yml b/.github/workflows/phpunit-pgsql.yml index 91a365e301b5c..789ac9ff0d54c 100644 --- a/.github/workflows/phpunit-pgsql.yml +++ b/.github/workflows/phpunit-pgsql.yml @@ -59,17 +59,16 @@ jobs: strategy: fail-fast: false matrix: - php-versions: ['8.2'] - # To keep the matrix smaller we ignore PostgreSQL versions in between as we already test the minimum and the maximum - postgres-versions: ['14'] include: + - php-versions: '8.2' + postgres-versions: '14' - php-versions: '8.3' postgres-versions: '18' - coverage: ${{ github.event_name != 'pull_request' }} - php-versions: '8.4' postgres-versions: '18' - php-versions: '8.5' postgres-versions: '18' + coverage: ${{ github.event_name != 'pull_request' }} name: PostgreSQL ${{ matrix.postgres-versions }} (PHP ${{ matrix.php-versions }}) - database tests @@ -108,6 +107,7 @@ jobs: ini-file: development ini-values: disable_functions="" env: + fail-fast: true GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Set up dependencies diff --git a/.github/workflows/phpunit-sqlite.yml b/.github/workflows/phpunit-sqlite.yml index 255b2ddeb6887..0ff8a56e5a14b 100644 --- a/.github/workflows/phpunit-sqlite.yml +++ b/.github/workflows/phpunit-sqlite.yml @@ -59,9 +59,8 @@ jobs: strategy: fail-fast: false matrix: - php-versions: ['8.3', '8.4', '8.5'] include: - - php-versions: '8.2' + - php-versions: '8.5' coverage: ${{ github.event_name != 'pull_request' }} name: SQLite (PHP ${{ matrix.php-versions }}) @@ -91,6 +90,7 @@ jobs: ini-file: development ini-values: disable_functions="" env: + fail-fast: true GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Set up dependencies @@ -111,7 +111,7 @@ jobs: run: ./occ app:list && echo "======= System config =======" && ./occ config:list system - name: PHPUnit database tests - run: composer run test:db -- --log-junit junit.xml ${{ matrix.coverage && '--coverage-clover ./clover.db.xml' || '' }} tests/lib/Preview/PostscriptTest.php + run: composer run test:db -- --log-junit junit.xml ${{ matrix.coverage && '--coverage-clover ./clover.db.xml' || '' }} - name: Upload db code coverage if: ${{ !cancelled() && matrix.coverage }} diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml new file mode 100644 index 0000000000000..6dbecc0f2f65b --- /dev/null +++ b/.github/workflows/playwright.yml @@ -0,0 +1,182 @@ +# SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors +# SPDX-License-Identifier: MIT + +name: Playwright Tests + +on: + pull_request: + branches: [ master ] + +permissions: + contents: read + +jobs: + playwright-setup: + timeout-minutes: 15 + name: Playwright setup + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + submodules: true # for 3rdparty + - name: Read package.json + uses: nextcloud-libraries/parse-package-engines-action@122ae05d4257008180a514e1ddeb0c1b9d094bdd # v0.1.0 + id: versions + - name: Set up node + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 + with: + node-version: ${{ steps.versions.outputs.node-version }} + - name: Set up npm + run: npm i -g 'npm@${{ steps.versions.outputs.package-manager-version }}' + - name: Install dependencies and build + run: | + npm ci + npm run build --if-present + - name: Save context + uses: buildjet/cache/save@3e70d19e31d6a8030aeddf6ed8dbe601f94d09f4 # v4.0.2 + with: + key: playwright-context-${{ github.run_id }} + path: ./ + + playwright-tests: + needs: [playwright-setup] + timeout-minutes: 60 + name: Playwright tests ${{ matrix.shardIndex }} / ${{ matrix.shardTotal }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + shardIndex: [1, 2, 3] + shardTotal: [3] + outputs: + node-version: ${{ steps.versions.outputs.node-version }} + package-manager-version: ${{ steps.versions.outputs.package-manager-version }} + + steps: + - name: Restore context + id: cache + uses: buildjet/cache/restore@3e70d19e31d6a8030aeddf6ed8dbe601f94d09f4 # v4.0.2 + with: + key: playwright-context-${{ github.run_id }} + path: ./ + + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + if: steps.cache.outputs.cache-hit != 'true' + with: + persist-credentials: false + submodules: true # for 3rdparty + + - name: Read package.json + if: steps.cache.outputs.cache-hit != 'true' + uses: nextcloud-libraries/parse-package-engines-action@122ae05d4257008180a514e1ddeb0c1b9d094bdd # v0.1.0 + id: versions + + - name: Set up node + if: steps.cache.outputs.cache-hit != 'true' + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 + with: + node-version: ${{ steps.versions.outputs.node-version }} + + - name: Set up npm + if: steps.cache.outputs.cache-hit != 'true' + run: npm i -g 'npm@${{ steps.versions.outputs.package-manager-version }}' + + - name: Install dependencies and build + if: steps.cache.outputs.cache-hit != 'true' + run: | + npm ci + npm run build --if-present + + - name: Install Playwright browsers + run: npx playwright install --with-deps + + - name: Run Playwright tests + run: npm run playwright -- --shard='${{ matrix.shardIndex }}/${{ matrix.shardTotal }}' + + - name: Show logs + if: failure() + run: | + for id in $(docker ps -aq); do + docker container inspect "$id" --format '=== Logs for container {{.Name}} ===' + docker logs "$id" >> nextcloud.log + done + echo '=== Nextcloud server logs ===' + docker exec nextcloud-e2e-test-server_server cat data/nextcloud.log + + - name: Upload blob report to GitHub Actions Artifacts + if: ${{ !cancelled() }} + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: blob-report-${{ matrix.shardIndex }} + path: blob-report + retention-days: 1 + + merge-reports: + # Merge reports after playwright-tests, even if some shards have failed + if: ${{ !cancelled() }} + needs: [playwright-tests] + + runs-on: ubuntu-latest-low + steps: + - name: Restore context + id: cache + uses: buildjet/cache/restore@3e70d19e31d6a8030aeddf6ed8dbe601f94d09f4 # v4.0.2 + with: + key: playwright-context-${{ github.run_id }} + path: ./ + + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + if: steps.cache.outputs.cache-hit != 'true' + with: + persist-credentials: false + + - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 + if: steps.cache.outputs.cache-hit != 'true' + with: + node-version: ${{ needs.playwright-tests.outputs.node-version }} + + - name: Set up npm + if: steps.cache.outputs.cache-hit != 'true' + run: npm i -g 'npm@${{ needs.playwright-tests.outputs.package-manager-version }}' + + - name: Install dependencies + if: steps.cache.outputs.cache-hit != 'true' + run: npm ci + + - name: Download blob reports from GitHub Actions Artifacts + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 + with: + path: all-blob-reports + pattern: blob-report-* + merge-multiple: true + + - name: Merge into HTML Report + run: npx playwright merge-reports --config tests/playwright/merge.config.ts --reporter html,github ./all-blob-reports + + - name: Upload HTML report + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: html-report--attempt-${{ github.run_attempt }} + path: playwright-report + retention-days: 7 + + - name: Show the logs + run: | + echo 'To view the report:' + echo ' 1. Extract the folder from the zip file' + echo ' 2. run "npx playwright show-report name-of-my-extracted-playwright-report"' + + summary: + permissions: + contents: none + runs-on: ubuntu-latest-low + needs: [playwright-tests] + + if: always() + + name: playwright-test-summary + + steps: + - name: Summary status + run: if ${{ needs.playwright-tests.result != 'success' }}; then exit 1; fi diff --git a/.github/workflows/static-code-analysis.yml b/.github/workflows/static-code-analysis.yml index f350ec59b67af..f71a22c9f49d3 100644 --- a/.github/workflows/static-code-analysis.yml +++ b/.github/workflows/static-code-analysis.yml @@ -43,6 +43,8 @@ jobs: - 'vendor-bin/**' - 'composer.json' - 'composer.lock' + - 'psalm*.xml' + - 'build/psalm-baseline*.xml' - '**.php' static-code-analysis: diff --git a/.gitignore b/.gitignore index 87035b7f67546..a53c847f4a1da 100644 --- a/.gitignore +++ b/.gitignore @@ -145,7 +145,9 @@ Vagrantfile # Tests - auto-generated files /data-autotest +/playwright-report /results.sarif +/test-results /tests/.phpunit.cache /tests/.phpunit.result.cache /tests/coverage* diff --git a/3rdparty b/3rdparty index f7176e8becad6..577d9952b0a51 160000 --- a/3rdparty +++ b/3rdparty @@ -1 +1 @@ -Subproject commit f7176e8becad6b9ef000422fc6039025b58dd2c9 +Subproject commit 577d9952b0a511876b18ac86f6b8f4f088c747d0 diff --git a/AUTHORS b/AUTHORS index fe478401fddb4..648c3aa52212d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -32,6 +32,7 @@ - Andreas Pflug - Andrew Brown - Andrey Borysenko + - Andrey Dyakov - André Gaul - Andy Xheli - Anna Larch @@ -629,6 +630,7 @@ - zorn-v - zulan - Łukasz Buśko + - Michał Roszak - Nextcloud GmbH - ownCloud GmbH - ownCloud, Inc. diff --git a/README.md b/README.md index c88668fa4a905..2f526984a6a66 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,16 @@ Several apps that are included by default in regular releases such as [First run Otherwise, git checkouts can be handled the same as release archives, by using the `stable*` branches. Note they should never be used on production systems. +#### Testing your code + +We use multiple test frameworks for specific areas of the code: +- PHPUnit for PHP unit tests +- Behat for PHP integration tests +- Vitest for Javascript / Typescript unit tests +- Playwright for end-to-end tests + +For our end-to-end tests using Playwright you can refer [to our documentation](./tests/playwright/README.md) +on how to debug errors and to contribute new test cases. ### Tools we use 🛠 diff --git a/apps/admin_audit/l10n/bn_BD.js b/apps/admin_audit/l10n/bn_BD.js index d68dffdc27c8c..a1fdcc9f251e6 100644 --- a/apps/admin_audit/l10n/bn_BD.js +++ b/apps/admin_audit/l10n/bn_BD.js @@ -1,7 +1,7 @@ OC.L10N.register( "admin_audit", { - "Auditing / Logging" : "নিরীক্ষা", - "Provides logging abilities for Nextcloud such as logging file accesses or otherwise sensitive actions." : "নেক্সটক্লাউডের নিরীক্ষামূলক সক্ষমতা প্রদান করে যেমন লগিং ফাইল অ্যাক্সেস বা অন্য কোনো জরুরী পদক্ষেপসমূহ" + "Auditing / Logging" : "Auditing / Logging ", + "Provides logging abilities for Nextcloud such as logging file accesses or otherwise sensitive actions." : "Provides logging abilities for Nextcloud such as logging file accesses or otherwise sensitive actions." }, "nplurals=2; plural=(n != 1);"); diff --git a/apps/admin_audit/l10n/bn_BD.json b/apps/admin_audit/l10n/bn_BD.json index 57b1931a56134..978234382f718 100644 --- a/apps/admin_audit/l10n/bn_BD.json +++ b/apps/admin_audit/l10n/bn_BD.json @@ -1,5 +1,5 @@ { "translations": { - "Auditing / Logging" : "নিরীক্ষা", - "Provides logging abilities for Nextcloud such as logging file accesses or otherwise sensitive actions." : "নেক্সটক্লাউডের নিরীক্ষামূলক সক্ষমতা প্রদান করে যেমন লগিং ফাইল অ্যাক্সেস বা অন্য কোনো জরুরী পদক্ষেপসমূহ" + "Auditing / Logging" : "Auditing / Logging ", + "Provides logging abilities for Nextcloud such as logging file accesses or otherwise sensitive actions." : "Provides logging abilities for Nextcloud such as logging file accesses or otherwise sensitive actions." },"pluralForm" :"nplurals=2; plural=(n != 1);" } \ No newline at end of file diff --git a/apps/appstore/l10n/fa.js b/apps/appstore/l10n/fa.js new file mode 100644 index 0000000000000..0925e0e9fc3d2 --- /dev/null +++ b/apps/appstore/l10n/fa.js @@ -0,0 +1,157 @@ +OC.L10N.register( + "appstore", + { + "App store" : "فروشگاه برنامه‌ها", + "Apps" : "کاره‌ها", + "Nextcloud Appstore" : "فروشگاه برنامه‌های Nextcloud", + "Appstore" : "فروشگاه برنامه‌ها", + "App name" : "نام کاره", + "Version" : "نسخه", + "Support level" : "سطح پشتیبانی", + "Groups" : "گروه ها", + "Actions" : "کنش‌ها", + "Show details" : "نمایش جزئیات", + "is loading…" : "در حال بارگذاری…", + "Filter view" : "نمای فیلتر", + "Grid size" : "اندازه گرید", + "Small grid size" : "اندازه گرید کوچک", + "Medium grid size" : "اندازه گرید متوسط", + "Large grid size" : "اندازه گرید بزرگ", + "Show incompatible" : "نمایش موارد ناسازگار", + "Grid view" : "نمایش گرید", + "Force enable {suite}?" : "فعال‌سازی اجباری {suite}؟", + "Enabling {suite} requires force enabling the app. This may cause issues with your Nextcloud instance. Are you sure you want to proceed?" : "فعال‌سازی {suite} مستلزم فعال‌سازی اجباری برنامه است. این کار ممکن است باعث بروز مشکلاتی در نمونه‌ی Nextcloud شما شود. آیا مطمئن هستید که می‌خواهید ادامه دهید؟", + "Force enable" : "فعال‌سازی اجباری", + "Cancel" : "انصراف", + "Office suite switching is managed through the Nextcloud All-in-One interface." : "جابجایی مجموعه‌ی اداری از طریق رابط Nextcloud All-in-One مدیریت می‌شود.", + "Please use the AIO interface to switch between office suites." : "لطفاً برای جابجایی میان مجموعه‌های اداری از رابط AIO استفاده کنید.", + "Select your preferred office suite." : "مجموعه‌ی اداری مورد نظر خود را انتخاب کنید.", + "Please note that installing requires manual server setup." : "توجه داشته باشید که نصب نیازمند پیکربندی دستی سرور است.", + "Disable office suites" : "غیرفعال کردن مجموعه‌های اداری", + "installed" : "نصب‌شده", + "Features" : "ویژگی‌ها", + "Learn more" : "بیشتر بدانید", + "Daemon" : "فرایندهای پس زمینه", + "Deploy Daemon" : "دیمن استقرار", + "Type" : "نوع", + "Name" : "نام", + "Display Name" : "نام نمایشی", + "GPUs support" : "پشتیبانی از پردازنده‌های گرافیکی (GPU)", + "Compute device" : "دستگاه محاسباتی", + "Advanced deploy options" : "گزینه‌های پیشرفته استقرار", + "Edit ExApp deploy options before installation" : "ویرایش گزینه‌های استقرار ExApp پیش از نصب", + "Configured ExApp deploy options. Can be set only during installation" : "گزینه‌های استقرار پیکربندی‌شده‌ی ExApp. تنها در هنگام نصب قابل تنظیم است", + "Environment variables" : "متغیرهای محیطی", + "ExApp container environment variables" : "متغیرهای محیطی کانتینر ExApp", + "No environment variables defined" : "هیچ متغیر محیطی‌ای تعریف نشده است", + "Mounts" : "سوارسازی‌ها", + "Define host folder mounts to bind to the ExApp container" : "پوشه‌های میزبان را برای اتصال به کانتینر ExApp تعریف کنید", + "Must exist on the Deploy daemon host prior to installing the ExApp" : "باید پیش از نصب ExApp روی میزبان دیمن استقرار وجود داشته باشد", + "Host path" : "مسیر میزبان", + "Container path" : "مسیر کانتینر", + "Read-only" : "فقط‌خواندنی", + "Remove mount" : "حذف نقطه نصب", + "New mount" : "نقطه نصب جدید", + "Enter path to host folder" : "مسیر پوشه میزبان را وارد کنید", + "Enter path to container folder" : "مسیر پوشه کانتینر را وارد کنید", + "Toggle read-only mode" : "تغییر حالت فقط خواندنی", + "Confirm adding new mount" : "تأیید افزودن نقطه نصب جدید", + "Confirm" : "تأیید", + "Cancel adding mount" : "لغو افزودن نقطه نصب", + "Add mount" : "افزودن نقطه نصب", + "ExApp container mounts" : "نقطه‌های نصب کانتینر ExApp", + "No mounts defined" : "هیچ نقطه نصبی تعریف نشده است", + "Description" : "شرح", + "View in store" : "نمایش در فروشگاه", + "Visit website" : "سر زدن به پایگاه وب", + "Usage documentation" : "مستندات کاربری", + "Admin documentation" : "مستندات مدیریتی", + "Developer documentation" : "مستندات توسعه", + "Details" : "جزییات", + "This app cannot be installed because the following dependencies are not fulfilled:" : "امکان نصب این برنامه وجود ندارد، این پیش‌نیازها انجام نشده‌اند:", + "Missing dependencies" : "وابستگی‌های ناموجود", + "This app cannot be limited to groups because it provides functionality that is executed before group membership is determined." : "این برنامه را نمی‌توان به گروه‌ها محدود کرد، زیرا قابلیتی را فراهم می‌کند که پیش از مشخص‌شدن عضویت در گروه اجرا می‌شود.", + "Limited to groups" : "محدود به گروه‌ها", + "Latest updated" : "آخرین به‌روزرسانی", + "Author" : "نویسنده", + "Categories" : "دسته‌ها", + "Resources" : "منابع", + "Documentation resources" : "منابع مستندات", + "Changelog" : "تغییر", + "Supported" : "پشتیبانی شده", + "Featured" : "برگزیده", + "This app is supported via your current Nextcloud subscription." : "این کاره از طریق اشتراک فعلی نکست کلودتان پشتیبانی می شود.", + "Featured apps are developed by and within the community. They offer central functionality and are ready for production use." : "برنامه‌های برگزیده توسط و در داخل جامعه توسعه داده می‌شوند. این برنامه‌ها، عملکردهای مرکزی را ارائه می‌دهند و برای استفاده نهایی آماده هستند.", + "Community rating: {score}/5" : "امتیاز جامعه: {score}/5", + "Choose Deploy Daemon for {appName}" : "انتخاب Deploy Daemon برای {appName}", + "No Deploy daemons configured" : "هیچ دیمن استقراری پیکربندی نشده است", + "Register a custom one or setup from available templates" : "یک مورد سفارشی ثبت کنید یا از قالب‌های موجود پیکربندی کنید", + "Manage Deploy daemons" : "مدیریت دیمن‌های استقرار", + "Registered Deploy daemons list" : "فهرست دیمن‌های استقرار ثبت‌شده", + "Default" : "پیش‌فرض", + "Carousel" : "نمایش چرخشی", + "Previous slide" : "اسلاید قبلی", + "Next slide" : "اسلاید بعدی", + "Choose slide to display" : "اسلاید موردنظر برای نمایش را انتخاب کنید", + "{index} of {total}" : "{index} از {total}", + "Limit to groups" : "محدود کردن به گروه‌ها", + "Restrict the usage of {app} to members of the following groups." : "استفاده از {app} را به اعضای گروه‌های زیر محدود کنید.", + "Reset limitation" : "بازنشانی محدودیت", + "Save" : "ذخیره", + "Loading app list" : "در حال بارگذاری فهرست کاره‌ها", + "No matching apps found" : "هیچ کاره‌ای مطابق یافت نشد", + "Search everywhere" : "جستجو در هر کجا.", + "Download and enable all" : "بارگیری و فعال کردن همه", + "Could not load app discover section" : "بارگذاری بخش کاوش کاره‌ها ممکن نشد", + "Could not render element" : "نمایش عنصر ممکن نشد", + "Nothing to show" : "چیزی برای نمایش وجود ندارد", + "Could not load section content from app store." : "بارگذاری محتوای این بخش از فروشگاه برنامه‌ها ممکن نشد.", + "Loading" : "در حال بارگذاری", + "Fetching the latest news…" : "در حال دریافت تازه‌ترین اخبار…", + "Appstore categories" : "دسته‌های فروشگاه برنامه‌ها", + "Search apps…" : "جستجوی برنامه‌ها…", + "Loading categories" : "در حال بارگذاری دسته‌ها", + "Please enter more characters to search." : "برای جستجو، نویسه‌های بیشتری وارد کنید.", + "Search apps" : "جستجوی برنامه‌ها", + "Version {version}, {license}-licensed" : "نسخه {version}، با مجوز {license}", + "Version {version}" : "نسخه {version}", + "Disable" : "غیرفعال کردن", + "Enable" : "فعالسازی", + "Deploy and enable" : "استقرار و فعال سازی", + "Download and enable" : "بارگیری و فعال سازی", + "Install and enable" : "نصب و فعال سازی", + "Deploy and force enable" : "استقرار و فعال سازی اجباری", + "Download and force enable" : "بارگیری و فعال سازی اجباری", + "Install and force enable" : "نصب و فعال سازی اجباری", + "Rate the app" : "امتیازدهی به برنامه", + "Report a bug" : "گزارش یک اشکال", + "Ask questions or discuss the app" : "پرسش یا گفتگو درباره برنامه", + "Visit the website" : "بازدید از وب‌سایت", + "Remove" : "حذف", + "Update to {version}" : "به‌روز رسانی به {version}", + "Discover" : "کشف", + "Your apps" : "برنامه‌های شما", + "Active apps" : "برنامه‌های فعال", + "Disabled apps" : "برنامه‌های غیرفعال", + "Updates" : "به روز رسانی ها", + "App bundles" : "بسته های برنامه", + "Featured apps" : "کاره‌های برگزیده", + "Supported apps" : "برنامه‌های پشتیبانی‌شده", + "Search results" : "نتایج جستجو", + "Powered by Euro-Office" : "با قدرت Euro-Office", + "Good Nextcloud integration" : "یکپارچگی خوب با Nextcloud", + "Open source" : "متن‌باز", + "Best performance" : "بهترین کارایی", + "Limited ODF compatibility" : "سازگاری محدود با ODF", + "Best Microsoft compatibility" : "بهترین سازگاری با Microsoft", + "Best Nextcloud integration" : "بهترین یکپارچگی با Nextcloud", + "Good performance" : "کارایی خوب", + "Best security: documents never leave your server" : "بهترین امنیت: اسناد هرگز از سرور شما خارج نمی‌شوند", + "Best ODF compatibility" : "بهترین سازگاری با ODF", + "Best support for legacy files" : "بهترین پشتیبانی از پرونده‌های قدیمی", + "Could not load app categories. Please try again later." : "بارگذاری دسته‌بندی برنامه‌ها ممکن نشد. لطفاً بعداً دوباره تلاش کنید.", + "Could not load apps list. Please try again later." : "بارگذاری فهرست برنامه‌ها ممکن نشد. لطفاً بعداً دوباره تلاش کنید.", + "Could not update the app. Please try again later." : "به‌روزرسانی این برنامه ممکن نشد. لطفاً بعداً دوباره تلاش کنید.", + "An error occurred during the request. Unable to proceed." : "هنگام درخواست خطایی رخ داد. امکان ادامه وجود ندارد." +}, +"nplurals=2; plural=(n > 1);"); diff --git a/apps/appstore/l10n/fa.json b/apps/appstore/l10n/fa.json new file mode 100644 index 0000000000000..6335e341e50ec --- /dev/null +++ b/apps/appstore/l10n/fa.json @@ -0,0 +1,155 @@ +{ "translations": { + "App store" : "فروشگاه برنامه‌ها", + "Apps" : "کاره‌ها", + "Nextcloud Appstore" : "فروشگاه برنامه‌های Nextcloud", + "Appstore" : "فروشگاه برنامه‌ها", + "App name" : "نام کاره", + "Version" : "نسخه", + "Support level" : "سطح پشتیبانی", + "Groups" : "گروه ها", + "Actions" : "کنش‌ها", + "Show details" : "نمایش جزئیات", + "is loading…" : "در حال بارگذاری…", + "Filter view" : "نمای فیلتر", + "Grid size" : "اندازه گرید", + "Small grid size" : "اندازه گرید کوچک", + "Medium grid size" : "اندازه گرید متوسط", + "Large grid size" : "اندازه گرید بزرگ", + "Show incompatible" : "نمایش موارد ناسازگار", + "Grid view" : "نمایش گرید", + "Force enable {suite}?" : "فعال‌سازی اجباری {suite}؟", + "Enabling {suite} requires force enabling the app. This may cause issues with your Nextcloud instance. Are you sure you want to proceed?" : "فعال‌سازی {suite} مستلزم فعال‌سازی اجباری برنامه است. این کار ممکن است باعث بروز مشکلاتی در نمونه‌ی Nextcloud شما شود. آیا مطمئن هستید که می‌خواهید ادامه دهید؟", + "Force enable" : "فعال‌سازی اجباری", + "Cancel" : "انصراف", + "Office suite switching is managed through the Nextcloud All-in-One interface." : "جابجایی مجموعه‌ی اداری از طریق رابط Nextcloud All-in-One مدیریت می‌شود.", + "Please use the AIO interface to switch between office suites." : "لطفاً برای جابجایی میان مجموعه‌های اداری از رابط AIO استفاده کنید.", + "Select your preferred office suite." : "مجموعه‌ی اداری مورد نظر خود را انتخاب کنید.", + "Please note that installing requires manual server setup." : "توجه داشته باشید که نصب نیازمند پیکربندی دستی سرور است.", + "Disable office suites" : "غیرفعال کردن مجموعه‌های اداری", + "installed" : "نصب‌شده", + "Features" : "ویژگی‌ها", + "Learn more" : "بیشتر بدانید", + "Daemon" : "فرایندهای پس زمینه", + "Deploy Daemon" : "دیمن استقرار", + "Type" : "نوع", + "Name" : "نام", + "Display Name" : "نام نمایشی", + "GPUs support" : "پشتیبانی از پردازنده‌های گرافیکی (GPU)", + "Compute device" : "دستگاه محاسباتی", + "Advanced deploy options" : "گزینه‌های پیشرفته استقرار", + "Edit ExApp deploy options before installation" : "ویرایش گزینه‌های استقرار ExApp پیش از نصب", + "Configured ExApp deploy options. Can be set only during installation" : "گزینه‌های استقرار پیکربندی‌شده‌ی ExApp. تنها در هنگام نصب قابل تنظیم است", + "Environment variables" : "متغیرهای محیطی", + "ExApp container environment variables" : "متغیرهای محیطی کانتینر ExApp", + "No environment variables defined" : "هیچ متغیر محیطی‌ای تعریف نشده است", + "Mounts" : "سوارسازی‌ها", + "Define host folder mounts to bind to the ExApp container" : "پوشه‌های میزبان را برای اتصال به کانتینر ExApp تعریف کنید", + "Must exist on the Deploy daemon host prior to installing the ExApp" : "باید پیش از نصب ExApp روی میزبان دیمن استقرار وجود داشته باشد", + "Host path" : "مسیر میزبان", + "Container path" : "مسیر کانتینر", + "Read-only" : "فقط‌خواندنی", + "Remove mount" : "حذف نقطه نصب", + "New mount" : "نقطه نصب جدید", + "Enter path to host folder" : "مسیر پوشه میزبان را وارد کنید", + "Enter path to container folder" : "مسیر پوشه کانتینر را وارد کنید", + "Toggle read-only mode" : "تغییر حالت فقط خواندنی", + "Confirm adding new mount" : "تأیید افزودن نقطه نصب جدید", + "Confirm" : "تأیید", + "Cancel adding mount" : "لغو افزودن نقطه نصب", + "Add mount" : "افزودن نقطه نصب", + "ExApp container mounts" : "نقطه‌های نصب کانتینر ExApp", + "No mounts defined" : "هیچ نقطه نصبی تعریف نشده است", + "Description" : "شرح", + "View in store" : "نمایش در فروشگاه", + "Visit website" : "سر زدن به پایگاه وب", + "Usage documentation" : "مستندات کاربری", + "Admin documentation" : "مستندات مدیریتی", + "Developer documentation" : "مستندات توسعه", + "Details" : "جزییات", + "This app cannot be installed because the following dependencies are not fulfilled:" : "امکان نصب این برنامه وجود ندارد، این پیش‌نیازها انجام نشده‌اند:", + "Missing dependencies" : "وابستگی‌های ناموجود", + "This app cannot be limited to groups because it provides functionality that is executed before group membership is determined." : "این برنامه را نمی‌توان به گروه‌ها محدود کرد، زیرا قابلیتی را فراهم می‌کند که پیش از مشخص‌شدن عضویت در گروه اجرا می‌شود.", + "Limited to groups" : "محدود به گروه‌ها", + "Latest updated" : "آخرین به‌روزرسانی", + "Author" : "نویسنده", + "Categories" : "دسته‌ها", + "Resources" : "منابع", + "Documentation resources" : "منابع مستندات", + "Changelog" : "تغییر", + "Supported" : "پشتیبانی شده", + "Featured" : "برگزیده", + "This app is supported via your current Nextcloud subscription." : "این کاره از طریق اشتراک فعلی نکست کلودتان پشتیبانی می شود.", + "Featured apps are developed by and within the community. They offer central functionality and are ready for production use." : "برنامه‌های برگزیده توسط و در داخل جامعه توسعه داده می‌شوند. این برنامه‌ها، عملکردهای مرکزی را ارائه می‌دهند و برای استفاده نهایی آماده هستند.", + "Community rating: {score}/5" : "امتیاز جامعه: {score}/5", + "Choose Deploy Daemon for {appName}" : "انتخاب Deploy Daemon برای {appName}", + "No Deploy daemons configured" : "هیچ دیمن استقراری پیکربندی نشده است", + "Register a custom one or setup from available templates" : "یک مورد سفارشی ثبت کنید یا از قالب‌های موجود پیکربندی کنید", + "Manage Deploy daemons" : "مدیریت دیمن‌های استقرار", + "Registered Deploy daemons list" : "فهرست دیمن‌های استقرار ثبت‌شده", + "Default" : "پیش‌فرض", + "Carousel" : "نمایش چرخشی", + "Previous slide" : "اسلاید قبلی", + "Next slide" : "اسلاید بعدی", + "Choose slide to display" : "اسلاید موردنظر برای نمایش را انتخاب کنید", + "{index} of {total}" : "{index} از {total}", + "Limit to groups" : "محدود کردن به گروه‌ها", + "Restrict the usage of {app} to members of the following groups." : "استفاده از {app} را به اعضای گروه‌های زیر محدود کنید.", + "Reset limitation" : "بازنشانی محدودیت", + "Save" : "ذخیره", + "Loading app list" : "در حال بارگذاری فهرست کاره‌ها", + "No matching apps found" : "هیچ کاره‌ای مطابق یافت نشد", + "Search everywhere" : "جستجو در هر کجا.", + "Download and enable all" : "بارگیری و فعال کردن همه", + "Could not load app discover section" : "بارگذاری بخش کاوش کاره‌ها ممکن نشد", + "Could not render element" : "نمایش عنصر ممکن نشد", + "Nothing to show" : "چیزی برای نمایش وجود ندارد", + "Could not load section content from app store." : "بارگذاری محتوای این بخش از فروشگاه برنامه‌ها ممکن نشد.", + "Loading" : "در حال بارگذاری", + "Fetching the latest news…" : "در حال دریافت تازه‌ترین اخبار…", + "Appstore categories" : "دسته‌های فروشگاه برنامه‌ها", + "Search apps…" : "جستجوی برنامه‌ها…", + "Loading categories" : "در حال بارگذاری دسته‌ها", + "Please enter more characters to search." : "برای جستجو، نویسه‌های بیشتری وارد کنید.", + "Search apps" : "جستجوی برنامه‌ها", + "Version {version}, {license}-licensed" : "نسخه {version}، با مجوز {license}", + "Version {version}" : "نسخه {version}", + "Disable" : "غیرفعال کردن", + "Enable" : "فعالسازی", + "Deploy and enable" : "استقرار و فعال سازی", + "Download and enable" : "بارگیری و فعال سازی", + "Install and enable" : "نصب و فعال سازی", + "Deploy and force enable" : "استقرار و فعال سازی اجباری", + "Download and force enable" : "بارگیری و فعال سازی اجباری", + "Install and force enable" : "نصب و فعال سازی اجباری", + "Rate the app" : "امتیازدهی به برنامه", + "Report a bug" : "گزارش یک اشکال", + "Ask questions or discuss the app" : "پرسش یا گفتگو درباره برنامه", + "Visit the website" : "بازدید از وب‌سایت", + "Remove" : "حذف", + "Update to {version}" : "به‌روز رسانی به {version}", + "Discover" : "کشف", + "Your apps" : "برنامه‌های شما", + "Active apps" : "برنامه‌های فعال", + "Disabled apps" : "برنامه‌های غیرفعال", + "Updates" : "به روز رسانی ها", + "App bundles" : "بسته های برنامه", + "Featured apps" : "کاره‌های برگزیده", + "Supported apps" : "برنامه‌های پشتیبانی‌شده", + "Search results" : "نتایج جستجو", + "Powered by Euro-Office" : "با قدرت Euro-Office", + "Good Nextcloud integration" : "یکپارچگی خوب با Nextcloud", + "Open source" : "متن‌باز", + "Best performance" : "بهترین کارایی", + "Limited ODF compatibility" : "سازگاری محدود با ODF", + "Best Microsoft compatibility" : "بهترین سازگاری با Microsoft", + "Best Nextcloud integration" : "بهترین یکپارچگی با Nextcloud", + "Good performance" : "کارایی خوب", + "Best security: documents never leave your server" : "بهترین امنیت: اسناد هرگز از سرور شما خارج نمی‌شوند", + "Best ODF compatibility" : "بهترین سازگاری با ODF", + "Best support for legacy files" : "بهترین پشتیبانی از پرونده‌های قدیمی", + "Could not load app categories. Please try again later." : "بارگذاری دسته‌بندی برنامه‌ها ممکن نشد. لطفاً بعداً دوباره تلاش کنید.", + "Could not load apps list. Please try again later." : "بارگذاری فهرست برنامه‌ها ممکن نشد. لطفاً بعداً دوباره تلاش کنید.", + "Could not update the app. Please try again later." : "به‌روزرسانی این برنامه ممکن نشد. لطفاً بعداً دوباره تلاش کنید.", + "An error occurred during the request. Unable to proceed." : "هنگام درخواست خطایی رخ داد. امکان ادامه وجود ندارد." +},"pluralForm" :"nplurals=2; plural=(n > 1);" +} \ No newline at end of file diff --git a/apps/appstore/l10n/fr.js b/apps/appstore/l10n/fr.js index 282758f4b57b7..bbcb379229054 100644 --- a/apps/appstore/l10n/fr.js +++ b/apps/appstore/l10n/fr.js @@ -7,6 +7,7 @@ OC.L10N.register( "Groups" : "Groupes", "Actions" : "Actions", "Show details" : "Afficher les détails", + "Show incompatible" : "Afficher les incompatibles", "Grid view" : "Vue en grille", "Cancel" : "Annuler", "Office suite switching is managed through the Nextcloud All-in-One interface." : "Le changement de suite bureautique est géré via l'interface Nextcloud All-in-One.", @@ -93,6 +94,7 @@ OC.L10N.register( "Download and enable" : "Télécharger et activer", "Rate the app" : "Évaluer l’application", "Report a bug" : "Signaler un bug", + "Visit the website" : "Visiter le site web", "Remove" : "Supprimer", "Update to {version}" : "Mettre à jour vers {version}", "Discover" : "Découvrir", diff --git a/apps/appstore/l10n/fr.json b/apps/appstore/l10n/fr.json index b6bf9ab2f4e36..e13a3ba606a0d 100644 --- a/apps/appstore/l10n/fr.json +++ b/apps/appstore/l10n/fr.json @@ -5,6 +5,7 @@ "Groups" : "Groupes", "Actions" : "Actions", "Show details" : "Afficher les détails", + "Show incompatible" : "Afficher les incompatibles", "Grid view" : "Vue en grille", "Cancel" : "Annuler", "Office suite switching is managed through the Nextcloud All-in-One interface." : "Le changement de suite bureautique est géré via l'interface Nextcloud All-in-One.", @@ -91,6 +92,7 @@ "Download and enable" : "Télécharger et activer", "Rate the app" : "Évaluer l’application", "Report a bug" : "Signaler un bug", + "Visit the website" : "Visiter le site web", "Remove" : "Supprimer", "Update to {version}" : "Mettre à jour vers {version}", "Discover" : "Découvrir", diff --git a/apps/appstore/src/components/UpdateAllDialog.vue b/apps/appstore/src/components/UpdateAllDialog.vue new file mode 100644 index 0000000000000..5b44dd002d1bd --- /dev/null +++ b/apps/appstore/src/components/UpdateAllDialog.vue @@ -0,0 +1,166 @@ + + + + + + + diff --git a/apps/appstore/src/store/updates.ts b/apps/appstore/src/store/updates.ts index c17e5a95a24b9..b12c17603ceec 100644 --- a/apps/appstore/src/store/updates.ts +++ b/apps/appstore/src/store/updates.ts @@ -49,6 +49,7 @@ export const useUpdatesStore = defineStore('updates', () => { internalUpdateCount.value = Math.max(internalUpdateCount.value - 1, 0) } + app.update = undefined rebuildNavigation() } catch (error) { logger.error('Failed to update app', { appId, error }) diff --git a/apps/appstore/src/views/AppstoreManage.vue b/apps/appstore/src/views/AppstoreManage.vue index 139bf014d30ff..1c04ec3018af4 100644 --- a/apps/appstore/src/views/AppstoreManage.vue +++ b/apps/appstore/src/views/AppstoreManage.vue @@ -4,8 +4,10 @@ --> diff --git a/apps/files/src/store/files.ts b/apps/files/src/store/files.ts index 90dffe19a21cf..6ea456fb6bdb0 100644 --- a/apps/files/src/store/files.ts +++ b/apps/files/src/store/files.ts @@ -11,6 +11,7 @@ import { defineStore } from 'pinia' import Vue, { ref } from 'vue' import { fetchNode } from '../services/WebdavClient.ts' import { logger } from '../utils/logger.ts' +import { useActiveStore } from './active.ts' import { usePathsStore } from './paths.ts' /** @@ -124,6 +125,12 @@ export const useFilesStore = defineStore('files', () => { }, {} as FilesStore) files.value = { ...files.value, ...newNodes } + + // handle updating the active node + const activeStore = useActiveStore() + if (activeStore.activeNode && activeStore.activeNode.source in newNodes) { + activeStore.activeNode = files.value[activeStore.activeNode.source] + } } /** @@ -232,7 +239,8 @@ export const useFilesStore = defineStore('files', () => { } // Otherwise, it means we receive an event for a node that is not in the store - fetchNode(node.path).then((n) => updateNodes([n])) + const newNode = await fetchNode(node.path) + updateNodes([newNode]) } /** diff --git a/apps/files/tests/Controller/ViewControllerTest.php b/apps/files/tests/Controller/ViewControllerTest.php index 37b9a6602d961..ee2753da58b6f 100644 --- a/apps/files/tests/Controller/ViewControllerTest.php +++ b/apps/files/tests/Controller/ViewControllerTest.php @@ -307,14 +307,11 @@ public function testTwoFactorAuthEnabled(): void { 'backup_codes' => true, ]); - $invokedCountProvideInitialState = $this->exactly(13); - $this->initialState->expects($invokedCountProvideInitialState) + $initialStates = []; + $this->initialState->expects(self::atLeast(13)) ->method('provideInitialState') - ->willReturnCallback(function ($key, $data) use ($invokedCountProvideInitialState): void { - if ($invokedCountProvideInitialState->numberOfInvocations() === 13) { - $this->assertEquals('isTwoFactorEnabled', $key); - $this->assertTrue($data); - } + ->willReturnCallback(function ($key, $data) use (&$initialStates): void { + $initialStates[$key] = $data; }); $this->config @@ -324,5 +321,6 @@ public function testTwoFactorAuthEnabled(): void { ]); $this->viewController->index('', '', null); + $this->assertTrue($initialStates['isTwoFactorEnabled'] ?? false); } } diff --git a/apps/files_external/l10n/fa.js b/apps/files_external/l10n/fa.js index 3ace94f6b61b2..0614cb0544f45 100644 --- a/apps/files_external/l10n/fa.js +++ b/apps/files_external/l10n/fa.js @@ -4,7 +4,7 @@ OC.L10N.register( "You are not logged in" : "شما وارد سیستم نشده‌اید", "Permission denied" : "مجوز رد شد", "Storage with ID \"%d\" not found" : "ذخیره سازی با شناسه  \"%d\"  پیدا نشد", - "Forbidden to manage local mounts" : "Forbidden to manage local mounts", + "Forbidden to manage local mounts" : "مدیریت نقاط نصب محلی ممنوع است", "Invalid backend or authentication mechanism class" : "کلاس مکانیسم اعتبار پس زمینه یا تأیید اعتبار نامعتبر است", "Invalid mount point" : "نقطه نصب نامعتبر است", "Objectstore forbidden" : "فروشگاه شی ممنوع است", @@ -15,9 +15,12 @@ OC.L10N.register( "Unsatisfied authentication mechanism parameters" : "پارامترهای مکانیسم تأیید ناخوشایند", "Insufficient data: %s" : "داده ها کافی نیست:  %s ", "Storage with ID \"%d\" is not editable by non-admins" : "فضای ذخیره‌سازی با شناسه «%d» توسط افراد غیر مدیران قابل ویرایش نیست.", + "Static credentials" : "اعتبارنامه‌های ثابت", + "Access key ID" : "شناسه کلید دسترسی", + "Secret access key" : "کلید دسترسی محرمانه", "Builtin" : "ساخته شده", "None" : "هیچ‌کدام", - "OpenStack v2" : "Open Stack v2", + "OpenStack v2" : "OpenStack v2", "Login" : "ورود", "Password" : "گذرواژه", "Tenant name" : "نام مستاجر", @@ -36,60 +39,86 @@ OC.L10N.register( "Public key" : "کلید عمومی", "RSA private key" : "کلید خصوصی RSA", "Private key" : "کلید خصوصی", - "Kerberos default realm, defaults to \"WORKGROUP\"" : "Kerberos default realm, defaults to \"WORKGROUP\"", - "Kerberos ticket Apache mode" : "Kerberos ticket Apache mode", + "Kerberos default realm, defaults to \"WORKGROUP\"" : "قلمرو پیش‌فرض Kerberos؛ مقدار پیش‌فرض \"WORKGROUP\" است", + "Kerberos ticket Apache mode" : "حالت Apache برای بلیت Kerberos", "Kerberos ticket" : "تیکت کربروس", + "S3-Compatible Object Storage" : "ذخیره‌سازی شیء سازگار با S3", "Bucket" : "باکت", "Hostname" : "نام میزبان", "Port" : "درگاه", "Proxy" : "پروکسی", "Region" : "ناحیه", - "Storage Class" : "Storage Class", + "Storage Class" : "کلاس ذخیره‌سازی", + "Use HTTPS" : "استفاده از HTTPS", + "Use Path Style (https://example.com/bucket)" : "استفاده از سبک مسیر (https://example.com/bucket)", + "Use Legacy S3 signing (v2)" : "استفاده از امضای قدیمی S3 (v2)", "Enable multipart copy" : "فعال کردن کپی چند قسمتی", + "Enable Direct Downloads (presigned URLs)" : "فعال کردن بارگیری مستقیم (نشانی‌های اینترنتی از پیش امضاشده)", "SSE-C encryption key" : "کلید رمزگذاری SSE-C", "WebDAV" : "WebDAV", "URL" : "آدرس", "Remote subfolder" : "زیر پوشه از راه دور", "Secure https://" : "امن https:// ", + "FTP/FTPS" : "FTP/FTPS", "Host" : "میزبانی", "Secure ftps://" : "امن ftps:// ", + "Local (server storage)" : "محلی (ذخیره‌سازی سرور)", "Location" : "محل", + "Nextcloud (WebDAV)" : "Nextcloud (WebDAV)", + "SFTP (SSH file transfer)" : "SFTP (انتقال پرونده با SSH)", "Root" : "ریشه", + "SFTP with public key authentication" : "SFTP با احراز هویت کلید عمومی", + "SMB/CIFS (Windows network share)" : "SMB/CIFS (اشتراک شبکه ویندوز)", "Share" : "اشتراک‌گذاری", "Show hidden files" : "نمایش فایل‌های مخفی", "Case sensitive file system" : "سیستم فایل حساس به حروف بزرگ و کوچک", "Disabling it will allow to use a case insensitive file system, but comes with a performance penalty" : "غیرفعال کردن آن امکان استفاده از سیستم فایل غیرحساس به حروف بزرگ و کوچک را فراهم می‌کند، اما با کاهش عملکرد همراه است.", - "Verify ACL access when listing files" : "Verify ACL access when listing files", + "Verify ACL access when listing files" : "بررسی دسترسی ACL هنگام فهرست کردن پرونده‌ها", "Check the ACL's of each file or folder inside a directory to filter out items where the account has no read permissions, comes with a performance penalty" : "ACL های هر فایل یا پوشه داخل یک دایرکتوری را بررسی کنید تا مواردی را که حساب کاربری مجوز خواندن ندارد فیلتر کنید، که با کاهش عملکرد همراه است.", "Timeout" : "زمان پایان", + "SMB/CIFS using Nextcloud login" : "SMB/CIFS با استفاده از ورود Nextcloud", "Login as share" : "ورود به عنوان اشتراک‌گذاری", + "OpenStack Swift Object Storage" : "ذخیره‌سازی شیء OpenStack Swift", "Service name" : "نام سرویس", "Request timeout (seconds)" : "درخواست زمان (ثانیه)", "External storage" : "حافظه خارجی", "External storage support" : "پشتیبانی از ذخیره سازی خارجی", "Adds basic external storage support" : "پشتیبانی اصلی حافظه خارجی را اضافه می کند", "This application enables administrators to configure connections to external storage providers, such as FTP servers, S3 or SWIFT object stores, other Nextcloud servers, WebDAV servers, and more. Administration can choose which types of storage to enable and can mount these storage locations for an account, a group, or the entire system. Users will see a new folder appear in their root Nextcloud directory, which they can access and use like any other Nextcloud folder. External storage also allows people to share files stored in these external locations. In these cases, the credentials for the owner of the file are used when the recipient requests the file from external storage, thereby ensuring that the recipient can access the shared file.\n\nExternal storage can be configured using the GUI or at the command line. This second option provides the administration with more flexibility for configuring bulk external storage mounts and setting mount priorities. More information is available in the external storage GUI documentation and the external storage Configuration File documentation." : "این برنامه به مدیران امکان می‌دهد تا اتصالات به ارائه‌دهندگان ذخیره‌سازی خارجی، مانند سرورهای FTP، فروشگاه‌های شیء S3 یا SWIFT، سایر سرورهای Nextcloud، سرورهای WebDAV و موارد دیگر را پیکربندی کنند. مدیریت می‌تواند انواع ذخیره‌سازی را برای فعال کردن انتخاب کند و می‌تواند این مکان‌های ذخیره‌سازی را برای یک حساب، یک گروه یا کل سیستم مونت کند. کاربران یک پوشه جدید را در دایرکتوری ریشه Nextcloud خود مشاهده می‌کنند که می‌توانند مانند هر پوشه Nextcloud دیگری به آن دسترسی داشته باشند و از آن استفاده کنند. ذخیره‌سازی خارجی همچنین به افراد اجازه می‌دهد تا فایل‌های ذخیره شده در این مکان‌های خارجی را به اشتراک بگذارند. در این موارد، اعتبارنامه‌های مالک فایل هنگام درخواست فایل از ذخیره‌سازی خارجی توسط گیرنده استفاده می‌شود و در نتیجه اطمینان حاصل می‌شود که گیرنده می‌تواند به فایل مشترک دسترسی داشته باشد.\n\nذخیره‌سازی خارجی را می‌توان با استفاده از رابط کاربری گرافیکی یا در خط فرمان پیکربندی کرد. این گزینه دوم، انعطاف‌پذیری بیشتری را برای پیکربندی مونت‌های ذخیره‌سازی خارجی انبوه و تنظیم اولویت‌های مونت در اختیار مدیریت قرار می‌دهد. اطلاعات بیشتر در مستندات رابط کاربری گرافیکی ذخیره‌سازی خارجی و مستندات فایل پیکربندی ذخیره‌سازی خارجی موجود است.", + "Edit storage" : "ویرایش ذخیره‌سازی", "Add storage" : "اضافه کردن حافظه", "Folder name" : "نام پوشه", "Authentication" : "احراز هویت", "Cancel" : "انصراف", "Edit" : "ویرایش", "Create" : "ایجاد", + "Restrict to" : "محدود به", + "Storage configuration" : "پیکربندی ذخیره‌سازی", "Never" : "هرگز", "Once every direct access" : "دسترسی مستقیم یکبار برای همیشه", "Always" : "همیشه", + "Mount options" : "گزینه‌های نصب", + "Check filesystem changes" : "بررسی تغییرات سیستم پرونده", "Read only" : "فقط خواندنی", "Enable previews" : "فعال سازی پیش نمایش", "Enable sharing" : "فعال سازی اشتراک گذاری", "Enable encryption" : "فعال کردن رمزگذاری", "Compatibility with Mac NFD encoding (slow)" : "سازگاری با رمزگذاری Mac NFD (کند)", "External storages" : "حافظه خارجی", - "Status" : "Status", + "Status" : "وضعیت", + "Restricted to" : "محدود شده به", "Actions" : "کنش‌ها", + "Checking …" : "در حال بررسی …", + "Recheck status" : "بررسی دوباره وضعیت", "Delete" : "حذف", + "System provided storage" : "ذخیره‌سازی فراهم‌شده توسط سیستم", "Saved" : "ذخیره شد", - "Error while saving" : "Error while saving", + "Error while saving" : "خطا هنگام ذخیره", + "Saved allowed backends" : "backendهای مجاز ذخیره شد", + "Failed to save allowed backends" : "ذخیره backendهای مجاز ناموفق بود", + "Advanced options for external storage mounts" : "گزینه‌های پیشرفته برای نصب‌های ذخیره‌سازی خارجی", "Allow people to mount external storage" : "به افراد اجازه دهید حافظه خارجی را نصب کنند", + "External storage backends people are allowed to mount" : "backendهای ذخیره‌سازی خارجی که افراد مجاز به نصب آن‌ها هستند", "Error generating key pair" : "خطا در تولید جفت کلید", "Key size" : "اندازه کلیدی", "Generate keys" : "تولید کلید", @@ -98,43 +127,57 @@ OC.L10N.register( "To access the storage, you need to provide the authentication credentials." : "برای دسترسی به فضای ذخیره‌سازی، باید اعتبار عمومی احراز هویت را ارائه دهید.", "Enter the storage login" : "ورود به سیستم ذخیره‌سازی را وارد کنید", "Enter the storage password" : "گذرواژه ذخیره سازی را وارد کنید", + "External storage enables you to mount external storage services and devices as secondary Nextcloud storage devices." : "ذخیره‌سازی خارجی به شما امکان می‌دهد سرویس‌ها و دستگاه‌های ذخیره‌سازی خارجی را به‌عنوان دستگاه‌های ذخیره‌سازی ثانویه Nextcloud نصب کنید.", + "You may also allow people to mount their own external storage services." : "همچنین می‌توانید به افراد اجازه دهید سرویس‌های ذخیره‌سازی خارجی خود را نصب کنند.", + "The cURL support in PHP is not enabled or installed." : "پشتیبانی cURL در PHP فعال یا نصب نشده است.", + "The FTP support in PHP is not enabled or installed." : "پشتیبانی FTP در PHP فعال یا نصب نشده است.", + "{module} is not installed." : "{module} نصب نشده است.", + "Dependant backends" : "backendهای وابسته", + "No external storage configured or you do not have the permission to configure them" : "هیچ ذخیره‌سازی خارجی پیکربندی نشده است یا اجازه پیکربندی آن‌ها را ندارید", + "Add external storage" : "افزودن ذخیره‌سازی خارجی", + "Global credentials saved" : "اعتبار جهانی ذخیره شد", + "Could not save global credentials" : "ذخیره اعتبار جهانی ناموفق بود", "Global credentials can be used to authenticate with multiple external storages that have the same credentials." : "از اعتبار جهانی می توان برای تأیید اعتبار با چندین انبار خارجی که دارای اعتبار یکسانی هستند استفاده کرد.", "Saving …" : "در حال ذخیره", "Save" : "ذخیره", - "Unable to update this external storage config. {statusMessage}" : "Unable to update this external storage config. {statusMessage}", - "New configuration successfully saved" : "New configuration successfully saved", - "Enter missing credentials" : "Enter missing credentials", + "Unable to update this external storage config. {statusMessage}" : "به‌روزرسانی این پیکربندی ذخیره‌سازی خارجی ممکن نشد. {statusMessage}", + "New configuration successfully saved" : "پیکربندی جدید با موفقیت ذخیره شد", + "Enter missing credentials" : "اعتبار ناقص را وارد کنید", "Credentials successfully set" : "اعتبار ها با موفقیت تنظیم شدند", "Error while setting credentials: {error}" : "خطا هنگام تنظیمات اعتبار : {error}", "Checking storage …" : "بررسی فضا ذخیره‌سازی", - "There was an error with this external storage." : "There was an error with this external storage.", - "We were unable to check the external storage {basename}" : "We were unable to check the external storage {basename}", - "Examine this faulty external storage configuration" : "Examine this faulty external storage configuration", + "There was an error with this external storage." : "در این ذخیره‌سازی خارجی خطایی رخ داد.", + "We were unable to check the external storage {basename}" : "بررسی ذخیره‌سازی خارجی {basename} امکان‌پذیر نبود", + "Examine this faulty external storage configuration" : "بررسی این پیکربندی معیوب حافظه خارجی", "Open in Files" : "در فایل باز کنید", "External mount error" : "خظای نصب خارجی", - "There was an error with this external storage. Do you want to review this mount point config in the settings page?" : "There was an error with this external storage. Do you want to review this mount point config in the settings page?", + "There was an error with this external storage. Do you want to review this mount point config in the settings page?" : "در این حافظه خارجی خطایی رخ داد. آیا می‌خواهید پیکربندی این نقطه نصب را در صفحه تنظیمات بازبینی کنید؟", "Open settings" : "تنظیمات را باز کنید", "Ignore" : "چشم پوشی", - "List of external storage." : "List of external storage.", - "There is no external storage configured. You can configure them in your Personal settings." : "There is no external storage configured. You can configure them in your Personal settings.", - "There is no external storage configured and you don't have the permission to configure them." : "There is no external storage configured and you don't have the permission to configure them.", - "No external storage" : "No external storage", + "List of external storage." : "فهرست حافظه‌های خارجی.", + "There is no external storage configured. You can configure them in your Personal settings." : "هیچ حافظه خارجی پیکربندی نشده است. می‌توانید آن‌ها را در تنظیمات شخصی خود پیکربندی کنید.", + "There is no external storage configured and you don't have the permission to configure them." : "هیچ حافظه خارجی پیکربندی نشده است و شما دسترسی لازم برای پیکربندی آن‌ها را ندارید.", + "No external storage" : "بدون حافظه خارجی", "Storage type" : "نوع فضای ذخیره‌سازی", "Unknown" : "ناشناخته", "Scope" : "حوزه", "Personal" : "شخصی", "System" : "سیستم", + "Connected" : "متصل", "Error" : "خطا", + "Indeterminate" : "نامشخص", + "Incomplete configuration" : "پیکربندی ناقص", "Unauthorized" : "غیرمجاز", "Network error" : "خطای شبکه", "Grant access" : " مجوز اعطا دسترسی", "Error configuring OAuth2" : "خطا پیکربندی OAuth2", - "%s" : " %s ", + "%s" : "%s", "Access key" : "کلید دسترسی", "Secret key" : "کلید مخفی", "OAuth2" : "OAuth2", "Client ID" : "شناسه مشتری", "Client secret" : "رمز مشتری", + "S3 Storage" : "حافظه S3", "Enable SSL" : "فعال‌سازی SSL", "Enable Path Style" : "سبک مسیر را فعال کنید", "Legacy (v2) authentication" : "احراز هویت، ارث بری (v2)", @@ -144,7 +187,7 @@ OC.L10N.register( "SFTP" : "SFTP", "SFTP with secret key login" : "SFTP با ورود به سیستم کلید مخفی", "SMB/CIFS" : "SMB/CIFS", - "SMB/CIFS using OC login" : "SMB/CIFS using OC login", + "SMB/CIFS using OC login" : "SMB/CIFS با استفاده از ورود OC", "OpenStack Object Storage" : "فضای ذخیره سازی شیء OpenStack", "The cURL support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." : "پشتیبانی cURL در PHP فعال یا نصب نشده است. نصب %s امکان پذیر نیست. لطفاً از سرپرست سیستم خود بخواهید که آن را نصب کند.", "The FTP support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." : "پشتیبانی FTP در PHP فعال یا نصب نشده است. نصب %s امکان پذیر نیست. لطفاً از سرپرست سیستم خود بخواهید که آن را نصب کند.", @@ -156,7 +199,7 @@ OC.L10N.register( "Disconnect" : "قطع شدن", "Unknown backend: {backendName}" : "بک‌اند ناشناخته: {backendName}", "Admin defined" : "مدیر تعریف شده", - "Automatic status checking is disabled due to the large number of configured storages, click to check status" : "Automatic status checking is disabled due to the large number of configured storages, click to check status", + "Automatic status checking is disabled due to the large number of configured storages, click to check status" : "بررسی خودکار وضعیت به دلیل تعداد زیاد حافظه‌های خارجی پیکربندی‌شده غیرفعال است؛ برای بررسی وضعیت کلیک کنید", "Are you sure you want to disconnect this external storage?" : "آیا مطمئن هستید که می‌خواهید اتصال این حافظه خارجی را قطع کنید؟", "It will make the storage unavailable in {instanceName} and will lead to a deletion of these files and folders on any sync client that is currently connected but will not delete any files and folders on the external storage itself." : "این کار باعث می‌شود فضای ذخیره‌سازی در {instanceName} از دسترس خارج شود و منجر به حذف این فایل‌ها و پوشه‌ها در هر کلاینت همگام‌سازی که در حال حاضر متصل است، می‌شود، اما هیچ فایل و پوشه‌ای را در خود فضای ذخیره‌سازی خارجی حذف نمی‌کند.", "Delete storage?" : "فضای ذخیره سازی را حذف می کنید؟", @@ -170,6 +213,7 @@ OC.L10N.register( "Configuration" : "پیکربندی", "Available for" : "در دسترس برای", "All people" : "همه مردم", - "Advanced settings" : "تنظیمات پیشرفته" + "Advanced settings" : "تنظیمات پیشرفته", + "Use presigned S3 url" : "استفاده از URL از پیش‌امضاشده S3" }, "nplurals=2; plural=(n > 1);"); diff --git a/apps/files_external/l10n/fa.json b/apps/files_external/l10n/fa.json index 113a95083d13d..0fae8da849bd2 100644 --- a/apps/files_external/l10n/fa.json +++ b/apps/files_external/l10n/fa.json @@ -2,7 +2,7 @@ "You are not logged in" : "شما وارد سیستم نشده‌اید", "Permission denied" : "مجوز رد شد", "Storage with ID \"%d\" not found" : "ذخیره سازی با شناسه  \"%d\"  پیدا نشد", - "Forbidden to manage local mounts" : "Forbidden to manage local mounts", + "Forbidden to manage local mounts" : "مدیریت نقاط نصب محلی ممنوع است", "Invalid backend or authentication mechanism class" : "کلاس مکانیسم اعتبار پس زمینه یا تأیید اعتبار نامعتبر است", "Invalid mount point" : "نقطه نصب نامعتبر است", "Objectstore forbidden" : "فروشگاه شی ممنوع است", @@ -13,9 +13,12 @@ "Unsatisfied authentication mechanism parameters" : "پارامترهای مکانیسم تأیید ناخوشایند", "Insufficient data: %s" : "داده ها کافی نیست:  %s ", "Storage with ID \"%d\" is not editable by non-admins" : "فضای ذخیره‌سازی با شناسه «%d» توسط افراد غیر مدیران قابل ویرایش نیست.", + "Static credentials" : "اعتبارنامه‌های ثابت", + "Access key ID" : "شناسه کلید دسترسی", + "Secret access key" : "کلید دسترسی محرمانه", "Builtin" : "ساخته شده", "None" : "هیچ‌کدام", - "OpenStack v2" : "Open Stack v2", + "OpenStack v2" : "OpenStack v2", "Login" : "ورود", "Password" : "گذرواژه", "Tenant name" : "نام مستاجر", @@ -34,60 +37,86 @@ "Public key" : "کلید عمومی", "RSA private key" : "کلید خصوصی RSA", "Private key" : "کلید خصوصی", - "Kerberos default realm, defaults to \"WORKGROUP\"" : "Kerberos default realm, defaults to \"WORKGROUP\"", - "Kerberos ticket Apache mode" : "Kerberos ticket Apache mode", + "Kerberos default realm, defaults to \"WORKGROUP\"" : "قلمرو پیش‌فرض Kerberos؛ مقدار پیش‌فرض \"WORKGROUP\" است", + "Kerberos ticket Apache mode" : "حالت Apache برای بلیت Kerberos", "Kerberos ticket" : "تیکت کربروس", + "S3-Compatible Object Storage" : "ذخیره‌سازی شیء سازگار با S3", "Bucket" : "باکت", "Hostname" : "نام میزبان", "Port" : "درگاه", "Proxy" : "پروکسی", "Region" : "ناحیه", - "Storage Class" : "Storage Class", + "Storage Class" : "کلاس ذخیره‌سازی", + "Use HTTPS" : "استفاده از HTTPS", + "Use Path Style (https://example.com/bucket)" : "استفاده از سبک مسیر (https://example.com/bucket)", + "Use Legacy S3 signing (v2)" : "استفاده از امضای قدیمی S3 (v2)", "Enable multipart copy" : "فعال کردن کپی چند قسمتی", + "Enable Direct Downloads (presigned URLs)" : "فعال کردن بارگیری مستقیم (نشانی‌های اینترنتی از پیش امضاشده)", "SSE-C encryption key" : "کلید رمزگذاری SSE-C", "WebDAV" : "WebDAV", "URL" : "آدرس", "Remote subfolder" : "زیر پوشه از راه دور", "Secure https://" : "امن https:// ", + "FTP/FTPS" : "FTP/FTPS", "Host" : "میزبانی", "Secure ftps://" : "امن ftps:// ", + "Local (server storage)" : "محلی (ذخیره‌سازی سرور)", "Location" : "محل", + "Nextcloud (WebDAV)" : "Nextcloud (WebDAV)", + "SFTP (SSH file transfer)" : "SFTP (انتقال پرونده با SSH)", "Root" : "ریشه", + "SFTP with public key authentication" : "SFTP با احراز هویت کلید عمومی", + "SMB/CIFS (Windows network share)" : "SMB/CIFS (اشتراک شبکه ویندوز)", "Share" : "اشتراک‌گذاری", "Show hidden files" : "نمایش فایل‌های مخفی", "Case sensitive file system" : "سیستم فایل حساس به حروف بزرگ و کوچک", "Disabling it will allow to use a case insensitive file system, but comes with a performance penalty" : "غیرفعال کردن آن امکان استفاده از سیستم فایل غیرحساس به حروف بزرگ و کوچک را فراهم می‌کند، اما با کاهش عملکرد همراه است.", - "Verify ACL access when listing files" : "Verify ACL access when listing files", + "Verify ACL access when listing files" : "بررسی دسترسی ACL هنگام فهرست کردن پرونده‌ها", "Check the ACL's of each file or folder inside a directory to filter out items where the account has no read permissions, comes with a performance penalty" : "ACL های هر فایل یا پوشه داخل یک دایرکتوری را بررسی کنید تا مواردی را که حساب کاربری مجوز خواندن ندارد فیلتر کنید، که با کاهش عملکرد همراه است.", "Timeout" : "زمان پایان", + "SMB/CIFS using Nextcloud login" : "SMB/CIFS با استفاده از ورود Nextcloud", "Login as share" : "ورود به عنوان اشتراک‌گذاری", + "OpenStack Swift Object Storage" : "ذخیره‌سازی شیء OpenStack Swift", "Service name" : "نام سرویس", "Request timeout (seconds)" : "درخواست زمان (ثانیه)", "External storage" : "حافظه خارجی", "External storage support" : "پشتیبانی از ذخیره سازی خارجی", "Adds basic external storage support" : "پشتیبانی اصلی حافظه خارجی را اضافه می کند", "This application enables administrators to configure connections to external storage providers, such as FTP servers, S3 or SWIFT object stores, other Nextcloud servers, WebDAV servers, and more. Administration can choose which types of storage to enable and can mount these storage locations for an account, a group, or the entire system. Users will see a new folder appear in their root Nextcloud directory, which they can access and use like any other Nextcloud folder. External storage also allows people to share files stored in these external locations. In these cases, the credentials for the owner of the file are used when the recipient requests the file from external storage, thereby ensuring that the recipient can access the shared file.\n\nExternal storage can be configured using the GUI or at the command line. This second option provides the administration with more flexibility for configuring bulk external storage mounts and setting mount priorities. More information is available in the external storage GUI documentation and the external storage Configuration File documentation." : "این برنامه به مدیران امکان می‌دهد تا اتصالات به ارائه‌دهندگان ذخیره‌سازی خارجی، مانند سرورهای FTP، فروشگاه‌های شیء S3 یا SWIFT، سایر سرورهای Nextcloud، سرورهای WebDAV و موارد دیگر را پیکربندی کنند. مدیریت می‌تواند انواع ذخیره‌سازی را برای فعال کردن انتخاب کند و می‌تواند این مکان‌های ذخیره‌سازی را برای یک حساب، یک گروه یا کل سیستم مونت کند. کاربران یک پوشه جدید را در دایرکتوری ریشه Nextcloud خود مشاهده می‌کنند که می‌توانند مانند هر پوشه Nextcloud دیگری به آن دسترسی داشته باشند و از آن استفاده کنند. ذخیره‌سازی خارجی همچنین به افراد اجازه می‌دهد تا فایل‌های ذخیره شده در این مکان‌های خارجی را به اشتراک بگذارند. در این موارد، اعتبارنامه‌های مالک فایل هنگام درخواست فایل از ذخیره‌سازی خارجی توسط گیرنده استفاده می‌شود و در نتیجه اطمینان حاصل می‌شود که گیرنده می‌تواند به فایل مشترک دسترسی داشته باشد.\n\nذخیره‌سازی خارجی را می‌توان با استفاده از رابط کاربری گرافیکی یا در خط فرمان پیکربندی کرد. این گزینه دوم، انعطاف‌پذیری بیشتری را برای پیکربندی مونت‌های ذخیره‌سازی خارجی انبوه و تنظیم اولویت‌های مونت در اختیار مدیریت قرار می‌دهد. اطلاعات بیشتر در مستندات رابط کاربری گرافیکی ذخیره‌سازی خارجی و مستندات فایل پیکربندی ذخیره‌سازی خارجی موجود است.", + "Edit storage" : "ویرایش ذخیره‌سازی", "Add storage" : "اضافه کردن حافظه", "Folder name" : "نام پوشه", "Authentication" : "احراز هویت", "Cancel" : "انصراف", "Edit" : "ویرایش", "Create" : "ایجاد", + "Restrict to" : "محدود به", + "Storage configuration" : "پیکربندی ذخیره‌سازی", "Never" : "هرگز", "Once every direct access" : "دسترسی مستقیم یکبار برای همیشه", "Always" : "همیشه", + "Mount options" : "گزینه‌های نصب", + "Check filesystem changes" : "بررسی تغییرات سیستم پرونده", "Read only" : "فقط خواندنی", "Enable previews" : "فعال سازی پیش نمایش", "Enable sharing" : "فعال سازی اشتراک گذاری", "Enable encryption" : "فعال کردن رمزگذاری", "Compatibility with Mac NFD encoding (slow)" : "سازگاری با رمزگذاری Mac NFD (کند)", "External storages" : "حافظه خارجی", - "Status" : "Status", + "Status" : "وضعیت", + "Restricted to" : "محدود شده به", "Actions" : "کنش‌ها", + "Checking …" : "در حال بررسی …", + "Recheck status" : "بررسی دوباره وضعیت", "Delete" : "حذف", + "System provided storage" : "ذخیره‌سازی فراهم‌شده توسط سیستم", "Saved" : "ذخیره شد", - "Error while saving" : "Error while saving", + "Error while saving" : "خطا هنگام ذخیره", + "Saved allowed backends" : "backendهای مجاز ذخیره شد", + "Failed to save allowed backends" : "ذخیره backendهای مجاز ناموفق بود", + "Advanced options for external storage mounts" : "گزینه‌های پیشرفته برای نصب‌های ذخیره‌سازی خارجی", "Allow people to mount external storage" : "به افراد اجازه دهید حافظه خارجی را نصب کنند", + "External storage backends people are allowed to mount" : "backendهای ذخیره‌سازی خارجی که افراد مجاز به نصب آن‌ها هستند", "Error generating key pair" : "خطا در تولید جفت کلید", "Key size" : "اندازه کلیدی", "Generate keys" : "تولید کلید", @@ -96,43 +125,57 @@ "To access the storage, you need to provide the authentication credentials." : "برای دسترسی به فضای ذخیره‌سازی، باید اعتبار عمومی احراز هویت را ارائه دهید.", "Enter the storage login" : "ورود به سیستم ذخیره‌سازی را وارد کنید", "Enter the storage password" : "گذرواژه ذخیره سازی را وارد کنید", + "External storage enables you to mount external storage services and devices as secondary Nextcloud storage devices." : "ذخیره‌سازی خارجی به شما امکان می‌دهد سرویس‌ها و دستگاه‌های ذخیره‌سازی خارجی را به‌عنوان دستگاه‌های ذخیره‌سازی ثانویه Nextcloud نصب کنید.", + "You may also allow people to mount their own external storage services." : "همچنین می‌توانید به افراد اجازه دهید سرویس‌های ذخیره‌سازی خارجی خود را نصب کنند.", + "The cURL support in PHP is not enabled or installed." : "پشتیبانی cURL در PHP فعال یا نصب نشده است.", + "The FTP support in PHP is not enabled or installed." : "پشتیبانی FTP در PHP فعال یا نصب نشده است.", + "{module} is not installed." : "{module} نصب نشده است.", + "Dependant backends" : "backendهای وابسته", + "No external storage configured or you do not have the permission to configure them" : "هیچ ذخیره‌سازی خارجی پیکربندی نشده است یا اجازه پیکربندی آن‌ها را ندارید", + "Add external storage" : "افزودن ذخیره‌سازی خارجی", + "Global credentials saved" : "اعتبار جهانی ذخیره شد", + "Could not save global credentials" : "ذخیره اعتبار جهانی ناموفق بود", "Global credentials can be used to authenticate with multiple external storages that have the same credentials." : "از اعتبار جهانی می توان برای تأیید اعتبار با چندین انبار خارجی که دارای اعتبار یکسانی هستند استفاده کرد.", "Saving …" : "در حال ذخیره", "Save" : "ذخیره", - "Unable to update this external storage config. {statusMessage}" : "Unable to update this external storage config. {statusMessage}", - "New configuration successfully saved" : "New configuration successfully saved", - "Enter missing credentials" : "Enter missing credentials", + "Unable to update this external storage config. {statusMessage}" : "به‌روزرسانی این پیکربندی ذخیره‌سازی خارجی ممکن نشد. {statusMessage}", + "New configuration successfully saved" : "پیکربندی جدید با موفقیت ذخیره شد", + "Enter missing credentials" : "اعتبار ناقص را وارد کنید", "Credentials successfully set" : "اعتبار ها با موفقیت تنظیم شدند", "Error while setting credentials: {error}" : "خطا هنگام تنظیمات اعتبار : {error}", "Checking storage …" : "بررسی فضا ذخیره‌سازی", - "There was an error with this external storage." : "There was an error with this external storage.", - "We were unable to check the external storage {basename}" : "We were unable to check the external storage {basename}", - "Examine this faulty external storage configuration" : "Examine this faulty external storage configuration", + "There was an error with this external storage." : "در این ذخیره‌سازی خارجی خطایی رخ داد.", + "We were unable to check the external storage {basename}" : "بررسی ذخیره‌سازی خارجی {basename} امکان‌پذیر نبود", + "Examine this faulty external storage configuration" : "بررسی این پیکربندی معیوب حافظه خارجی", "Open in Files" : "در فایل باز کنید", "External mount error" : "خظای نصب خارجی", - "There was an error with this external storage. Do you want to review this mount point config in the settings page?" : "There was an error with this external storage. Do you want to review this mount point config in the settings page?", + "There was an error with this external storage. Do you want to review this mount point config in the settings page?" : "در این حافظه خارجی خطایی رخ داد. آیا می‌خواهید پیکربندی این نقطه نصب را در صفحه تنظیمات بازبینی کنید؟", "Open settings" : "تنظیمات را باز کنید", "Ignore" : "چشم پوشی", - "List of external storage." : "List of external storage.", - "There is no external storage configured. You can configure them in your Personal settings." : "There is no external storage configured. You can configure them in your Personal settings.", - "There is no external storage configured and you don't have the permission to configure them." : "There is no external storage configured and you don't have the permission to configure them.", - "No external storage" : "No external storage", + "List of external storage." : "فهرست حافظه‌های خارجی.", + "There is no external storage configured. You can configure them in your Personal settings." : "هیچ حافظه خارجی پیکربندی نشده است. می‌توانید آن‌ها را در تنظیمات شخصی خود پیکربندی کنید.", + "There is no external storage configured and you don't have the permission to configure them." : "هیچ حافظه خارجی پیکربندی نشده است و شما دسترسی لازم برای پیکربندی آن‌ها را ندارید.", + "No external storage" : "بدون حافظه خارجی", "Storage type" : "نوع فضای ذخیره‌سازی", "Unknown" : "ناشناخته", "Scope" : "حوزه", "Personal" : "شخصی", "System" : "سیستم", + "Connected" : "متصل", "Error" : "خطا", + "Indeterminate" : "نامشخص", + "Incomplete configuration" : "پیکربندی ناقص", "Unauthorized" : "غیرمجاز", "Network error" : "خطای شبکه", "Grant access" : " مجوز اعطا دسترسی", "Error configuring OAuth2" : "خطا پیکربندی OAuth2", - "%s" : " %s ", + "%s" : "%s", "Access key" : "کلید دسترسی", "Secret key" : "کلید مخفی", "OAuth2" : "OAuth2", "Client ID" : "شناسه مشتری", "Client secret" : "رمز مشتری", + "S3 Storage" : "حافظه S3", "Enable SSL" : "فعال‌سازی SSL", "Enable Path Style" : "سبک مسیر را فعال کنید", "Legacy (v2) authentication" : "احراز هویت، ارث بری (v2)", @@ -142,7 +185,7 @@ "SFTP" : "SFTP", "SFTP with secret key login" : "SFTP با ورود به سیستم کلید مخفی", "SMB/CIFS" : "SMB/CIFS", - "SMB/CIFS using OC login" : "SMB/CIFS using OC login", + "SMB/CIFS using OC login" : "SMB/CIFS با استفاده از ورود OC", "OpenStack Object Storage" : "فضای ذخیره سازی شیء OpenStack", "The cURL support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." : "پشتیبانی cURL در PHP فعال یا نصب نشده است. نصب %s امکان پذیر نیست. لطفاً از سرپرست سیستم خود بخواهید که آن را نصب کند.", "The FTP support in PHP is not enabled or installed. Mounting of %s is not possible. Please ask your system administrator to install it." : "پشتیبانی FTP در PHP فعال یا نصب نشده است. نصب %s امکان پذیر نیست. لطفاً از سرپرست سیستم خود بخواهید که آن را نصب کند.", @@ -154,7 +197,7 @@ "Disconnect" : "قطع شدن", "Unknown backend: {backendName}" : "بک‌اند ناشناخته: {backendName}", "Admin defined" : "مدیر تعریف شده", - "Automatic status checking is disabled due to the large number of configured storages, click to check status" : "Automatic status checking is disabled due to the large number of configured storages, click to check status", + "Automatic status checking is disabled due to the large number of configured storages, click to check status" : "بررسی خودکار وضعیت به دلیل تعداد زیاد حافظه‌های خارجی پیکربندی‌شده غیرفعال است؛ برای بررسی وضعیت کلیک کنید", "Are you sure you want to disconnect this external storage?" : "آیا مطمئن هستید که می‌خواهید اتصال این حافظه خارجی را قطع کنید؟", "It will make the storage unavailable in {instanceName} and will lead to a deletion of these files and folders on any sync client that is currently connected but will not delete any files and folders on the external storage itself." : "این کار باعث می‌شود فضای ذخیره‌سازی در {instanceName} از دسترس خارج شود و منجر به حذف این فایل‌ها و پوشه‌ها در هر کلاینت همگام‌سازی که در حال حاضر متصل است، می‌شود، اما هیچ فایل و پوشه‌ای را در خود فضای ذخیره‌سازی خارجی حذف نمی‌کند.", "Delete storage?" : "فضای ذخیره سازی را حذف می کنید؟", @@ -168,6 +211,7 @@ "Configuration" : "پیکربندی", "Available for" : "در دسترس برای", "All people" : "همه مردم", - "Advanced settings" : "تنظیمات پیشرفته" + "Advanced settings" : "تنظیمات پیشرفته", + "Use presigned S3 url" : "استفاده از URL از پیش‌امضاشده S3" },"pluralForm" :"nplurals=2; plural=(n > 1);" } \ No newline at end of file diff --git a/apps/files_reminders/lib/BackgroundJob/ScheduledNotifications.php b/apps/files_reminders/lib/BackgroundJob/ScheduledNotifications.php index 8e615c3a6d6f2..51c752f46d150 100644 --- a/apps/files_reminders/lib/BackgroundJob/ScheduledNotifications.php +++ b/apps/files_reminders/lib/BackgroundJob/ScheduledNotifications.php @@ -11,10 +11,10 @@ use OCA\FilesReminders\Db\ReminderMapper; use OCA\FilesReminders\Service\ReminderService; -use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Utility\ITimeFactory; use OCP\BackgroundJob\TimedJob; use Psr\Log\LoggerInterface; +use Throwable; class ScheduledNotifications extends TimedJob { public function __construct( @@ -37,8 +37,11 @@ public function run($argument) { foreach ($reminders as $reminder) { try { $this->reminderService->send($reminder); - } catch (DoesNotExistException $e) { - $this->logger->debug('Could not send notification for reminder with id ' . $reminder->getId()); + } catch (Throwable $e) { + // A single broken reminder (e.g. orphaned user record) must not + // stall the rest of the queue, which is ordered by due_date ASC + // and would otherwise re-hit the same row on every cron tick. + $this->logger->error('Could not send notification for reminder with id ' . $reminder->getId(), ['exception' => $e]); } } } diff --git a/apps/files_reminders/lib/Db/ReminderMapper.php b/apps/files_reminders/lib/Db/ReminderMapper.php index 63cba437d0777..3ef80fbda0c58 100644 --- a/apps/files_reminders/lib/Db/ReminderMapper.php +++ b/apps/files_reminders/lib/Db/ReminderMapper.php @@ -3,7 +3,7 @@ declare(strict_types=1); /** - * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2023-2026 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later */ @@ -12,6 +12,7 @@ use DateTime; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\QBMapper; +use OCP\AppFramework\Utility\ITimeFactory; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Files\Folder; use OCP\Files\Node; @@ -25,12 +26,11 @@ class ReminderMapper extends QBMapper { public const TABLE_NAME = 'files_reminders'; - public function __construct(IDBConnection $db) { - parent::__construct( - $db, - static::TABLE_NAME, - Reminder::class, - ); + public function __construct( + IDBConnection $db, + private ITimeFactory $timeFactory, + ) { + parent::__construct($db, self::TABLE_NAME, Reminder::class); } public function markNotified(Reminder $reminder): Reminder { @@ -108,9 +108,18 @@ public function findAllForNode(Node $node) { public function findOverdue() { $qb = $this->db->getQueryBuilder(); + $now = $this->timeFactory->getDateTime(); + $now->setTimezone(new \DateTimeZone('UTC')); + $qb->select('id', 'user_id', 'file_id', 'due_date', 'updated_at', 'created_at', 'notified') ->from($this->getTableName()) - ->where($qb->expr()->lt('due_date', $qb->createFunction('NOW()'))) + ->where( + $qb->expr()->lt( + 'due_date', + $qb->createNamedParameter($now, IQueryBuilder::PARAM_DATETIME_MUTABLE), + IQueryBuilder::PARAM_DATETIME_MUTABLE, + ) + ) ->andWhere($qb->expr()->eq('notified', $qb->createNamedParameter(false, IQueryBuilder::PARAM_BOOL))) ->orderBy('due_date', 'ASC'); diff --git a/apps/files_sharing/l10n/fa.js b/apps/files_sharing/l10n/fa.js index ac652c0a9cda4..1ce8fcfff83d7 100644 --- a/apps/files_sharing/l10n/fa.js +++ b/apps/files_sharing/l10n/fa.js @@ -60,10 +60,10 @@ OC.L10N.register( "Sharing" : "اشتراک گذاری", "A file or folder has been shared" : "فایل یا پوشه ای به اشتراک گذاشته شد", "Shared link" : "پیوند به اشتراک گذاری شده", - "Wrong share ID, share does not exist" : "Wrong share ID, share does not exist", + "Wrong share ID, share does not exist" : "شناسهٔ اشتراک‌گذاری نادرست است؛ این اشتراک وجود ندارد", "Could not delete share" : "اشتراک گذاری حذف نشد", "Please specify a file or folder path" : "لطفاً مسیر فایل یا پوشه را مشخص کنید", - "Wrong path, file/folder does not exist" : "Wrong path, file/folder does not exist", + "Wrong path, file/folder does not exist" : "مسیر نادرست است؛ فایل/پوشه وجود ندارد", "Could not create share" : "امکان ایجاد اشتراک گذاری وجود ندارد", "Please specify a valid account to share with" : "لطفاً یک حساب معتبر برای اشتراک‌گذاری مشخص کنید", "Group sharing is disabled by the administrator" : "اشتراک گروه توسط مدیر غیرفعال شده است.", @@ -73,7 +73,7 @@ OC.L10N.register( "Sharing %s sending the password by Nextcloud Talk failed because Nextcloud Talk is not enabled" : ".اشتراک‍‍‍%s ارسال رمز عبور توسط Nextcloud Talk به دلیل فعال نشدن Nextcloud Talk انجام نشد.", "Sharing %1$s failed because the back end does not allow shares from type %2$s" : "%2$sاشتراک گذاری%1$s انجام نشد زیرا بک اِند اجازه نمی دهد نوع از سهام استفاده شود", "Please specify a valid federated account ID" : "لطفا یک شناسه حساب کاربری معتبر مشخص کنید", - "Please specify a valid federated group ID" : "Please specify a valid federated group ID", + "Please specify a valid federated group ID" : "لطفاً یک شناسهٔ گروه فدرات‌شده معتبر مشخص کنید", "You cannot share to a Team if the app is not enabled" : "اگر برنامه فعال نباشد، نمی‌توانید آن را با یک تیم به اشتراک بگذارید", "Please specify a valid team" : "لطفا یک تیم معتبر معرفی کنید", "Sharing %s failed because the back end does not support room shares" : "اشتراک گذاری %sانجام نشد زیرا قسمت پشتی سهام اتاق را پشتیبانی نمی کند", @@ -81,14 +81,14 @@ OC.L10N.register( "Not a directory" : "این یک پوشه نیست", "Could not lock node" : "گره را نمی توان قفل کرد", "Public upload is only possible for publicly shared folders" : "بارگذاری عمومی فقط برای پوشه هایی که به طور عمومی به اشتراک گذاشته می شوند ممکن است", - "Share must at least have READ or CREATE permissions" : "Share must at least have READ or CREATE permissions", - "Share must have READ permission if UPDATE or DELETE permission is set" : "Share must have READ permission if UPDATE or DELETE permission is set", + "Share must at least have READ or CREATE permissions" : "اشتراک‌گذاری باید دست‌کم دسترسی خواندن (READ) یا ایجاد (CREATE) داشته باشد", + "Share must have READ permission if UPDATE or DELETE permission is set" : "اگر دسترسی به‌روزرسانی (UPDATE) یا حذف (DELETE) تنظیم شده باشد، اشتراک‌گذاری باید دسترسی خواندن (READ) نیز داشته باشد", "Public upload disabled by the administrator" : "آپلود عمومی توسط سرپرست غیرفعال شده است", "Could not lock path" : "امکان قفل کردن مسیر وجود ندارد.", "no sharing rights on this item" : "حق اشتراک گذاری در این مورد وجود ندارد", "You are not allowed to edit incoming shares" : "شما مجاز به ویرایش اشتراک‌های دریافتی نیستید", "Wrong or no update parameter given" : "اشتباهی و یا پارامتر بروزرسانی داده نشد", - "\"Sending the password by Nextcloud Talk\" for sharing a file or folder failed because Nextcloud Talk is not enabled." : "\"Sending the password by Nextcloud Talk\" for sharing a file or folder failed because Nextcloud Talk is not enabled.", + "\"Sending the password by Nextcloud Talk\" for sharing a file or folder failed because Nextcloud Talk is not enabled." : "«ارسال گذرواژه از طریق Nextcloud Talk» برای اشتراک‌گذاری یک فایل یا پوشه ناموفق بود، زیرا Nextcloud Talk فعال نیست.", "Custom share link tokens have been disabled by the administrator" : "توکن‌های لینک اشتراک‌گذاری سفارشی توسط مدیر غیرفعال شده‌اند", "Tokens must contain at least 1 character and may only contain letters, numbers, or a hyphen" : "توکن‌ها باید حداقل شامل ۱ کاراکتر باشند و فقط می‌توانند شامل حروف، اعداد یا خط تیره باشند.", "Invalid date. Format must be YYYY-MM-DD" : "تاریخ نامعتبر است. قالب باید YYYY-MM-DD باشد.", @@ -102,7 +102,7 @@ OC.L10N.register( "This share does not exist or is no longer available" : "این سهم وجود ندارد یا دیگر در دسترس نیست", "shared by %s" : "اشتراک گذاری شده به میزان %s", "Download" : "دانلود", - "Add to your %s" : "Add to your %s", + "Add to your %s" : "افزودن به %s شما", "Direct link" : "لینک مستقیم", "Share API is disabled" : "اشتراک API غیرفعال شده است", "File sharing" : "اشتراک گذاری پرونده", @@ -145,7 +145,7 @@ OC.L10N.register( "Send link via email" : "ارسال لینک از طریق ایمیل", "Enter an email address or paste a list" : "یک آدرس ایمیل وارد کنید یا یک لیست را جای‌گذاری کنید", "Remove email" : "حذف ایمیل", - "Select a destination" : "Select a destination", + "Select a destination" : "یک مقصد انتخاب کنید", "Select" : "گزینش
", "What are you requesting?" : "چه درخواستی دارید؟", "Request subject" : "موضوع درخواست", @@ -176,7 +176,7 @@ OC.L10N.register( "Cancel the file request creation" : "لغو درخواست فایل", "Close without sending emails" : "بستن بدون ارسال ایمیل", "Continue" : "ادامه", - "Error while toggling options" : "Error while toggling options", + "Error while toggling options" : "خطا هنگام تغییر وضعیت گزینه‌ها", "Accept shares from other accounts and groups by default" : "به صورت پیش‌فرض اشتراک‌گذاری‌ها را از حساب‌ها و گروه‌های دیگر بپذیرید", "Choose a default folder for accepted shares" : "انتخاب پوشه پیشفرض برای به اشتراک گذاشته های تایید شده", "Invalid path selected" : "مسیر انتخاب شده نامعتبر است", @@ -197,7 +197,7 @@ OC.L10N.register( "Shared with {user} by {owner}" : "مشترک با {user} توسط {owner}", "Open Sharing Details" : "جزئیات اشتراک‌گذاری را باز کنید", "Added by {initiator}" : "اضافه شده توسط {initiator}", - "Via “{folder}”" : "Via “{folder}”", + "Via “{folder}”" : "از طریق «{folder}»", "Unshare" : "لغو اشتراک", "Cannot copy, please copy the link manually" : "کپی کردن امکان پذیر نیست ، لطفا پیوند را به صورت دستی کپی کنید", "Copy internal link" : "کپی کردن پیوند داخلی", @@ -206,21 +206,21 @@ OC.L10N.register( "{shareWith} by {initiator}" : "{shareWith} توسط {initiator}", "Shared via link by {initiator}" : "از طریق لینک توسط {initiator} به اشتراک گذاشته شد", "File request ({label})" : "درخواست فایل ({label})", - "Mail share ({label})" : "Mail share ({label})", - "Share link ({label})" : "Share link ({label})", + "Mail share ({label})" : "اشتراک‌گذاری ایمیل ({label})", + "Share link ({label})" : "اشتراک‌گذاری لینک ({label})", "Mail share" : "اشتراک گذاری ایمیل", "Share link ({index})" : "اشتراک‌گذاری لینک ({index})", "Create public link" : "ایجاد لینک عمومی", - "Actions for \"{title}\"" : "Actions for \"{title}\"", + "Actions for \"{title}\"" : "کنش‌های «{title}»", "Copy public link of \"{title}\"" : "کپی کردن لینک عمومی \"{title}\"", "Error, please enter proper password and/or expiration date" : "خطا ، لطفاً رمز عبور مناسب و یا تاریخ انقضا را وارد کنید", - "Link share created" : "Link share created", - "Error while creating the share" : "Error while creating the share", + "Link share created" : "اشتراک‌گذاری لینک ایجاد شد", + "Error while creating the share" : "خطا هنگام ایجاد اشتراک", "Your browser does not support copying, please copy the link manually:" : "مرورگر شما از کپی کردن پشتیبانی نمی‌کند، لطفاً لینک را به صورت دستی کپی کنید:", "Successfully copied public link" : "لینک عمومی با موفقیت کپی شد", "Please enter the following required information before creating the share" : "لطفا قبل از ایجاد اشتراک ، اطلاعات لازم را وارد کنید", "Password protection (enforced)" : "محافظت از رمز عبور (اجباری)", - "Password protection" : "Password protection", + "Password protection" : "محافظت از رمز عبور", "Enter a password" : "یک گذرواژه وارد کنید", "Enable link expiration (enforced)" : "فعال کردن انقضای لینک (اجباری)", "Enable link expiration" : "فعال کردن انقضای لینک ", @@ -234,7 +234,7 @@ OC.L10N.register( "Quick share options, the current selected is \"{selectedOption}\"" : "گزینه‌های اشتراک‌گذاری سریع، گزینه‌ی فعلی \"{selectedOption}\" است.", "View only" : "تنها مشاهده", "Can edit" : "توانایی ویرایش", - "Custom permissions" : "Custom permissions", + "Custom permissions" : "دسترسی‌های سفارشی", "Resharing is not allowed" : "اشتراک گذاری مجدد مجاز نمی باشد", "Name or email …" : "نام یا ایمیل", "Name, email, or Federated Cloud ID …" : "نام یا ایمیل یا شناسه‌ی فدرال ابری", @@ -245,7 +245,7 @@ OC.L10N.register( "Group" : "گروه", "Email" : "رایانامه", "Team" : "تیم", - "Talk conversation" : "Talk conversation", + "Talk conversation" : "گفتگوی Talk", "Deck board" : "تخته deck", "ScienceMesh" : "ScienceMesh", "on {server}" : "روی{server}", @@ -280,14 +280,14 @@ OC.L10N.register( "Allow editing" : "اجازه‌ی ویرایش", "Upload only" : "فقط آپلود", "Advanced settings" : "تنظیمات پیشرفته", - "Share label" : "Share label", + "Share label" : "برچسب اشتراک‌گذاری", "Share link token" : "اشتراک‌گذاری توکن لینک", "Set the public share link token to something easy to remember or generate a new token. It is not recommended to use a guessable token for shares which contain sensitive information." : "توکن لینک اشتراک‌گذاری عمومی را روی چیزی تنظیم کنید که به راحتی قابل یادآوری باشد یا یک توکن جدید ایجاد کنید. استفاده از توکن قابل حدس برای اشتراک‌گذاری‌هایی که حاوی اطلاعات حساس هستند توصیه نمی‌شود.", "Generating…" : "در حال ساخت", "Generate new token" : "توکن جدید ایجاد کنید", "Set password" : "تنظیم گذرواژه", - "Password expires {passwordExpirationTime}" : "Password expires {passwordExpirationTime}", - "Password expired" : "Password expired", + "Password expires {passwordExpirationTime}" : "گذرواژه در {passwordExpirationTime} منقضی می‌شود", + "Password expired" : "گذرواژه منقضی شده است", "Video verification" : "تأیید صحت ویدیو", "Expiration date (enforced)" : "تاریخ انقضا (اجباری)", "Set expiration date" : "تنظیم تاریخ انقضا", @@ -302,7 +302,7 @@ OC.L10N.register( "Toggle list of others with access to this directory" : "لیست دسترسی دیگران به این فهرست را تغییر دهید", "Toggle list of others with access to this file" : "لیست سایرین را با دسترسی به این پرونده تغییر دهید", "Unable to fetch inherited shares" : "واگذاری سهام ارثی امکان پذیر نیست", - "Link shares" : "Link shares", + "Link shares" : "اشتراک‌گذاری‌های لینک", "Shares" : "اشتراک گذاری ها", "Share files within your organization. Recipients who can already view the file can also use this link for easy access." : "فایل‌ها را در سازمان خود به اشتراک بگذارید. گیرندگانی که از قبل می‌توانند فایل را مشاهده کنند، می‌توانند از این لینک نیز برای دسترسی آسان استفاده کنند.", "Share files with others outside your organization via public links and email addresses. You can also share to {productName} accounts on other instances using their federated cloud ID." : "فایل‌ها را از طریق لینک‌های عمومی و آدرس‌های ایمیل با دیگران در خارج از سازمان خود به اشتراک بگذارید. همچنین می‌توانید با استفاده از شناسه ابری فدرال آنها، فایل‌ها را با حساب‌های {productName} در موارد دیگر به اشتراک بگذارید.", @@ -402,7 +402,7 @@ OC.L10N.register( "Share not found" : "اشتراک گذاری یافت نشد", "Back to %s" : "بازگشت به %s", "Add to your Nextcloud" : "به نکست‌کلود خود اضافه کنید", - "Sharing %s failed because the back end does not support ScienceMesh shares" : "Sharing %s failed because the back end does not support ScienceMesh shares", + "Sharing %s failed because the back end does not support ScienceMesh shares" : "اشتراک‌گذاری %s انجام نشد زیرا بک‌اند از اشتراک‌گذاری‌های ScienceMesh پشتیبانی نمی‌کند", "Name or email …" : "نام یا ایمیل", "Name, email, or Federated Cloud ID …" : "نام یا ایمیل یا شناسه‌ی فدرال ابری", "Searching …" : "جستجوکردن …" diff --git a/apps/files_sharing/l10n/fa.json b/apps/files_sharing/l10n/fa.json index e09e13ad90ba3..dda53b9fd385f 100644 --- a/apps/files_sharing/l10n/fa.json +++ b/apps/files_sharing/l10n/fa.json @@ -58,10 +58,10 @@ "Sharing" : "اشتراک گذاری", "A file or folder has been shared" : "فایل یا پوشه ای به اشتراک گذاشته شد", "Shared link" : "پیوند به اشتراک گذاری شده", - "Wrong share ID, share does not exist" : "Wrong share ID, share does not exist", + "Wrong share ID, share does not exist" : "شناسهٔ اشتراک‌گذاری نادرست است؛ این اشتراک وجود ندارد", "Could not delete share" : "اشتراک گذاری حذف نشد", "Please specify a file or folder path" : "لطفاً مسیر فایل یا پوشه را مشخص کنید", - "Wrong path, file/folder does not exist" : "Wrong path, file/folder does not exist", + "Wrong path, file/folder does not exist" : "مسیر نادرست است؛ فایل/پوشه وجود ندارد", "Could not create share" : "امکان ایجاد اشتراک گذاری وجود ندارد", "Please specify a valid account to share with" : "لطفاً یک حساب معتبر برای اشتراک‌گذاری مشخص کنید", "Group sharing is disabled by the administrator" : "اشتراک گروه توسط مدیر غیرفعال شده است.", @@ -71,7 +71,7 @@ "Sharing %s sending the password by Nextcloud Talk failed because Nextcloud Talk is not enabled" : ".اشتراک‍‍‍%s ارسال رمز عبور توسط Nextcloud Talk به دلیل فعال نشدن Nextcloud Talk انجام نشد.", "Sharing %1$s failed because the back end does not allow shares from type %2$s" : "%2$sاشتراک گذاری%1$s انجام نشد زیرا بک اِند اجازه نمی دهد نوع از سهام استفاده شود", "Please specify a valid federated account ID" : "لطفا یک شناسه حساب کاربری معتبر مشخص کنید", - "Please specify a valid federated group ID" : "Please specify a valid federated group ID", + "Please specify a valid federated group ID" : "لطفاً یک شناسهٔ گروه فدرات‌شده معتبر مشخص کنید", "You cannot share to a Team if the app is not enabled" : "اگر برنامه فعال نباشد، نمی‌توانید آن را با یک تیم به اشتراک بگذارید", "Please specify a valid team" : "لطفا یک تیم معتبر معرفی کنید", "Sharing %s failed because the back end does not support room shares" : "اشتراک گذاری %sانجام نشد زیرا قسمت پشتی سهام اتاق را پشتیبانی نمی کند", @@ -79,14 +79,14 @@ "Not a directory" : "این یک پوشه نیست", "Could not lock node" : "گره را نمی توان قفل کرد", "Public upload is only possible for publicly shared folders" : "بارگذاری عمومی فقط برای پوشه هایی که به طور عمومی به اشتراک گذاشته می شوند ممکن است", - "Share must at least have READ or CREATE permissions" : "Share must at least have READ or CREATE permissions", - "Share must have READ permission if UPDATE or DELETE permission is set" : "Share must have READ permission if UPDATE or DELETE permission is set", + "Share must at least have READ or CREATE permissions" : "اشتراک‌گذاری باید دست‌کم دسترسی خواندن (READ) یا ایجاد (CREATE) داشته باشد", + "Share must have READ permission if UPDATE or DELETE permission is set" : "اگر دسترسی به‌روزرسانی (UPDATE) یا حذف (DELETE) تنظیم شده باشد، اشتراک‌گذاری باید دسترسی خواندن (READ) نیز داشته باشد", "Public upload disabled by the administrator" : "آپلود عمومی توسط سرپرست غیرفعال شده است", "Could not lock path" : "امکان قفل کردن مسیر وجود ندارد.", "no sharing rights on this item" : "حق اشتراک گذاری در این مورد وجود ندارد", "You are not allowed to edit incoming shares" : "شما مجاز به ویرایش اشتراک‌های دریافتی نیستید", "Wrong or no update parameter given" : "اشتباهی و یا پارامتر بروزرسانی داده نشد", - "\"Sending the password by Nextcloud Talk\" for sharing a file or folder failed because Nextcloud Talk is not enabled." : "\"Sending the password by Nextcloud Talk\" for sharing a file or folder failed because Nextcloud Talk is not enabled.", + "\"Sending the password by Nextcloud Talk\" for sharing a file or folder failed because Nextcloud Talk is not enabled." : "«ارسال گذرواژه از طریق Nextcloud Talk» برای اشتراک‌گذاری یک فایل یا پوشه ناموفق بود، زیرا Nextcloud Talk فعال نیست.", "Custom share link tokens have been disabled by the administrator" : "توکن‌های لینک اشتراک‌گذاری سفارشی توسط مدیر غیرفعال شده‌اند", "Tokens must contain at least 1 character and may only contain letters, numbers, or a hyphen" : "توکن‌ها باید حداقل شامل ۱ کاراکتر باشند و فقط می‌توانند شامل حروف، اعداد یا خط تیره باشند.", "Invalid date. Format must be YYYY-MM-DD" : "تاریخ نامعتبر است. قالب باید YYYY-MM-DD باشد.", @@ -100,7 +100,7 @@ "This share does not exist or is no longer available" : "این سهم وجود ندارد یا دیگر در دسترس نیست", "shared by %s" : "اشتراک گذاری شده به میزان %s", "Download" : "دانلود", - "Add to your %s" : "Add to your %s", + "Add to your %s" : "افزودن به %s شما", "Direct link" : "لینک مستقیم", "Share API is disabled" : "اشتراک API غیرفعال شده است", "File sharing" : "اشتراک گذاری پرونده", @@ -143,7 +143,7 @@ "Send link via email" : "ارسال لینک از طریق ایمیل", "Enter an email address or paste a list" : "یک آدرس ایمیل وارد کنید یا یک لیست را جای‌گذاری کنید", "Remove email" : "حذف ایمیل", - "Select a destination" : "Select a destination", + "Select a destination" : "یک مقصد انتخاب کنید", "Select" : "گزینش
", "What are you requesting?" : "چه درخواستی دارید؟", "Request subject" : "موضوع درخواست", @@ -174,7 +174,7 @@ "Cancel the file request creation" : "لغو درخواست فایل", "Close without sending emails" : "بستن بدون ارسال ایمیل", "Continue" : "ادامه", - "Error while toggling options" : "Error while toggling options", + "Error while toggling options" : "خطا هنگام تغییر وضعیت گزینه‌ها", "Accept shares from other accounts and groups by default" : "به صورت پیش‌فرض اشتراک‌گذاری‌ها را از حساب‌ها و گروه‌های دیگر بپذیرید", "Choose a default folder for accepted shares" : "انتخاب پوشه پیشفرض برای به اشتراک گذاشته های تایید شده", "Invalid path selected" : "مسیر انتخاب شده نامعتبر است", @@ -195,7 +195,7 @@ "Shared with {user} by {owner}" : "مشترک با {user} توسط {owner}", "Open Sharing Details" : "جزئیات اشتراک‌گذاری را باز کنید", "Added by {initiator}" : "اضافه شده توسط {initiator}", - "Via “{folder}”" : "Via “{folder}”", + "Via “{folder}”" : "از طریق «{folder}»", "Unshare" : "لغو اشتراک", "Cannot copy, please copy the link manually" : "کپی کردن امکان پذیر نیست ، لطفا پیوند را به صورت دستی کپی کنید", "Copy internal link" : "کپی کردن پیوند داخلی", @@ -204,21 +204,21 @@ "{shareWith} by {initiator}" : "{shareWith} توسط {initiator}", "Shared via link by {initiator}" : "از طریق لینک توسط {initiator} به اشتراک گذاشته شد", "File request ({label})" : "درخواست فایل ({label})", - "Mail share ({label})" : "Mail share ({label})", - "Share link ({label})" : "Share link ({label})", + "Mail share ({label})" : "اشتراک‌گذاری ایمیل ({label})", + "Share link ({label})" : "اشتراک‌گذاری لینک ({label})", "Mail share" : "اشتراک گذاری ایمیل", "Share link ({index})" : "اشتراک‌گذاری لینک ({index})", "Create public link" : "ایجاد لینک عمومی", - "Actions for \"{title}\"" : "Actions for \"{title}\"", + "Actions for \"{title}\"" : "کنش‌های «{title}»", "Copy public link of \"{title}\"" : "کپی کردن لینک عمومی \"{title}\"", "Error, please enter proper password and/or expiration date" : "خطا ، لطفاً رمز عبور مناسب و یا تاریخ انقضا را وارد کنید", - "Link share created" : "Link share created", - "Error while creating the share" : "Error while creating the share", + "Link share created" : "اشتراک‌گذاری لینک ایجاد شد", + "Error while creating the share" : "خطا هنگام ایجاد اشتراک", "Your browser does not support copying, please copy the link manually:" : "مرورگر شما از کپی کردن پشتیبانی نمی‌کند، لطفاً لینک را به صورت دستی کپی کنید:", "Successfully copied public link" : "لینک عمومی با موفقیت کپی شد", "Please enter the following required information before creating the share" : "لطفا قبل از ایجاد اشتراک ، اطلاعات لازم را وارد کنید", "Password protection (enforced)" : "محافظت از رمز عبور (اجباری)", - "Password protection" : "Password protection", + "Password protection" : "محافظت از رمز عبور", "Enter a password" : "یک گذرواژه وارد کنید", "Enable link expiration (enforced)" : "فعال کردن انقضای لینک (اجباری)", "Enable link expiration" : "فعال کردن انقضای لینک ", @@ -232,7 +232,7 @@ "Quick share options, the current selected is \"{selectedOption}\"" : "گزینه‌های اشتراک‌گذاری سریع، گزینه‌ی فعلی \"{selectedOption}\" است.", "View only" : "تنها مشاهده", "Can edit" : "توانایی ویرایش", - "Custom permissions" : "Custom permissions", + "Custom permissions" : "دسترسی‌های سفارشی", "Resharing is not allowed" : "اشتراک گذاری مجدد مجاز نمی باشد", "Name or email …" : "نام یا ایمیل", "Name, email, or Federated Cloud ID …" : "نام یا ایمیل یا شناسه‌ی فدرال ابری", @@ -243,7 +243,7 @@ "Group" : "گروه", "Email" : "رایانامه", "Team" : "تیم", - "Talk conversation" : "Talk conversation", + "Talk conversation" : "گفتگوی Talk", "Deck board" : "تخته deck", "ScienceMesh" : "ScienceMesh", "on {server}" : "روی{server}", @@ -278,14 +278,14 @@ "Allow editing" : "اجازه‌ی ویرایش", "Upload only" : "فقط آپلود", "Advanced settings" : "تنظیمات پیشرفته", - "Share label" : "Share label", + "Share label" : "برچسب اشتراک‌گذاری", "Share link token" : "اشتراک‌گذاری توکن لینک", "Set the public share link token to something easy to remember or generate a new token. It is not recommended to use a guessable token for shares which contain sensitive information." : "توکن لینک اشتراک‌گذاری عمومی را روی چیزی تنظیم کنید که به راحتی قابل یادآوری باشد یا یک توکن جدید ایجاد کنید. استفاده از توکن قابل حدس برای اشتراک‌گذاری‌هایی که حاوی اطلاعات حساس هستند توصیه نمی‌شود.", "Generating…" : "در حال ساخت", "Generate new token" : "توکن جدید ایجاد کنید", "Set password" : "تنظیم گذرواژه", - "Password expires {passwordExpirationTime}" : "Password expires {passwordExpirationTime}", - "Password expired" : "Password expired", + "Password expires {passwordExpirationTime}" : "گذرواژه در {passwordExpirationTime} منقضی می‌شود", + "Password expired" : "گذرواژه منقضی شده است", "Video verification" : "تأیید صحت ویدیو", "Expiration date (enforced)" : "تاریخ انقضا (اجباری)", "Set expiration date" : "تنظیم تاریخ انقضا", @@ -300,7 +300,7 @@ "Toggle list of others with access to this directory" : "لیست دسترسی دیگران به این فهرست را تغییر دهید", "Toggle list of others with access to this file" : "لیست سایرین را با دسترسی به این پرونده تغییر دهید", "Unable to fetch inherited shares" : "واگذاری سهام ارثی امکان پذیر نیست", - "Link shares" : "Link shares", + "Link shares" : "اشتراک‌گذاری‌های لینک", "Shares" : "اشتراک گذاری ها", "Share files within your organization. Recipients who can already view the file can also use this link for easy access." : "فایل‌ها را در سازمان خود به اشتراک بگذارید. گیرندگانی که از قبل می‌توانند فایل را مشاهده کنند، می‌توانند از این لینک نیز برای دسترسی آسان استفاده کنند.", "Share files with others outside your organization via public links and email addresses. You can also share to {productName} accounts on other instances using their federated cloud ID." : "فایل‌ها را از طریق لینک‌های عمومی و آدرس‌های ایمیل با دیگران در خارج از سازمان خود به اشتراک بگذارید. همچنین می‌توانید با استفاده از شناسه ابری فدرال آنها، فایل‌ها را با حساب‌های {productName} در موارد دیگر به اشتراک بگذارید.", @@ -400,7 +400,7 @@ "Share not found" : "اشتراک گذاری یافت نشد", "Back to %s" : "بازگشت به %s", "Add to your Nextcloud" : "به نکست‌کلود خود اضافه کنید", - "Sharing %s failed because the back end does not support ScienceMesh shares" : "Sharing %s failed because the back end does not support ScienceMesh shares", + "Sharing %s failed because the back end does not support ScienceMesh shares" : "اشتراک‌گذاری %s انجام نشد زیرا بک‌اند از اشتراک‌گذاری‌های ScienceMesh پشتیبانی نمی‌کند", "Name or email …" : "نام یا ایمیل", "Name, email, or Federated Cloud ID …" : "نام یا ایمیل یا شناسه‌ی فدرال ابری", "Searching …" : "جستجوکردن …" diff --git a/apps/files_sharing/lib/External/Storage.php b/apps/files_sharing/lib/External/Storage.php index 90a50f785dd98..929d9cbf976ea 100644 --- a/apps/files_sharing/lib/External/Storage.php +++ b/apps/files_sharing/lib/External/Storage.php @@ -63,7 +63,7 @@ public function __construct($options) { $this->manager = $options['manager']; $this->cloudId = $options['cloudId']; $this->logger = Server::get(LoggerInterface::class); - $discoveryService = Server::get(IOCMDiscoveryService::class); + $discoveryService = $options['discoveryService'] ?? Server::get(IOCMDiscoveryService::class); $this->config = Server::get(IConfig::class); $this->appConfig = Server::get(IAppConfig::class); $this->shareManager = Server::get(IShareManager::class); diff --git a/apps/files_sharing/lib/SharesReminderJob.php b/apps/files_sharing/lib/SharesReminderJob.php index 1a1ee1873902d..4ec9aabe716ea 100644 --- a/apps/files_sharing/lib/SharesReminderJob.php +++ b/apps/files_sharing/lib/SharesReminderJob.php @@ -35,7 +35,6 @@ */ class SharesReminderJob extends TimedJob { private const SECONDS_BEFORE_REMINDER = 24 * 60 * 60; - private const CHUNK_SIZE = 1000; private int $folderMimeTypeId; public function __construct( @@ -131,7 +130,7 @@ private function getSharesData(): array { $qb->expr()->isNull('f.fileid') ) ) - ->setMaxResults(SharesReminderJob::CHUNK_SIZE); + ->setMaxResults(IQueryBuilder::MAX_IN_PARAMETERS); $shares = $qb->executeQuery()->fetchAllAssociative(); return array_map(fn ($share): array => [ @@ -178,7 +177,7 @@ private function getSharesDataSharded(): array|\Iterator { 'share_type' => (int)$share['share_type'], 'file_source' => (int)$share['file_source'], ], $shares); - return $this->filterSharesWithEmptyFolders($shares, self::CHUNK_SIZE); + return $this->filterSharesWithEmptyFolders($shares, IQueryBuilder::MAX_IN_PARAMETERS); } /** @@ -193,7 +192,7 @@ private function filterSharesWithEmptyFolders(array $shares, int $maxResults): a ->where($query->expr()->eq('size', $query->createNamedParameter(0), IQueryBuilder::PARAM_INT_ARRAY)) ->andWhere($query->expr()->eq('mimetype', $query->createNamedParameter($this->folderMimeTypeId, IQueryBuilder::PARAM_INT))) ->andWhere($query->expr()->in('fileid', $query->createParameter('fileids'))); - $chunks = array_chunk($shares, SharesReminderJob::CHUNK_SIZE); + $chunks = array_chunk($shares, IQueryBuilder::MAX_IN_PARAMETERS); $results = []; foreach ($chunks as $chunk) { $chunkFileIds = array_map(fn ($share): int => $share['file_source'], $chunk); diff --git a/apps/files_sharing/tests/CacheTest.php b/apps/files_sharing/tests/CacheTest.php index a3c62db96b75a..e8c63e6fc126b 100644 --- a/apps/files_sharing/tests/CacheTest.php +++ b/apps/files_sharing/tests/CacheTest.php @@ -34,8 +34,6 @@ class CacheTest extends TestCase { protected Cache $sharedCache; protected Storage $ownerStorage; protected Storage $sharedStorage; - /** @var \OCP\Share\IManager $shareManager */ - protected $shareManager; protected function setUp(): void { parent::setUp(); diff --git a/apps/files_sharing/tests/Controller/ShareControllerTest.php b/apps/files_sharing/tests/Controller/ShareControllerTest.php index 2d73ea2da464c..8bec64ef42143 100644 --- a/apps/files_sharing/tests/Controller/ShareControllerTest.php +++ b/apps/files_sharing/tests/Controller/ShareControllerTest.php @@ -50,12 +50,14 @@ use OCP\Share\IPublicShareTemplateFactory; use OCP\Share\IShare; use PHPUnit\Framework\MockObject\MockObject; +use Test\Traits\UserTrait; /** * @package OCA\Files_Sharing\Controllers */ #[\PHPUnit\Framework\Attributes\Group(name: 'DB')] class ShareControllerTest extends \Test\TestCase { + use UserTrait; private string $user; private string $oldUser; @@ -150,7 +152,7 @@ protected function setUp(): void { // Create a dummy user $this->user = Server::get(ISecureRandom::class)->generate(12, ISecureRandom::CHAR_LOWER); - Server::get(IUserManager::class)->createUser($this->user, $this->user); + $this->createUser($this->user, $this->user); \OC_Util::tearDownFS(); $this->loginAsUser($this->user); } diff --git a/apps/files_sharing/tests/DeleteOrphanedSharesJobTest.php b/apps/files_sharing/tests/DeleteOrphanedSharesJobTest.php index 5a43419113233..03fc302b9a5ba 100644 --- a/apps/files_sharing/tests/DeleteOrphanedSharesJobTest.php +++ b/apps/files_sharing/tests/DeleteOrphanedSharesJobTest.php @@ -17,6 +17,7 @@ use OCP\IUserManager; use OCP\Server; use OCP\Share\IShare; +use Test\Traits\UserTrait; /** * Class DeleteOrphanedSharesJobTest @@ -26,6 +27,8 @@ */ #[\PHPUnit\Framework\Attributes\Group(name: 'DB')] class DeleteOrphanedSharesJobTest extends \Test\TestCase { + use UserTrait; + /** * @var bool */ @@ -76,9 +79,8 @@ protected function setUp(): void { $this->user1 = $this->getUniqueID('user1_'); $this->user2 = $this->getUniqueID('user2_'); - $userManager = Server::get(IUserManager::class); - $userManager->createUser($this->user1, 'pass'); - $userManager->createUser($this->user2, 'pass'); + $this->createUser($this->user1, 'pass'); + $this->createUser($this->user2, 'pass'); \OC::registerShareHooks(Server::get(SystemConfig::class)); diff --git a/apps/files_sharing/tests/EtagPropagationTest.php b/apps/files_sharing/tests/EtagPropagationTest.php index 8c977cfe9db34..551e744f7493e 100644 --- a/apps/files_sharing/tests/EtagPropagationTest.php +++ b/apps/files_sharing/tests/EtagPropagationTest.php @@ -32,16 +32,15 @@ class EtagPropagationTest extends PropagationTestCase { * "user4" puts the received "inside" folder into "sub1/sub2/inside" (this is to check if it propagates across multiple subfolders) */ protected function setUpShares() { - $this->fileIds[self::TEST_FILES_SHARING_API_USER1] = []; - $this->fileIds[self::TEST_FILES_SHARING_API_USER2] = []; - $this->fileIds[self::TEST_FILES_SHARING_API_USER3] = []; - $this->fileIds[self::TEST_FILES_SHARING_API_USER4] = []; + $this->fileInfos[self::TEST_FILES_SHARING_API_USER1] = []; + $this->fileInfos[self::TEST_FILES_SHARING_API_USER2] = []; + $this->fileInfos[self::TEST_FILES_SHARING_API_USER3] = []; + $this->fileInfos[self::TEST_FILES_SHARING_API_USER4] = []; $rootFolder = Server::get(IRootFolder::class); $shareManager = Server::get(\OCP\Share\IManager::class); $this->rootView = new View(''); - $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); $view1 = new View('/' . self::TEST_FILES_SHARING_API_USER1 . '/files'); $view1->mkdir('/sub1/sub2/folder/inside'); $view1->mkdir('/directReshare'); @@ -99,9 +98,9 @@ protected function setUpShares() { $share = $shareManager->createShare($share); $this->shareManager->acceptShare($share, self::TEST_FILES_SHARING_API_USER2); - $this->fileIds[self::TEST_FILES_SHARING_API_USER1][''] = $view1->getFileInfo('')->getId(); - $this->fileIds[self::TEST_FILES_SHARING_API_USER1]['sub1'] = $view1->getFileInfo('sub1')->getId(); - $this->fileIds[self::TEST_FILES_SHARING_API_USER1]['sub1/sub2'] = $view1->getFileInfo('sub1/sub2')->getId(); + $this->fileInfos[self::TEST_FILES_SHARING_API_USER1][''] = $view1->getFileInfo(''); + $this->fileInfos[self::TEST_FILES_SHARING_API_USER1]['sub1'] = $view1->getFileInfo('sub1'); + $this->fileInfos[self::TEST_FILES_SHARING_API_USER1]['sub1/sub2'] = $view1->getFileInfo('sub1/sub2'); /* * User 2 @@ -110,7 +109,6 @@ protected function setUpShares() { $view2 = new View('/' . self::TEST_FILES_SHARING_API_USER2 . '/files'); $view2->mkdir('/sub1/sub2'); $view2->rename('/folder', '/sub1/sub2/folder'); - $this->loginAsUser(self::TEST_FILES_SHARING_API_USER2); $insideInfo = $view2->getFileInfo('/sub1/sub2/folder/inside'); $this->assertInstanceOf('\OC\Files\FileInfo', $insideInfo); @@ -140,9 +138,9 @@ protected function setUpShares() { $share = $shareManager->createShare($share); $this->shareManager->acceptShare($share, self::TEST_FILES_SHARING_API_USER4); - $this->fileIds[self::TEST_FILES_SHARING_API_USER2][''] = $view2->getFileInfo('')->getId(); - $this->fileIds[self::TEST_FILES_SHARING_API_USER2]['sub1'] = $view2->getFileInfo('sub1')->getId(); - $this->fileIds[self::TEST_FILES_SHARING_API_USER2]['sub1/sub2'] = $view2->getFileInfo('sub1/sub2')->getId(); + $this->fileInfos[self::TEST_FILES_SHARING_API_USER2][''] = $view2->getFileInfo(''); + $this->fileInfos[self::TEST_FILES_SHARING_API_USER2]['sub1'] = $view2->getFileInfo('sub1'); + $this->fileInfos[self::TEST_FILES_SHARING_API_USER2]['sub1/sub2'] = $view2->getFileInfo('sub1/sub2'); /* * User 3 @@ -151,9 +149,9 @@ protected function setUpShares() { $view3 = new View('/' . self::TEST_FILES_SHARING_API_USER3 . '/files'); $view3->mkdir('/sub1/sub2'); $view3->rename('/folder', '/sub1/sub2/folder'); - $this->fileIds[self::TEST_FILES_SHARING_API_USER3][''] = $view3->getFileInfo('')->getId(); - $this->fileIds[self::TEST_FILES_SHARING_API_USER3]['sub1'] = $view3->getFileInfo('sub1')->getId(); - $this->fileIds[self::TEST_FILES_SHARING_API_USER3]['sub1/sub2'] = $view3->getFileInfo('sub1/sub2')->getId(); + $this->fileInfos[self::TEST_FILES_SHARING_API_USER3][''] = $view3->getFileInfo(''); + $this->fileInfos[self::TEST_FILES_SHARING_API_USER3]['sub1'] = $view3->getFileInfo('sub1'); + $this->fileInfos[self::TEST_FILES_SHARING_API_USER3]['sub1/sub2'] = $view3->getFileInfo('sub1/sub2'); /* * User 4 @@ -162,18 +160,9 @@ protected function setUpShares() { $view4 = new View('/' . self::TEST_FILES_SHARING_API_USER4 . '/files'); $view4->mkdir('/sub1/sub2'); $view4->rename('/inside', '/sub1/sub2/inside'); - $this->fileIds[self::TEST_FILES_SHARING_API_USER4][''] = $view4->getFileInfo('')->getId(); - $this->fileIds[self::TEST_FILES_SHARING_API_USER4]['sub1'] = $view4->getFileInfo('sub1')->getId(); - $this->fileIds[self::TEST_FILES_SHARING_API_USER4]['sub1/sub2'] = $view4->getFileInfo('sub1/sub2')->getId(); - - foreach ($this->fileIds as $user => $ids) { - $this->loginAsUser($user); - foreach ($ids as $id) { - $path = $this->rootView->getPath($id); - $ls = $this->rootView->getDirectoryContent($path); - $this->fileEtags[$id] = $this->rootView->getFileInfo($path)->getEtag(); - } - } + $this->fileInfos[self::TEST_FILES_SHARING_API_USER4][''] = $view4->getFileInfo(''); + $this->fileInfos[self::TEST_FILES_SHARING_API_USER4]['sub1'] = $view4->getFileInfo('sub1'); + $this->fileInfos[self::TEST_FILES_SHARING_API_USER4]['sub1/sub2'] = $view4->getFileInfo('sub1/sub2'); } public function testOwnerWritesToShare(): void { diff --git a/apps/files_sharing/tests/ExpireSharesJobTest.php b/apps/files_sharing/tests/ExpireSharesJobTest.php index 22d2487005c68..667c781ad8274 100644 --- a/apps/files_sharing/tests/ExpireSharesJobTest.php +++ b/apps/files_sharing/tests/ExpireSharesJobTest.php @@ -15,11 +15,11 @@ use OCP\Files\IRootFolder; use OCP\IDBConnection; use OCP\IUser; -use OCP\IUserManager; use OCP\Server; use OCP\Share\IManager; use OCP\Share\IShare; use PHPUnit\Framework\Attributes\DataProvider; +use Test\Traits\UserTrait; /** * Class ExpireSharesJobTest @@ -29,6 +29,7 @@ */ #[\PHPUnit\Framework\Attributes\Group(name: 'DB')] class ExpireSharesJobTest extends \Test\TestCase { + use UserTrait; private ExpireSharesJob $job; @@ -51,9 +52,8 @@ protected function setUp(): void { $user1 = $this->getUniqueID('user1_'); $user2 = $this->getUniqueID('user2_'); - $userManager = Server::get(IUserManager::class); - $this->user1 = $userManager->createUser($user1, 'longrandompassword'); - $this->user2 = $userManager->createUser($user2, 'longrandompassword'); + $this->user1 = $this->createUser($user1, 'longrandompassword'); + $this->user2 = $this->createUser($user2, 'longrandompassword'); \OC::registerShareHooks(Server::get(SystemConfig::class)); diff --git a/apps/files_sharing/tests/External/ManagerTest.php b/apps/files_sharing/tests/External/ManagerTest.php index 44efcb3412671..804f415f522de 100644 --- a/apps/files_sharing/tests/External/ManagerTest.php +++ b/apps/files_sharing/tests/External/ManagerTest.php @@ -70,7 +70,7 @@ class ManagerTest extends TestCase { protected ICloudFederationFactory&MockObject $cloudFederationFactory; protected IGroupManager&MockObject $groupManager; protected IUserManager&MockObject $userManager; - protected ISetupManager&MockObject $setupManager; + protected ISetupManager&MockObject $setupManagerEncTrait; protected ICertificateManager&MockObject $certificateManager; private ExternalShareMapper $externalShareMapper; private IConfig $config; @@ -88,7 +88,7 @@ protected function setUp(): void { $this->groupManager = $this->createMock(IGroupManager::class); $this->userManager = $this->createMock(IUserManager::class); $this->eventDispatcher = $this->createMock(IEventDispatcher::class); - $this->setupManager = $this->createMock(ISetupManager::class); + $this->setupManagerEncTrait = $this->createMock(ISetupManager::class); $this->rootFolder = $this->createMock(IRootFolder::class); $this->rootFolder->method('getUserFolder') ->willReturnCallback(function (string $userId): Folder { @@ -170,7 +170,7 @@ private function createManagerForUser(IUser $user): Manager&MockObject { $this->eventDispatcher, $this->logger, $this->rootFolder, - $this->setupManager, + $this->setupManagerEncTrait, $this->certificateManager, $this->externalShareMapper, $this->config, diff --git a/apps/files_sharing/tests/ExternalStorageTest.php b/apps/files_sharing/tests/ExternalStorageTest.php index b038d2c73497e..38040a47d71f2 100644 --- a/apps/files_sharing/tests/ExternalStorageTest.php +++ b/apps/files_sharing/tests/ExternalStorageTest.php @@ -15,6 +15,8 @@ use OCP\Http\Client\IClientService; use OCP\Http\Client\IResponse; use OCP\ICertificateManager; +use OCP\OCM\IOCMDiscoveryService; +use OCP\OCM\IOCMProvider; use OCP\Server; /** @@ -62,6 +64,9 @@ private function getTestStorage($uri) { $manager = $this->createMock(ExternalShareManager::class); $client = $this->createMock(IClient::class); $response = $this->createMock(IResponse::class); + $discoveryService = $this->createMock(IOCMDiscoveryService::class); + $ocmProvider = $this->createMock(IOCMProvider::class); + $client ->expects($this->any()) ->method('get') @@ -74,6 +79,12 @@ private function getTestStorage($uri) { ->expects($this->any()) ->method('newClient') ->willReturn($client); + $discoveryService->method('discover') + ->willReturn($ocmProvider); + $ocmProvider->method('extractProtocolEntry') + ->willReturn('/public.php/webdav'); + $ocmProvider->method('getEndPoint') + ->willReturn($uri); return new TestSharingExternalStorage( [ @@ -86,6 +97,7 @@ private function getTestStorage($uri) { 'manager' => $manager, 'certificateManager' => $certificateManager, 'HttpClientService' => $httpClientService, + 'discoveryService' => $discoveryService, ] ); } diff --git a/apps/files_sharing/tests/GroupEtagPropagationTest.php b/apps/files_sharing/tests/GroupEtagPropagationTest.php index 47357f206269c..6dc3b8b35c0aa 100644 --- a/apps/files_sharing/tests/GroupEtagPropagationTest.php +++ b/apps/files_sharing/tests/GroupEtagPropagationTest.php @@ -25,10 +25,10 @@ class GroupEtagPropagationTest extends PropagationTestCase { * "user4" (in group 3) */ protected function setUpShares() { - $this->fileIds[self::TEST_FILES_SHARING_API_USER1] = []; - $this->fileIds[self::TEST_FILES_SHARING_API_USER2] = []; - $this->fileIds[self::TEST_FILES_SHARING_API_USER3] = []; - $this->fileIds[self::TEST_FILES_SHARING_API_USER4] = []; + $this->fileInfos[self::TEST_FILES_SHARING_API_USER1] = []; + $this->fileInfos[self::TEST_FILES_SHARING_API_USER2] = []; + $this->fileInfos[self::TEST_FILES_SHARING_API_USER3] = []; + $this->fileInfos[self::TEST_FILES_SHARING_API_USER4] = []; $this->rootView = new View(''); $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); @@ -43,9 +43,9 @@ protected function setUpShares() { Constants::PERMISSION_ALL ); $this->shareManager->acceptShare($share, self::TEST_FILES_SHARING_API_USER2); - $this->fileIds[self::TEST_FILES_SHARING_API_USER1][''] = $view1->getFileInfo('')->getId(); - $this->fileIds[self::TEST_FILES_SHARING_API_USER1]['test'] = $view1->getFileInfo('test')->getId(); - $this->fileIds[self::TEST_FILES_SHARING_API_USER1]['test/sub'] = $view1->getFileInfo('test/sub')->getId(); + $this->fileInfos[self::TEST_FILES_SHARING_API_USER1][''] = $view1->getFileInfo(''); + $this->fileInfos[self::TEST_FILES_SHARING_API_USER1]['test'] = $view1->getFileInfo('test'); + $this->fileInfos[self::TEST_FILES_SHARING_API_USER1]['test/sub'] = $view1->getFileInfo('test/sub'); $this->loginAsUser(self::TEST_FILES_SHARING_API_USER2); $view2 = new View('/' . self::TEST_FILES_SHARING_API_USER2 . '/files'); @@ -67,28 +67,20 @@ protected function setUpShares() { ); $this->shareManager->acceptShare($share, self::TEST_FILES_SHARING_API_USER4); - $this->fileIds[self::TEST_FILES_SHARING_API_USER2][''] = $view2->getFileInfo('')->getId(); - $this->fileIds[self::TEST_FILES_SHARING_API_USER2]['test'] = $view2->getFileInfo('test')->getId(); - $this->fileIds[self::TEST_FILES_SHARING_API_USER2]['test/sub'] = $view2->getFileInfo('test/sub')->getId(); + $this->fileInfos[self::TEST_FILES_SHARING_API_USER2][''] = $view2->getFileInfo(''); + $this->fileInfos[self::TEST_FILES_SHARING_API_USER2]['test'] = $view2->getFileInfo('test'); + $this->fileInfos[self::TEST_FILES_SHARING_API_USER2]['test/sub'] = $view2->getFileInfo('test/sub'); $this->loginAsUser(self::TEST_FILES_SHARING_API_USER3); $view3 = new View('/' . self::TEST_FILES_SHARING_API_USER3 . '/files'); - $this->fileIds[self::TEST_FILES_SHARING_API_USER3][''] = $view3->getFileInfo('')->getId(); - $this->fileIds[self::TEST_FILES_SHARING_API_USER3]['test'] = $view3->getFileInfo('test')->getId(); - $this->fileIds[self::TEST_FILES_SHARING_API_USER3]['test/sub'] = $view3->getFileInfo('test/sub')->getId(); + $this->fileInfos[self::TEST_FILES_SHARING_API_USER3][''] = $view3->getFileInfo(''); + $this->fileInfos[self::TEST_FILES_SHARING_API_USER3]['test'] = $view3->getFileInfo('test'); + $this->fileInfos[self::TEST_FILES_SHARING_API_USER3]['test/sub'] = $view3->getFileInfo('test/sub'); $this->loginAsUser(self::TEST_FILES_SHARING_API_USER4); $view4 = new View('/' . self::TEST_FILES_SHARING_API_USER4 . '/files'); - $this->fileIds[self::TEST_FILES_SHARING_API_USER4][''] = $view4->getFileInfo('')->getId(); - $this->fileIds[self::TEST_FILES_SHARING_API_USER4]['sub'] = $view4->getFileInfo('sub')->getId(); - - foreach ($this->fileIds as $user => $ids) { - $this->loginAsUser($user); - foreach ($ids as $id) { - $path = $this->rootView->getPath($id); - $this->fileEtags[$id] = $this->rootView->getFileInfo($path)->getEtag(); - } - } + $this->fileInfos[self::TEST_FILES_SHARING_API_USER4][''] = $view4->getFileInfo(''); + $this->fileInfos[self::TEST_FILES_SHARING_API_USER4]['sub'] = $view4->getFileInfo('sub'); } public function testGroupReShareRecipientWrites(): void { diff --git a/apps/files_sharing/tests/LockingTest.php b/apps/files_sharing/tests/LockingTest.php index 9ec54c2d47f1e..b2bd0dae9f2fd 100644 --- a/apps/files_sharing/tests/LockingTest.php +++ b/apps/files_sharing/tests/LockingTest.php @@ -11,11 +11,10 @@ use OC\Files\Filesystem; use OC\Files\View; use OCP\Constants; -use OCP\IUserManager; use OCP\Lock\ILockingProvider; use OCP\Lock\LockedException; -use OCP\Server; use OCP\Share\IShare; +use Test\Traits\UserTrait; /** * Class LockingTest @@ -25,10 +24,7 @@ */ #[\PHPUnit\Framework\Attributes\Group(name: 'DB')] class LockingTest extends TestCase { - /** - * @var \Test\Util\User\Dummy - */ - private $userBackend; + use UserTrait; private $ownerUid; private $recipientUid; @@ -36,13 +32,10 @@ class LockingTest extends TestCase { protected function setUp(): void { parent::setUp(); - $this->userBackend = new \Test\Util\User\Dummy(); - Server::get(IUserManager::class)->registerBackend($this->userBackend); - $this->ownerUid = $this->getUniqueID('owner_'); $this->recipientUid = $this->getUniqueID('recipient_'); - $this->userBackend->createUser($this->ownerUid, ''); - $this->userBackend->createUser($this->recipientUid, ''); + $this->createUser($this->ownerUid, ''); + $this->createUser($this->recipientUid, ''); $this->loginAsUser($this->ownerUid); Filesystem::mkdir('/foo'); @@ -61,11 +54,6 @@ protected function setUp(): void { $this->assertTrue(Filesystem::file_exists('bar.txt')); } - protected function tearDown(): void { - Server::get(IUserManager::class)->removeBackend($this->userBackend); - parent::tearDown(); - } - public function testLockAsRecipient(): void { $this->expectException(LockedException::class); diff --git a/apps/files_sharing/tests/PropagationTestCase.php b/apps/files_sharing/tests/PropagationTestCase.php index a3f7164600868..fed90fbfcae1f 100644 --- a/apps/files_sharing/tests/PropagationTestCase.php +++ b/apps/files_sharing/tests/PropagationTestCase.php @@ -10,6 +10,7 @@ use OC\Files\View; use OCA\Files_Sharing\Helper; +use OCP\Files\FileInfo; use OCP\IUserSession; use OCP\Server; @@ -18,7 +19,8 @@ abstract class PropagationTestCase extends TestCase { * @var View */ protected $rootView; - protected $fileIds = []; // [$user=>[$path=>$id]] + /** @var array */ + protected array $fileInfos = []; // [$user=>[$path=>$info]] protected $fileEtags = []; // [$id=>$etag] public static function setUpBeforeClass(): void { @@ -29,6 +31,13 @@ public static function setUpBeforeClass(): void { protected function setUp(): void { parent::setUp(); $this->setUpShares(); + + foreach ($this->fileInfos as $infos) { + /** @var FileInfo $info */ + foreach ($infos as $info) { + $this->fileEtags[$info->getId()] = $info->getEtag(); + } + } } protected function tearDown(): void { @@ -49,7 +58,7 @@ protected function assertEtagsChanged($users, $subPath = '') { $oldUser = Server::get(IUserSession::class)->getUser(); foreach ($users as $user) { $this->loginAsUser($user); - $id = $this->fileIds[$user][$subPath]; + $id = $this->fileInfos[$user][$subPath]->getId(); $path = $this->rootView->getPath($id); $etag = $this->rootView->getFileInfo($path)->getEtag(); $this->assertNotEquals($this->fileEtags[$id], $etag, 'Failed asserting that the etag for "' . $subPath . '" of user ' . $user . ' has changed'); @@ -66,7 +75,7 @@ protected function assertEtagsNotChanged($users, $subPath = '') { $oldUser = Server::get(IUserSession::class)->getUser(); foreach ($users as $user) { $this->loginAsUser($user); - $id = $this->fileIds[$user][$subPath]; + $id = $this->fileInfos[$user][$subPath]->getId(); $path = $this->rootView->getPath($id); $etag = $this->rootView->getFileInfo($path)->getEtag(); $this->assertEquals($this->fileEtags[$id], $etag, 'Failed asserting that the etag for "' . $subPath . '" of user ' . $user . ' has not changed'); diff --git a/apps/files_sharing/tests/ShareTest.php b/apps/files_sharing/tests/ShareTest.php index 1ed6bc9b3fe01..eedd3aba726ae 100644 --- a/apps/files_sharing/tests/ShareTest.php +++ b/apps/files_sharing/tests/ShareTest.php @@ -148,8 +148,6 @@ public function testShareWithDifferentShareFolder(): void { } public function testShareWithGroupUniqueName(): void { - $this->markTestSkipped('TODO: Disable because fails on drone'); - $this->loginHelper(self::TEST_FILES_SHARING_API_USER1); Filesystem::file_put_contents('test.txt', 'test'); diff --git a/apps/files_sharing/tests/SharesReminderJobTest.php b/apps/files_sharing/tests/SharesReminderJobTest.php index c26122883d5d2..6c3113a2aaa6f 100644 --- a/apps/files_sharing/tests/SharesReminderJobTest.php +++ b/apps/files_sharing/tests/SharesReminderJobTest.php @@ -26,6 +26,7 @@ use OCP\Share\IShare; use PHPUnit\Framework\MockObject\MockObject; use Psr\Log\LoggerInterface; +use Test\Traits\UserTrait; /** * Class SharesReminderJobTest @@ -35,6 +36,8 @@ */ #[\PHPUnit\Framework\Attributes\Group(name: 'DB')] class SharesReminderJobTest extends \Test\TestCase { + use UserTrait; + private SharesReminderJob $job; private IDBConnection $db; private IManager $shareManager; @@ -57,8 +60,8 @@ protected function setUp(): void { $this->user1 = $this->getUniqueID('user1_'); $this->user2 = $this->getUniqueID('user2_'); - $user1 = $this->userManager->createUser($this->user1, 'longrandompassword'); - $user2 = $this->userManager->createUser($this->user2, 'longrandompassword'); + $user1 = $this->createUser($this->user1, 'longrandompassword'); + $user2 = $this->createUser($this->user2, 'longrandompassword'); $user1->setSystemEMailAddress('user1@test.com'); $user2->setSystemEMailAddress('user2@test.com'); @@ -81,16 +84,6 @@ protected function setUp(): void { protected function tearDown(): void { $this->db->executeUpdate('DELETE FROM `*PREFIX*share`'); - $userManager = Server::get(IUserManager::class); - $user1 = $userManager->get($this->user1); - if ($user1) { - $user1->delete(); - } - $user2 = $userManager->get($this->user2); - if ($user2) { - $user2->delete(); - } - $this->logout(); parent::tearDown(); diff --git a/apps/files_sharing/tests/TestCase.php b/apps/files_sharing/tests/TestCase.php index e9732b5ff0209..80c0a3573b662 100644 --- a/apps/files_sharing/tests/TestCase.php +++ b/apps/files_sharing/tests/TestCase.php @@ -8,25 +8,26 @@ namespace OCA\Files_Sharing\Tests; -use OC\Files\Cache\Storage; -use OC\Files\Filesystem; use OC\Files\View; -use OC\Group\Database; use OC\SystemConfig; use OC\User\DisplayNameCache; +use OC\User\Session; use OCA\Files_Sharing\AppInfo\Application; use OCA\Files_Sharing\External\MountProvider as ExternalMountProvider; use OCA\Files_Sharing\Listener\SharesUpdatedListener; use OCA\Files_Sharing\MountProvider; use OCP\Files\Config\IMountProviderCollection; use OCP\Files\IRootFolder; +use OCP\Files\ISetupManager; use OCP\IDBConnection; -use OCP\IGroupManager; use OCP\IUserManager; use OCP\IUserSession; use OCP\Server; +use OCP\Share\IManager; use OCP\Share\IShare; +use Test\Traits\GroupTrait; use Test\Traits\MountProviderTrait; +use Test\Traits\UserTrait; /** * Base class for sharing tests. @@ -34,6 +35,8 @@ #[\PHPUnit\Framework\Attributes\Group(name: 'DB')] abstract class TestCase extends \Test\TestCase { use MountProviderTrait; + use UserTrait; + use GroupTrait; public const TEST_FILES_SHARING_API_USER1 = 'test-share-user1'; public const TEST_FILES_SHARING_API_USER2 = 'test-share-user2'; @@ -55,13 +58,21 @@ abstract class TestCase extends \Test\TestCase { public $folder; public $subfolder; - /** @var \OCP\Share\IManager */ + /** @var IManager */ protected $shareManager; /** @var IRootFolder */ protected $rootFolder; + protected Session $userSession; + protected ISetupManager $setupManager; - public static function setUpBeforeClass(): void { - parent::setUpBeforeClass(); + protected function setUp(): void { + parent::setUp(); + + $this->shareManager = Server::get(IManager::class); + $this->rootFolder = Server::get(IRootFolder::class); + + $this->setupManager = Server::get(ISetupManager::class); + $this->userSession = Server::get(IUserSession::class); $app = new Application(); $app->registerMountProviders( @@ -70,54 +81,43 @@ public static function setUpBeforeClass(): void { Server::get(ExternalMountProvider::class), ); - // reset backend - Server::get(IUserManager::class)->clearBackends(); - Server::get(IGroupManager::class)->clearBackends(); - // clear share hooks \OC_Hook::clear('OCP\\Share'); \OC::registerShareHooks(Server::get(SystemConfig::class)); - // create users - $backend = new \Test\Util\User\Dummy(); - Server::get(IUserManager::class)->registerBackend($backend); - $backend->createUser(self::TEST_FILES_SHARING_API_USER1, self::TEST_FILES_SHARING_API_USER1); - $backend->createUser(self::TEST_FILES_SHARING_API_USER2, self::TEST_FILES_SHARING_API_USER2); - $backend->createUser(self::TEST_FILES_SHARING_API_USER3, self::TEST_FILES_SHARING_API_USER3); - $backend->createUser(self::TEST_FILES_SHARING_API_USER4, self::TEST_FILES_SHARING_API_USER4); - - // create group - $groupBackend = new \Test\Util\Group\Dummy(); - $groupBackend->createGroup(self::TEST_FILES_SHARING_API_GROUP1); - $groupBackend->createGroup('group'); - $groupBackend->createGroup('group1'); - $groupBackend->createGroup('group2'); - $groupBackend->createGroup('group3'); - $groupBackend->addToGroup(self::TEST_FILES_SHARING_API_USER1, 'group'); - $groupBackend->addToGroup(self::TEST_FILES_SHARING_API_USER2, 'group'); - $groupBackend->addToGroup(self::TEST_FILES_SHARING_API_USER3, 'group'); - $groupBackend->addToGroup(self::TEST_FILES_SHARING_API_USER2, 'group1'); - $groupBackend->addToGroup(self::TEST_FILES_SHARING_API_USER3, 'group2'); - $groupBackend->addToGroup(self::TEST_FILES_SHARING_API_USER4, 'group3'); - $groupBackend->addToGroup(self::TEST_FILES_SHARING_API_USER2, self::TEST_FILES_SHARING_API_GROUP1); - Server::get(IGroupManager::class)->addBackend($groupBackend); - Server::get(SharesUpdatedListener::class)->setCutOffMarkTime(-1); - } - protected function setUp(): void { - parent::setUp(); Server::get(DisplayNameCache::class)->clear(); + $this->createUser(self::TEST_FILES_SHARING_API_USER1, self::TEST_FILES_SHARING_API_USER1); + $this->createUser(self::TEST_FILES_SHARING_API_USER2, self::TEST_FILES_SHARING_API_USER2); + $this->createUser(self::TEST_FILES_SHARING_API_USER3, self::TEST_FILES_SHARING_API_USER3); + $this->createUser(self::TEST_FILES_SHARING_API_USER4, self::TEST_FILES_SHARING_API_USER4); + + $this->createGroup(self::TEST_FILES_SHARING_API_GROUP1, [ + self::TEST_FILES_SHARING_API_USER2, + ]); + $this->createGroup('group', [ + self::TEST_FILES_SHARING_API_USER1, + self::TEST_FILES_SHARING_API_USER2, + self::TEST_FILES_SHARING_API_USER3, + ]); + $this->createGroup('group1', [ + self::TEST_FILES_SHARING_API_USER2, + ]); + $this->createGroup('group2', [ + self::TEST_FILES_SHARING_API_USER3, + ]); + $this->createGroup('group3', [ + self::TEST_FILES_SHARING_API_USER4, + ]); + //login as user1 $this->loginHelper(self::TEST_FILES_SHARING_API_USER1); $this->data = 'foobar'; $this->view = new View('/' . self::TEST_FILES_SHARING_API_USER1 . '/files'); $this->view2 = new View('/' . self::TEST_FILES_SHARING_API_USER2 . '/files'); - - $this->shareManager = Server::get(\OCP\Share\IManager::class); - $this->rootFolder = Server::get(IRootFolder::class); } protected function tearDown(): void { @@ -133,73 +133,18 @@ protected function tearDown(): void { $qb->delete('filecache')->runAcrossAllShards(); $qb->executeStatement(); + $this->userSession->setUser(null); + $this->setupManager->tearDown(); + parent::tearDown(); } - public static function tearDownAfterClass(): void { - // cleanup users - $user = Server::get(IUserManager::class)->get(self::TEST_FILES_SHARING_API_USER1); - if ($user !== null) { - $user->delete(); - } - $user = Server::get(IUserManager::class)->get(self::TEST_FILES_SHARING_API_USER2); - if ($user !== null) { - $user->delete(); - } - $user = Server::get(IUserManager::class)->get(self::TEST_FILES_SHARING_API_USER3); - if ($user !== null) { - $user->delete(); - } - - // delete group - $group = Server::get(IGroupManager::class)->get(self::TEST_FILES_SHARING_API_GROUP1); - if ($group) { - $group->delete(); - } - - \OC_Util::tearDownFS(); - \OC_User::setUserId(''); - Filesystem::tearDown(); - - // reset backend - Server::get(IUserManager::class)->clearBackends(); - Server::get(IUserManager::class)->registerBackend(new \OC\User\Database()); - Server::get(IGroupManager::class)->clearBackends(); - Server::get(IGroupManager::class)->addBackend(new Database()); - - parent::tearDownAfterClass(); - } + protected function loginHelper(string $uid) { + $this->setupManager->tearDown(); + $user = Server::get(IUserManager::class)->get($uid); + $this->userSession->completeLogin($user, ['loginName' => $uid, 'password' => $uid], false); - /** - * @param string $user - * @param bool $create - * @param bool $password - */ - protected function loginHelper($user, $create = false, $password = false) { - if ($password === false) { - $password = $user; - } - - if ($create) { - $userManager = Server::get(IUserManager::class); - $groupManager = Server::get(IGroupManager::class); - - $userObject = $userManager->createUser($user, $password); - $group = $groupManager->createGroup('group'); - - if ($group && $userObject) { - $group->addUser($userObject); - } - } - - \OC_Util::tearDownFS(); - Storage::getGlobalCache()->clearCache(); - Server::get(IUserSession::class)->setUser(null); - Filesystem::tearDown(); - Server::get(IUserSession::class)->login($user, $password); - \OC::$server->getUserFolder($user); - - \OC_Util::setupFS($user); + $this->rootFolder->newFolder('/' . $uid . '/files'); } /** diff --git a/apps/files_trashbin/l10n/fa.js b/apps/files_trashbin/l10n/fa.js index 16a33e28fab47..10df37d4ebc2c 100644 --- a/apps/files_trashbin/l10n/fa.js +++ b/apps/files_trashbin/l10n/fa.js @@ -3,7 +3,7 @@ OC.L10N.register( { "restored" : "بازیابی شد", "Deleted files" : "فایل های حذف شده", - "Deleted files and folders in the trash bin (may expire during export if you are low on storage space)" : "Deleted files and folders in the trash bin (may expire during export if you are low on storage space)", + "Deleted files and folders in the trash bin (may expire during export if you are low on storage space)" : "فایل‌ها و پوشه‌های حذف‌شده در سطل زباله (در صورت کمبود فضای ذخیره‌سازی، ممکن است هنگام خروجی گرفتن منقضی شوند)", "This application enables people to restore files that were deleted from the system." : "این برنامه به کاربران این امکان را می‌دهد که فایل‌های حذف شده از سیستم خود را بازیابی کنند.", "This application enables people to restore files that were deleted from the system. It displays a list of deleted files in the web interface, and has options to restore those deleted files back to the people file directories or remove them permanently from the system. Restoring a file also restores related file versions, if the versions application is enabled. When a file is deleted from a share, it can be restored in the same manner, though it is no longer shared. By default, these files remain in the trash bin for 30 days.\nTo prevent an account from running out of disk space, the Deleted files app will not utilize more than 50% of the currently available free quota for deleted files. If the deleted files exceed this limit, the app deletes the oldest files until it gets below this limit. More information is available in the Deleted Files documentation." : "این برنامه به افراد امکان می‌دهد فایل‌هایی را که از سیستم حذف شده‌اند، بازیابی کنند. این برنامه لیستی از فایل‌های حذف شده را در رابط وب نمایش می‌دهد و گزینه‌هایی برای بازیابی آن فایل‌های حذف شده به دایرکتوری‌های فایل‌های افراد یا حذف دائمی آنها از سیستم دارد. بازیابی یک فایل، در صورت فعال بودن نسخه‌های برنامه، نسخه‌های مرتبط فایل را نیز بازیابی می‌کند. هنگامی که یک فایل از یک اشتراک حذف می‌شود، می‌توان آن را به همان روش بازیابی کرد، اگرچه دیگر به اشتراک گذاشته نمی‌شود. به طور پیش‌فرض، این فایل‌ها به مدت 30 روز در سطل زباله باقی می‌مانند. برای جلوگیری از اتمام فضای دیسک یک حساب، برنامه فایل‌های حذف شده بیش از 50٪ از سهمیه رایگان موجود برای فایل‌های حذف شده را استفاده نمی‌کند. اگر فایل‌های حذف شده از این حد تجاوز کنند، برنامه قدیمی‌ترین فایل‌ها را تا زمانی که به کمتر از این حد برسد، حذف می‌کند. اطلاعات بیشتر در مستندات فایل‌های حذف شده موجود است.", "Restore" : "بازیابی", @@ -19,10 +19,10 @@ OC.L10N.register( "A long time ago" : "مدت ها پیش", "Unknown" : "ناشناخته", "All files" : "تمامی فایل‌ها", - "You" : "You", - "List of files that have been deleted." : "List of files that have been deleted.", + "You" : "شما", + "List of files that have been deleted." : "لیست فایل‌هایی که حذف شده‌اند.", "No deleted files" : "هیچ فایل حذف شده وجود ندارد", - "Files and folders you have deleted will show up here" : "Files and folders you have deleted will show up here", + "Files and folders you have deleted will show up here" : "فایل‌ها و پوشه‌هایی که حذف کرده‌اید در اینجا نمایش داده می‌شوند", "All files have been permanently deleted" : "همه فایل‌ ها به طور دائم حذف شده‌ اند", "Failed to empty deleted files" : "خالی کردن فایل‌ های حذف شده ناموفق بود" }, diff --git a/apps/files_trashbin/l10n/fa.json b/apps/files_trashbin/l10n/fa.json index 9e312f7e8d018..4b0d99ab4c8b2 100644 --- a/apps/files_trashbin/l10n/fa.json +++ b/apps/files_trashbin/l10n/fa.json @@ -1,7 +1,7 @@ { "translations": { "restored" : "بازیابی شد", "Deleted files" : "فایل های حذف شده", - "Deleted files and folders in the trash bin (may expire during export if you are low on storage space)" : "Deleted files and folders in the trash bin (may expire during export if you are low on storage space)", + "Deleted files and folders in the trash bin (may expire during export if you are low on storage space)" : "فایل‌ها و پوشه‌های حذف‌شده در سطل زباله (در صورت کمبود فضای ذخیره‌سازی، ممکن است هنگام خروجی گرفتن منقضی شوند)", "This application enables people to restore files that were deleted from the system." : "این برنامه به کاربران این امکان را می‌دهد که فایل‌های حذف شده از سیستم خود را بازیابی کنند.", "This application enables people to restore files that were deleted from the system. It displays a list of deleted files in the web interface, and has options to restore those deleted files back to the people file directories or remove them permanently from the system. Restoring a file also restores related file versions, if the versions application is enabled. When a file is deleted from a share, it can be restored in the same manner, though it is no longer shared. By default, these files remain in the trash bin for 30 days.\nTo prevent an account from running out of disk space, the Deleted files app will not utilize more than 50% of the currently available free quota for deleted files. If the deleted files exceed this limit, the app deletes the oldest files until it gets below this limit. More information is available in the Deleted Files documentation." : "این برنامه به افراد امکان می‌دهد فایل‌هایی را که از سیستم حذف شده‌اند، بازیابی کنند. این برنامه لیستی از فایل‌های حذف شده را در رابط وب نمایش می‌دهد و گزینه‌هایی برای بازیابی آن فایل‌های حذف شده به دایرکتوری‌های فایل‌های افراد یا حذف دائمی آنها از سیستم دارد. بازیابی یک فایل، در صورت فعال بودن نسخه‌های برنامه، نسخه‌های مرتبط فایل را نیز بازیابی می‌کند. هنگامی که یک فایل از یک اشتراک حذف می‌شود، می‌توان آن را به همان روش بازیابی کرد، اگرچه دیگر به اشتراک گذاشته نمی‌شود. به طور پیش‌فرض، این فایل‌ها به مدت 30 روز در سطل زباله باقی می‌مانند. برای جلوگیری از اتمام فضای دیسک یک حساب، برنامه فایل‌های حذف شده بیش از 50٪ از سهمیه رایگان موجود برای فایل‌های حذف شده را استفاده نمی‌کند. اگر فایل‌های حذف شده از این حد تجاوز کنند، برنامه قدیمی‌ترین فایل‌ها را تا زمانی که به کمتر از این حد برسد، حذف می‌کند. اطلاعات بیشتر در مستندات فایل‌های حذف شده موجود است.", "Restore" : "بازیابی", @@ -17,10 +17,10 @@ "A long time ago" : "مدت ها پیش", "Unknown" : "ناشناخته", "All files" : "تمامی فایل‌ها", - "You" : "You", - "List of files that have been deleted." : "List of files that have been deleted.", + "You" : "شما", + "List of files that have been deleted." : "لیست فایل‌هایی که حذف شده‌اند.", "No deleted files" : "هیچ فایل حذف شده وجود ندارد", - "Files and folders you have deleted will show up here" : "Files and folders you have deleted will show up here", + "Files and folders you have deleted will show up here" : "فایل‌ها و پوشه‌هایی که حذف کرده‌اید در اینجا نمایش داده می‌شوند", "All files have been permanently deleted" : "همه فایل‌ ها به طور دائم حذف شده‌ اند", "Failed to empty deleted files" : "خالی کردن فایل‌ های حذف شده ناموفق بود" },"pluralForm" :"nplurals=2; plural=(n > 1);" diff --git a/apps/files_trashbin/lib/Trashbin.php b/apps/files_trashbin/lib/Trashbin.php index b5edf37bf2f2c..b32b2ccb7c687 100644 --- a/apps/files_trashbin/lib/Trashbin.php +++ b/apps/files_trashbin/lib/Trashbin.php @@ -850,7 +850,7 @@ private static function calculateFreeSpace(Folder $userFolder, int|float $trashb $softQuota = true; $quota = $user->getQuota(); - if ($quota === null || $quota === 'none') { + if ($quota === 'none') { $quota = Filesystem::free_space('/'); $softQuota = false; // inf or unknown free space diff --git a/apps/files_versions/l10n/fa.js b/apps/files_versions/l10n/fa.js index bb0e79698748b..214aec4e23b7d 100644 --- a/apps/files_versions/l10n/fa.js +++ b/apps/files_versions/l10n/fa.js @@ -10,24 +10,24 @@ OC.L10N.register( "This application automatically maintains older versions of files that are changed. When enabled, a hidden versions folder is provisioned in every user's directory and is used to store old file versions. A user can revert to an older version through the web interface at any time, with the replaced file becoming a version. The app automatically manages the versions folder to ensure the account does not run out of Quota because of versions.\n\t\tIn addition to the expiry of versions, the versions app makes certain never to use more than 50% of the account's currently available free space. If stored versions exceed this limit, the app will delete the oldest versions first until it meets this limit. More information is available in the Versions documentation." : "این برنامهب طور خودکار نسخه‌های قدیمی‌تر فایل‌هایی را که تغییر می‌کنند، نگهداری می‌کند. در صورت فعال بودن، یک پوشه نسخه‌های مخفی در دایرکتوری هر کاربر ایجاد می‌شود و برای ذخیره نسخه‌های قدیمی فایل استفاده می‌شود. کاربر می‌تواند در هر زمان از طریق رابط وب به نسخه قدیمی‌تر برگردد و فایل جایگزین شده به یک نسخه تبدیل می‌شود. برنامه به طور خودکار پوشه نسخه‌ها را مدیریت می‌کند تا اطمینان حاصل شود که حساب به دلیل نسخه‌ها، سهمیه خود را از دست نمی‌دهد.\n\nعلاوه بر انقضای نسخه‌ها، نسخه‌های برنامه اطمینان حاصل می‌کنند که هرگز بیش از 50٪ از فضای خالی موجود در حساب را استفاده نکنند. اگر نسخه‌های ذخیره شده از این حد تجاوز کنند، برنامه ابتدا قدیمی‌ترین نسخه‌ها را حذف می‌کند تا به این حد برسد. اطلاعات بیشتر در مستندات نسخه‌ها موجود است.", "Current version" : "نسخه فعلی", "Initial version" : "نسخه اولیه", - "You" : "You", + "You" : "شما", "Actions for version from {versionHumanExplicitDate}" : "اقدامات برای نسخه از {versionHumanExplicitDate}", - "Name this version" : "Name this version", - "Edit version name" : "Edit version name", - "Compare to current version" : "Compare to current version", - "Restore version" : "Restore version", - "Download version" : "Download version", - "Delete version" : "Delete version", + "Name this version" : "نام‌گذاری این نسخه", + "Edit version name" : "ویرایش نام نسخه", + "Compare to current version" : "مقایسه با نسخه فعلی", + "Restore version" : "بازگردانی نسخه", + "Download version" : "بارگیری نسخه", + "Delete version" : "حذف نسخه", "Cancel" : "منصرف شدن", - "Remove version name" : "Remove version name", - "Save version name" : "Save version name", - "Version name" : "Version name", - "Named versions are persisted, and excluded from automatic cleanups when your storage quota is full." : "Named versions are persisted, and excluded from automatic cleanups when your storage quota is full.", - "Initial version restored" : "Initial version restored", - "Version restored" : "Version restored", - "Could not restore version" : "Could not restore version", + "Remove version name" : "حذف نام نسخه", + "Save version name" : "ذخیره نام نسخه", + "Version name" : "نام نسخه", + "Named versions are persisted, and excluded from automatic cleanups when your storage quota is full." : "نسخه‌های نام‌گذاری‌شده حفظ می‌شوند و هنگام پر شدن سهمیه ذخیره‌سازی شما، از پاک‌سازی‌های خودکار مستثنا می‌مانند.", + "Initial version restored" : "نسخه اولیه بازگردانده شد", + "Version restored" : "نسخه بازگردانده شد", + "Could not restore version" : "بازگردانی نسخه ناموفق بود", "Could not set version label" : "نمی‌تواند برچسب نسخه را تنظیم کند", - "Could not delete version" : "Could not delete version", + "Could not delete version" : "حذف نسخه ناموفق بود", "File versions" : "نسخه‌های فایل" }, "nplurals=2; plural=(n > 1);"); diff --git a/apps/files_versions/l10n/fa.json b/apps/files_versions/l10n/fa.json index 9583b817c4939..4cb0370ca9c24 100644 --- a/apps/files_versions/l10n/fa.json +++ b/apps/files_versions/l10n/fa.json @@ -8,24 +8,24 @@ "This application automatically maintains older versions of files that are changed. When enabled, a hidden versions folder is provisioned in every user's directory and is used to store old file versions. A user can revert to an older version through the web interface at any time, with the replaced file becoming a version. The app automatically manages the versions folder to ensure the account does not run out of Quota because of versions.\n\t\tIn addition to the expiry of versions, the versions app makes certain never to use more than 50% of the account's currently available free space. If stored versions exceed this limit, the app will delete the oldest versions first until it meets this limit. More information is available in the Versions documentation." : "این برنامهب طور خودکار نسخه‌های قدیمی‌تر فایل‌هایی را که تغییر می‌کنند، نگهداری می‌کند. در صورت فعال بودن، یک پوشه نسخه‌های مخفی در دایرکتوری هر کاربر ایجاد می‌شود و برای ذخیره نسخه‌های قدیمی فایل استفاده می‌شود. کاربر می‌تواند در هر زمان از طریق رابط وب به نسخه قدیمی‌تر برگردد و فایل جایگزین شده به یک نسخه تبدیل می‌شود. برنامه به طور خودکار پوشه نسخه‌ها را مدیریت می‌کند تا اطمینان حاصل شود که حساب به دلیل نسخه‌ها، سهمیه خود را از دست نمی‌دهد.\n\nعلاوه بر انقضای نسخه‌ها، نسخه‌های برنامه اطمینان حاصل می‌کنند که هرگز بیش از 50٪ از فضای خالی موجود در حساب را استفاده نکنند. اگر نسخه‌های ذخیره شده از این حد تجاوز کنند، برنامه ابتدا قدیمی‌ترین نسخه‌ها را حذف می‌کند تا به این حد برسد. اطلاعات بیشتر در مستندات نسخه‌ها موجود است.", "Current version" : "نسخه فعلی", "Initial version" : "نسخه اولیه", - "You" : "You", + "You" : "شما", "Actions for version from {versionHumanExplicitDate}" : "اقدامات برای نسخه از {versionHumanExplicitDate}", - "Name this version" : "Name this version", - "Edit version name" : "Edit version name", - "Compare to current version" : "Compare to current version", - "Restore version" : "Restore version", - "Download version" : "Download version", - "Delete version" : "Delete version", + "Name this version" : "نام‌گذاری این نسخه", + "Edit version name" : "ویرایش نام نسخه", + "Compare to current version" : "مقایسه با نسخه فعلی", + "Restore version" : "بازگردانی نسخه", + "Download version" : "بارگیری نسخه", + "Delete version" : "حذف نسخه", "Cancel" : "منصرف شدن", - "Remove version name" : "Remove version name", - "Save version name" : "Save version name", - "Version name" : "Version name", - "Named versions are persisted, and excluded from automatic cleanups when your storage quota is full." : "Named versions are persisted, and excluded from automatic cleanups when your storage quota is full.", - "Initial version restored" : "Initial version restored", - "Version restored" : "Version restored", - "Could not restore version" : "Could not restore version", + "Remove version name" : "حذف نام نسخه", + "Save version name" : "ذخیره نام نسخه", + "Version name" : "نام نسخه", + "Named versions are persisted, and excluded from automatic cleanups when your storage quota is full." : "نسخه‌های نام‌گذاری‌شده حفظ می‌شوند و هنگام پر شدن سهمیه ذخیره‌سازی شما، از پاک‌سازی‌های خودکار مستثنا می‌مانند.", + "Initial version restored" : "نسخه اولیه بازگردانده شد", + "Version restored" : "نسخه بازگردانده شد", + "Could not restore version" : "بازگردانی نسخه ناموفق بود", "Could not set version label" : "نمی‌تواند برچسب نسخه را تنظیم کند", - "Could not delete version" : "Could not delete version", + "Could not delete version" : "حذف نسخه ناموفق بود", "File versions" : "نسخه‌های فایل" },"pluralForm" :"nplurals=2; plural=(n > 1);" } \ No newline at end of file diff --git a/apps/files_versions/lib/Listener/FileEventsListener.php b/apps/files_versions/lib/Listener/FileEventsListener.php index 5a4b25f0ccd0d..71cb25bb02952 100644 --- a/apps/files_versions/lib/Listener/FileEventsListener.php +++ b/apps/files_versions/lib/Listener/FileEventsListener.php @@ -208,6 +208,9 @@ public function write_hook(Node $node): void { } $path = $this->getPathForNode($node); + if ($path === null) { + return; + } $result = Storage::store($path); // Store the result of the version creation so it can be used in post_write_hook. @@ -312,6 +315,9 @@ public function remove_hook(Node $node): void { $node = $this->versionsDeleted[$path]; $relativePath = $this->getPathForNode($node); unset($this->versionsDeleted[$path]); + if ($relativePath === null) { + return; + } Storage::delete($relativePath); // If no new version was stored in the FS, no new version should be added in the DB. // So we simply update the associated version. @@ -325,6 +331,9 @@ public function remove_hook(Node $node): void { */ public function pre_remove_hook(Node $node): void { $path = $this->getPathForNode($node); + if ($path === null) { + return; + } Storage::markDeletedFile($path); $this->versionsDeleted[$node->getPath()] = $node; } @@ -345,6 +354,9 @@ public function rename_hook(Node $source, Node $target): void { $oldPath = $this->getPathForNode($source); $newPath = $this->getPathForNode($target); + if ($oldPath === null || $newPath === null) { + return; + } Storage::renameOrCopy($oldPath, $newPath, 'rename'); } @@ -364,6 +376,9 @@ public function copy_hook(Node $source, Node $target): void { $oldPath = $this->getPathForNode($source); $newPath = $this->getPathForNode($target); + if ($oldPath === null || $newPath === null) { + return; + } Storage::renameOrCopy($oldPath, $newPath, 'copy'); } diff --git a/apps/files_versions/lib/Storage.php b/apps/files_versions/lib/Storage.php index 47f14d96b61c6..cf49e5a1d4d26 100644 --- a/apps/files_versions/lib/Storage.php +++ b/apps/files_versions/lib/Storage.php @@ -879,7 +879,7 @@ public static function expire($filename, $uid) { $softQuota = true; $quota = $user->getQuota(); - if ($quota === null || $quota === 'none') { + if ($quota === 'none') { $quota = Filesystem::free_space('/'); $softQuota = false; } else { diff --git a/apps/files_versions/src/views/FilesVersionsSidebarTab.vue b/apps/files_versions/src/views/FilesVersionsSidebarTab.vue index 1337dbc1b9396..b35fe6118773b 100644 --- a/apps/files_versions/src/views/FilesVersionsSidebarTab.vue +++ b/apps/files_versions/src/views/FilesVersionsSidebarTab.vue @@ -48,7 +48,8 @@ import { showError, showSuccess } from '@nextcloud/dialogs' import { emit } from '@nextcloud/event-bus' import { t } from '@nextcloud/l10n' import { useIsMobile } from '@nextcloud/vue/composables/useIsMobile' -import { computed, ref, toRef, watch } from 'vue' +import { watchDebounced } from '@vueuse/core' +import { computed, ref, watch } from 'vue' import NcLoadingIcon from '@nextcloud/vue/components/NcLoadingIcon' import VersionEntry from '../components/VersionEntry.vue' import VersionLabelDialog from '../components/VersionLabelDialog.vue' @@ -72,19 +73,6 @@ const loading = ref(false) const showVersionLabelForm = ref(false) const editedVersion = ref(null) -watch(toRef(() => props.node), async () => { - if (!props.node) { - return - } - - try { - loading.value = true - versions.value = await fetchVersions(props.node) - } finally { - loading.value = false - } -}, { immediate: true }) - const currentVersionMtime = computed(() => props.node?.mtime?.getTime() ?? 0) /** @@ -139,6 +127,24 @@ const canCompare = computed(() => { && window.OCA.Viewer?.mimetypesCompare?.includes(props.node?.mime) }) +// When either the current node to show or its mtime changes we need to refetch the versions +// When the id changed we immediately show changes +watch(() => props.node.id, loadVersions, { immediate: true }) +// On mtime changes we debounce to prevent too many requests. +watchDebounced(currentVersionMtime, loadVersions, { debounce: 600 }) + +/** + * Load versions for the current node + */ +async function loadVersions() { + try { + loading.value = true + versions.value = await fetchVersions(props.node) + } finally { + loading.value = false + } +} + /** * Handle restored event from Version.vue * diff --git a/apps/files_versions/tests/Listener/FileEventsListenerTest.php b/apps/files_versions/tests/Listener/FileEventsListenerTest.php new file mode 100644 index 0000000000000..24f78d89ad908 --- /dev/null +++ b/apps/files_versions/tests/Listener/FileEventsListenerTest.php @@ -0,0 +1,94 @@ +rootFolder = $this->createMock(IRootFolder::class); + $this->versionManager = $this->createMock(IVersionManager::class); + $this->mimeTypeLoader = $this->createMock(IMimeTypeLoader::class); + $this->userSession = $this->createMock(IUserSession::class); + $this->logger = $this->createMock(LoggerInterface::class); + + $this->listener = new FileEventsListener( + $this->rootFolder, + $this->versionManager, + $this->mimeTypeLoader, + $this->userSession, + $this->logger, + ); + } + + private function createUnresolvableFile(): File&MockObject { + $this->userSession->method('getUser')->willReturn(null); + + $node = $this->createMock(File::class); + $node->method('getOwner')->willReturn(null); + $node->method('getPath')->willReturn('/test.txt'); + $node->method('getId')->willReturn(42); + $node->method('getSize')->willReturn(100); + $node->method('getMTime')->willReturn(1234567890); + + return $node; + } + + private function getPrivateProperty(string $property): mixed { + $ref = new \ReflectionProperty(FileEventsListener::class, $property); + $ref->setAccessible(true); + return $ref->getValue($this->listener); + } + + public function testGetPathForNodeReturnsNullWhenUnresolvable(): void { + $node = $this->createUnresolvableFile(); + + $this->logger->expects($this->once()) + ->method('debug') + ->with('Failed to compute path for node', $this->anything()); + + $method = new \ReflectionMethod(FileEventsListener::class, 'getPathForNode'); + $method->setAccessible(true); + + $this->assertNull($method->invoke($this->listener, $node)); + } + + public function testWriteHookSkipsWhenPathUnresolvable(): void { + $node = $this->createUnresolvableFile(); + + $this->listener->write_hook($node); + + $this->assertSame([], $this->getPrivateProperty('writeHookInfo')); + } + + public function testPreRemoveHookSkipsWhenPathUnresolvable(): void { + $node = $this->createUnresolvableFile(); + + $this->listener->pre_remove_hook($node); + + $this->assertSame([], $this->getPrivateProperty('versionsDeleted')); + } +} diff --git a/apps/profile/l10n/fa.js b/apps/profile/l10n/fa.js index ef91d98c0aad5..4c47ef31ab232 100644 --- a/apps/profile/l10n/fa.js +++ b/apps/profile/l10n/fa.js @@ -11,15 +11,15 @@ OC.L10N.register( "Search for a user profile" : "جست‌وجوی نمایه کاربر", "Search for a user profile. Start typing" : "جست‌وجوی نمایه کاربر. شروع به تایپ کنید", "Insert selected user profile link" : "درج پیوند نمایه کاربر انتخاب‌شده", - "Insert" : "Insert", - "You have not added any info yet" : "You have not added any info yet", - "{user} has not added any info yet" : "{user} has not added any info yet", - "Error opening the user status modal, try hard refreshing the page" : "Error opening the user status modal, try hard refreshing the page", + "Insert" : "درج", + "You have not added any info yet" : "هنوز اطلاعاتی اضافه نکرده‌اید", + "{user} has not added any info yet" : "{user} هنوز اطلاعاتی اضافه نکرده است", + "Error opening the user status modal, try hard refreshing the page" : "خطا در باز کردن پنجره وضعیت کاربر؛ صفحه را به‌طور کامل تازه‌سازی کنید", "Edit Profile" : "ویرایش نمایه", - "The headline and about sections will show up here" : "The headline and about sections will show up here", + "The headline and about sections will show up here" : "بخش‌های عنوان و درباره در اینجا نمایش داده می‌شوند", "Profile not found" : "نمایه، یافت نشد", "The profile does not exist or is unavailable." : "نمایه وجود ندارد یا در دسترس نیست.", - "Back to %s" : "Back to %s", + "Back to %s" : "بازگشت به %s", "The profile does not exist." : "این نمایه وجود ندارد." }, "nplurals=2; plural=(n > 1);"); diff --git a/apps/profile/l10n/fa.json b/apps/profile/l10n/fa.json index 617f8689d9217..0818bf5b80543 100644 --- a/apps/profile/l10n/fa.json +++ b/apps/profile/l10n/fa.json @@ -9,15 +9,15 @@ "Search for a user profile" : "جست‌وجوی نمایه کاربر", "Search for a user profile. Start typing" : "جست‌وجوی نمایه کاربر. شروع به تایپ کنید", "Insert selected user profile link" : "درج پیوند نمایه کاربر انتخاب‌شده", - "Insert" : "Insert", - "You have not added any info yet" : "You have not added any info yet", - "{user} has not added any info yet" : "{user} has not added any info yet", - "Error opening the user status modal, try hard refreshing the page" : "Error opening the user status modal, try hard refreshing the page", + "Insert" : "درج", + "You have not added any info yet" : "هنوز اطلاعاتی اضافه نکرده‌اید", + "{user} has not added any info yet" : "{user} هنوز اطلاعاتی اضافه نکرده است", + "Error opening the user status modal, try hard refreshing the page" : "خطا در باز کردن پنجره وضعیت کاربر؛ صفحه را به‌طور کامل تازه‌سازی کنید", "Edit Profile" : "ویرایش نمایه", - "The headline and about sections will show up here" : "The headline and about sections will show up here", + "The headline and about sections will show up here" : "بخش‌های عنوان و درباره در اینجا نمایش داده می‌شوند", "Profile not found" : "نمایه، یافت نشد", "The profile does not exist or is unavailable." : "نمایه وجود ندارد یا در دسترس نیست.", - "Back to %s" : "Back to %s", + "Back to %s" : "بازگشت به %s", "The profile does not exist." : "این نمایه وجود ندارد." },"pluralForm" :"nplurals=2; plural=(n > 1);" } \ No newline at end of file diff --git a/apps/provisioning_api/l10n/fa.js b/apps/provisioning_api/l10n/fa.js index b86463caa6d01..6c9b73613194f 100644 --- a/apps/provisioning_api/l10n/fa.js +++ b/apps/provisioning_api/l10n/fa.js @@ -3,7 +3,7 @@ OC.L10N.register( { "Logged in account must be an administrator or have authorization to edit this setting." : "حساب کاربری وارد شده باید مدیر باشد یا مجوز ویرایش این تنظیم را داشته باشد.", "Could not create non-existing user ID" : "شناسه کاربری ناموجود ایجاد نشد", - "User already exists" : "User already exists", + "User already exists" : "کاربر از پیش وجود دارد", "Group %1$s does not exist" : "گروه %1$s وجود ندارد", "Insufficient privileges for group %1$s" : "گروه %1$s وجود ندارد", "No group specified (required for sub-admins)" : "هیچ گروهی مشخص نشده است (برای مدیران فرعی الزامی است)", @@ -39,14 +39,14 @@ OC.L10N.register( "Email address not available" : "آدرس ایمیل موجود نیست", "Sending email failed" : "ارسال ایمیل ناموفق بود", "Logged in account is not mail address owner" : "حساب کاربری وارد شده، مالک آدرس ایمیل نیست", - "Email confirmation" : "Email confirmation", - "To enable the email address %s please click the button below." : "To enable the email address %s please click the button below.", + "Email confirmation" : "تأیید ایمیل", + "To enable the email address %s please click the button below." : "برای فعال کردن آدرس ایمیل %s، لطفاً دکمه زیر را کلیک کنید.", "Confirm" : "تائید", - "Email was already removed from account and cannot be confirmed anymore." : "Email was already removed from account and cannot be confirmed anymore.", - "Could not verify mail because the token is expired." : "Could not verify mail because the token is expired.", - "Could not verify mail because the token is invalid." : "Could not verify mail because the token is invalid.", - "An unexpected error occurred. Please contact your admin." : "An unexpected error occurred. Please contact your admin.", - "Email confirmation successful" : "Email confirmation successful", + "Email was already removed from account and cannot be confirmed anymore." : "ایمیل پیش‌تر از حساب کاربری حذف شده است و دیگر نمی‌توان آن را تأیید کرد.", + "Could not verify mail because the token is expired." : "به‌دلیل منقضی‌شدن توکن، تأیید ایمیل ممکن نشد.", + "Could not verify mail because the token is invalid." : "به‌دلیل نامعتبر بودن توکن، تأیید ایمیل ممکن نشد.", + "An unexpected error occurred. Please contact your admin." : "خطایی غیرمنتظره رخ داد. لطفاً با مدیر خود تماس بگیرید.", + "Email confirmation successful" : "تأیید ایمیل با موفقیت انجام شد", "Provisioning API" : "Provisioning API", "This application enables a set of APIs that external systems can use to manage accounts, groups and apps." : "این برنامه مجموعه‌ای از APIها را فعال می‌کند که سیستم‌های خارجی می‌توانند از آنها برای مدیریت حساب‌ها، گروه‌ها و برنامه‌ها استفاده کنند.", "This application enables a set of APIs that external systems can use to create, edit, delete and query account\n\t\tattributes, query, set and remove groups, set quota and query total storage used in Nextcloud. Group admin accounts\n\t\tcan also query Nextcloud and perform the same functions as an admin for groups they manage. The API also enables\n\t\tan admin to query for active Nextcloud applications, application info, and to enable or disable an app remotely.\n\t\tOnce the app is enabled, HTTP requests can be used via a Basic Auth header to perform any of the functions\n\t\tlisted above. More information is available in the Provisioning API documentation, including example calls\n\t\tand server responses." : "این برنامه مجموعه‌ای از APIها را فعال می‌کند که سیستم‌های خارجی می‌توانند از آنها برای ایجاد، ویرایش، حذف و پرس‌وجوی ویژگی‌های حساب کاربری، پرس‌وجو، تنظیم و حذف گروه‌ها، تنظیم سهمیه و پرس‌وجوی کل فضای ذخیره‌سازی استفاده شده در Nextcloud استفاده کنند. حساب‌های کاربری ادمین گروه نیز می‌توانند Nextcloud را پرس‌وجو کنند و همان عملکردهای یک ادمین را برای گروه‌هایی که مدیریت می‌کنند، انجام دهند. این API همچنین به ادمین امکان می‌دهد تا برای برنامه‌های فعال Nextcloud، اطلاعات برنامه و فعال یا غیرفعال کردن یک برنامه از راه دور پرس‌وجو کند. پس از فعال شدن برنامه، می‌توان از درخواست‌های HTTP از طریق یک هدر Basic Auth برای انجام هر یک از عملکردهای ذکر شده در بالا استفاده کرد. اطلاعات بیشتر، از جمله مثال‌های فراخوانی و پاسخ‌های سرور، در مستندات Provisioning API موجود است." diff --git a/apps/provisioning_api/l10n/fa.json b/apps/provisioning_api/l10n/fa.json index 654fd13283496..cd4ffecede424 100644 --- a/apps/provisioning_api/l10n/fa.json +++ b/apps/provisioning_api/l10n/fa.json @@ -1,7 +1,7 @@ { "translations": { "Logged in account must be an administrator or have authorization to edit this setting." : "حساب کاربری وارد شده باید مدیر باشد یا مجوز ویرایش این تنظیم را داشته باشد.", "Could not create non-existing user ID" : "شناسه کاربری ناموجود ایجاد نشد", - "User already exists" : "User already exists", + "User already exists" : "کاربر از پیش وجود دارد", "Group %1$s does not exist" : "گروه %1$s وجود ندارد", "Insufficient privileges for group %1$s" : "گروه %1$s وجود ندارد", "No group specified (required for sub-admins)" : "هیچ گروهی مشخص نشده است (برای مدیران فرعی الزامی است)", @@ -37,14 +37,14 @@ "Email address not available" : "آدرس ایمیل موجود نیست", "Sending email failed" : "ارسال ایمیل ناموفق بود", "Logged in account is not mail address owner" : "حساب کاربری وارد شده، مالک آدرس ایمیل نیست", - "Email confirmation" : "Email confirmation", - "To enable the email address %s please click the button below." : "To enable the email address %s please click the button below.", + "Email confirmation" : "تأیید ایمیل", + "To enable the email address %s please click the button below." : "برای فعال کردن آدرس ایمیل %s، لطفاً دکمه زیر را کلیک کنید.", "Confirm" : "تائید", - "Email was already removed from account and cannot be confirmed anymore." : "Email was already removed from account and cannot be confirmed anymore.", - "Could not verify mail because the token is expired." : "Could not verify mail because the token is expired.", - "Could not verify mail because the token is invalid." : "Could not verify mail because the token is invalid.", - "An unexpected error occurred. Please contact your admin." : "An unexpected error occurred. Please contact your admin.", - "Email confirmation successful" : "Email confirmation successful", + "Email was already removed from account and cannot be confirmed anymore." : "ایمیل پیش‌تر از حساب کاربری حذف شده است و دیگر نمی‌توان آن را تأیید کرد.", + "Could not verify mail because the token is expired." : "به‌دلیل منقضی‌شدن توکن، تأیید ایمیل ممکن نشد.", + "Could not verify mail because the token is invalid." : "به‌دلیل نامعتبر بودن توکن، تأیید ایمیل ممکن نشد.", + "An unexpected error occurred. Please contact your admin." : "خطایی غیرمنتظره رخ داد. لطفاً با مدیر خود تماس بگیرید.", + "Email confirmation successful" : "تأیید ایمیل با موفقیت انجام شد", "Provisioning API" : "Provisioning API", "This application enables a set of APIs that external systems can use to manage accounts, groups and apps." : "این برنامه مجموعه‌ای از APIها را فعال می‌کند که سیستم‌های خارجی می‌توانند از آنها برای مدیریت حساب‌ها، گروه‌ها و برنامه‌ها استفاده کنند.", "This application enables a set of APIs that external systems can use to create, edit, delete and query account\n\t\tattributes, query, set and remove groups, set quota and query total storage used in Nextcloud. Group admin accounts\n\t\tcan also query Nextcloud and perform the same functions as an admin for groups they manage. The API also enables\n\t\tan admin to query for active Nextcloud applications, application info, and to enable or disable an app remotely.\n\t\tOnce the app is enabled, HTTP requests can be used via a Basic Auth header to perform any of the functions\n\t\tlisted above. More information is available in the Provisioning API documentation, including example calls\n\t\tand server responses." : "این برنامه مجموعه‌ای از APIها را فعال می‌کند که سیستم‌های خارجی می‌توانند از آنها برای ایجاد، ویرایش، حذف و پرس‌وجوی ویژگی‌های حساب کاربری، پرس‌وجو، تنظیم و حذف گروه‌ها، تنظیم سهمیه و پرس‌وجوی کل فضای ذخیره‌سازی استفاده شده در Nextcloud استفاده کنند. حساب‌های کاربری ادمین گروه نیز می‌توانند Nextcloud را پرس‌وجو کنند و همان عملکردهای یک ادمین را برای گروه‌هایی که مدیریت می‌کنند، انجام دهند. این API همچنین به ادمین امکان می‌دهد تا برای برنامه‌های فعال Nextcloud، اطلاعات برنامه و فعال یا غیرفعال کردن یک برنامه از راه دور پرس‌وجو کند. پس از فعال شدن برنامه، می‌توان از درخواست‌های HTTP از طریق یک هدر Basic Auth برای انجام هر یک از عملکردهای ذکر شده در بالا استفاده کرد. اطلاعات بیشتر، از جمله مثال‌های فراخوانی و پاسخ‌های سرور، در مستندات Provisioning API موجود است." diff --git a/apps/provisioning_api/lib/Controller/GroupsController.php b/apps/provisioning_api/lib/Controller/GroupsController.php index b251610e67f96..3cda3ae74ba2a 100644 --- a/apps/provisioning_api/lib/Controller/GroupsController.php +++ b/apps/provisioning_api/lib/Controller/GroupsController.php @@ -216,7 +216,7 @@ public function getGroupUsersDetails(string $groupId, string $search = '', ?int foreach ($users as $user) { try { /** @var IUser $user */ - $userId = (string)$user->getUID(); + $userId = $user->getUID(); $userData = $this->getUserData($userId); // Do not insert empty entry if ($userData !== null) { diff --git a/apps/settings/appinfo/info.xml b/apps/settings/appinfo/info.xml index 272e54c6d3722..0792bb1d0a35f 100644 --- a/apps/settings/appinfo/info.xml +++ b/apps/settings/appinfo/info.xml @@ -38,6 +38,7 @@ OCA\Settings\Sections\Admin\Additional OCA\Settings\Sections\Admin\Delegation OCA\Settings\Sections\Admin\Groupware + OCA\Settings\Sections\Admin\Office OCA\Settings\Sections\Admin\Overview OCA\Settings\Sections\Admin\Presets OCA\Settings\Sections\Admin\ArtificialIntelligence diff --git a/apps/settings/composer/composer/autoload_classmap.php b/apps/settings/composer/composer/autoload_classmap.php index 4a0b2981e8d32..cfa1b1ec0c11d 100644 --- a/apps/settings/composer/composer/autoload_classmap.php +++ b/apps/settings/composer/composer/autoload_classmap.php @@ -53,6 +53,7 @@ 'OCA\\Settings\\Sections\\Admin\\ArtificialIntelligence' => $baseDir . '/../lib/Sections/Admin/ArtificialIntelligence.php', 'OCA\\Settings\\Sections\\Admin\\Delegation' => $baseDir . '/../lib/Sections/Admin/Delegation.php', 'OCA\\Settings\\Sections\\Admin\\Groupware' => $baseDir . '/../lib/Sections/Admin/Groupware.php', + 'OCA\\Settings\\Sections\\Admin\\Office' => $baseDir . '/../lib/Sections/Admin/Office.php', 'OCA\\Settings\\Sections\\Admin\\Overview' => $baseDir . '/../lib/Sections/Admin/Overview.php', 'OCA\\Settings\\Sections\\Admin\\Presets' => $baseDir . '/../lib/Sections/Admin/Presets.php', 'OCA\\Settings\\Sections\\Admin\\Security' => $baseDir . '/../lib/Sections/Admin/Security.php', diff --git a/apps/settings/composer/composer/autoload_static.php b/apps/settings/composer/composer/autoload_static.php index 35797f3206a70..2900792c91478 100644 --- a/apps/settings/composer/composer/autoload_static.php +++ b/apps/settings/composer/composer/autoload_static.php @@ -68,6 +68,7 @@ class ComposerStaticInitSettings 'OCA\\Settings\\Sections\\Admin\\ArtificialIntelligence' => __DIR__ . '/..' . '/../lib/Sections/Admin/ArtificialIntelligence.php', 'OCA\\Settings\\Sections\\Admin\\Delegation' => __DIR__ . '/..' . '/../lib/Sections/Admin/Delegation.php', 'OCA\\Settings\\Sections\\Admin\\Groupware' => __DIR__ . '/..' . '/../lib/Sections/Admin/Groupware.php', + 'OCA\\Settings\\Sections\\Admin\\Office' => __DIR__ . '/..' . '/../lib/Sections/Admin/Office.php', 'OCA\\Settings\\Sections\\Admin\\Overview' => __DIR__ . '/..' . '/../lib/Sections/Admin/Overview.php', 'OCA\\Settings\\Sections\\Admin\\Presets' => __DIR__ . '/..' . '/../lib/Sections/Admin/Presets.php', 'OCA\\Settings\\Sections\\Admin\\Security' => __DIR__ . '/..' . '/../lib/Sections/Admin/Security.php', diff --git a/apps/settings/l10n/et_EE.js b/apps/settings/l10n/et_EE.js index 9fd13afdeca1e..912191dbee6b6 100644 --- a/apps/settings/l10n/et_EE.js +++ b/apps/settings/l10n/et_EE.js @@ -75,7 +75,7 @@ OC.L10N.register( "%1$s changed your password on %2$s." : "%1$s muutis sinu salasõna teenuses %2$s.", "Your password on %s was changed." : "Sinu %s salasõna on muudetud.", "Your password on %s was reset by an administrator." : "Peakasutaja lähtestas sinu %s salasõna.", - "Your password on %s was reset." : "Sinu salasõna „%s“ sai lähtestatud.", + "Your password on %s was reset." : "Sinu „%s“ teenuse salasõna sai lähtestatud.", "Password for %1$s changed on %2$s" : "%1$s salasõna teenuses %2$s on muutunud", "Password changed for %s" : "%s salasõna on muudetud", "If you did not request this, please contact an administrator." : "Kui sa pole seda taotlenud, võta ühendust peakasutajaga.", @@ -147,6 +147,9 @@ OC.L10N.register( "Cron last run" : "Croni viimane käivitamine", "Last background job execution ran %s. Something seems wrong. {link}." : "Viimase taustaülesande käivitas cron viimati %s. Midagi tundub valesti olevat. {link}.", "Last background job execution ran %s." : "Viimase taustaülesande käivitas cron viimati %s.", + "Data directory protected" : "Andmekaust on kaitstud", + "Your data directory and files are probably accessible from the internet. The .htaccess file is not working. It is strongly recommended that you configure your web server so that the data directory is no longer accessible, or move the data directory outside the web server document root." : "Sinu serveri andmekaust ja failid on tõenäoliselt internetist kättesaadavad. .htaccess-fail ei tööta. Soovitame tungivalt, et seadista veebiserver nii, et andmekaust ei oleks enam kättesaadav või paiguta andmekaust veebiserveri dokumendikaustast väljaspoole.", + "Could not check that the data directory is protected. Please check manually that your server does not allow access to the data directory." : "Andmekausta turvalisuse kontrollimine ei õnnestunud. Palun kontrolli käsitsi, et sinu server ei lubaks juurdepääsu andmekaustale.", "Database missing columns" : "Andmebaasis on puudu veerge", "Missing optional column \"%s\" in table \"%s\"." : "Mittekohustuslik veerg „%s“ on puudu tabelist „%s“.", "The database is missing some optional columns. Due to the fact that adding columns on big tables could take some time they were not added automatically when they can be optional. By running \"occ db:add-missing-columns\" those missing columns could be added manually while the instance keeps running. Once the columns are added some features might improve responsiveness or usability." : "Andmebaasist on puudu mõned täiendavad väljad. Kuna suurtesse tabelitesse uute veergude lisamine on ressursimahukas ja kui veerg ise soovituslik, siis see tihti jäetakse automaatse lisamise puhul vahele. Kasutades käsku „occ db:add-missing-columns“ saad need veerud sobilikul hetkel ise lisada. Peale lisamistmõne funktsionaalsuse kiirus ja kasutatavus võivad paraneda.", @@ -168,13 +171,21 @@ OC.L10N.register( "Transactional File Locking is disabled. This is not a a supported configuraton. It may lead to difficult to isolate problems including file corruption. Please remove the `'filelocking.enabled' => false` configuration entry from your `config.php` to avoid these problems." : "Transaktsiooniline faililukustus on lülitatud välja ning selline seadistus pole toetatud. See võib tekitada olukorra, kus vigade tuvastamine, näiteks failide katkimineku korral, võib osutuda keeruliseks. Selliste probleemide vältimiseks palun eemalda serveri „config.php“ failist „'filelocking.enabled' => false“ seadistus.", "Your \"trusted_proxies\" setting is not correctly set, it should be an array." : "Serveri „trusted_proxies“ seadistus pole korrektne - seal peab leiduma massiiv, aga hetkel on midagi muud.", "Your \"trusted_proxies\" setting is not correctly set, it should be an array of IP addresses - optionally with range in CIDR notation." : "Sinu kasutatav „trusted_proxies“ seadistus pole korrektne - ta peaks olema IP-aadresside massiiv, kus soovi korral saad kasutada CIDR-vormingut vahemike kirjeldamiseks.", + "Your IP address was resolved as %s" : "Sinu IP-aadress on tuvastatud kui %s", + "The reverse proxy header configuration is incorrect, or you are accessing Nextcloud from a trusted proxy. If not, this is a security issue and can allow an attacker to spoof their IP address as visible to the Nextcloud." : "Pöördproksi päise seadistused on valed või kasutad Nextcloudile juurdepääsuks usaldusväärset proksit. Kui see nii ei ole, on tegemist turvaprobleemiga, mis võib võimaldada ründajal varjata oma IP-aadressi nii, et see pole Nextcloudile nähtav.", "HTTPS access and URLs" : "HTTPS-ligipääs ja võrguaadressid", + "Accessing site insecurely via HTTP." : "Sa kasutad saiti ebaturvalisel viisil HTTP-protokolli kaudu.", "You are accessing your instance over a secure connection, however your instance is generating insecure URLs. This likely means that your instance is behind a reverse proxy and the Nextcloud `overwrite*` config values are not set correctly." : "Sa kasutad serverit turvalise ühenduse alusel, kuid server loob võrguaadresse ebaurvalistena. Tavaliselt tähendab see, et server paikneb pöördproksi taga ning Nextcloudi „overwrite*“ seadistusvõtmed pole korralikult määratletud.", "Your instance is generating insecure URLs. If you access your instance over HTTPS, this likely means that your instance is behind a reverse proxy and the Nextcloud `overwrite*` config values are not set correctly." : "Sinu server loob võrguaadresse ebaurvalistena. Kui kasutad serverit HTTPS-ühenduse alusel, siis tavaliselt tähendab see, et server paikneb pöördproksi taga ning Nextcloudi „overwrite*“ seadistusvõtmed pole korralikult määratletud.", + "You are accessing your instance over a secure connection, and your instance is generating secure URLs." : "Kasutad oma serverit turvalise ühenduse kaudu ja sinu server genereerib turvalisi võrguaadresse.", + "Internet connectivity" : "Internetiühendus", + "Internet connectivity is disabled in configuration file." : "Internetiühendus on seadistuste failis lülitatud välja.", "This server has no working internet connection: Multiple endpoints could not be reached. This means that some of the features like mounting external storage, notifications about updates or installation of third-party apps will not work. Accessing files remotely and sending of notification emails might not work, either. Establish a connection from this server to the internet to enjoy all features." : "Sellel serveril puudub toimiv internetiühendus: mitmete otspunktidega ei ole leitavad. See tähendab, et mõned funktsionaalsused, nagu näiteks väliste andmehoidlate ühendamine, uuenduste teavitused või kolmandate osapoolte rakenduste paigaldamine ei tööta. Ligipääs failidele eemalt ning teavistuste saatmine e-kirjaga ei pruugi samuti toimida. Kui soovid kasutada täielikku funktsionaalsust, siis palun taga toimiv internetiühendus.", "JavaScript modules support" : "JavaScripti moodulite tugi", "Unable to run check for JavaScript support. Please remedy or confirm manually if your webserver serves `.mjs` files using the JavaScript MIME type." : "Ei õnnestunud kontrollida, kas JavaScripti tugi toimib. Palun kontrolli käsitsi, kas sinu veebiserver suudab edastada JavaScripti MIME-tüübi kontekstis .mjs faile.", "Your webserver does not serve `.mjs` files using the JavaScript MIME type. This will break some apps by preventing browsers from executing the JavaScript files. You should configure your webserver to serve `.mjs` files with either the `text/javascript` or `application/javascript` MIME type." : "Sinu veebiserver ei edasta JavaScripti MIME-tüübiga „.mjs“ faile. See põhjustab mõnede rakenduste tööhäireid, kuna takistab veebibrauseritel JavaScripti faile käivitamast. Seadista oma veebiserver nii, et see edastaks „.mjs“-faile kas MIME-tüübiga „text/javascript“ või „application/javascript“.", + "JavaScript source map support" : "JavaScripti vastendusandmete tugi", + "Your webserver is not set up to serve `.js.map` files. Without these files, JavaScript Source Maps won't function properly, making it more challenging to troubleshoot and debug any issues that may arise." : "Sinu veebiserver ei toeta „.js.map“ failide laadimist. Ilma nende failideta JavaScripti vastendamine (source maps) ei toimi korralikult ja teeb veaotsingu ning silumise keerukamaks.", "Old server-side-encryption" : "Vana serveripoolne krüptimine", "Disabled" : "Keelatud", "The old server-side-encryption format is enabled. We recommend disabling this." : "Vana serveripoolse krüptimise vorming on kasutusel. Mes soovitame, et lülitad selle välja.", diff --git a/apps/settings/l10n/et_EE.json b/apps/settings/l10n/et_EE.json index 1f35498d4ad66..47064285d9e69 100644 --- a/apps/settings/l10n/et_EE.json +++ b/apps/settings/l10n/et_EE.json @@ -73,7 +73,7 @@ "%1$s changed your password on %2$s." : "%1$s muutis sinu salasõna teenuses %2$s.", "Your password on %s was changed." : "Sinu %s salasõna on muudetud.", "Your password on %s was reset by an administrator." : "Peakasutaja lähtestas sinu %s salasõna.", - "Your password on %s was reset." : "Sinu salasõna „%s“ sai lähtestatud.", + "Your password on %s was reset." : "Sinu „%s“ teenuse salasõna sai lähtestatud.", "Password for %1$s changed on %2$s" : "%1$s salasõna teenuses %2$s on muutunud", "Password changed for %s" : "%s salasõna on muudetud", "If you did not request this, please contact an administrator." : "Kui sa pole seda taotlenud, võta ühendust peakasutajaga.", @@ -145,6 +145,9 @@ "Cron last run" : "Croni viimane käivitamine", "Last background job execution ran %s. Something seems wrong. {link}." : "Viimase taustaülesande käivitas cron viimati %s. Midagi tundub valesti olevat. {link}.", "Last background job execution ran %s." : "Viimase taustaülesande käivitas cron viimati %s.", + "Data directory protected" : "Andmekaust on kaitstud", + "Your data directory and files are probably accessible from the internet. The .htaccess file is not working. It is strongly recommended that you configure your web server so that the data directory is no longer accessible, or move the data directory outside the web server document root." : "Sinu serveri andmekaust ja failid on tõenäoliselt internetist kättesaadavad. .htaccess-fail ei tööta. Soovitame tungivalt, et seadista veebiserver nii, et andmekaust ei oleks enam kättesaadav või paiguta andmekaust veebiserveri dokumendikaustast väljaspoole.", + "Could not check that the data directory is protected. Please check manually that your server does not allow access to the data directory." : "Andmekausta turvalisuse kontrollimine ei õnnestunud. Palun kontrolli käsitsi, et sinu server ei lubaks juurdepääsu andmekaustale.", "Database missing columns" : "Andmebaasis on puudu veerge", "Missing optional column \"%s\" in table \"%s\"." : "Mittekohustuslik veerg „%s“ on puudu tabelist „%s“.", "The database is missing some optional columns. Due to the fact that adding columns on big tables could take some time they were not added automatically when they can be optional. By running \"occ db:add-missing-columns\" those missing columns could be added manually while the instance keeps running. Once the columns are added some features might improve responsiveness or usability." : "Andmebaasist on puudu mõned täiendavad väljad. Kuna suurtesse tabelitesse uute veergude lisamine on ressursimahukas ja kui veerg ise soovituslik, siis see tihti jäetakse automaatse lisamise puhul vahele. Kasutades käsku „occ db:add-missing-columns“ saad need veerud sobilikul hetkel ise lisada. Peale lisamistmõne funktsionaalsuse kiirus ja kasutatavus võivad paraneda.", @@ -166,13 +169,21 @@ "Transactional File Locking is disabled. This is not a a supported configuraton. It may lead to difficult to isolate problems including file corruption. Please remove the `'filelocking.enabled' => false` configuration entry from your `config.php` to avoid these problems." : "Transaktsiooniline faililukustus on lülitatud välja ning selline seadistus pole toetatud. See võib tekitada olukorra, kus vigade tuvastamine, näiteks failide katkimineku korral, võib osutuda keeruliseks. Selliste probleemide vältimiseks palun eemalda serveri „config.php“ failist „'filelocking.enabled' => false“ seadistus.", "Your \"trusted_proxies\" setting is not correctly set, it should be an array." : "Serveri „trusted_proxies“ seadistus pole korrektne - seal peab leiduma massiiv, aga hetkel on midagi muud.", "Your \"trusted_proxies\" setting is not correctly set, it should be an array of IP addresses - optionally with range in CIDR notation." : "Sinu kasutatav „trusted_proxies“ seadistus pole korrektne - ta peaks olema IP-aadresside massiiv, kus soovi korral saad kasutada CIDR-vormingut vahemike kirjeldamiseks.", + "Your IP address was resolved as %s" : "Sinu IP-aadress on tuvastatud kui %s", + "The reverse proxy header configuration is incorrect, or you are accessing Nextcloud from a trusted proxy. If not, this is a security issue and can allow an attacker to spoof their IP address as visible to the Nextcloud." : "Pöördproksi päise seadistused on valed või kasutad Nextcloudile juurdepääsuks usaldusväärset proksit. Kui see nii ei ole, on tegemist turvaprobleemiga, mis võib võimaldada ründajal varjata oma IP-aadressi nii, et see pole Nextcloudile nähtav.", "HTTPS access and URLs" : "HTTPS-ligipääs ja võrguaadressid", + "Accessing site insecurely via HTTP." : "Sa kasutad saiti ebaturvalisel viisil HTTP-protokolli kaudu.", "You are accessing your instance over a secure connection, however your instance is generating insecure URLs. This likely means that your instance is behind a reverse proxy and the Nextcloud `overwrite*` config values are not set correctly." : "Sa kasutad serverit turvalise ühenduse alusel, kuid server loob võrguaadresse ebaurvalistena. Tavaliselt tähendab see, et server paikneb pöördproksi taga ning Nextcloudi „overwrite*“ seadistusvõtmed pole korralikult määratletud.", "Your instance is generating insecure URLs. If you access your instance over HTTPS, this likely means that your instance is behind a reverse proxy and the Nextcloud `overwrite*` config values are not set correctly." : "Sinu server loob võrguaadresse ebaurvalistena. Kui kasutad serverit HTTPS-ühenduse alusel, siis tavaliselt tähendab see, et server paikneb pöördproksi taga ning Nextcloudi „overwrite*“ seadistusvõtmed pole korralikult määratletud.", + "You are accessing your instance over a secure connection, and your instance is generating secure URLs." : "Kasutad oma serverit turvalise ühenduse kaudu ja sinu server genereerib turvalisi võrguaadresse.", + "Internet connectivity" : "Internetiühendus", + "Internet connectivity is disabled in configuration file." : "Internetiühendus on seadistuste failis lülitatud välja.", "This server has no working internet connection: Multiple endpoints could not be reached. This means that some of the features like mounting external storage, notifications about updates or installation of third-party apps will not work. Accessing files remotely and sending of notification emails might not work, either. Establish a connection from this server to the internet to enjoy all features." : "Sellel serveril puudub toimiv internetiühendus: mitmete otspunktidega ei ole leitavad. See tähendab, et mõned funktsionaalsused, nagu näiteks väliste andmehoidlate ühendamine, uuenduste teavitused või kolmandate osapoolte rakenduste paigaldamine ei tööta. Ligipääs failidele eemalt ning teavistuste saatmine e-kirjaga ei pruugi samuti toimida. Kui soovid kasutada täielikku funktsionaalsust, siis palun taga toimiv internetiühendus.", "JavaScript modules support" : "JavaScripti moodulite tugi", "Unable to run check for JavaScript support. Please remedy or confirm manually if your webserver serves `.mjs` files using the JavaScript MIME type." : "Ei õnnestunud kontrollida, kas JavaScripti tugi toimib. Palun kontrolli käsitsi, kas sinu veebiserver suudab edastada JavaScripti MIME-tüübi kontekstis .mjs faile.", "Your webserver does not serve `.mjs` files using the JavaScript MIME type. This will break some apps by preventing browsers from executing the JavaScript files. You should configure your webserver to serve `.mjs` files with either the `text/javascript` or `application/javascript` MIME type." : "Sinu veebiserver ei edasta JavaScripti MIME-tüübiga „.mjs“ faile. See põhjustab mõnede rakenduste tööhäireid, kuna takistab veebibrauseritel JavaScripti faile käivitamast. Seadista oma veebiserver nii, et see edastaks „.mjs“-faile kas MIME-tüübiga „text/javascript“ või „application/javascript“.", + "JavaScript source map support" : "JavaScripti vastendusandmete tugi", + "Your webserver is not set up to serve `.js.map` files. Without these files, JavaScript Source Maps won't function properly, making it more challenging to troubleshoot and debug any issues that may arise." : "Sinu veebiserver ei toeta „.js.map“ failide laadimist. Ilma nende failideta JavaScripti vastendamine (source maps) ei toimi korralikult ja teeb veaotsingu ning silumise keerukamaks.", "Old server-side-encryption" : "Vana serveripoolne krüptimine", "Disabled" : "Keelatud", "The old server-side-encryption format is enabled. We recommend disabling this." : "Vana serveripoolse krüptimise vorming on kasutusel. Mes soovitame, et lülitad selle välja.", diff --git a/apps/settings/l10n/fa.js b/apps/settings/l10n/fa.js index 28af459902b9f..0ac4417d8b727 100644 --- a/apps/settings/l10n/fa.js +++ b/apps/settings/l10n/fa.js @@ -86,6 +86,7 @@ OC.L10N.register( "Administration" : "مدیریت", "Users" : "کاربران", "Additional settings" : "تنظیمات اضافی", + "Assistant" : "دستیار", "Administration privileges" : "اجازه‌های مدیریتی", "Groupware" : "کار گروهی", "Overview" : "نمای کلّی", @@ -105,6 +106,7 @@ OC.L10N.register( "Your data directory and files are probably accessible from the internet. The .htaccess file is not working. It is strongly recommended that you configure your web server so that the data directory is no longer accessible, or move the data directory outside the web server document root." : "Your data directory and files are probably accessible from the internet. The .htaccess file is not working. It is strongly recommended that you configure your web server so that the data directory is no longer accessible, or move the data directory outside the web server document root.", "The database is missing some optional columns. Due to the fact that adding columns on big tables could take some time they were not added automatically when they can be optional. By running \"occ db:add-missing-columns\" those missing columns could be added manually while the instance keeps running. Once the columns are added some features might improve responsiveness or usability." : "The database is missing some optional columns. Due to the fact that adding columns on big tables could take some time they were not added automatically when they can be optional. By running \"occ db:add-missing-columns\" those missing columns could be added manually while the instance keeps running. Once the columns are added some features might improve responsiveness or usability.", "The database is missing some primary keys. Due to the fact that adding primary keys on big tables could take some time they were not added automatically. By running \"occ db:add-missing-primary-keys\" those missing primary keys could be added manually while the instance keeps running." : "The database is missing some primary keys. Due to the fact that adding primary keys on big tables could take some time they were not added automatically. By running \"occ db:add-missing-primary-keys\" those missing primary keys could be added manually while the instance keeps running.", + "Debug mode" : "حالت اشکال‌زدایی", "This instance is running in debug mode. Only enable this for local development and not in production environments." : "This instance is running in debug mode. Only enable this for local development and not in production environments.", "This server has no working internet connection: Multiple endpoints could not be reached. This means that some of the features like mounting external storage, notifications about updates or installation of third-party apps will not work. Accessing files remotely and sending of notification emails might not work, either. Establish a connection from this server to the internet to enjoy all features." : "This server has no working internet connection: Multiple endpoints could not be reached. This means that some of the features like mounting external storage, notifications about updates or installation of third-party apps will not work. Accessing files remotely and sending of notification emails might not work, either. Establish a connection from this server to the internet to enjoy all features.", "Disabled" : "غیرفعال شده", @@ -112,6 +114,7 @@ OC.L10N.register( "Logging level" : "سطح گزارش‌دهی", "The logging level is set to debug level. Use debug level only when you have a problem to diagnose, and then reset your log level to a less-verbose level as it outputs a lot of information, and can affect your server performance." : "سطح گزارش‌دهی روی حالت اشکال‌زدایی (debug) تنظیم شده است. از سطح اشکال‌زدایی تنها زمانی استفاده کنید که مشکلی برای تشخیص دارید، سپس سطح گزارش خود را به حالت کم‌حجم‌تری بازگردانید زیرا این حالت حجم زیادی از اطلاعات را خروجی می‌دهد و می‌تواند بر عملکرد نمونهٔ شما تأثیر منفی بگذارد.", "Logging level configured correctly." : "سطح گزارش‌دهی به درستی تنظیم شد.", + "Configured" : "پیکربندی شده", "The PHP function \"set_time_limit\" is not available. This could result in scripts being halted mid-execution, breaking your installation. Enabling this function is strongly recommended." : "The PHP function \"set_time_limit\" is not available. This could result in scripts being halted mid-execution, breaking your installation. Enabling this function is strongly recommended.", "Supported" : "پشتیبانی شده", "PHP does not seem to be setup properly to query system environment variables. The test with getenv(\"PATH\") only returns an empty response." : "PHP does not seem to be setup properly to query system environment variables. The test with getenv(\"PATH\") only returns an empty response.", @@ -214,6 +217,7 @@ OC.L10N.register( "Confirm" : "تأیید", "Submit" : "ارسال", "Rename group" : "Rename group", + "Delete group" : "حذف گروه", "Current password" : "گذرواژه کنونی", "New password" : "گذرواژه جدید", "Change password" : "تغییر گذرواژه", @@ -309,6 +313,7 @@ OC.L10N.register( "Unknown" : "ناشناخته", "Never" : "هرگز", "{size} used" : "{size} مورد استفاده", + "Delete account" : "حذف حساب", "Resend welcome email" : "ارسال ایمیل خوش آمدید", "Remote wipe of devices" : "پاک کردن از راه دور دستگاه ها", "Wipe {userid}'s devices" : "دستگاه های {userid} را پاک کنید", @@ -353,6 +358,7 @@ OC.L10N.register( "Authentication" : "احراز هویت", "Authentication required" : "احراز هویت مورد نیاز است", "Saving…" : "در حال ذخیره", + "Save settings" : "ذخیره تنظیمات", "Security & setup warnings" : "اخطارهای نصب و امنیتی", "Try again" : "تلاش دوباره", "All checks passed." : "تمامی موارد با موفقیت چک شدند.", @@ -375,6 +381,8 @@ OC.L10N.register( "Phone number" : "شماره تلفن", "Pronouns" : "ضمایر", "Role" : "نقش", + "X (formerly Twitter)" : "X (توییتر سابق)", + "Bluesky" : "Bluesky", "Website" : "وب‌ سایت", "Profile visibility" : "امکان دیده شدن پروفایل", "Locale" : "محل", @@ -388,6 +396,7 @@ OC.L10N.register( "Only synchronize to trusted servers" : "هم‌گام سازی تنها با کارسازهای مورد اعتماد", "Published" : "منتشر شده", "Synchronize to trusted servers and the global and public address book" : "هم‌گام سازی با کارسازهای مورد اعتماد و دفترچه نشانی‌های عمومی", + "Discover" : "کشف", "Your apps" : "برنامه‌های شما", "Active apps" : "برنامه‌های فعال", "Disabled apps" : "برنامه‌های غیرفعال", @@ -404,6 +413,7 @@ OC.L10N.register( "An error occurred during the request. Unable to proceed." : "An error occurred during the request. Unable to proceed.", "There were too many requests from your network. Retry later or contact your administrator if this is an error." : "There were too many requests from your network. Retry later or contact your administrator if this is an error.", "Error" : "خطا", + "Administration documentation" : "مستندات مدیریت", "Documentation" : "مستندسازی", "Forum" : "انجمن", "Nextcloud help & privacy resources" : "منابع راهنما و حریم شخصی نکست‌کلود", @@ -430,12 +440,19 @@ OC.L10N.register( "Could not remove app." : "امکان حذف برنامه وجود ندارد.", "Could not update app." : "برنامه را نمی توان به روزرسانی کرد .", "Apps" : "برنامه ها", + "Choose Deploy Daemon for {appName}" : "انتخاب Deploy Daemon برای {appName}", + "Registered Deploy daemons list" : "فهرست دیمن‌های استقرار ثبت‌شده", + "No Deploy daemons configured" : "هیچ دیمن استقراری پیکربندی نشده است", + "Register a custom one or setup from available templates" : "یک مورد سفارشی ثبت کنید یا از قالب‌های موجود پیکربندی کنید", + "Manage Deploy daemons" : "مدیریت دیمن‌های استقرار", "Update to {update}" : "به‌روز رسانی به {update} ", "Remove" : "برداشتن", "Featured" : "برگزیده", "This app is supported via your current Nextcloud subscription." : "این کاره از طریق اشتراک فعلی نکست کلودتان پشتیبانی می شود.", "Featured apps are developed by and within the community. They offer central functionality and are ready for production use." : "برنامه‌های برگزیده توسط و در داخل جامعه توسعه داده می‌شوند. این برنامه‌ها، عملکردهای مرکزی را ارائه می‌دهند و برای استفاده نهایی آماده هستند.", + "Community rating: {score}/5" : "امتیاز جامعه: {score}/5", "Disable all" : "از کار انداختن همه", + "Download and enable all" : "بارگیری و فعال کردن همه", "All apps are up-to-date." : "تمامی کاره‌ها به‌روزند.", "Icon" : "Icon", "Name" : "نام", @@ -444,20 +461,61 @@ OC.L10N.register( "No apps found for your version" : "هیچ برنامه‌ای برای نسخه‌ی شما یافت نشد", "_%n app has an update available_::_%n apps have an update available_" : ["۱ کاره به‌روز رسانی دارد","%n کاره به‌روز رسانی دارند"], "_Update_::_Update all_" : ["به‌روز رسانی","به‌روز رسانی همه"], + "Could not load app discover section" : "بارگذاری بخش کاوش کاره‌ها ممکن نشد", + "Could not render element" : "نمایش عنصر ممکن نشد", + "Nothing to show" : "چیزی برای نمایش وجود ندارد", + "Could not load section content from app store." : "بارگذاری محتوای این بخش از فروشگاه برنامه‌ها ممکن نشد.", "Loading" : "Loading", + "Fetching the latest news…" : "در حال دریافت تازه‌ترین اخبار…", + "Carousel" : "نمایش چرخشی", + "Previous slide" : "اسلاید قبلی", + "Next slide" : "اسلاید بعدی", + "Choose slide to display" : "اسلاید موردنظر برای نمایش را انتخاب کنید", + "{index} of {total}" : "{index} از {total}", "Daemon" : "فرایندهای پس زمینه", + "Deploy Daemon" : "دیمن استقرار", "Type" : "نوع", + "Display Name" : "نام نمایشی", + "GPUs support" : "پشتیبانی از پردازنده‌های گرافیکی (GPU)", + "Compute device" : "دستگاه محاسباتی", + "Advanced deploy options" : "گزینه‌های پیشرفته استقرار", + "Edit ExApp deploy options before installation" : "ویرایش گزینه‌های استقرار ExApp پیش از نصب", + "Configured ExApp deploy options. Can be set only during installation" : "گزینه‌های استقرار پیکربندی‌شده‌ی ExApp. تنها در هنگام نصب قابل تنظیم است", "Learn more" : "بیشتر بدانید", + "Environment variables" : "متغیرهای محیطی", + "ExApp container environment variables" : "متغیرهای محیطی کانتینر ExApp", + "No environment variables defined" : "هیچ متغیر محیطی‌ای تعریف نشده است", + "Mounts" : "سوارسازی‌ها", + "Define host folder mounts to bind to the ExApp container" : "پوشه‌های میزبان را برای اتصال به کانتینر ExApp تعریف کنید", + "Must exist on the Deploy daemon host prior to installing the ExApp" : "باید پیش از نصب ExApp روی میزبان دیمن استقرار وجود داشته باشد", + "Host path" : "مسیر میزبان", + "Container path" : "مسیر کانتینر", + "Read-only" : "فقط‌خواندنی", + "Remove mount" : "حذف نقطه نصب", + "New mount" : "نقطه نصب جدید", + "Enter path to host folder" : "مسیر پوشه میزبان را وارد کنید", + "Enter path to container folder" : "مسیر پوشه کانتینر را وارد کنید", + "Toggle read-only mode" : "تغییر حالت فقط خواندنی", + "Confirm adding new mount" : "تأیید افزودن نقطه نصب جدید", + "Cancel adding mount" : "لغو افزودن نقطه نصب", + "Add mount" : "افزودن نقطه نصب", + "ExApp container mounts" : "نقطه‌های نصب کانتینر ExApp", + "No mounts defined" : "هیچ نقطه نصبی تعریف نشده است", "Description" : "شرح", "View in store" : "نمایش در فروشگاه", "Visit website" : "سر زدن به پایگاه وب", + "Usage documentation" : "مستندات کاربری", "Admin documentation" : "مستندات مدیریتی", "Developer documentation" : "مستندات توسعه", "All" : "همه", "Limit app usage to groups" : "محدودیت استفاده از برنامه به گروه‌ها", "No results" : "بدون نتیجه", "Update to {version}" : "به‌روز رسانی به {version}", + "Deploy options" : "گزینه‌های استقرار", + "Default Deploy daemon is not accessible" : "دسترسی به Deploy daemon پیش‌فرض ممکن نیست", + "Delete data on remove" : "حذف داده‌ها هنگام برچیدن", "This app cannot be installed because the following dependencies are not fulfilled:" : "امکان نصب این برنامه وجود ندارد، این پیش‌نیازها انجام نشده‌اند:", + "Latest updated" : "آخرین به‌روزرسانی", "Author" : "نویسنده", "Categories" : "دسته‌ها", "Resources" : "منابع", @@ -465,6 +523,7 @@ OC.L10N.register( "Report a bug" : "گزارش یک اشکال", "Request feature" : "درخواست ویژگی", "Ask questions or discuss" : "پرسش یا بحث", + "Rate the app" : "امتیازدهی به برنامه", "Rate" : "رتبه‌بندی", "Changelog" : "تغییر", "_{userCount} account …_::_{userCount} accounts …_" : ["{userCount} حساب ...","{userCount} حساب ..."], @@ -475,18 +534,38 @@ OC.L10N.register( "Set the language" : "Set the language", "Done" : "Done", "Adding your device …" : "Adding your device …", + "Loading app list" : "در حال بارگذاری فهرست کاره‌ها", + "Loading categories" : "در حال بارگذاری دسته‌ها", + "Version {version}, {license}-licensed" : "نسخه {version}، با مجوز {license}", + "Version {version}" : "نسخه {version}", "Sending…" : "در حال ارسال", "Email sent" : "ایمیل ارسال شد", + "Deploy and Enable" : "استقرار و فعالسازی", "Download and enable" : "بارگیری و فعال سازی", "Disable" : "غیرفعال", "Allow untested app" : "Allow untested app", "The app will be downloaded from the App Store" : "The app will be downloaded from the App Store", + "The app has been enabled but needs to be updated." : "این برنامه فعال شده است اما باید به‌روزرسانی شود.", "Error: This app cannot be enabled because it makes the server unstable" : "Error: This app cannot be enabled because it makes the server unstable", "The app has been enabled but needs to be updated. You will be redirected to the update page in 5 seconds." : "برنامه فعال شده است اما باید به روز شود. شما پس از 5 ثانیه به صفحه بروزرسانی هدایت می شوید.", "None/STARTTLS" : "None/STARTTLS", "SSL" : "SSL", "Credentials" : "اعتبارهای", "SMTP Password" : "رمز عبور SMTP", - "Test and verify email settings" : "Test and verify email settings" + "Test and verify email settings" : "Test and verify email settings", + "Office suite switching is managed through the Nextcloud All-in-One interface." : "جابجایی مجموعه‌ی اداری از طریق رابط Nextcloud All-in-One مدیریت می‌شود.", + "Please use the AIO interface to switch between office suites." : "لطفاً برای جابجایی میان مجموعه‌های اداری از رابط AIO استفاده کنید.", + "installed" : "نصب‌شده", + "Disable office suites" : "غیرفعال کردن مجموعه‌های اداری", + "Best Nextcloud integration" : "بهترین یکپارچگی با Nextcloud", + "Open source" : "متن‌باز", + "Good performance" : "کارایی خوب", + "Best security: documents never leave your server" : "بهترین امنیت: اسناد هرگز از سرور شما خارج نمی‌شوند", + "Best ODF compatibility" : "بهترین سازگاری با ODF", + "Best support for legacy files" : "بهترین پشتیبانی از پرونده‌های قدیمی", + "Good Nextcloud integration" : "یکپارچگی خوب با Nextcloud", + "Best performance" : "بهترین کارایی", + "Limited ODF compatibility" : "سازگاری محدود با ODF", + "Best Microsoft compatibility" : "بهترین سازگاری با Microsoft" }, "nplurals=2; plural=(n > 1);"); diff --git a/apps/settings/l10n/fa.json b/apps/settings/l10n/fa.json index f7aa15ebef787..96e36e5b6a0c8 100644 --- a/apps/settings/l10n/fa.json +++ b/apps/settings/l10n/fa.json @@ -84,6 +84,7 @@ "Administration" : "مدیریت", "Users" : "کاربران", "Additional settings" : "تنظیمات اضافی", + "Assistant" : "دستیار", "Administration privileges" : "اجازه‌های مدیریتی", "Groupware" : "کار گروهی", "Overview" : "نمای کلّی", @@ -103,6 +104,7 @@ "Your data directory and files are probably accessible from the internet. The .htaccess file is not working. It is strongly recommended that you configure your web server so that the data directory is no longer accessible, or move the data directory outside the web server document root." : "Your data directory and files are probably accessible from the internet. The .htaccess file is not working. It is strongly recommended that you configure your web server so that the data directory is no longer accessible, or move the data directory outside the web server document root.", "The database is missing some optional columns. Due to the fact that adding columns on big tables could take some time they were not added automatically when they can be optional. By running \"occ db:add-missing-columns\" those missing columns could be added manually while the instance keeps running. Once the columns are added some features might improve responsiveness or usability." : "The database is missing some optional columns. Due to the fact that adding columns on big tables could take some time they were not added automatically when they can be optional. By running \"occ db:add-missing-columns\" those missing columns could be added manually while the instance keeps running. Once the columns are added some features might improve responsiveness or usability.", "The database is missing some primary keys. Due to the fact that adding primary keys on big tables could take some time they were not added automatically. By running \"occ db:add-missing-primary-keys\" those missing primary keys could be added manually while the instance keeps running." : "The database is missing some primary keys. Due to the fact that adding primary keys on big tables could take some time they were not added automatically. By running \"occ db:add-missing-primary-keys\" those missing primary keys could be added manually while the instance keeps running.", + "Debug mode" : "حالت اشکال‌زدایی", "This instance is running in debug mode. Only enable this for local development and not in production environments." : "This instance is running in debug mode. Only enable this for local development and not in production environments.", "This server has no working internet connection: Multiple endpoints could not be reached. This means that some of the features like mounting external storage, notifications about updates or installation of third-party apps will not work. Accessing files remotely and sending of notification emails might not work, either. Establish a connection from this server to the internet to enjoy all features." : "This server has no working internet connection: Multiple endpoints could not be reached. This means that some of the features like mounting external storage, notifications about updates or installation of third-party apps will not work. Accessing files remotely and sending of notification emails might not work, either. Establish a connection from this server to the internet to enjoy all features.", "Disabled" : "غیرفعال شده", @@ -110,6 +112,7 @@ "Logging level" : "سطح گزارش‌دهی", "The logging level is set to debug level. Use debug level only when you have a problem to diagnose, and then reset your log level to a less-verbose level as it outputs a lot of information, and can affect your server performance." : "سطح گزارش‌دهی روی حالت اشکال‌زدایی (debug) تنظیم شده است. از سطح اشکال‌زدایی تنها زمانی استفاده کنید که مشکلی برای تشخیص دارید، سپس سطح گزارش خود را به حالت کم‌حجم‌تری بازگردانید زیرا این حالت حجم زیادی از اطلاعات را خروجی می‌دهد و می‌تواند بر عملکرد نمونهٔ شما تأثیر منفی بگذارد.", "Logging level configured correctly." : "سطح گزارش‌دهی به درستی تنظیم شد.", + "Configured" : "پیکربندی شده", "The PHP function \"set_time_limit\" is not available. This could result in scripts being halted mid-execution, breaking your installation. Enabling this function is strongly recommended." : "The PHP function \"set_time_limit\" is not available. This could result in scripts being halted mid-execution, breaking your installation. Enabling this function is strongly recommended.", "Supported" : "پشتیبانی شده", "PHP does not seem to be setup properly to query system environment variables. The test with getenv(\"PATH\") only returns an empty response." : "PHP does not seem to be setup properly to query system environment variables. The test with getenv(\"PATH\") only returns an empty response.", @@ -212,6 +215,7 @@ "Confirm" : "تأیید", "Submit" : "ارسال", "Rename group" : "Rename group", + "Delete group" : "حذف گروه", "Current password" : "گذرواژه کنونی", "New password" : "گذرواژه جدید", "Change password" : "تغییر گذرواژه", @@ -307,6 +311,7 @@ "Unknown" : "ناشناخته", "Never" : "هرگز", "{size} used" : "{size} مورد استفاده", + "Delete account" : "حذف حساب", "Resend welcome email" : "ارسال ایمیل خوش آمدید", "Remote wipe of devices" : "پاک کردن از راه دور دستگاه ها", "Wipe {userid}'s devices" : "دستگاه های {userid} را پاک کنید", @@ -351,6 +356,7 @@ "Authentication" : "احراز هویت", "Authentication required" : "احراز هویت مورد نیاز است", "Saving…" : "در حال ذخیره", + "Save settings" : "ذخیره تنظیمات", "Security & setup warnings" : "اخطارهای نصب و امنیتی", "Try again" : "تلاش دوباره", "All checks passed." : "تمامی موارد با موفقیت چک شدند.", @@ -373,6 +379,8 @@ "Phone number" : "شماره تلفن", "Pronouns" : "ضمایر", "Role" : "نقش", + "X (formerly Twitter)" : "X (توییتر سابق)", + "Bluesky" : "Bluesky", "Website" : "وب‌ سایت", "Profile visibility" : "امکان دیده شدن پروفایل", "Locale" : "محل", @@ -386,6 +394,7 @@ "Only synchronize to trusted servers" : "هم‌گام سازی تنها با کارسازهای مورد اعتماد", "Published" : "منتشر شده", "Synchronize to trusted servers and the global and public address book" : "هم‌گام سازی با کارسازهای مورد اعتماد و دفترچه نشانی‌های عمومی", + "Discover" : "کشف", "Your apps" : "برنامه‌های شما", "Active apps" : "برنامه‌های فعال", "Disabled apps" : "برنامه‌های غیرفعال", @@ -402,6 +411,7 @@ "An error occurred during the request. Unable to proceed." : "An error occurred during the request. Unable to proceed.", "There were too many requests from your network. Retry later or contact your administrator if this is an error." : "There were too many requests from your network. Retry later or contact your administrator if this is an error.", "Error" : "خطا", + "Administration documentation" : "مستندات مدیریت", "Documentation" : "مستندسازی", "Forum" : "انجمن", "Nextcloud help & privacy resources" : "منابع راهنما و حریم شخصی نکست‌کلود", @@ -428,12 +438,19 @@ "Could not remove app." : "امکان حذف برنامه وجود ندارد.", "Could not update app." : "برنامه را نمی توان به روزرسانی کرد .", "Apps" : "برنامه ها", + "Choose Deploy Daemon for {appName}" : "انتخاب Deploy Daemon برای {appName}", + "Registered Deploy daemons list" : "فهرست دیمن‌های استقرار ثبت‌شده", + "No Deploy daemons configured" : "هیچ دیمن استقراری پیکربندی نشده است", + "Register a custom one or setup from available templates" : "یک مورد سفارشی ثبت کنید یا از قالب‌های موجود پیکربندی کنید", + "Manage Deploy daemons" : "مدیریت دیمن‌های استقرار", "Update to {update}" : "به‌روز رسانی به {update} ", "Remove" : "برداشتن", "Featured" : "برگزیده", "This app is supported via your current Nextcloud subscription." : "این کاره از طریق اشتراک فعلی نکست کلودتان پشتیبانی می شود.", "Featured apps are developed by and within the community. They offer central functionality and are ready for production use." : "برنامه‌های برگزیده توسط و در داخل جامعه توسعه داده می‌شوند. این برنامه‌ها، عملکردهای مرکزی را ارائه می‌دهند و برای استفاده نهایی آماده هستند.", + "Community rating: {score}/5" : "امتیاز جامعه: {score}/5", "Disable all" : "از کار انداختن همه", + "Download and enable all" : "بارگیری و فعال کردن همه", "All apps are up-to-date." : "تمامی کاره‌ها به‌روزند.", "Icon" : "Icon", "Name" : "نام", @@ -442,20 +459,61 @@ "No apps found for your version" : "هیچ برنامه‌ای برای نسخه‌ی شما یافت نشد", "_%n app has an update available_::_%n apps have an update available_" : ["۱ کاره به‌روز رسانی دارد","%n کاره به‌روز رسانی دارند"], "_Update_::_Update all_" : ["به‌روز رسانی","به‌روز رسانی همه"], + "Could not load app discover section" : "بارگذاری بخش کاوش کاره‌ها ممکن نشد", + "Could not render element" : "نمایش عنصر ممکن نشد", + "Nothing to show" : "چیزی برای نمایش وجود ندارد", + "Could not load section content from app store." : "بارگذاری محتوای این بخش از فروشگاه برنامه‌ها ممکن نشد.", "Loading" : "Loading", + "Fetching the latest news…" : "در حال دریافت تازه‌ترین اخبار…", + "Carousel" : "نمایش چرخشی", + "Previous slide" : "اسلاید قبلی", + "Next slide" : "اسلاید بعدی", + "Choose slide to display" : "اسلاید موردنظر برای نمایش را انتخاب کنید", + "{index} of {total}" : "{index} از {total}", "Daemon" : "فرایندهای پس زمینه", + "Deploy Daemon" : "دیمن استقرار", "Type" : "نوع", + "Display Name" : "نام نمایشی", + "GPUs support" : "پشتیبانی از پردازنده‌های گرافیکی (GPU)", + "Compute device" : "دستگاه محاسباتی", + "Advanced deploy options" : "گزینه‌های پیشرفته استقرار", + "Edit ExApp deploy options before installation" : "ویرایش گزینه‌های استقرار ExApp پیش از نصب", + "Configured ExApp deploy options. Can be set only during installation" : "گزینه‌های استقرار پیکربندی‌شده‌ی ExApp. تنها در هنگام نصب قابل تنظیم است", "Learn more" : "بیشتر بدانید", + "Environment variables" : "متغیرهای محیطی", + "ExApp container environment variables" : "متغیرهای محیطی کانتینر ExApp", + "No environment variables defined" : "هیچ متغیر محیطی‌ای تعریف نشده است", + "Mounts" : "سوارسازی‌ها", + "Define host folder mounts to bind to the ExApp container" : "پوشه‌های میزبان را برای اتصال به کانتینر ExApp تعریف کنید", + "Must exist on the Deploy daemon host prior to installing the ExApp" : "باید پیش از نصب ExApp روی میزبان دیمن استقرار وجود داشته باشد", + "Host path" : "مسیر میزبان", + "Container path" : "مسیر کانتینر", + "Read-only" : "فقط‌خواندنی", + "Remove mount" : "حذف نقطه نصب", + "New mount" : "نقطه نصب جدید", + "Enter path to host folder" : "مسیر پوشه میزبان را وارد کنید", + "Enter path to container folder" : "مسیر پوشه کانتینر را وارد کنید", + "Toggle read-only mode" : "تغییر حالت فقط خواندنی", + "Confirm adding new mount" : "تأیید افزودن نقطه نصب جدید", + "Cancel adding mount" : "لغو افزودن نقطه نصب", + "Add mount" : "افزودن نقطه نصب", + "ExApp container mounts" : "نقطه‌های نصب کانتینر ExApp", + "No mounts defined" : "هیچ نقطه نصبی تعریف نشده است", "Description" : "شرح", "View in store" : "نمایش در فروشگاه", "Visit website" : "سر زدن به پایگاه وب", + "Usage documentation" : "مستندات کاربری", "Admin documentation" : "مستندات مدیریتی", "Developer documentation" : "مستندات توسعه", "All" : "همه", "Limit app usage to groups" : "محدودیت استفاده از برنامه به گروه‌ها", "No results" : "بدون نتیجه", "Update to {version}" : "به‌روز رسانی به {version}", + "Deploy options" : "گزینه‌های استقرار", + "Default Deploy daemon is not accessible" : "دسترسی به Deploy daemon پیش‌فرض ممکن نیست", + "Delete data on remove" : "حذف داده‌ها هنگام برچیدن", "This app cannot be installed because the following dependencies are not fulfilled:" : "امکان نصب این برنامه وجود ندارد، این پیش‌نیازها انجام نشده‌اند:", + "Latest updated" : "آخرین به‌روزرسانی", "Author" : "نویسنده", "Categories" : "دسته‌ها", "Resources" : "منابع", @@ -463,6 +521,7 @@ "Report a bug" : "گزارش یک اشکال", "Request feature" : "درخواست ویژگی", "Ask questions or discuss" : "پرسش یا بحث", + "Rate the app" : "امتیازدهی به برنامه", "Rate" : "رتبه‌بندی", "Changelog" : "تغییر", "_{userCount} account …_::_{userCount} accounts …_" : ["{userCount} حساب ...","{userCount} حساب ..."], @@ -473,18 +532,38 @@ "Set the language" : "Set the language", "Done" : "Done", "Adding your device …" : "Adding your device …", + "Loading app list" : "در حال بارگذاری فهرست کاره‌ها", + "Loading categories" : "در حال بارگذاری دسته‌ها", + "Version {version}, {license}-licensed" : "نسخه {version}، با مجوز {license}", + "Version {version}" : "نسخه {version}", "Sending…" : "در حال ارسال", "Email sent" : "ایمیل ارسال شد", + "Deploy and Enable" : "استقرار و فعالسازی", "Download and enable" : "بارگیری و فعال سازی", "Disable" : "غیرفعال", "Allow untested app" : "Allow untested app", "The app will be downloaded from the App Store" : "The app will be downloaded from the App Store", + "The app has been enabled but needs to be updated." : "این برنامه فعال شده است اما باید به‌روزرسانی شود.", "Error: This app cannot be enabled because it makes the server unstable" : "Error: This app cannot be enabled because it makes the server unstable", "The app has been enabled but needs to be updated. You will be redirected to the update page in 5 seconds." : "برنامه فعال شده است اما باید به روز شود. شما پس از 5 ثانیه به صفحه بروزرسانی هدایت می شوید.", "None/STARTTLS" : "None/STARTTLS", "SSL" : "SSL", "Credentials" : "اعتبارهای", "SMTP Password" : "رمز عبور SMTP", - "Test and verify email settings" : "Test and verify email settings" + "Test and verify email settings" : "Test and verify email settings", + "Office suite switching is managed through the Nextcloud All-in-One interface." : "جابجایی مجموعه‌ی اداری از طریق رابط Nextcloud All-in-One مدیریت می‌شود.", + "Please use the AIO interface to switch between office suites." : "لطفاً برای جابجایی میان مجموعه‌های اداری از رابط AIO استفاده کنید.", + "installed" : "نصب‌شده", + "Disable office suites" : "غیرفعال کردن مجموعه‌های اداری", + "Best Nextcloud integration" : "بهترین یکپارچگی با Nextcloud", + "Open source" : "متن‌باز", + "Good performance" : "کارایی خوب", + "Best security: documents never leave your server" : "بهترین امنیت: اسناد هرگز از سرور شما خارج نمی‌شوند", + "Best ODF compatibility" : "بهترین سازگاری با ODF", + "Best support for legacy files" : "بهترین پشتیبانی از پرونده‌های قدیمی", + "Good Nextcloud integration" : "یکپارچگی خوب با Nextcloud", + "Best performance" : "بهترین کارایی", + "Limited ODF compatibility" : "سازگاری محدود با ODF", + "Best Microsoft compatibility" : "بهترین سازگاری با Microsoft" },"pluralForm" :"nplurals=2; plural=(n > 1);" } \ No newline at end of file diff --git a/apps/settings/l10n/nb.js b/apps/settings/l10n/nb.js index 531a3f43da746..5130a93894c40 100644 --- a/apps/settings/l10n/nb.js +++ b/apps/settings/l10n/nb.js @@ -97,6 +97,7 @@ OC.L10N.register( "Administration privileges" : "Administratorrettigheter", "Groupware" : "Gruppevare", "Overview" : "Oversikt", + "Quick presets" : "Hurtig-forhåndsinnstillinger", "Basic settings" : "Grunninnstillinger", "Sharing" : "Deling", "Availability" : "Tilgjengelighet", diff --git a/apps/settings/l10n/nb.json b/apps/settings/l10n/nb.json index 1711415c8c30a..40507ad7063ed 100644 --- a/apps/settings/l10n/nb.json +++ b/apps/settings/l10n/nb.json @@ -95,6 +95,7 @@ "Administration privileges" : "Administratorrettigheter", "Groupware" : "Gruppevare", "Overview" : "Oversikt", + "Quick presets" : "Hurtig-forhåndsinnstillinger", "Basic settings" : "Grunninnstillinger", "Sharing" : "Deling", "Availability" : "Tilgjengelighet", diff --git a/apps/settings/lib/Controller/CheckSetupController.php b/apps/settings/lib/Controller/CheckSetupController.php index b0b240bd85512..f6c2a642e23c1 100644 --- a/apps/settings/lib/Controller/CheckSetupController.php +++ b/apps/settings/lib/Controller/CheckSetupController.php @@ -19,23 +19,17 @@ use OCP\AppFramework\Http\DataDisplayResponse; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\RedirectResponse; -use OCP\IConfig; -use OCP\IL10N; use OCP\IRequest; use OCP\IURLGenerator; use OCP\SetupCheck\ISetupCheckManager; -use Psr\Log\LoggerInterface; #[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)] class CheckSetupController extends Controller { public function __construct( $appName, IRequest $request, - private IConfig $config, private IURLGenerator $urlGenerator, - private IL10N $l10n, private Checker $checker, - private LoggerInterface $logger, private ISetupCheckManager $setupCheckManager, ) { parent::__construct($appName, $request); diff --git a/apps/settings/lib/Sections/Admin/Office.php b/apps/settings/lib/Sections/Admin/Office.php new file mode 100644 index 0000000000000..438a3245de19f --- /dev/null +++ b/apps/settings/lib/Sections/Admin/Office.php @@ -0,0 +1,43 @@ +urlGenerator->imagePath('core', 'apps/richdocuments.svg'); + } + + #[\Override] + public function getID(): string { + return 'office'; + } + + #[\Override] + public function getName(): string { + return $this->l->t('Office'); + } + + #[\Override] + public function getPriority(): int { + return 50; + } +} diff --git a/apps/settings/lib/SetupChecks/MaintenanceWindowStart.php b/apps/settings/lib/SetupChecks/MaintenanceWindowStart.php index fc1bf53ffa040..61a02e1d68ee9 100644 --- a/apps/settings/lib/SetupChecks/MaintenanceWindowStart.php +++ b/apps/settings/lib/SetupChecks/MaintenanceWindowStart.php @@ -44,7 +44,7 @@ public function run(): SetupResult { } $startValue = (int)$configValue; - $endValue = ($startValue + 6) % 24; + $endValue = ($startValue + 4) % 24; return SetupResult::success( str_replace( ['{start}', '{end}'], diff --git a/apps/settings/src/components/UserList.vue b/apps/settings/src/components/UserList.vue index 4c2b8a3a4b008..0cb78e12b0e9a 100644 --- a/apps/settings/src/components/UserList.vue +++ b/apps/settings/src/components/UserList.vue @@ -21,10 +21,10 @@ + :name="loading.users ? null : t('settings', 'No accounts')">