Compare commits

..

259 Commits

Author SHA1 Message Date
Git'Fellow 18713e5e9f fix: psalm
Signed-off-by: Git'Fellow <12234510+solracsf@users.noreply.github.com>
2025-08-18 11:56:01 +02:00
Git'Fellow aaec698ece fix(migration): Less error-prone migration
Signed-off-by: Git'Fellow <12234510+solracsf@users.noreply.github.com>
2025-08-18 11:41:15 +02:00
Nextcloud bot 0bc2b34793 fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-08-18 00:12:13 +00:00
Nextcloud bot 23573c4947 fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-08-17 00:12:00 +00:00
dependabot[bot] 545194bc94 Merge pull request #54453 from nextcloud/dependabot/npm_and_yarn/puppeteer-24.16.2 2025-08-16 02:58:10 +00:00
dependabot[bot] 48f43b5583 chore(deps-dev): bump puppeteer from 24.16.0 to 24.16.2
Bumps [puppeteer](https://github.com/puppeteer/puppeteer) from 24.16.0 to 24.16.2.
- [Release notes](https://github.com/puppeteer/puppeteer/releases)
- [Changelog](https://github.com/puppeteer/puppeteer/blob/main/CHANGELOG.md)
- [Commits](https://github.com/puppeteer/puppeteer/compare/puppeteer-v24.16.0...puppeteer-v24.16.2)

---
updated-dependencies:
- dependency-name: puppeteer
  dependency-version: 24.16.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-16 01:58:00 +00:00
Nextcloud bot 22a51aace2 fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-08-16 00:12:03 +00:00
Richard Steinmetz b38c07c682 Merge pull request #54229 from nextcloud/perf/mount-provider-join-array-merge
perf: join array_merge calls in MountProvider::getMountsForUser()
2025-08-15 15:25:39 +02:00
Richard Steinmetz 7bb0e0de7a Merge pull request #54405 from nextcloud/perf/custom-properties-default-value
perf: delete commonly used custom properties instead of setting the default value
2025-08-15 13:34:29 +02:00
Salvatore Martire 75d9aaa3b5 Merge pull request #54318 from nextcloud/feat/54115/emitPreloadCollectionEvent
Emits a `preloadCollection` event in the DAV server, so that plugins can listen to it and preload DAV properties for files inside a collection, to avoid the N+1 issue that would follow if loading properties on a per-file basis.
2025-08-15 11:06:48 +02:00
Robin Appelman 89fa14fd77 Merge pull request #54386 from nextcloud/fix-n+1-caldav
fix(performance): Fix n+1 issue when fetching calendar properties
2025-08-15 10:56:11 +02:00
Salvatore Martire 4a0a00a5a2 perf(shares): avoid array_merge in loops
Signed-off-by: Salvatore Martire <4652631+salmart-dev@users.noreply.github.com>
2025-08-15 10:46:43 +02:00
Salvatore Martire bdcd583045 feat: make use of preloadCollection in core apps
Signed-off-by: Salvatore Martire <4652631+salmart-dev@users.noreply.github.com>
2025-08-15 10:46:43 +02:00
Salvatore Martire 9bbebd6034 feat: emit preloadCollection event in DAV
This allows plugins to preload the content of a Collection to speed-up
subsequent per-node PROPFINDs and reduce database load.

Signed-off-by: Salvatore Martire <4652631+salmart-dev@users.noreply.github.com>
2025-08-15 10:46:43 +02:00
Carl Schwan 9df79bae10 perf(caldav): Only prefetch published properties
Signed-off-by: Carl Schwan <carl.schwan@nextclound.com>
2025-08-15 09:57:58 +02:00
Carl Schwan 46f0c6ebb5 perf(caldav): Cache calendars in CustomPropertiesBackend
We already do that for files, we are now also doing for calendars.
With relatively small amount of calendars, I managed to reduce the
number of DB requests by 35% and from 23 DB requests touching the
oc_properties table to only 3.

Signed-off-by: Carl Schwan <carl.schwan@nextclound.com>
2025-08-15 09:57:58 +02:00
Carl Schwan 977541cedf perf(caldav): Add multi users support in findPropertiesByPaths
Signed-off-by: Carl Schwan <carl.schwan@nextclound.com>
2025-08-15 09:57:56 +02:00
Daniel 64c52006dd Merge pull request #54272 from nextcloud/enh/noid/taskprocessing-task-add-cleanup-flag
feat(taskprocessing): add cleanup flag to tasks
2025-08-15 09:48:47 +02:00
Nextcloud bot 2fc58bf45d fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-08-15 00:12:18 +00:00
John Molakvoæ 5f81a77c72 Merge pull request #54370 from nextcloud/chore-remove-unused-ocs-providers 2025-08-14 22:02:23 +02:00
Daniel 92e282af0b Merge pull request #53996 from nextcloud/feat/unified_search/online_providers
feat(UnifiedSearch): Online search providers support, toggle option
2025-08-14 21:08:21 +02:00
Maxence Lange 61fe4c1aba Merge pull request #54336 from nextcloud/feat/noid/preset-on-share-password
feat(preset): share password protection
2025-08-14 16:07:13 -01:00
Carl Schwan 806fe6619f perf(caldav): Fetch additional properties for multiple calendars in one query
Instead of doing it for each calendar individually.

Signed-off-by: Carl Schwan <carl.schwan@nextclound.com>
2025-08-14 18:25:40 +02:00
Robin Appelman e0a21e5927 Merge pull request #54384 from nextcloud/getpath-node
perf: use more optimized node-by-id logic in View::getPath
2025-08-14 18:21:23 +02:00
Benjamin Gaussorgues d7f66eaee4 Merge pull request #54418 from nextcloud/fix/more-transparency 2025-08-14 17:12:39 +02:00
Robin Appelman 06aa7035db Merge pull request #54420 from nextcloud/disable-custom-props-events
perf(calendar): Disable custom properties for individual calendar events
2025-08-14 15:55:36 +02:00
Anna 1a2d0d5c1d Merge pull request #54117 from nextcloud/feat/noid/add-bulk-activity
feat(activity): add bulk activity option
2025-08-14 15:53:33 +02:00
Robin Appelman b69109395b Merge pull request #52786 from nextcloud/multi-object-store
allow configuring multiple object store backends
2025-08-14 15:49:00 +02:00
Andrey Borysenko 5c4e84f128 feat(UnifiedSearch): External search providers support
Signed-off-by: Andrey Borysenko <andrey18106x@gmail.com>
2025-08-14 16:37:39 +03:00
Stephan Orbaugh 7fe5c8fdda Merge pull request #54120 from IONOS-Productivity/fix/unified_search.providers_allowed
fix(search): Fix SearchComposer.php filtering logic
2025-08-14 15:02:42 +02:00
Salvatore Martire 50c9c7e332 Merge pull request #54153 from nextcloud/feat/54114/reportSlowPropfinds
This PR adds an error log when a DAV Plugin fetches data for nodes in a directory on a per-node basis instead of using an eager loading mechanism, which is more efficient.

This is only enabled when `config.php` has `'debug'` set to enabled.
2025-08-14 13:40:31 +02:00
Robin Appelman 1f24090770 Merge pull request #54411 from nextcloud/dav-customerproperties-ignore-nc
perf: ignore any custom property in the nc/oc namespace that isn't explicitly allowed
2025-08-14 13:22:10 +02:00
Robin Appelman 4990d75007 feat: multi object store rename command
Signed-off-by: Robin Appelman <robin@icewind.nl>
2025-08-14 13:06:51 +02:00
Robin Appelman b3c53c7436 feat: allow object store configuration aliases for easier migrations
Signed-off-by: Robin Appelman <robin@icewind.nl>
2025-08-14 13:06:51 +02:00
Robin Appelman 2d4bba7b0c feat: add command to get user objectstore config mappings
Signed-off-by: Robin Appelman <robin@icewind.nl>
2025-08-14 13:06:51 +02:00
Robin Appelman 385dd36ff8 feat: allow configuring multiple objectstore configurations
Signed-off-by: Robin Appelman <robin@icewind.nl>
2025-08-14 13:06:51 +02:00
Maxence Lange cf8203a5f6 fix(lexicon): fix definition for share password by default
Signed-off-by: Maxence Lange <maxence@artificial-owl.com>
2025-08-14 09:52:28 -01:00
Anna Larch 6a3a244807 feat(activity): add bulk activity option
Signed-off-by: Anna Larch <anna@nextcloud.com>
2025-08-14 11:31:47 +02:00
Carl Schwan 90cc0454a7 perf(calendar): Disable custom properties for individual calendar events
Save a query per event stored in the calendar and at least on the
production instance, there is no entries in the table for the events.

Signed-off-by: Carl Schwan <carl.schwan@nextclound.com>
2025-08-14 11:27:44 +02:00
Benjamin Gaussorgues 055b5ddb9e Merge pull request #54393 from nextcloud/automated/noid/master-update-ca-cert-bundle 2025-08-14 10:32:04 +02:00
Ferdinand Thiessen 0305004709 fix: add more transparency to the header icon gradient
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2025-08-14 10:06:46 +02:00
nextcloud-command 248adf535c fix(security): Update CA certificate bundle
Signed-off-by: GitHub <noreply@github.com>
2025-08-14 02:43:58 +00:00
Nextcloud bot 1b63b565d4 fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-08-14 00:12:21 +00:00
Simon L. ec3be25955 Merge pull request #54413 from nextcloud/perf/render-sharing-tab-sections-only-once
perf: render sharing tab sections only once
2025-08-13 22:59:32 +02:00
Daniel 9dd661f3d8 Merge pull request #53756 from nextcloud/feat/settings/app_api/daemon-selection
feat(settings): Deploy daemon selection support during ExApp installation
2025-08-13 22:57:13 +02:00
Andrey Borysenko 71ef47e70b feat(settings): Deploy daemon selection support during ExApp installation
Signed-off-by: Andrey Borysenko <andrey18106x@gmail.com>
2025-08-13 21:19:38 +03:00
Richard Steinmetz 798f3734be perf: render sharing tab sections only once
Signed-off-by: Richard Steinmetz <richard@steinmetz.cloud>
2025-08-13 20:10:47 +02:00
Richard Steinmetz 7c0ffc0759 perf: delete commonly used custom properties instead of setting the default value
Signed-off-by: Richard Steinmetz <richard@steinmetz.cloud>
2025-08-13 20:07:43 +02:00
Salvatore Martire ec176a933a feat(dav): report inefficient DAV plugins in logs
Signed-off-by: Salvatore Martire <4652631+salmart-dev@users.noreply.github.com>
2025-08-13 19:46:18 +02:00
Robin Appelman 06154dd116 fix: don't use view->getPath in ownership transfer
Signed-off-by: Robin Appelman <robin@icewind.nl>
2025-08-13 19:42:42 +02:00
Robin Appelman 34d5a103b4 test: adjust tests to getPath changes
Signed-off-by: Robin Appelman <robin@icewind.nl>
2025-08-13 19:42:42 +02:00
Robin Appelman 5987584b90 perf: use more optimized node-by-id logic in View::getPath
Signed-off-by: Robin Appelman <robin@icewind.nl>
2025-08-13 19:42:42 +02:00
Robin Appelman 46b5ff7467 perf: ignore any customer property in the nc/oc namespace that isn't explicitly allowed
Signed-off-by: Robin Appelman <robin@icewind.nl>
2025-08-13 19:32:45 +02:00
Nextcloud bot d658b9b558 fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-08-13 16:59:00 +00:00
Andy Scherzinger 7d45e9b2a3 Merge pull request #54401 from nextcloud/fix/streamer-directory-mtime
fix(ZipFolderPlugin): set mtime of directories in archive
2025-08-13 17:58:45 +02:00
Ferdinand Thiessen 32703d0500 fix(ZipFolderPlugin): set mtime of directories in archive
Directories should also have the correct mtime set and not the current
time. For this the `Streamer` class needs to support passing a time
attribute for creating folders, the underlying library already supports
this.

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2025-08-13 10:23:47 +02:00
Nextcloud bot 2979a2bab0 fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-08-13 00:12:03 +00:00
Ferdinand Thiessen ec986d479b Merge pull request #54380 from nextcloud/feat/3rdparty-polyfills
chore: update 3rdparty to include PHP polyfills
2025-08-12 22:55:58 +02:00
Ferdinand Thiessen 9e3269d3c4 chore: update 3rdparty to include PHP polyfills
| Production Changes        | From    | To      | Compare                                                                    |
|---------------------------|---------|---------|----------------------------------------------------------------------------|
| symfony/polyfill-ctype    | v1.32.0 | REMOVED |                                                                            |
| symfony/polyfill-mbstring | v1.32.0 | REMOVED |                                                                            |
| symfony/polyfill-php80    | v1.32.0 | REMOVED |                                                                            |
| symfony/polyfill-php83    | v1.31.0 | v1.32.0 | [...](https://github.com/symfony/polyfill-php83/compare/v1.31.0...v1.32.0) |
| symfony/polyfill-php82    | NEW     | v1.32.0 |                                                                            |
| symfony/polyfill-php84    | NEW     | v1.32.0 |                                                                            |

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2025-08-12 18:31:11 +02:00
Maxence Lange cb84ccc57d feat(preset): share password protection
Signed-off-by: Maxence Lange <maxence@artificial-owl.com>
2025-08-12 14:37:32 -01:00
John Molakvoæ aaca29bdad Merge pull request #54202 from nextcloud/fix/less-words 2025-08-12 17:16:18 +02:00
nextcloud-command 98d37090f4 chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2025-08-12 13:59:29 +00:00
rakekniven 17b4deb800 fix: Update tests
Signed-off-by: rakekniven <2069590+rakekniven@users.noreply.github.com>
2025-08-12 15:48:22 +02:00
kramo c5d1f2f9ff fix(l10n): Less jargon in source strings
This removes:
- “enable” for settings
- “settings” for settings sections
- “all” when there is no obvious subset of items
- “show” and “open” for navigation actions
- “changes” for applying/discarding
- “to clipboard” when copying
- Explaining things that cannot happen
- Explaining things twice, right below each other
- Unnecessary technical jargon
- Text that sounds like marketing copy and serves no other purpose

Signed-off-by: kramo <git@kramo.page>
2025-08-12 14:37:34 +02:00
Richard Steinmetz b4302fe57c Merge pull request #54371 from nextcloud/fix-getting-trusted-server-other-than-the-first
fix: Fix getting trusted server other than the first
2025-08-12 13:25:55 +02:00
John Molakvoæ cf0b709ddf Merge pull request #54376 from nextcloud/ci/fix-desktop-team 2025-08-12 11:28:16 +02:00
Ferdinand Thiessen a86662705e ci: adjust update desktop version workflow to assign correct team
2 of 3 persons that were assigned are not working in that area anymore.
So just assign the team.
Moreover the date was wrong as the file was created 2025.

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2025-08-12 10:53:22 +02:00
Daniel Calviño Sánchez b42d125950 fix: Fix getting trusted server other than the first
"array_filter" preserves the keys, so after the trusted servers were
filtered "$server[0]" existed only if the server to get was the first
one in the original array.

Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
2025-08-12 06:47:51 +02:00
Nextcloud bot 2427b864ec fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-08-12 00:12:12 +00:00
Josh de39c5122c chore: drop unused ocs/providers.php
This appears to have been originally intended to support the providers list functionality mentioned in the OCS <=v1.7 spec. That is, appears to be an implementation of what was suggested be made available at `https://domain.tld/ocs/providers.xml`.

However best as I can tell it never worked. It also doesn't appear in the v2.0 spec drafts.


Signed-off-by: Josh <josh.t.richards@gmail.com>
2025-08-11 18:20:19 -04:00
John Molakvoæ 43f6f9b25c Merge pull request #54353 from nextcloud/automated/noid/-update-min-supported-desktop-version 2025-08-11 21:54:30 +02:00
John Molakvoæ 6309931feb Merge pull request #54367 from nextcloud/patch-1 2025-08-11 21:11:02 +02:00
F. E Noel Nfebe 49e35f111c Merge pull request #54310 from nextcloud/fix/conditional-federation-placeholders
fix(files_sharing): Implementation conditional federation placeholder
2025-08-11 19:21:43 +01:00
nextcloud-command e54f990757 chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2025-08-11 18:16:07 +00:00
Atemu d69e8923fe fix(core): search f keycode
Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
2025-08-11 18:14:05 +00:00
Andy Scherzinger 65f8de6d94 Merge pull request #54354 from nextcloud/fix/codeowners
chore(CODEOWNERS): ensure every file change has at least 2 reviewers
2025-08-11 20:03:30 +02:00
nextcloud-command dd882127ab chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2025-08-11 18:00:00 +00:00
nfebe e7f4de6296 fix(files_sharing): Implementation conditional federation placeholder
This commits addresses an annoyance where the share input placeholder would
suggest sharing via federated cloud ID even if federation was disabled.

Signed-off-by: nfebe <fenn25.fn@gmail.com>
2025-08-11 18:54:56 +01:00
Ferdinand Thiessen 2e7ac880bb Merge pull request #54360 from nextcloud/fix/display-shares-as-internal
fix(files_sharing): show shares to remote as internal if configured so
2025-08-11 18:05:38 +02:00
nextcloud-command 6001eba392 chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2025-08-11 15:20:32 +00:00
Ferdinand Thiessen 5cbfe61b18 fix(files_sharing): show shares to remote as internal if configured so
- if this is a GS instance
- or was configured by administrator

Then show the share as internal (remote and remote group shares).

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2025-08-11 15:18:29 +00:00
Ferdinand Thiessen c60cc977b4 Merge pull request #54345 from nextcloud/dependabot/npm_and_yarn/nextcloud/initial-state-3.0.0
chore(deps): bump @nextcloud/initial-state from 2.2.0 to 3.0.0
2025-08-11 17:15:31 +02:00
nextcloud-command 38f0ca875a chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2025-08-11 17:01:32 +02:00
Ferdinand Thiessen 9ad5214fce test(files): adjust mocking initial state for updated library
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2025-08-11 17:01:32 +02:00
dependabot[bot] 4d1051891e chore(deps): bump @nextcloud/initial-state from 2.2.0 to 3.0.0
Bumps [@nextcloud/initial-state](https://github.com/nextcloud-libraries/nextcloud-initial-state) from 2.2.0 to 3.0.0.
- [Release notes](https://github.com/nextcloud-libraries/nextcloud-initial-state/releases)
- [Changelog](https://github.com/nextcloud-libraries/nextcloud-initial-state/blob/main/CHANGELOG.md)
- [Commits](https://github.com/nextcloud-libraries/nextcloud-initial-state/compare/v2.2.0...v3.0.0)

---
updated-dependencies:
- dependency-name: "@nextcloud/initial-state"
  dependency-version: 3.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-11 16:13:04 +02:00
Robin Appelman 615c6916e1 Merge pull request #54200 from nextcloud/test-disable-autoload-apps
test: add env option to disable loading all installed apps during tests
2025-08-11 15:43:49 +02:00
John Molakvoæ 7fb59a4493 Merge pull request #54356 from nextcloud/fix/ai-apps 2025-08-11 15:35:50 +02:00
Robin Appelman 106ac1b123 Merge pull request #54357 from nextcloud/displayname-cache-duration
fix: increase how long we cache display names
2025-08-11 14:21:31 +02:00
nextcloud-command 75ecce0b4c chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2025-08-11 11:50:52 +00:00
skjnldsv 23c5856b39 fix(settings): add missing AI icon
Signed-off-by: skjnldsv <skjnldsv@protonmail.com>
2025-08-11 11:46:12 +00:00
Robin Appelman 06be851b1f test: add env option to disable loading all installed apps during tests
Signed-off-by: Robin Appelman <robin@icewind.nl>
2025-08-11 13:35:57 +02:00
dependabot[bot] d9bd75bae9 Merge pull request #54343 from nextcloud/dependabot/npm_and_yarn/zip.js/zip.js-2.7.72 2025-08-11 11:14:15 +00:00
dependabot[bot] f0085789e6 Merge pull request #54342 from nextcloud/dependabot/npm_and_yarn/core-js-3.45.0 2025-08-11 10:55:14 +00:00
nextcloud-command 05c90f5c46 chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2025-08-11 10:39:16 +00:00
dependabot[bot] 9603805779 Merge pull request #54344 from nextcloud/dependabot/npm_and_yarn/stylelint-16.23.1 2025-08-11 10:37:41 +00:00
dependabot[bot] 0e3d986bca chore(deps): bump core-js from 3.44.0 to 3.45.0
Bumps [core-js](https://github.com/zloirock/core-js/tree/HEAD/packages/core-js) from 3.44.0 to 3.45.0.
- [Release notes](https://github.com/zloirock/core-js/releases)
- [Changelog](https://github.com/zloirock/core-js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/zloirock/core-js/commits/v3.45.0/packages/core-js)

---
updated-dependencies:
- dependency-name: core-js
  dependency-version: 3.45.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-11 12:30:47 +02:00
dependabot[bot] 5f1f95e2a6 chore(deps-dev): bump stylelint from 16.23.0 to 16.23.1
Bumps [stylelint](https://github.com/stylelint/stylelint) from 16.23.0 to 16.23.1.
- [Release notes](https://github.com/stylelint/stylelint/releases)
- [Changelog](https://github.com/stylelint/stylelint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/stylelint/stylelint/compare/16.23.0...16.23.1)

---
updated-dependencies:
- dependency-name: stylelint
  dependency-version: 16.23.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-11 10:24:35 +00:00
Ferdinand Thiessen 52665f5b62 Merge pull request #54294 from nextcloud/feat/show-gs-users-like-internal
feat(files_sharing): show GS users similar to internal users
2025-08-11 12:21:06 +02:00
dependabot[bot] 9330bc3200 Merge pull request #54346 from nextcloud/dependabot/npm_and_yarn/sass-1.90.0 2025-08-11 10:20:43 +00:00
dependabot[bot] 73d7dde5e2 Merge pull request #54341 from nextcloud/dependabot/npm_and_yarn/puppeteer-24.16.0 2025-08-11 09:46:39 +00:00
Robin Appelman 21c7ae62e0 Merge pull request #53528 from nextcloud/jtr-maint-refresh-part-1
fix(maintenance): refresh page when turned off (sometimes)
2025-08-11 11:44:35 +02:00
dependabot[bot] f3824f7d2c chore(deps-dev): bump sass from 1.89.2 to 1.90.0
Bumps [sass](https://github.com/sass/dart-sass) from 1.89.2 to 1.90.0.
- [Release notes](https://github.com/sass/dart-sass/releases)
- [Changelog](https://github.com/sass/dart-sass/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sass/dart-sass/compare/1.89.2...1.90.0)

---
updated-dependencies:
- dependency-name: sass
  dependency-version: 1.90.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-11 11:42:55 +02:00
Ferdinand Thiessen f5c90b2f3d Merge pull request #54340 from nextcloud/fix/ai-settings
feat(settings): Improve AI settings task list and rename to Assistant
2025-08-11 11:33:34 +02:00
nextcloud-command a25af74028 chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2025-08-11 09:20:10 +00:00
Robin Appelman 8350fea770 fix: increase how long we cache display names
Signed-off-by: Robin Appelman <robin@icewind.nl>
2025-08-11 11:20:05 +02:00
Ferdinand Thiessen ae5d57371a feat(files_sharing): show GS users similar to internal users
- resolves: https://github.com/nextcloud/server/issues/52497

Ensure that when global scale is enabled the federated users are shown
just like internal users.
Meaning no server part but instead use the email address like with internal.

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2025-08-11 09:18:16 +00:00
Ferdinand Thiessen 109cd123fe Merge pull request #54347 from nextcloud/dependabot/npm_and_yarn/cypress/webpack-preprocessor-7.0.0
chore(deps-dev): bump @cypress/webpack-preprocessor from 6.0.4 to 7.0.0
2025-08-11 10:45:09 +02:00
John Molakvoæ a5139f93eb Merge pull request #54337 from nextcloud/fix/legacyView 2025-08-11 10:22:17 +02:00
Ferdinand Thiessen 7165e54b4e chore(CODEOWNERS): ensure every file change has at least 2 reviewers
For proper review every PR needs 2 reviews, so every file needs at least
2 codeowners. Thus @blizzz is added for `workflowengine` as the app
maintainer.

Additionally added the desktop client team for changes related to them.

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2025-08-11 10:09:16 +02:00
Misha M.-Kupriyanov 55f55984f0 test(SearchComposerTest): add unit test for getProviders with mixed order values
Signed-off-by: Misha M.-Kupriyanov <kupriyanov@strato.de>
2025-08-11 09:42:56 +02:00
Misha M.-Kupriyanov d0341bb323 test(SearchComposerTest): add unit tests for getProviders with allowed providers restriction and empty configuration
Signed-off-by: Misha M.-Kupriyanov <kupriyanov@strato.de>
2025-08-11 09:42:56 +02:00
Misha M.-Kupriyanov 9dfc0b1b23 test(SearchComposerTest): add test for provider icon generation
Signed-off-by: Misha M.-Kupriyanov <kupriyanov@strato.de>
2025-08-11 09:42:56 +02:00
Misha M.-Kupriyanov 32d263e363 test(SearchComposerTest): add unit test for getProviders with multiple providers
Signed-off-by: Misha M.-Kupriyanov <kupriyanov@strato.de>
2025-08-11 09:42:56 +02:00
Misha M.-Kupriyanov d08fe6d6b3 test(SearchComposerTest): add test for handling unknown search provider
Signed-off-by: Misha M.-Kupriyanov <kupriyanov@strato.de>
2025-08-11 09:42:56 +02:00
Misha M.-Kupriyanov 75770a00dd test(SearchComposerTest): add unit tests for SearchComposer
Signed-off-by: Misha M.-Kupriyanov <kupriyanov@strato.de>
2025-08-11 09:42:56 +02:00
Misha M.-Kupriyanov 69c51bb163 fix(search): Fix SearchComposer.php filtering logic
keep the $this->providers types

Test via ./occ config:app:set --value '["files","settings"]' --type array core unified_search.providers_allowed

should be part of 8e570041

Signed-off-by: Misha M.-Kupriyanov <kupriyanov@strato.de>
2025-08-11 09:42:56 +02:00
nextcloud-command f5a5b005b6 chore: Update minimum supported desktop version
Signed-off-by: GitHub <noreply@github.com>
2025-08-11 00:34:04 +00:00
Nextcloud bot de4a35b202 fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-08-11 00:12:44 +00:00
nextcloud-command 9ce1066bc8 chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2025-08-10 17:47:53 +00:00
skjnldsv c1003692f0 fix(files): drop usage of FileInfoModel
Signed-off-by: skjnldsv <skjnldsv@protonmail.com>
2025-08-10 17:43:55 +00:00
Nextcloud bot b60a8ddc24 fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-08-10 00:12:02 +00:00
Joas Schilling 44b4741384 Merge pull request #54339 from nextcloud/techdebt/noid/reduce-deprecation-spam-2
fix(container): Reduce general deprecation spam on all requests
2025-08-09 12:11:47 +02:00
Joas Schilling 57f09b642e fix(container): Reduce general deprecation spam on all requests
Signed-off-by: Joas Schilling <coding@schilljs.com>
2025-08-09 11:53:30 +02:00
dependabot[bot] 50ee26424d chore(deps-dev): bump @cypress/webpack-preprocessor from 6.0.4 to 7.0.0
Bumps [@cypress/webpack-preprocessor](https://github.com/cypress-io/cypress) from 6.0.4 to 7.0.0.
- [Release notes](https://github.com/cypress-io/cypress/releases)
- [Changelog](https://github.com/cypress-io/cypress/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/cypress-io/cypress/compare/@cypress/webpack-preprocessor-v6.0.4...@cypress/webpack-preprocessor-v7.0.0)

---
updated-dependencies:
- dependency-name: "@cypress/webpack-preprocessor"
  dependency-version: 7.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-09 02:00:39 +00:00
dependabot[bot] 1f64ed9104 chore(deps-dev): bump @zip.js/zip.js from 2.7.71 to 2.7.72
Bumps [@zip.js/zip.js](https://github.com/gildas-lormeau/zip.js) from 2.7.71 to 2.7.72.
- [Release notes](https://github.com/gildas-lormeau/zip.js/releases)
- [Commits](https://github.com/gildas-lormeau/zip.js/compare/v2.7.71...v2.7.72)

---
updated-dependencies:
- dependency-name: "@zip.js/zip.js"
  dependency-version: 2.7.72
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-09 01:58:06 +00:00
dependabot[bot] 51d72dc4e3 chore(deps-dev): bump puppeteer from 24.15.0 to 24.16.0
Bumps [puppeteer](https://github.com/puppeteer/puppeteer) from 24.15.0 to 24.16.0.
- [Release notes](https://github.com/puppeteer/puppeteer/releases)
- [Changelog](https://github.com/puppeteer/puppeteer/blob/main/CHANGELOG.md)
- [Commits](https://github.com/puppeteer/puppeteer/compare/puppeteer-v24.15.0...puppeteer-v24.16.0)

---
updated-dependencies:
- dependency-name: puppeteer
  dependency-version: 24.16.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-09 01:57:10 +00:00
Nextcloud bot b8c6eb82ca fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-08-09 00:12:21 +00:00
nextcloud-command cb7669d72a chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2025-08-08 18:31:48 +00:00
Lukas Schaefer 375369cf68 feat(settings): More condensed task list
Signed-off-by: Lukas Schaefer <lukas@lschaefer.xyz>
2025-08-08 14:25:46 -04:00
Lukas Schaefer 1ae4764073 chore(settings): Rename ai settings to assistant
Signed-off-by: Lukas Schaefer <lukas@lschaefer.xyz>
2025-08-08 13:54:08 -04:00
Maxence Lange eaae5e16d8 Merge pull request #54158 from nextcloud/feat/preset/profile-visibility+presetmanager
feat(preset): profile visibility
2025-08-08 11:21:16 -01:00
Maxence Lange bbc9ed138c fix(config): fix tests+psalm
Signed-off-by: Maxence Lange <maxence@artificial-owl.com>
2025-08-08 11:00:49 -01:00
Maxence Lange a4b9edc8eb feat(preset): Profile Visibility
Signed-off-by: Maxence Lange <maxence@artificial-owl.com>
2025-08-08 10:58:59 -01:00
Maxence Lange f32d865716 fix(lexicon): only filter out null default
Signed-off-by: Maxence Lange <maxence@artificial-owl.com>
2025-08-08 10:58:58 -01:00
Maxence Lange 33c4fe504d feat(preset): new PresetManager
Signed-off-by: Maxence Lange <maxence@artificial-owl.com>
2025-08-08 10:58:58 -01:00
Benjamin Gaussorgues 8783679a49 Merge pull request #54303 from nextcloud/jtr-oc-appframework-app-cleanup 2025-08-08 10:41:51 +02:00
John Molakvoæ 7328754eb4 Merge pull request #54312 from nextcloud/fix/smarter-loadmore-unified-search 2025-08-08 10:38:15 +02:00
John Molakvoæ 93296c2679 Merge pull request #54069 from nextcloud/feature/51791/add-bsky-option-to-accounts 2025-08-08 10:32:03 +02:00
nextcloud-command 16f80a8d47 chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2025-08-08 08:09:23 +00:00
nextcloud-command e53ad663f1 chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2025-08-08 08:08:47 +00:00
nfebe 7fbf997420 fix(unified-search): Smarter load more button
This commit introduces a change to prevent showing the load more button,
if the length of existing results is not equal to the requested limit (which implies
it is less than because we never expect it to be more)

Additionally, there is an enhancment to override provider filders passed to the find method.
This would improve speed.

Signed-off-by: nfebe <fenn25.fn@gmail.com>
2025-08-08 08:07:26 +00:00
yemkareems 1b4e967604 fix: fontawesome license text added and vue file linting errors fixed
Signed-off-by: yemkareems <yemkareems@gmail.com>
2025-08-08 08:06:46 +00:00
yemkareems 4c887ec12b fix: eslint errors fixed in vue file, IAccountManager since added, bluesky svg added to REUSE.toml file
Signed-off-by: yemkareems <yemkareems@gmail.com>
2025-08-08 08:06:46 +00:00
Ferdinand Thiessen d31be348af chore: add deprecated IAccountManager::PROPERTY_TWITTER to psalm baseline
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2025-08-08 08:06:45 +00:00
Ferdinand Thiessen d36602acd5 chore: update openapi and autoloaders
Co-authored-by: Ferdinand Thiessen <opensource@fthiessen.de>
Signed-off-by: yemkareems <yemkareems@gmail.com>
2025-08-08 08:06:45 +00:00
yemkareems aa227f1c55 feat: add bluesky to accounts, show the same in profile edit and visibility option, in view profile and also in sharing tab
Signed-off-by: yemkareems <yemkareems@gmail.com>
2025-08-08 08:06:45 +00:00
John Molakvoæ 3dac5b33ee Merge pull request #54282 from nextcloud/techdebt/noid/reduce-deprecation-spam 2025-08-08 10:04:22 +02:00
Joas Schilling 5eeda5fcad Merge pull request #54228 from nextcloud/pulsejet/db-check-maria
feat(db): add method to test mariadb
2025-08-08 08:43:22 +02:00
Joas Schilling 2f18996347 fix(container): Don't use deprecated things to set up controllers for apps
Signed-off-by: Joas Schilling <coding@schilljs.com>
2025-08-08 08:42:56 +02:00
Joas Schilling 17c40b9474 fix(container): Log the deprecation to the app when possible
Signed-off-by: Joas Schilling <coding@schilljs.com>
2025-08-08 08:42:55 +02:00
John Molakvoæ 117d8dea0a Merge pull request #54328 from nextcloud/jtr-refactor-files-external-oauth1
refactor(files_external): Drop unused oauth1 code
2025-08-08 08:22:14 +02:00
Varun Patil 5ecd3c4b49 feat(database): Add option to test for mariadb
There are some behavioral differences that apps may need to check for.
See discussion on #51175 for more info.

This preserves the existing behavior of getDatabaseProvider()

Signed-off-by: Varun Patil <varunpatil@ucla.edu>
2025-08-08 07:50:51 +02:00
Josh 9c4aaeff75 chore(files_external): update autoload_static
Signed-off-by: Josh <josh.t.richards@gmail.com>
2025-08-07 22:41:18 -04:00
Josh 1121ae0838 chore(files_external): update autoload_classmap
Signed-off-by: Josh <josh.t.richards@gmail.com>
2025-08-07 22:40:35 -04:00
Josh af53e9ca06 chore(files_external): Remove oauth1 constant
Signed-off-by: Josh <josh.t.richards@gmail.com>
2025-08-07 22:25:51 -04:00
Josh 2b21913015 refactor(files_external): Drop oauth1 auth mechanism registration
Signed-off-by: Josh <josh.t.richards@gmail.com>
2025-08-07 22:23:25 -04:00
Josh e37bc0b580 refactor(files_external): Remove oauth1 route
Signed-off-by: Josh <josh.t.richards@gmail.com>
2025-08-07 22:21:18 -04:00
Josh 57c62e1ca1 refactor(files_external): Drop OAuth1.php
Signed-off-by: Josh <josh.t.richards@gmail.com>
2025-08-07 21:54:33 -04:00
Josh e87bfbe278 refactor(files_external): Drop unused oauth1.js
Signed-off-by: Josh <josh.t.richards@gmail.com>
2025-08-07 21:52:46 -04:00
Nextcloud bot 1518ded8b1 fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-08-08 00:12:32 +00:00
Josh 2a96042db1 Merge pull request #54260 from nextcloud/fix/less-useless-toasts
fix: Remove redundant toast notifications
2025-08-07 17:08:07 -04:00
nextcloud-command da3a4c6a12 chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2025-08-07 20:35:56 +00:00
kramo 718cf4e15d fix: Remove redundant toast notifications
Since these actions already have highly visible visual confirmation,
no extra notifications are needed.

Split out from #54202

Signed-off-by: kramo <git@kramo.page>
2025-08-07 20:33:55 +00:00
Julien Veyssier aa2ca86fb3 feat(taskprocessing): avoid generator cascade
Signed-off-by: Julien Veyssier <julien-nc@posteo.net>
2025-08-07 15:12:22 +02:00
Julien Veyssier e2c65b2493 feat(taskprocessing): fix mistake
Signed-off-by: Julien Veyssier <julien-nc@posteo.net>
2025-08-07 15:12:22 +02:00
Julien Veyssier 222b19b805 feat(taskprocessing): rename cleanup column to allow_cleanup
Signed-off-by: Julien Veyssier <julien-nc@posteo.net>
2025-08-07 15:12:22 +02:00
Julien Veyssier 2b9af820bd feat(taskprocessing): add strict types to all taskprocessing command classes
Signed-off-by: Julien Veyssier <julien-nc@posteo.net>
2025-08-07 15:12:22 +02:00
Julien Veyssier 0dc93bc320 feat(taskprocessing): fix phpunit tests
Signed-off-by: Julien Veyssier <julien-nc@posteo.net>
2025-08-07 15:12:22 +02:00
Julien Veyssier 10921c05b7 feat(taskprocessing): fix some CI
Signed-off-by: Julien Veyssier <julien-nc@posteo.net>
2025-08-07 15:12:21 +02:00
Julien Veyssier cc295f2452 feat(taskprocessing): use Generator::getReturn to get the list of deleted tasks in the cleanup command
Signed-off-by: Julien Veyssier <julien-nc@posteo.net>
2025-08-07 15:12:21 +02:00
Julien Veyssier 19801f7ec4 feat(taskprocessing): move cleanup method to private taskprocessing manager, use it in the cleanup bg job and implement a cleanup command
Signed-off-by: Julien Veyssier <julien-nc@posteo.net>
2025-08-07 15:12:21 +02:00
Julien Veyssier e6adbd921e feat(taskprocessing): generate OpenAPI specs, fix lint issue, fix tests
Signed-off-by: Julien Veyssier <julien-nc@posteo.net>
2025-08-07 15:12:21 +02:00
Julien Veyssier 8c52b6c0fe feat(taskprocessing): add cleanup flag to tasks to decide if they should be cleaned up automatically
Signed-off-by: Julien Veyssier <julien-nc@posteo.net>
2025-08-07 15:12:21 +02:00
Robin Appelman 5660a73a3d Merge pull request #54238 from nextcloud/extra_sensitive_values
feat: allow admins to configure additional sensitive config values
2025-08-07 15:08:56 +02:00
Carl Schwan 43aa92f6c3 Merge pull request #54275 from nextcloud/fix-order-metadata-deletion
fix(trash): Fix order of metadata deletion
2025-08-07 15:04:49 +02:00
John Molakvoæ 2e0baa5801 Merge pull request #54279 from nextcloud/fix/tags-icon 2025-08-07 11:57:20 +02:00
John Molakvoæ a488bff27a Merge pull request #54309 from nextcloud/fix/dav-wrong-return-type 2025-08-07 11:35:45 +02:00
Carl Schwan 9fef97c9ec fix(trash): Fix order of metadata deletion
First query metadata for live photo and then delete the metadata, by
making the SyncLivePhotosListener priority higher than the default.

Partially fix #54274

Signed-off-by: Carl Schwan <carl.schwan@nextclound.com>
2025-08-07 11:19:30 +02:00
nextcloud-command f16c449275 chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2025-08-07 07:47:39 +00:00
skjnldsv 7139cac9d8 fix(systemtags): outline icon css selector
Signed-off-by: skjnldsv <skjnldsv@protonmail.com>
2025-08-07 07:45:40 +00:00
Nextcloud bot f0c392e21c fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-08-07 00:12:14 +00:00
Ferdinand Thiessen 378ddda301 fix(files_versions): has-preview must be either true or false
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2025-08-07 01:24:07 +02:00
Ferdinand Thiessen 6b0bce8a2d fix(files_trashbin): has-preview must return true or false
Currently it returned the boolean value, but PHP will turn it into an
integer...

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2025-08-07 01:23:10 +02:00
dependabot[bot] 58ae326f83 Merge pull request #54299 from nextcloud/dependabot/npm_and_yarn/tmp-0.2.4 2025-08-06 23:00:14 +00:00
dependabot[bot] 7f3fcdf495 chore(deps): bump tmp from 0.2.3 to 0.2.4
Bumps [tmp](https://github.com/raszi/node-tmp) from 0.2.3 to 0.2.4.
- [Changelog](https://github.com/raszi/node-tmp/blob/master/CHANGELOG.md)
- [Commits](https://github.com/raszi/node-tmp/compare/v0.2.3...v0.2.4)

---
updated-dependencies:
- dependency-name: tmp
  dependency-version: 0.2.4
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-07 00:46:45 +02:00
Ferdinand Thiessen 09b0698255 Merge pull request #54298 from nextcloud/fix/dav/addressbook-permissions-principal
fix(dav): calculate permissions based on addressbook principal
2025-08-06 23:36:39 +02:00
Josh 67c14b0f11 refactor(App): \OC\AppFramework\App clean-up
Signed-off-by: Josh <josh.t.richards@gmail.com>
2025-08-06 17:10:45 -04:00
Matthieu Gallien e7a28e787d Merge pull request #54285 from nextcloud/automated/noid/master-update-code-signing-crl
[master] fix(security): Update code signing revocation list
2025-08-06 21:45:22 +02:00
Daniel Calviño Sánchez 3ba7888537 Merge pull request #53950 from nextcloud/fix-button-alignment-for-email-templates-in-outlook
fix: Fix button alignment for email templates in Outlook
2025-08-06 19:43:11 +02:00
Christoph Wurst 0c4bb5e8c5 fix(dav): calculate permissions based on addressbook principal
Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
2025-08-06 18:15:43 +02:00
nextcloud-command 67610f387c fix(security): Update code signing revocation list
Signed-off-by: GitHub <noreply@github.com>
2025-08-06 14:57:14 +00:00
Daniel Calviño Sánchez 8b7aad82c7 fix: Fix button alignment for email templates in Outlook
The button group generated in email templates is expected to show the
two buttons side by side in a single row, but in Outlook both buttons
took the full width of the wrapper row and each button was shown in
its own row. To solve that the buttons are wrapped in an additional
table that shows each button in its own cell, limiting their width and
showing them in a single row; this is done conditionally and only
applied in Outlook, so it should not affect other clients.

Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
2025-08-06 15:18:01 +02:00
Hamza 6e9d48b558 Merge pull request #50092 from nextcloud/feat/sync-truncation
feat(cardav): support result truncation for addressbook federation
2025-08-06 14:57:13 +02:00
Daniel Kesselberg 13f25c9316 fix(carddav): return correct sync token for non-truncated requests
Signed-off-by: Daniel Kesselberg <mail@danielkesselberg.de>
2025-08-06 14:32:25 +02:00
Daniel Kesselberg b54c5392b6 feat(carddav): handle truncated non-initial requests
Signed-off-by: Daniel Kesselberg <mail@danielkesselberg.de>
2025-08-06 14:04:55 +02:00
Hamza Mahjoubi 36d9fcbb4d feat(cardav): support result truncation for addressbook federation
Signed-off-by: Hamza Mahjoubi <hamzamahjoubi221@gmail.com>
2025-08-06 14:04:55 +02:00
John Molakvoæ b514d75323 Merge pull request #51855 from Phreeman33/master
fix: display chinese character avatar
2025-08-06 13:43:23 +02:00
Ferdinand Thiessen c67bdf02f0 chore: add missing license ref for new fonts
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2025-08-06 13:12:57 +02:00
Ferdinand Thiessen 7e76c91677 fix: resolve psalm errors
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2025-08-06 12:52:50 +02:00
John Molakvoæ 535bf3af9f Merge pull request #54049 from nextcloud/fix/only-show-reshare-if-there-is 2025-08-06 12:40:28 +02:00
Ferdinand Thiessen 6cae7d1f81 Merge pull request #54183 from nextcloud/refactor/unified-search-
refactor(UnifiedSearch): use `NcHeaderButton` for proper styles
2025-08-06 12:24:43 +02:00
Phreeman33 655ef1031b fix: display chinese character avatar
Signed-off-by: Phreeman33 <12772373+Phreeman33@users.noreply.github.com>
2025-08-06 12:24:09 +02:00
John Molakvoæ 48dc04b571 Merge pull request #54237 from nextcloud/test/fix-cypress 2025-08-06 11:48:31 +02:00
nextcloud-command 3ff3ceae5d chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2025-08-06 09:43:08 +00:00
Ferdinand Thiessen 6adbe44976 refactor(UnifiedSearch): use NcHeaderButton for proper styles
Allowing to have the new styles from
https://github.com/nextcloud/server/pull/54178 and
https://github.com/nextcloud-libraries/nextcloud-vue/pull/7242

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2025-08-06 09:41:09 +00:00
nextcloud-command 96011a1579 chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2025-08-06 09:40:06 +00:00
Ferdinand Thiessen 61c9a6eb22 fix(files_sharing): only show "shared with me" information if shared
The variable is intialized to `{}` which is truthy, so instead just
check if there is a user assigned to the share info.

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2025-08-06 09:37:58 +00:00
Ferdinand Thiessen 7c69862a5d Merge pull request #54186 from nextcloud/chore/refactor-header-scss
refactor: de-duplicate header styles, add comments and mark deprecations
2025-08-06 11:35:28 +02:00
Ferdinand Thiessen 761093daf8 Merge pull request #54264 from nextcloud/smb-timeout-unavailable
fix: handle smb timeouts as storage not available
2025-08-06 11:27:02 +02:00
Ferdinand Thiessen 1fdda4b202 Merge pull request #54043 from nextcloud/fix/mysql-removed-auth
fix(db): use `caching_sha2_password` for MySQL
2025-08-06 11:26:30 +02:00
Joas Schilling 84323ff6dd Merge pull request #54241 from nextcloud/feat/noid/add-busy-status-capability
feat(userstatus): add busy capability
2025-08-06 10:42:25 +02:00
Richard Steinmetz ee6596782f Merge pull request #54232 from nextcloud/feat/notifications/preload-many
feat(notifications): provide method to preload many notifications at once
2025-08-06 09:57:53 +02:00
Richard Steinmetz ad39dab6ca feat(notifications): provide method to preload many notifications at once
Signed-off-by: Richard Steinmetz <richard@steinmetz.cloud>
2025-08-06 09:22:56 +02:00
nextcloud-command 89577f6f94 chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2025-08-06 05:37:57 +00:00
Ferdinand Thiessen ab49a6d8a9 refactor: de-duplicate header styles, add comments and mark deprecations
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2025-08-06 05:36:05 +00:00
John Molakvoæ 687df7c8ab Merge pull request #54165 from nextcloud/fix/unique-vcategory
fix(core): ensure unique vcategory
2025-08-06 07:13:46 +02:00
Nextcloud bot 1064c676cc fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-08-06 00:12:56 +00:00
Ferdinand Thiessen 88be308b06 test(cypress): split helpers for files actions to make tests less flaky
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2025-08-05 23:49:46 +02:00
skjnldsv 9aac182109 fix(core): ensure unique vcategory
Signed-off-by: skjnldsv <skjnldsv@protonmail.com>
2025-08-05 22:11:21 +02:00
Kate 51e5f7b159 Merge pull request #54125 from nextcloud/s3-multipart-size-check 2025-08-05 20:57:15 +02:00
Ferdinand Thiessen c695bf1a95 Merge pull request #53437 from nextcloud/refactor/files-hotkeys
refactor(files): move hotkey handling to composable
2025-08-05 20:42:43 +02:00
Ferdinand Thiessen b814f3bba6 fix(db): use caching_sha2_password for MySQL
`caching_sha2_password` was added in 8.0.4 as the default
authentication plugin. `mysql_native_password` is deprecated since then.
In MySQL 8.4 it was disabled by default so a user need to manually
reenable it to make it work.
In MySQL 9.0 it is removed and causes the following error:

> SQLSTATE[HY000] [1524] Plugin 'mysql_native_password' is not loaded

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2025-08-05 20:40:38 +02:00
Robin Appelman 97efc95efc fix: better object store write error propagation
Signed-off-by: Robin Appelman <robin@icewind.nl>
2025-08-05 19:17:58 +02:00
Robin Appelman 83b8a390cd fix: always do stream counting for object store upload
Signed-off-by: Robin Appelman <robin@icewind.nl>
2025-08-05 19:17:58 +02:00
Robin Appelman 398b106f0c fix: validate written size for s3 multipart uploads
Signed-off-by: Robin Appelman <robin@icewind.nl>
2025-08-05 19:17:58 +02:00
Robin Appelman 064e1133f5 feat: allow admins to configure additional sensitive config values
Signed-off-by: Robin Appelman <robin@icewind.nl>
2025-08-05 19:16:14 +02:00
nextcloud-command 2cf7e038b3 chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2025-08-05 17:08:50 +00:00
Ferdinand Thiessen 3a5769e8f9 refactor(files): move hotkey handling to composable
This is a composable - not a service, because it is using the
`useHotKey` composable. At this moment it works, but in general
its only safe to put composables into `setup`-context.

This makes it future prove.

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2025-08-05 19:00:32 +02:00
Joas Schilling db8dd9f7f6 Merge pull request #54207 from nextcloud/feat/noid/cache-user-keys
Cache IdentityProof keys
2025-08-05 18:43:56 +02:00
Ferdinand Thiessen ad85d65064 Merge pull request #54077 from nextcloud/jtr-files-detection-refactor-finfo
refactor(IMimeTypeDetector): streamline + minor tidying
2025-08-05 18:10:46 +02:00
Robin Appelman 6f0120af16 fix: handle smb timeouts as storage not available
Signed-off-by: Robin Appelman <robin@icewind.nl>
2025-08-05 17:39:12 +02:00
Marcel Müller 520d8beaf5 feat: Cache user keys
Signed-off-by: Marcel Müller <marcel-mueller@gmx.de>
2025-08-05 16:42:14 +02:00
John Molakvoæ a4795a216c Merge pull request #54233 from nextcloud/fix/ignore-shares-in-encrypt-all 2025-08-05 15:31:35 +02:00
Anna Larch b819d71337 feat(userstatus): add busy capability
Signed-off-by: Anna Larch <anna@nextcloud.com>
2025-08-05 14:52:33 +02:00
Josh 2aa6894c38 Merge pull request #54239 from nextcloud/cleanup-appframework-spreed
refactor(AppFramework): drop unused spreed/Talk fallback
2025-08-05 08:42:31 -04:00
Joas Schilling c9fa1db925 Merge pull request #54230 from nextcloud/bugfix/spreed-15625/log-query-with-immutable-dates
fix(logging): Fix query logging with DateTimeImmutable parameters
2025-08-05 12:29:04 +02:00
Joas Schilling ff3043346a Merge pull request #54196 from nextcloud/enh/noid/taskprocessing-lazy-config
[TaskProcessing] Load and store some config keys lazily
2025-08-05 12:28:34 +02:00
Joas Schilling a3f1b079bb Merge pull request #54203 from nextcloud/oci-string-length-empty
fix: fix oci string length with empty strings
2025-08-05 12:18:41 +02:00
Joas Schilling 1750708509 Merge pull request #54240 from nextcloud/fix/noid/oracle-test-failure
fix(caldav): get UID value
2025-08-05 12:18:07 +02:00
Joas Schilling 12f31b69a0 Merge pull request #54250 from nextcloud/revert/50258
Revert "feat: Implement Directory Check"
2025-08-05 12:11:05 +02:00
nextcloud-command d24f0a3a54 chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2025-08-05 09:25:58 +00:00
Joas Schilling 78dcf3744d Revert "feat: Implement Directory Check"
This reverts commit feae8ba16a.
2025-08-05 11:18:50 +02:00
Nextcloud bot ccda64fc66 fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-08-05 00:12:37 +00:00
John Molakvoæ 662838b542 Merge pull request #54159 from nextcloud/fix/file-request-enforced 2025-08-04 19:18:26 +02:00
Josh 3535149418 refactor(IMimeTypeDetector): streamline finfo usage + misc tidying
Signed-off-by: Josh <josh.t.richards@gmail.com>
2025-08-04 11:24:58 -04:00
Anna Larch b694d2659b fix(caldav): get UID value
Signed-off-by: Anna Larch <anna@nextcloud.com>
2025-08-04 16:43:50 +02:00
Josh 9195987d14 refactor(AppFramework): drop unused spreed/Talk fallback
No longer needed from what I can tell since #20114 & nextcloud/spreed#3134

Signed-off-by: Josh <josh.t.richards@gmail.com>
2025-08-04 08:59:49 -04:00
Côme Chilliet f95fef9938 chore(encryption): Adapt tests to code changes
Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
2025-08-04 11:53:19 +02:00
Côme Chilliet d711d68701 fix(encryption): Ignore shared files in encrypt-all command
Copying and renaming a share will not encrypt it anyway. It will get
 encrypted when the owner’s files get encrypted.

Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
2025-08-04 10:42:04 +02:00
Julien Veyssier be7ef439cf fix(taskprocessing): move LAZY_CONFIG_KEYS constant to the private namespace
Signed-off-by: Julien Veyssier <julien-nc@posteo.net>
2025-08-04 10:11:24 +02:00
Julien Veyssier f22f7311de fix(taskprocessing): fix TaskProcessingTest
Signed-off-by: Julien Veyssier <julien-nc@posteo.net>
2025-08-04 10:11:24 +02:00
Julien Veyssier 282bedcfb0 fix(taskprocessing): remove unused psalm baseline entries
Signed-off-by: Julien Veyssier <julien-nc@posteo.net>
2025-08-04 10:11:24 +02:00
Julien Veyssier 2e3fa51132 feat(taskprocessing): load and store some config keys lazily
Signed-off-by: Julien Veyssier <julien-nc@posteo.net>
2025-08-04 10:11:24 +02:00
Joas Schilling c722f8c88b fix(logging): Fix query logging with DateTimeImmutable parameters
Signed-off-by: Joas Schilling <coding@schilljs.com>
2025-08-04 08:47:55 +02:00
Richard Steinmetz 0bd5128d45 perf: join array_merge calls in MountProvider::getMountsForUser()
Signed-off-by: Richard Steinmetz <richard@steinmetz.cloud>
2025-08-04 08:18:12 +02:00
nextcloud-command 7f93711e68 chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2025-08-02 12:24:34 +00:00
skjnldsv 627f8cabd6 fix(files_sharing): ensure password or date is enabled by default to be enforced
Signed-off-by: skjnldsv <skjnldsv@protonmail.com>
2025-08-02 12:22:31 +00:00
Robin Appelman 222cbd0af6 fix: fix oci string length with empty strings
Signed-off-by: Robin Appelman <robin@icewind.nl>
2025-08-01 17:45:35 +02:00
Josh 54142b2b20 fix(maintenanceMode): Refresh web page when turned off (part 1)
Signed-off-by: Josh <josh.t.richards@gmail.com>
2025-06-16 15:41:59 -04:00
1527 changed files with 16586 additions and 11041 deletions
+13 -11
View File
@@ -56,7 +56,7 @@ package-lock.json @nextcloud/server-dependabot
/apps/webhook_listeners/appinfo/info.xml @come-nc @julien-nc
/apps/workflowengine/appinfo/info.xml @blizzz @juliusknorr
# Frontend expertise
# Files frontend expertise
/apps/files/src* @skjnldsv @nextcloud/server-frontend
/apps/files_external/src* @skjnldsv @nextcloud/server-frontend
/apps/files_reminders/src* @skjnldsv @nextcloud/server-frontend
@@ -91,17 +91,19 @@ ResponseDefinitions.php @provokateurin @nextcloud/server-backend
/lib/public/OCM @nickvergessen @nextcloud/talk-backend @nextcloud/server-backend
/lib/public/Talk @nickvergessen @nextcloud/talk-backend
/lib/public/UserStatus @nickvergessen @nextcloud/talk-backend
*/Notifications/* @nickvergessen @nextcloud/talk-backend
# Groupware
/build/integration/dav_features/caldav.feature @st3iny @SebastianKrupinski @tcitworld
/build/integration/dav_features/carddav.feature @hamza221 @SebastianKrupinski
/lib/private/Calendar @st3iny @SebastianKrupinski @tcitworld
/lib/private/Contacts @hamza221 @SebastianKrupinski
/lib/public/Calendar @st3iny @SebastianKrupinski @tcitworld
/lib/public/Contacts @hamza221 @SebastianKrupinski
# Groupware team
/build/integration/dav_features/caldav.feature @st3iny @SebastianKrupinski @tcitworld
/build/integration/dav_features/carddav.feature @hamza221 @SebastianKrupinski
/lib/private/Calendar @st3iny @SebastianKrupinski @tcitworld
/lib/private/Contacts @hamza221 @SebastianKrupinski
/lib/public/Calendar @st3iny @SebastianKrupinski @tcitworld
/lib/public/Contacts @hamza221 @SebastianKrupinski
# Desktop client teamn
/apps/dav/lib/Connector/Sabre/BlockLegacyClientPlugin.php @nextcloud/desktop
# Personal interest
*/Activity/* @nickvergessen @nextcloud/server-backend
*/Notifications/* @nickvergessen @nextcloud/talk-backend
/apps/workflowengine/lib @nickvergessen
/apps/workflowengine/lib @nickvergessen @blizzz
@@ -1,5 +1,6 @@
# SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
# SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
# SPDX-License-Identifier: MIT
name: Update min supported desktop version
on:
@@ -124,4 +125,4 @@ jobs:
client: 💻 desktop
automated
3. to review
reviewers: tobiasKaminsky, camilasan, claucambra
reviewers: '@nextcloud/desktop'
-356
View File
File diff suppressed because one or more lines are too long
+532
View File
File diff suppressed because one or more lines are too long
+21
View File
@@ -0,0 +1,21 @@
OC.L10N.register(
"dashboard",
{
"Dashboard" : "Панэль кіравання",
"Dashboard app" : "Праграма Панэль кіравання",
"Weather" : "Надвор'е",
"Status" : "Стан",
"Good morning" : "Добрай раніцы",
"Good morning, {name}" : "Добрай раніцы, {name}",
"Good afternoon" : "Добры дзень",
"Good afternoon, {name}" : "Добры дзень, {name}",
"Good evening" : "Добры вечар",
"Good evening, {name}" : "Добры вечар, {name}",
"Hello" : "Вітаем",
"Hello, {name}" : "Вітаем, {name}",
"Happy birthday 🥳🤩🎂🎉" : "З народзінамі 🥳🤩🎂🎉",
"Happy birthday, {name} 🥳🤩🎂🎉" : "З народзінамі, {name} 🥳🤩🎂🎉",
"Customize" : "Дапасаваць",
"Edit widgets" : "Рэдагаваць віджэты"
},
"nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);");
+19
View File
@@ -0,0 +1,19 @@
{ "translations": {
"Dashboard" : "Панэль кіравання",
"Dashboard app" : "Праграма Панэль кіравання",
"Weather" : "Надвор'е",
"Status" : "Стан",
"Good morning" : "Добрай раніцы",
"Good morning, {name}" : "Добрай раніцы, {name}",
"Good afternoon" : "Добры дзень",
"Good afternoon, {name}" : "Добры дзень, {name}",
"Good evening" : "Добры вечар",
"Good evening, {name}" : "Добры вечар, {name}",
"Hello" : "Вітаем",
"Hello, {name}" : "Вітаем, {name}",
"Happy birthday 🥳🤩🎂🎉" : "З народзінамі 🥳🤩🎂🎉",
"Happy birthday, {name} 🥳🤩🎂🎉" : "З народзінамі, {name} 🥳🤩🎂🎉",
"Customize" : "Дапасаваць",
"Edit widgets" : "Рэдагаваць віджэты"
},"pluralForm" :"nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);"
}
+1 -1
View File
@@ -10,7 +10,7 @@
<name>WebDAV</name>
<summary>WebDAV endpoint</summary>
<description>WebDAV endpoint</description>
<version>1.34.0</version>
<version>1.34.1</version>
<licence>agpl</licence>
<author>owncloud.org</author>
<namespace>DAV</namespace>
+1
View File
@@ -63,6 +63,7 @@ $cardDavBackend = new CardDavBackend(
Server::get(IUserManager::class),
Server::get(IEventDispatcher::class),
Server::get(\OCA\DAV\CardDAV\Sharing\Backend::class),
Server::get(IConfig::class),
);
$debugging = Server::get(IConfig::class)->getSystemValue('debug', false);
@@ -216,6 +216,8 @@ return array(
'OCA\\DAV\\Connector\\Sabre\\Node' => $baseDir . '/../lib/Connector/Sabre/Node.php',
'OCA\\DAV\\Connector\\Sabre\\ObjectTree' => $baseDir . '/../lib/Connector/Sabre/ObjectTree.php',
'OCA\\DAV\\Connector\\Sabre\\Principal' => $baseDir . '/../lib/Connector/Sabre/Principal.php',
'OCA\\DAV\\Connector\\Sabre\\PropFindMonitorPlugin' => $baseDir . '/../lib/Connector/Sabre/PropFindMonitorPlugin.php',
'OCA\\DAV\\Connector\\Sabre\\PropFindPreloadNotifyPlugin' => $baseDir . '/../lib/Connector/Sabre/PropFindPreloadNotifyPlugin.php',
'OCA\\DAV\\Connector\\Sabre\\PropfindCompressionPlugin' => $baseDir . '/../lib/Connector/Sabre/PropfindCompressionPlugin.php',
'OCA\\DAV\\Connector\\Sabre\\PublicAuth' => $baseDir . '/../lib/Connector/Sabre/PublicAuth.php',
'OCA\\DAV\\Connector\\Sabre\\QuotaPlugin' => $baseDir . '/../lib/Connector/Sabre/QuotaPlugin.php',
@@ -353,6 +355,7 @@ return array(
'OCA\\DAV\\Migration\\Version1029Date20231004091403' => $baseDir . '/../lib/Migration/Version1029Date20231004091403.php',
'OCA\\DAV\\Migration\\Version1030Date20240205103243' => $baseDir . '/../lib/Migration/Version1030Date20240205103243.php',
'OCA\\DAV\\Migration\\Version1031Date20240610134258' => $baseDir . '/../lib/Migration/Version1031Date20240610134258.php',
'OCA\\DAV\\Migration\\Version1034Date20250813093701' => $baseDir . '/../lib/Migration/Version1034Date20250813093701.php',
'OCA\\DAV\\Model\\ExampleEvent' => $baseDir . '/../lib/Model/ExampleEvent.php',
'OCA\\DAV\\Paginate\\LimitedCopyIterator' => $baseDir . '/../lib/Paginate/LimitedCopyIterator.php',
'OCA\\DAV\\Paginate\\PaginateCache' => $baseDir . '/../lib/Paginate/PaginateCache.php',
@@ -231,6 +231,8 @@ class ComposerStaticInitDAV
'OCA\\DAV\\Connector\\Sabre\\Node' => __DIR__ . '/..' . '/../lib/Connector/Sabre/Node.php',
'OCA\\DAV\\Connector\\Sabre\\ObjectTree' => __DIR__ . '/..' . '/../lib/Connector/Sabre/ObjectTree.php',
'OCA\\DAV\\Connector\\Sabre\\Principal' => __DIR__ . '/..' . '/../lib/Connector/Sabre/Principal.php',
'OCA\\DAV\\Connector\\Sabre\\PropFindMonitorPlugin' => __DIR__ . '/..' . '/../lib/Connector/Sabre/PropFindMonitorPlugin.php',
'OCA\\DAV\\Connector\\Sabre\\PropFindPreloadNotifyPlugin' => __DIR__ . '/..' . '/../lib/Connector/Sabre/PropFindPreloadNotifyPlugin.php',
'OCA\\DAV\\Connector\\Sabre\\PropfindCompressionPlugin' => __DIR__ . '/..' . '/../lib/Connector/Sabre/PropfindCompressionPlugin.php',
'OCA\\DAV\\Connector\\Sabre\\PublicAuth' => __DIR__ . '/..' . '/../lib/Connector/Sabre/PublicAuth.php',
'OCA\\DAV\\Connector\\Sabre\\QuotaPlugin' => __DIR__ . '/..' . '/../lib/Connector/Sabre/QuotaPlugin.php',
@@ -368,6 +370,7 @@ class ComposerStaticInitDAV
'OCA\\DAV\\Migration\\Version1029Date20231004091403' => __DIR__ . '/..' . '/../lib/Migration/Version1029Date20231004091403.php',
'OCA\\DAV\\Migration\\Version1030Date20240205103243' => __DIR__ . '/..' . '/../lib/Migration/Version1030Date20240205103243.php',
'OCA\\DAV\\Migration\\Version1031Date20240610134258' => __DIR__ . '/..' . '/../lib/Migration/Version1031Date20240610134258.php',
'OCA\\DAV\\Migration\\Version1034Date20250813093701' => __DIR__ . '/..' . '/../lib/Migration/Version1034Date20250813093701.php',
'OCA\\DAV\\Model\\ExampleEvent' => __DIR__ . '/..' . '/../lib/Model/ExampleEvent.php',
'OCA\\DAV\\Paginate\\LimitedCopyIterator' => __DIR__ . '/..' . '/../lib/Paginate/LimitedCopyIterator.php',
'OCA\\DAV\\Paginate\\PaginateCache' => __DIR__ . '/..' . '/../lib/Paginate/PaginateCache.php',
+1 -1
View File
@@ -80,7 +80,7 @@ OC.L10N.register(
"_In a month on %1$s for the entire day_::_In %n months on %1$s for the entire day_" : ["I en måned på %1$s for hele dagen","Om %n måneder den %1$s for hele dagen"],
"_In a year on %1$s for the entire day_::_In %n years on %1$s for the entire day_" : ["I et år på %1$s for hele dagen","Om %n år den %1$s for hele dagen"],
"In the past on %1$s between %2$s - %3$s" : "Tidligere den %1$s mellem %2$s - %3$s",
"_In a minute on %1$s between %2$s - %3$s_::_In %n minutes on %1$s between %2$s - %3$s_" : ["I et minut på %1$s mellem% %2$s - %3$s","Om %n minutter den %1$s mellem %2$s - %3$s"],
"_In a minute on %1$s between %2$s - %3$s_::_In %n minutes on %1$s between %2$s - %3$s_" : ["I et minut på %1$s mellem %2$s - %3$s","Om %n minutter den %1$s mellem %2$s - %3$s"],
"_In a hour on %1$s between %2$s - %3$s_::_In %n hours on %1$s between %2$s - %3$s_" : ["I en time på %1$s mellem %2$s - %3$s","Om %n timer den %1$s mellem %2$s - %3$s"],
"_In a day on %1$s between %2$s - %3$s_::_In %n days on %1$s between %2$s - %3$s_" : ["I en dag på %1$s mellem %2$s - %3$s","Om %n dage den %1$s mellem %2$s - %3$s"],
"_In a week on %1$s between %2$s - %3$s_::_In %n weeks on %1$s between %2$s - %3$s_" : ["I en uge på %1$s mellem %2$s - %3$s","Om %n uger den %1$s mellem %2$s - %3$s"],
+1 -1
View File
@@ -78,7 +78,7 @@
"_In a month on %1$s for the entire day_::_In %n months on %1$s for the entire day_" : ["I en måned på %1$s for hele dagen","Om %n måneder den %1$s for hele dagen"],
"_In a year on %1$s for the entire day_::_In %n years on %1$s for the entire day_" : ["I et år på %1$s for hele dagen","Om %n år den %1$s for hele dagen"],
"In the past on %1$s between %2$s - %3$s" : "Tidligere den %1$s mellem %2$s - %3$s",
"_In a minute on %1$s between %2$s - %3$s_::_In %n minutes on %1$s between %2$s - %3$s_" : ["I et minut på %1$s mellem% %2$s - %3$s","Om %n minutter den %1$s mellem %2$s - %3$s"],
"_In a minute on %1$s between %2$s - %3$s_::_In %n minutes on %1$s between %2$s - %3$s_" : ["I et minut på %1$s mellem %2$s - %3$s","Om %n minutter den %1$s mellem %2$s - %3$s"],
"_In a hour on %1$s between %2$s - %3$s_::_In %n hours on %1$s between %2$s - %3$s_" : ["I en time på %1$s mellem %2$s - %3$s","Om %n timer den %1$s mellem %2$s - %3$s"],
"_In a day on %1$s between %2$s - %3$s_::_In %n days on %1$s between %2$s - %3$s_" : ["I en dag på %1$s mellem %2$s - %3$s","Om %n dage den %1$s mellem %2$s - %3$s"],
"_In a week on %1$s between %2$s - %3$s_::_In %n weeks on %1$s between %2$s - %3$s_" : ["I en uge på %1$s mellem %2$s - %3$s","Om %n uger den %1$s mellem %2$s - %3$s"],
+35
View File
@@ -79,6 +79,9 @@ OC.L10N.register(
"_In a week on %1$s for the entire day_::_In %n weeks on %1$s for the entire day_" : ["Za tydzień, dnia %1$s, przez cały dzień","Za %n tygodnie, dnia %1$s, przez cały dzień","Za %n tygodni, dnia %1$s, przez cały dzień","Za %n tygodni, dnia %1$s, przez cały dzień"],
"_In a month on %1$s for the entire day_::_In %n months on %1$s for the entire day_" : ["W ciągu miesiąca, dnia %1$s przez cały dzień","W ciągu %n miesięcy, dnia %1$s przez cały dzień","W ciągu %n miesięcy, dnia %1$s przez cały dzień","W ciągu %n miesięcy, dnia %1$s przez cały dzień"],
"_In a year on %1$s for the entire day_::_In %n years on %1$s for the entire day_" : ["Za rok dnia %1$s przez cały dzień","Za %n lata dnia %1$s przez cały dzień","Za %n lat dnia %1$s przez cały dzień","Za %n lat dnia %1$s przez cały dzień"],
"In the past on %1$s between %2$s - %3$s" : "W przeszłości dnia %1$s między %2$s - %3$s",
"_In a minute on %1$s between %2$s - %3$s_::_In %n minutes on %1$s between %2$s - %3$s_" : ["Za minutę dnia %1$s między %2$s - %3$s","Za %n minuty dnia %1$s między %2$s - %3$s","Za %n minut dnia %1$s między %2$s - %3$s","Za %n minut dnia %1$s między %2$s - %3$s"],
"_In a hour on %1$s between %2$s - %3$s_::_In %n hours on %1$s between %2$s - %3$s_" : ["Za godzinę dnia %1$s między %2$s - %3$s","Za %n godziny dnia %1$s między %2$s - %3$s","Za %n godzin dnia %1$s między %2$s - %3$s","Za %n godzin dnia %1$s między %2$s - %3$s"],
"_In a day on %1$s between %2$s - %3$s_::_In %n days on %1$s between %2$s - %3$s_" : ["Za dzień, dnia %1$s między %2$s - %3$s","Za %n dni dnia %1$s między %2$s - %3$s","Za %n dni dnia %1$s między %2$s - %3$s","Za %n dni dnia %1$s między %2$s - %3$s"],
"_In a week on %1$s between %2$s - %3$s_::_In %n weeks on %1$s between %2$s - %3$s_" : ["Za tydzień dnia %1$s między %2$s - %3$s","Za %n tygodnie dnia %1$s między %2$s - %3$s","Za %n tygodni dnia %1$s między %2$s - %3$s","Za %n tygodni dnia %1$s między %2$s - %3$s"],
"_In a month on %1$s between %2$s - %3$s_::_In %n months on %1$s between %2$s - %3$s_" : ["Za miesiąc dnia %1$s między %2$s - %3$s","Za %n miesięcy dnia %1$s między %2$s - %3$s","Za %n miesięcy dnia %1$s między %2$s - %3$s","Za %n miesięcy dnia %1$s między %2$s - %3$s"],
@@ -87,18 +90,38 @@ OC.L10N.register(
"Every Day for the entire day" : "Codziennie przez cały dzień",
"Every Day for the entire day until %1$s" : "Codziennie przez cały dzień do %1$s",
"Every Day between %1$s - %2$s" : "Codziennie między %1$s %2$s",
"Every Day between %1$s - %2$s until %3$s" : "Codziennie między %1$s - %2$s do %3$s",
"Every %1$d Days for the entire day" : "Co %1$d dni przez cały dzień",
"Every %1$d Days for the entire day until %2$s" : "Co %1$d dni przez cały dzień aż do %2$s",
"Every %1$d Days between %2$s - %3$s" : "Co %1$d dni pomiędzy %2$s - %3$s",
"Every %1$d Days between %2$s - %3$s until %4$s" : "Co %1$d dni, pomiędzy %2$s - %3$s aż do %4$s",
"Could not generate event recurrence statement" : "Nie można wygenerować zestawienia powtórzeń zdarzenia",
"Every Week on %1$s for the entire day" : "Każdego tygodnia w %1$s przez cały dzień",
"Every Week on %1$s for the entire day until %2$s" : "Co tydzień w %1$s przez cały dzień do %2$s",
"Every Week on %1$s between %2$s - %3$s" : "Co tydzień w %1$s między %2$s - %3$s",
"Every Week on %1$s between %2$s - %3$s until %4$s" : "Co tydzień w %1$s między %2$s - %3$s do %4$s",
"Every %1$d Weeks on %2$s for the entire day" : "Co %1$d tygodni w %2$s przez cały dzień",
"Every %1$d Weeks on %2$s for the entire day until %3$s" : "Co %1$d tygodnie w %2$s przez cały dzień do %3$s",
"Every %1$d Weeks on %2$s between %3$s - %4$s" : "Co %1$d tygodnie w %2$s między %3$s - %4$s",
"Every %1$d Weeks on %2$s between %3$s - %4$s until %5$s" : "Co %1$d tygodnie w %2$s między %3$s - %4$s do %5$s",
"Every Month on the %1$s for the entire day" : "Co miesiąc dnia %1$s przez cały dzień",
"Every Month on the %1$s for the entire day until %2$s" : "Co miesiąc dnia %1$s przez cały dzień do %2$s",
"Every Month on the %1$s between %2$s - %3$s" : "Co miesiąc dnia %1$s między %2$s - %3$s",
"Every Month on the %1$s between %2$s - %3$s until %4$s" : "Co miesiąc dnia %1$s między %2$s - %3$s do %4$s",
"Every %1$d Months on the %2$s for the entire day" : "Co %1$d miesiący dnia %2$s przez cały dzień",
"Every %1$d Months on the %2$s for the entire day until %3$s" : "Co %1$d miesięcy dnia %2$s przez cały dzień do %3$s",
"Every %1$d Months on the %2$s between %3$s - %4$s" : "Co %1$d miesięcy dnia %2$s między %3$s - %4$s",
"Every %1$d Months on the %2$s between %3$s - %4$s until %5$s" : "Każdego %1$d miesiąca dnia %2$s między %3$s - %4$s do %5$s",
"Every Year in %1$s on the %2$s for the entire day" : "Co rok w %1$s dnia %2$s przez cały dzień",
"Every Year in %1$s on the %2$s for the entire day until %3$s" : "Co rok w %1$s dnia %2$s przez cały dzień do %3$s",
"Every Year in %1$s on the %2$s between %3$s - %4$s" : "Co roku za %1$s dnia %2$s między %3$s - %4$s",
"Every Year in %1$s on the %2$s between %3$s - %4$s until %5$s" : "Co roku za %1$s dnia %2$s między %3$s - %4$s do %5$s",
"Every %1$d Years in %2$s on the %3$s for the entire day" : "Co %1$d lat dnia %2$s o %3$s przez cały dzień",
"Every %1$d Years in %2$s on the %3$s for the entire day until %4$s" : "Co %1$d lat w %2$s dnia %3$s przez cały dzień do %4$s",
"Every %1$d Years in %2$s on the %3$s between %4$s - %5$s" : "Co %1$d lat w %2$s dnia %3$s między %4$s - %5$s",
"Every %1$d Years in %2$s on the %3$s between %4$s - %5$s until %6$s" : "Co %1$d lat w %2$s dnia %3$s między %4$s - %5$s do %6$s",
"On specific dates for the entire day until %1$s" : "W określonych datach przez cały dzień do %1$s",
"On specific dates between %1$s - %2$s until %3$s" : "W określonych datach między %1$s - %2$s do %3$s",
"In the past on %1$s" : "W przeszłości dnia %1$s",
"_In a minute on %1$s_::_In %n minutes on %1$s_" : ["Za minutę dnia %1$s","Za %n minut dnia %1$s","Za %n minut dnia %1$s","Za %n minut dnia %1$s"],
"_In a hour on %1$s_::_In %n hours on %1$s_" : ["Za godzinę dnia %1$s","Za %n godziny dnia %1$s","Za %n godzin dnia %1$s","Za %n godzin dnia %1$s"],
@@ -108,6 +131,18 @@ OC.L10N.register(
"_In a year on %1$s_::_In %n years on %1$s_" : ["Za rok dnia %1$s","Za %n lat dnia %1$s","Za %n lat dnia %1$s","Za %n lat dnia %1$s"],
"In the past on %1$s then on %2$s" : "W przeszłości dnia %1$s, a następnie %2$s",
"_In a minute on %1$s then on %2$s_::_In %n minutes on %1$s then on %2$s_" : ["Za minutę dnia %1$s, a następnie %2$s","Za %n minuty dnia %1$s, a następnie %2$s","Za %n minut dnia %1$s, a następnie %2$s","Za %n minut dnia %1$s, a następnie %2$s"],
"_In a hour on %1$s then on %2$s_::_In %n hours on %1$s then on %2$s_" : ["Za godzinę dnia %1$s, następnie dnia %2$s","Za %n godziny dnia %1$s, następnie dnia %2$s","Za %n godzin dnia %1$s, następnie dnia %2$s","Za %n godzin dnia %1$s, następnie dnia %2$s"],
"_In a day on %1$s then on %2$s_::_In %n days on %1$s then on %2$s_" : ["Jutro dnia %1$s, następnie dnia %2$s","Za %n dni dnia %1$s, następnie dnia %2$s","Za %n dni dnia %1$s, następnie dnia %2$s","Za %n dni dnia %1$s, następnie dnia %2$s"],
"_In a week on %1$s then on %2$s_::_In %n weeks on %1$s then on %2$s_" : ["Za tydzień dnia %1$s, następnie dnia %2$s","Za %n tygodnie dnia %1$s, następnie dnia %2$s","Za %n tygodni dnia %1$s, następnie dnia %2$s","Za %n tygodni dnia %1$s, następnie dnia %2$s"],
"_In a month on %1$s then on %2$s_::_In %n months on %1$s then on %2$s_" : ["Za miesiąc dnia %1$s, następnie dnia %2$s","Za %n miesiące dnia %1$s, następnie dnia %2$s","Za %n miesięcy dnia %1$s, następnie dnia %2$s","Za %n miesięcy dnia %1$s, następnie dnia %2$s"],
"_In a year on %1$s then on %2$s_::_In %n years on %1$s then on %2$s_" : ["Za rok dnia %1$s, następnie dnia %2$s","Za %n lata dnia %1$s, następnie dnia %2$s","Za %n lat dnia %1$s, następnie dnia %2$s","Za %n lat dnia %1$s, następnie dnia %2$s"],
"In the past on %1$s then on %2$s and %3$s" : "W przeszłości dnia %1$s, następnie dnia %2$s i %3$s",
"_In a minute on %1$s then on %2$s and %3$s_::_In %n minutes on %1$s then on %2$s and %3$s_" : ["Za minutę dnia %1$s, następnie dnia %2$s i %3$s","Za %n minuty dnia %1$s, następnie dnia %2$s i %3$s","Za %n minut dnia %1$s, następnie dnia %2$s i %3$s","Za %n minut dnia %1$s, następnie dnia %2$s i %3$s"],
"_In a hour on %1$s then on %2$s and %3$s_::_In %n hours on %1$s then on %2$s and %3$s_" : ["Za godzinę dnia %1$s, następnie dnia %2$s i %3$s","Za %%ngodziny dnia %1$s, następnie dnia %2$s i %3$s","Za %n godzin dnia %1$s, następnie dnia %2$s i %3$s","Za %n godzin dnia %1$s, następnie dnia %2$s i %3$s"],
"_In a day on %1$s then on %2$s and %3$s_::_In %n days on %1$s then on %2$s and %3$s_" : ["Jutro, dnia %1$s, następnie dnia %2$s i %3$s","Za %n dni dnia %1$s, następnie dnia %2$s i %3$s","Za %n dni dnia %1$s, następnie dnia %2$s i %3$s","Za %n dni dnia %1$s, następnie dnia %2$s i %3$s"],
"_In a week on %1$s then on %2$s and %3$s_::_In %n weeks on %1$s then on %2$s and %3$s_" : ["Za tydzień dnia %1$s, następnie dnia %2$s i %3$s","Za %n tygodnie dnia %1$s, następnie dnia %2$s i %3$s","Za %n tygodni dnia %1$s, następnie dnia %2$s i %3$s","Za %n tygodni dnia %1$s, następnie dnia %2$s i %3$s"],
"_In a month on %1$s then on %2$s and %3$s_::_In %n months on %1$s then on %2$s and %3$s_" : ["Za miesiąc dnia %1$s, następnie dnia %2$s i %3$s","Za %n miesiące dnia %1$s, następnie dnia %2$s i %3$s","Za %n miesięcy dnia %1$s, następnie dnia %2$s i %3$s","Za %n miesięcy dnia %1$s, następnie dnia %2$s i %3$s"],
"_In a year on %1$s then on %2$s and %3$s_::_In %n years on %1$s then on %2$s and %3$s_" : ["Za rok dnia %1$s, następnie dnia %2$s i %3$s","Za %n lata dnia %1$s, następnie dnia %2$s i %3$s","Za %n lat dnia %1$s, następnie dnia %2$s i %3$s","Za %n lat dnia %1$s, następnie dnia %2$s i %3$s"],
"Could not generate next recurrence statement" : "Nie można wygenerować następnej instrukcji powtarzania",
"Cancelled: %1$s" : "Anulowane: %1$s",
"\"%1$s\" has been canceled" : "\"%1$s\" zostało anulowane",
+35
View File
@@ -77,6 +77,9 @@
"_In a week on %1$s for the entire day_::_In %n weeks on %1$s for the entire day_" : ["Za tydzień, dnia %1$s, przez cały dzień","Za %n tygodnie, dnia %1$s, przez cały dzień","Za %n tygodni, dnia %1$s, przez cały dzień","Za %n tygodni, dnia %1$s, przez cały dzień"],
"_In a month on %1$s for the entire day_::_In %n months on %1$s for the entire day_" : ["W ciągu miesiąca, dnia %1$s przez cały dzień","W ciągu %n miesięcy, dnia %1$s przez cały dzień","W ciągu %n miesięcy, dnia %1$s przez cały dzień","W ciągu %n miesięcy, dnia %1$s przez cały dzień"],
"_In a year on %1$s for the entire day_::_In %n years on %1$s for the entire day_" : ["Za rok dnia %1$s przez cały dzień","Za %n lata dnia %1$s przez cały dzień","Za %n lat dnia %1$s przez cały dzień","Za %n lat dnia %1$s przez cały dzień"],
"In the past on %1$s between %2$s - %3$s" : "W przeszłości dnia %1$s między %2$s - %3$s",
"_In a minute on %1$s between %2$s - %3$s_::_In %n minutes on %1$s between %2$s - %3$s_" : ["Za minutę dnia %1$s między %2$s - %3$s","Za %n minuty dnia %1$s między %2$s - %3$s","Za %n minut dnia %1$s między %2$s - %3$s","Za %n minut dnia %1$s między %2$s - %3$s"],
"_In a hour on %1$s between %2$s - %3$s_::_In %n hours on %1$s between %2$s - %3$s_" : ["Za godzinę dnia %1$s między %2$s - %3$s","Za %n godziny dnia %1$s między %2$s - %3$s","Za %n godzin dnia %1$s między %2$s - %3$s","Za %n godzin dnia %1$s między %2$s - %3$s"],
"_In a day on %1$s between %2$s - %3$s_::_In %n days on %1$s between %2$s - %3$s_" : ["Za dzień, dnia %1$s między %2$s - %3$s","Za %n dni dnia %1$s między %2$s - %3$s","Za %n dni dnia %1$s między %2$s - %3$s","Za %n dni dnia %1$s między %2$s - %3$s"],
"_In a week on %1$s between %2$s - %3$s_::_In %n weeks on %1$s between %2$s - %3$s_" : ["Za tydzień dnia %1$s między %2$s - %3$s","Za %n tygodnie dnia %1$s między %2$s - %3$s","Za %n tygodni dnia %1$s między %2$s - %3$s","Za %n tygodni dnia %1$s między %2$s - %3$s"],
"_In a month on %1$s between %2$s - %3$s_::_In %n months on %1$s between %2$s - %3$s_" : ["Za miesiąc dnia %1$s między %2$s - %3$s","Za %n miesięcy dnia %1$s między %2$s - %3$s","Za %n miesięcy dnia %1$s między %2$s - %3$s","Za %n miesięcy dnia %1$s między %2$s - %3$s"],
@@ -85,18 +88,38 @@
"Every Day for the entire day" : "Codziennie przez cały dzień",
"Every Day for the entire day until %1$s" : "Codziennie przez cały dzień do %1$s",
"Every Day between %1$s - %2$s" : "Codziennie między %1$s %2$s",
"Every Day between %1$s - %2$s until %3$s" : "Codziennie między %1$s - %2$s do %3$s",
"Every %1$d Days for the entire day" : "Co %1$d dni przez cały dzień",
"Every %1$d Days for the entire day until %2$s" : "Co %1$d dni przez cały dzień aż do %2$s",
"Every %1$d Days between %2$s - %3$s" : "Co %1$d dni pomiędzy %2$s - %3$s",
"Every %1$d Days between %2$s - %3$s until %4$s" : "Co %1$d dni, pomiędzy %2$s - %3$s aż do %4$s",
"Could not generate event recurrence statement" : "Nie można wygenerować zestawienia powtórzeń zdarzenia",
"Every Week on %1$s for the entire day" : "Każdego tygodnia w %1$s przez cały dzień",
"Every Week on %1$s for the entire day until %2$s" : "Co tydzień w %1$s przez cały dzień do %2$s",
"Every Week on %1$s between %2$s - %3$s" : "Co tydzień w %1$s między %2$s - %3$s",
"Every Week on %1$s between %2$s - %3$s until %4$s" : "Co tydzień w %1$s między %2$s - %3$s do %4$s",
"Every %1$d Weeks on %2$s for the entire day" : "Co %1$d tygodni w %2$s przez cały dzień",
"Every %1$d Weeks on %2$s for the entire day until %3$s" : "Co %1$d tygodnie w %2$s przez cały dzień do %3$s",
"Every %1$d Weeks on %2$s between %3$s - %4$s" : "Co %1$d tygodnie w %2$s między %3$s - %4$s",
"Every %1$d Weeks on %2$s between %3$s - %4$s until %5$s" : "Co %1$d tygodnie w %2$s między %3$s - %4$s do %5$s",
"Every Month on the %1$s for the entire day" : "Co miesiąc dnia %1$s przez cały dzień",
"Every Month on the %1$s for the entire day until %2$s" : "Co miesiąc dnia %1$s przez cały dzień do %2$s",
"Every Month on the %1$s between %2$s - %3$s" : "Co miesiąc dnia %1$s między %2$s - %3$s",
"Every Month on the %1$s between %2$s - %3$s until %4$s" : "Co miesiąc dnia %1$s między %2$s - %3$s do %4$s",
"Every %1$d Months on the %2$s for the entire day" : "Co %1$d miesiący dnia %2$s przez cały dzień",
"Every %1$d Months on the %2$s for the entire day until %3$s" : "Co %1$d miesięcy dnia %2$s przez cały dzień do %3$s",
"Every %1$d Months on the %2$s between %3$s - %4$s" : "Co %1$d miesięcy dnia %2$s między %3$s - %4$s",
"Every %1$d Months on the %2$s between %3$s - %4$s until %5$s" : "Każdego %1$d miesiąca dnia %2$s między %3$s - %4$s do %5$s",
"Every Year in %1$s on the %2$s for the entire day" : "Co rok w %1$s dnia %2$s przez cały dzień",
"Every Year in %1$s on the %2$s for the entire day until %3$s" : "Co rok w %1$s dnia %2$s przez cały dzień do %3$s",
"Every Year in %1$s on the %2$s between %3$s - %4$s" : "Co roku za %1$s dnia %2$s między %3$s - %4$s",
"Every Year in %1$s on the %2$s between %3$s - %4$s until %5$s" : "Co roku za %1$s dnia %2$s między %3$s - %4$s do %5$s",
"Every %1$d Years in %2$s on the %3$s for the entire day" : "Co %1$d lat dnia %2$s o %3$s przez cały dzień",
"Every %1$d Years in %2$s on the %3$s for the entire day until %4$s" : "Co %1$d lat w %2$s dnia %3$s przez cały dzień do %4$s",
"Every %1$d Years in %2$s on the %3$s between %4$s - %5$s" : "Co %1$d lat w %2$s dnia %3$s między %4$s - %5$s",
"Every %1$d Years in %2$s on the %3$s between %4$s - %5$s until %6$s" : "Co %1$d lat w %2$s dnia %3$s między %4$s - %5$s do %6$s",
"On specific dates for the entire day until %1$s" : "W określonych datach przez cały dzień do %1$s",
"On specific dates between %1$s - %2$s until %3$s" : "W określonych datach między %1$s - %2$s do %3$s",
"In the past on %1$s" : "W przeszłości dnia %1$s",
"_In a minute on %1$s_::_In %n minutes on %1$s_" : ["Za minutę dnia %1$s","Za %n minut dnia %1$s","Za %n minut dnia %1$s","Za %n minut dnia %1$s"],
"_In a hour on %1$s_::_In %n hours on %1$s_" : ["Za godzinę dnia %1$s","Za %n godziny dnia %1$s","Za %n godzin dnia %1$s","Za %n godzin dnia %1$s"],
@@ -106,6 +129,18 @@
"_In a year on %1$s_::_In %n years on %1$s_" : ["Za rok dnia %1$s","Za %n lat dnia %1$s","Za %n lat dnia %1$s","Za %n lat dnia %1$s"],
"In the past on %1$s then on %2$s" : "W przeszłości dnia %1$s, a następnie %2$s",
"_In a minute on %1$s then on %2$s_::_In %n minutes on %1$s then on %2$s_" : ["Za minutę dnia %1$s, a następnie %2$s","Za %n minuty dnia %1$s, a następnie %2$s","Za %n minut dnia %1$s, a następnie %2$s","Za %n minut dnia %1$s, a następnie %2$s"],
"_In a hour on %1$s then on %2$s_::_In %n hours on %1$s then on %2$s_" : ["Za godzinę dnia %1$s, następnie dnia %2$s","Za %n godziny dnia %1$s, następnie dnia %2$s","Za %n godzin dnia %1$s, następnie dnia %2$s","Za %n godzin dnia %1$s, następnie dnia %2$s"],
"_In a day on %1$s then on %2$s_::_In %n days on %1$s then on %2$s_" : ["Jutro dnia %1$s, następnie dnia %2$s","Za %n dni dnia %1$s, następnie dnia %2$s","Za %n dni dnia %1$s, następnie dnia %2$s","Za %n dni dnia %1$s, następnie dnia %2$s"],
"_In a week on %1$s then on %2$s_::_In %n weeks on %1$s then on %2$s_" : ["Za tydzień dnia %1$s, następnie dnia %2$s","Za %n tygodnie dnia %1$s, następnie dnia %2$s","Za %n tygodni dnia %1$s, następnie dnia %2$s","Za %n tygodni dnia %1$s, następnie dnia %2$s"],
"_In a month on %1$s then on %2$s_::_In %n months on %1$s then on %2$s_" : ["Za miesiąc dnia %1$s, następnie dnia %2$s","Za %n miesiące dnia %1$s, następnie dnia %2$s","Za %n miesięcy dnia %1$s, następnie dnia %2$s","Za %n miesięcy dnia %1$s, następnie dnia %2$s"],
"_In a year on %1$s then on %2$s_::_In %n years on %1$s then on %2$s_" : ["Za rok dnia %1$s, następnie dnia %2$s","Za %n lata dnia %1$s, następnie dnia %2$s","Za %n lat dnia %1$s, następnie dnia %2$s","Za %n lat dnia %1$s, następnie dnia %2$s"],
"In the past on %1$s then on %2$s and %3$s" : "W przeszłości dnia %1$s, następnie dnia %2$s i %3$s",
"_In a minute on %1$s then on %2$s and %3$s_::_In %n minutes on %1$s then on %2$s and %3$s_" : ["Za minutę dnia %1$s, następnie dnia %2$s i %3$s","Za %n minuty dnia %1$s, następnie dnia %2$s i %3$s","Za %n minut dnia %1$s, następnie dnia %2$s i %3$s","Za %n minut dnia %1$s, następnie dnia %2$s i %3$s"],
"_In a hour on %1$s then on %2$s and %3$s_::_In %n hours on %1$s then on %2$s and %3$s_" : ["Za godzinę dnia %1$s, następnie dnia %2$s i %3$s","Za %%ngodziny dnia %1$s, następnie dnia %2$s i %3$s","Za %n godzin dnia %1$s, następnie dnia %2$s i %3$s","Za %n godzin dnia %1$s, następnie dnia %2$s i %3$s"],
"_In a day on %1$s then on %2$s and %3$s_::_In %n days on %1$s then on %2$s and %3$s_" : ["Jutro, dnia %1$s, następnie dnia %2$s i %3$s","Za %n dni dnia %1$s, następnie dnia %2$s i %3$s","Za %n dni dnia %1$s, następnie dnia %2$s i %3$s","Za %n dni dnia %1$s, następnie dnia %2$s i %3$s"],
"_In a week on %1$s then on %2$s and %3$s_::_In %n weeks on %1$s then on %2$s and %3$s_" : ["Za tydzień dnia %1$s, następnie dnia %2$s i %3$s","Za %n tygodnie dnia %1$s, następnie dnia %2$s i %3$s","Za %n tygodni dnia %1$s, następnie dnia %2$s i %3$s","Za %n tygodni dnia %1$s, następnie dnia %2$s i %3$s"],
"_In a month on %1$s then on %2$s and %3$s_::_In %n months on %1$s then on %2$s and %3$s_" : ["Za miesiąc dnia %1$s, następnie dnia %2$s i %3$s","Za %n miesiące dnia %1$s, następnie dnia %2$s i %3$s","Za %n miesięcy dnia %1$s, następnie dnia %2$s i %3$s","Za %n miesięcy dnia %1$s, następnie dnia %2$s i %3$s"],
"_In a year on %1$s then on %2$s and %3$s_::_In %n years on %1$s then on %2$s and %3$s_" : ["Za rok dnia %1$s, następnie dnia %2$s i %3$s","Za %n lata dnia %1$s, następnie dnia %2$s i %3$s","Za %n lat dnia %1$s, następnie dnia %2$s i %3$s","Za %n lat dnia %1$s, następnie dnia %2$s i %3$s"],
"Could not generate next recurrence statement" : "Nie można wygenerować następnej instrukcji powtarzania",
"Cancelled: %1$s" : "Anulowane: %1$s",
"\"%1$s\" has been canceled" : "\"%1$s\" zostało anulowane",
+5 -1
View File
@@ -36,7 +36,7 @@ class Calendar extends \Sabre\CalDAV\Calendar implements IRestorable, IShareable
public function __construct(
BackendInterface $caldavBackend,
$calendarInfo,
array $calendarInfo,
IL10N $l10n,
private IConfig $config,
private LoggerInterface $logger,
@@ -60,6 +60,10 @@ class Calendar extends \Sabre\CalDAV\Calendar implements IRestorable, IShareable
$this->l10n = $l10n;
}
public function getUri(): string {
return $this->calendarInfo['uri'];
}
/**
* {@inheritdoc}
* @throws Forbidden
+29 -6
View File
@@ -36,9 +36,14 @@ class CalendarProvider implements ICalendarProvider {
});
}
$additionalProperties = $this->getAdditionalPropertiesForCalendars($calendarInfos);
$iCalendars = [];
foreach ($calendarInfos as $calendarInfo) {
$calendarInfo = array_merge($calendarInfo, $this->getAdditionalProperties($calendarInfo['principaluri'], $calendarInfo['uri']));
$user = str_replace('principals/users/', '', $calendarInfo['principaluri']);
$path = 'calendars/' . $user . '/' . $calendarInfo['uri'];
$calendarInfo = array_merge($calendarInfo, $additionalProperties[$path] ?? []);
$calendar = new Calendar($this->calDavBackend, $calendarInfo, $this->l10n, $this->config, $this->logger);
$iCalendars[] = new CalendarImpl(
$calendar,
@@ -49,16 +54,34 @@ class CalendarProvider implements ICalendarProvider {
return $iCalendars;
}
public function getAdditionalProperties(string $principalUri, string $calendarUri): array {
$user = str_replace('principals/users/', '', $principalUri);
$path = 'calendars/' . $user . '/' . $calendarUri;
/**
* @param array{
* principaluri: string,
* uri: string,
* }[] $uris
* @return array<string, array<string, string|bool>>
*/
private function getAdditionalPropertiesForCalendars(array $uris): array {
$calendars = [];
foreach ($uris as $uri) {
/** @var string $user */
$user = str_replace('principals/users/', '', $uri['principaluri']);
if (!array_key_exists($user, $calendars)) {
$calendars[$user] = [];
}
$calendars[$user][] = 'calendars/' . $user . '/' . $uri['uri'];
}
$properties = $this->propertyMapper->findPropertiesByPath($user, $path);
$properties = $this->propertyMapper->findPropertiesByPathsAndUsers($calendars);
$list = [];
foreach ($properties as $property) {
if ($property instanceof Property) {
$list[$property->getPropertyname()] = match ($property->getPropertyname()) {
if (!isset($list[$property->getPropertypath()])) {
$list[$property->getPropertypath()] = [];
}
$list[$property->getPropertypath()][$property->getPropertyname()] = match ($property->getPropertyname()) {
'{http://owncloud.org/ns}calendar-enabled' => (bool)$property->getPropertyvalue(),
default => $property->getPropertyvalue()
};
@@ -20,6 +20,7 @@ use OCA\DAV\Connector\Sabre\DavAclPlugin;
use OCA\DAV\Connector\Sabre\ExceptionLoggerPlugin;
use OCA\DAV\Connector\Sabre\LockPlugin;
use OCA\DAV\Connector\Sabre\MaintenancePlugin;
use OCA\DAV\Connector\Sabre\PropFindPreloadNotifyPlugin;
use OCA\DAV\Events\SabrePluginAuthInitEvent;
use OCA\DAV\RootCollection;
use OCA\Theming\ThemingDefaults;
@@ -96,6 +97,9 @@ class EmbeddedCalDavServer {
$this->server->addPlugin(Server::get(\OCA\DAV\CalDAV\Schedule\IMipPlugin::class));
}
// collection preload plugin
$this->server->addPlugin(new PropFindPreloadNotifyPlugin());
// wait with registering these until auth is handled and the filesystem is setup
$this->server->on('beforeMethod:*', function () use ($root): void {
// register plugins from apps
+1 -1
View File
@@ -1110,7 +1110,7 @@ class IMipService {
$sequence = $iTipMessage->sequence;
$recurrenceId = isset($vevent->{'RECURRENCE-ID'})
? $vevent->{'RECURRENCE-ID'}->serialize() : null;
$uid = $vevent->{'UID'};
$uid = $vevent->{'UID'}?->getValue();
$query = $this->db->getQueryBuilder();
$query->insert('calendar_invitations')
-4
View File
@@ -8,7 +8,6 @@
namespace OCA\DAV\CardDAV;
use OCA\DAV\DAV\Sharing\IShareable;
use OCA\DAV\Exception\UnsupportedLimitOnInitialSyncException;
use OCP\DB\Exception;
use OCP\IL10N;
use OCP\Server;
@@ -234,9 +233,6 @@ class AddressBook extends \Sabre\CardDAV\AddressBook implements IShareable, IMov
}
public function getChanges($syncToken, $syncLevel, $limit = null) {
if (!$syncToken && $limit) {
throw new UnsupportedLimitOnInitialSyncException();
}
return parent::getChanges($syncToken, $syncLevel, $limit);
}
+4
View File
@@ -152,6 +152,10 @@ class AddressBookImpl implements IAddressBookEnabled {
$permissions = $this->addressBook->getACL();
$result = 0;
foreach ($permissions as $permission) {
if ($this->addressBookInfo['principaluri'] !== $permission['principal']) {
continue;
}
switch ($permission['privilege']) {
case '{DAV:}read':
$result |= Constants::PERMISSION_READ;
+71 -6
View File
@@ -23,6 +23,7 @@ use OCP\AppFramework\Db\TTransactional;
use OCP\DB\Exception;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\IUserManager;
use PDO;
@@ -59,6 +60,7 @@ class CardDavBackend implements BackendInterface, SyncSupport {
private IUserManager $userManager,
private IEventDispatcher $dispatcher,
private Sharing\Backend $sharingBackend,
private IConfig $config,
) {
}
@@ -851,6 +853,8 @@ class CardDavBackend implements BackendInterface, SyncSupport {
* @return array
*/
public function getChangesForAddressBook($addressBookId, $syncToken, $syncLevel, $limit = null) {
$maxLimit = $this->config->getSystemValueInt('carddav_sync_request_truncation', 2500);
$limit = ($limit === null) ? $maxLimit : min($limit, $maxLimit);
// Current synctoken
return $this->atomic(function () use ($addressBookId, $syncToken, $syncLevel, $limit) {
$qb = $this->db->getQueryBuilder();
@@ -873,10 +877,35 @@ class CardDavBackend implements BackendInterface, SyncSupport {
'modified' => [],
'deleted' => [],
];
if ($syncToken) {
if (str_starts_with($syncToken, 'init_')) {
$syncValues = explode('_', $syncToken);
$lastID = $syncValues[1];
$initialSyncToken = $syncValues[2];
$qb = $this->db->getQueryBuilder();
$qb->select('uri', 'operation')
$qb->select('id', 'uri')
->from('cards')
->where(
$qb->expr()->andX(
$qb->expr()->eq('addressbookid', $qb->createNamedParameter($addressBookId)),
$qb->expr()->gt('id', $qb->createNamedParameter($lastID)))
)->orderBy('id')
->setMaxResults($limit);
$stmt = $qb->executeQuery();
$values = $stmt->fetchAll(\PDO::FETCH_ASSOC);
$stmt->closeCursor();
if (count($values) === 0) {
$result['syncToken'] = $initialSyncToken;
$result['result_truncated'] = false;
$result['added'] = [];
} else {
$lastID = $values[array_key_last($values)]['id'];
$result['added'] = array_column($values, 'uri');
$result['syncToken'] = count($result['added']) >= $limit ? "init_{$lastID}_$initialSyncToken" : $initialSyncToken ;
$result['result_truncated'] = count($result['added']) >= $limit;
}
} elseif ($syncToken) {
$qb = $this->db->getQueryBuilder();
$qb->select('uri', 'operation', 'synctoken')
->from('addressbookchanges')
->where(
$qb->expr()->andX(
@@ -886,22 +915,31 @@ class CardDavBackend implements BackendInterface, SyncSupport {
)
)->orderBy('synctoken');
if (is_int($limit) && $limit > 0) {
if ($limit > 0) {
$qb->setMaxResults($limit);
}
// Fetching all changes
$stmt = $qb->executeQuery();
$rowCount = $stmt->rowCount();
$changes = [];
$highestSyncToken = 0;
// This loop ensures that any duplicates are overwritten, only the
// last change on a node is relevant.
while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
$changes[$row['uri']] = $row['operation'];
$highestSyncToken = $row['synctoken'];
}
$stmt->closeCursor();
// No changes found, use current token
if (empty($changes)) {
$result['syncToken'] = $currentToken;
}
foreach ($changes as $uri => $operation) {
switch ($operation) {
case 1:
@@ -915,16 +953,43 @@ class CardDavBackend implements BackendInterface, SyncSupport {
break;
}
}
/*
* The synctoken in oc_addressbooks is always the highest synctoken in oc_addressbookchanges for a given addressbook plus one (see addChange).
*
* For truncated results, it is expected that we return the highest token from the response, so the client can continue from the latest change.
*
* For non-truncated results, it is expected to return the currentToken. If we return the highest token, as with truncated results, the client will always think it is one change behind.
*
* Therefore, we differentiate between truncated and non-truncated results when returning the synctoken.
*/
if ($rowCount === $limit && $highestSyncToken < $currentToken) {
$result['syncToken'] = $highestSyncToken;
$result['result_truncated'] = true;
}
} else {
$qb = $this->db->getQueryBuilder();
$qb->select('uri')
$qb->select('id', 'uri')
->from('cards')
->where(
$qb->expr()->eq('addressbookid', $qb->createNamedParameter($addressBookId))
);
// No synctoken supplied, this is the initial sync.
$qb->setMaxResults($limit);
$stmt = $qb->executeQuery();
$result['added'] = $stmt->fetchAll(\PDO::FETCH_COLUMN);
$values = $stmt->fetchAll(\PDO::FETCH_ASSOC);
if (empty($values)) {
$result['added'] = [];
return $result;
}
$lastID = $values[array_key_last($values)]['id'];
if (count($values) >= $limit) {
$result['syncToken'] = 'init_' . $lastID . '_' . $currentToken;
$result['result_truncated'] = true;
}
$result['added'] = array_column($values, 'uri');
$stmt->closeCursor();
}
return $result;
+48 -13
View File
@@ -22,6 +22,7 @@ use Psr\Log\LoggerInterface;
use Sabre\DAV\Xml\Response\MultiStatus;
use Sabre\DAV\Xml\Service;
use Sabre\VObject\Reader;
use Sabre\Xml\ParseException;
use function is_null;
class SyncService {
@@ -43,9 +44,10 @@ class SyncService {
}
/**
* @psalm-return list{0: ?string, 1: boolean}
* @throws \Exception
*/
public function syncRemoteAddressBook(string $url, string $userName, string $addressBookUrl, string $sharedSecret, ?string $syncToken, string $targetBookHash, string $targetPrincipal, array $targetProperties): string {
public function syncRemoteAddressBook(string $url, string $userName, string $addressBookUrl, string $sharedSecret, ?string $syncToken, string $targetBookHash, string $targetPrincipal, array $targetProperties): array {
// 1. create addressbook
$book = $this->ensureSystemAddressBookExists($targetPrincipal, $targetBookHash, $targetProperties);
$addressBookId = $book['id'];
@@ -83,7 +85,10 @@ class SyncService {
}
}
return $response['token'];
return [
$response['token'],
$response['truncated'],
];
}
/**
@@ -127,7 +132,7 @@ class SyncService {
private function prepareUri(string $host, string $path): string {
/*
* The trailing slash is important for merging the uris together.
* The trailing slash is important for merging the uris.
*
* $host is stored in oc_trusted_servers.url and usually without a trailing slash.
*
@@ -158,7 +163,9 @@ class SyncService {
}
/**
* @return array{response: array<string, array<array-key, mixed>>, token: ?string, truncated: bool}
* @throws ClientExceptionInterface
* @throws ParseException
*/
protected function requestSyncReport(string $url, string $userName, string $addressBookUrl, string $sharedSecret, ?string $syncToken): array {
$client = $this->clientService->newClient();
@@ -181,7 +188,7 @@ class SyncService {
$body = $response->getBody();
assert(is_string($body));
return $this->parseMultiStatus($body);
return $this->parseMultiStatus($body, $addressBookUrl);
}
protected function download(string $url, string $userName, string $sharedSecret, string $resourcePath): string {
@@ -219,22 +226,50 @@ class SyncService {
}
/**
* @param string $body
* @return array
* @throws \Sabre\Xml\ParseException
* @return array{response: array<string, array<array-key, mixed>>, token: ?string, truncated: bool}
* @throws ParseException
*/
private function parseMultiStatus($body) {
$xml = new Service();
private function parseMultiStatus(string $body, string $addressBookUrl): array {
/** @var MultiStatus $multiStatus */
$multiStatus = $xml->expect('{DAV:}multistatus', $body);
$multiStatus = (new Service())->expect('{DAV:}multistatus', $body);
$result = [];
$truncated = false;
foreach ($multiStatus->getResponses() as $response) {
$result[$response->getHref()] = $response->getResponseProperties();
$href = $response->getHref();
if ($response->getHttpStatus() === '507' && $this->isResponseForRequestUri($href, $addressBookUrl)) {
$truncated = true;
} else {
$result[$response->getHref()] = $response->getResponseProperties();
}
}
return ['response' => $result, 'token' => $multiStatus->getSyncToken()];
return ['response' => $result, 'token' => $multiStatus->getSyncToken(), 'truncated' => $truncated];
}
/**
* Determines whether the provided response URI corresponds to the given request URI.
*/
private function isResponseForRequestUri(string $responseUri, string $requestUri): bool {
/*
* Example response uri:
*
* /remote.php/dav/addressbooks/system/system/system/
* /cloud/remote.php/dav/addressbooks/system/system/system/ (when installed in a subdirectory)
*
* Example request uri:
*
* remote.php/dav/addressbooks/system/system/system
*
* References:
* https://github.com/nextcloud/3rdparty/blob/e0a509739b13820f0a62ff9cad5d0fede00e76ee/sabre/dav/lib/DAV/Sync/Plugin.php#L172-L174
* https://github.com/nextcloud/server/blob/b40acb34a39592070d8455eb91c5364c07928c50/apps/federation/lib/SyncFederationAddressBooks.php#L41
*/
return str_ends_with(
rtrim($responseUri, '/'),
rtrim($requestUri, '/')
);
}
/**
@@ -8,7 +8,6 @@ declare(strict_types=1);
*/
namespace OCA\DAV\CardDAV;
use OCA\DAV\Exception\UnsupportedLimitOnInitialSyncException;
use OCA\Federation\TrustedServers;
use OCP\Accounts\IAccountManager;
use OCP\IConfig;
@@ -212,14 +211,7 @@ class SystemAddressbook extends AddressBook {
}
return new Card($this->carddavBackend, $this->addressBookInfo, $obj);
}
/**
* @throws UnsupportedLimitOnInitialSyncException
*/
public function getChanges($syncToken, $syncLevel, $limit = null) {
if (!$syncToken && $limit) {
throw new UnsupportedLimitOnInitialSyncException();
}
if (!$this->carddavBackend instanceof SyncSupport) {
return null;
@@ -49,7 +49,7 @@ class BlockLegacyClientPlugin extends ServerPlugin {
return;
}
$minimumSupportedDesktopVersion = $this->config->getSystemValueString('minimum.supported.desktop.version', '2.7.0');
$minimumSupportedDesktopVersion = $this->config->getSystemValueString('minimum.supported.desktop.version', '3.1.0');
$maximumSupportedDesktopVersion = $this->config->getSystemValueString('maximum.supported.desktop.version', '99.99.99');
// Check if the client is a desktop client
@@ -10,6 +10,7 @@ namespace OCA\DAV\Connector\Sabre;
use OCP\Comments\ICommentsManager;
use OCP\IUserSession;
use Sabre\DAV\ICollection;
use Sabre\DAV\PropFind;
use Sabre\DAV\Server;
use Sabre\DAV\ServerPlugin;
@@ -21,6 +22,7 @@ class CommentPropertiesPlugin extends ServerPlugin {
protected ?Server $server = null;
private array $cachedUnreadCount = [];
private array $cachedDirectories = [];
public function __construct(
private ICommentsManager $commentsManager,
@@ -41,6 +43,8 @@ class CommentPropertiesPlugin extends ServerPlugin {
*/
public function initialize(\Sabre\DAV\Server $server) {
$this->server = $server;
$this->server->on('preloadCollection', $this->preloadCollection(...));
$this->server->on('propFind', [$this, 'handleGetProperties']);
}
@@ -69,6 +73,21 @@ class CommentPropertiesPlugin extends ServerPlugin {
}
}
private function preloadCollection(PropFind $propFind, ICollection $collection):
void {
if (!($collection instanceof Directory)) {
return;
}
$collectionPath = $collection->getPath();
if (!isset($this->cachedDirectories[$collectionPath]) && $propFind->getStatus(
self::PROPERTY_NAME_UNREAD
) !== null) {
$this->cacheDirectory($collection);
$this->cachedDirectories[$collectionPath] = true;
}
}
/**
* Adds tags and favorites properties to the response,
* if requested.
@@ -85,14 +104,6 @@ class CommentPropertiesPlugin extends ServerPlugin {
return;
}
// need prefetch ?
if ($node instanceof Directory
&& $propFind->getDepth() !== 0
&& !is_null($propFind->getStatus(self::PROPERTY_NAME_UNREAD))
) {
$this->cacheDirectory($node);
}
$propFind->handle(self::PROPERTY_NAME_COUNT, function () use ($node): int {
return $this->commentsManager->getNumberOfCommentsForObject('files', (string)$node->getId());
});
+6 -6
View File
@@ -204,6 +204,9 @@ class File extends Node implements IFile {
}
}
$lengthHeader = $this->request->getHeader('content-length');
$expected = $lengthHeader !== '' ? (int)$lengthHeader : null;
if ($partStorage->instanceOfStorage(IWriteStreamStorage::class)) {
$isEOF = false;
$wrappedData = CallbackWrapper::wrap($data, null, null, null, null, function ($stream) use (&$isEOF): void {
@@ -215,7 +218,7 @@ class File extends Node implements IFile {
$count = -1;
try {
/** @var IWriteStreamStorage $partStorage */
$count = $partStorage->writeStream($internalPartPath, $wrappedData);
$count = $partStorage->writeStream($internalPartPath, $wrappedData, $expected);
} catch (GenericFileException $e) {
$logger = Server::get(LoggerInterface::class);
$logger->error('Error while writing stream to storage: ' . $e->getMessage(), ['exception' => $e, 'app' => 'webdav']);
@@ -235,10 +238,7 @@ class File extends Node implements IFile {
[$count, $result] = Files::streamCopy($data, $target, true);
fclose($target);
}
$lengthHeader = $this->request->getHeader('content-length');
$expected = $lengthHeader !== '' ? (int)$lengthHeader : -1;
if ($result === false && $expected >= 0) {
if ($result === false && $expected !== null) {
throw new Exception(
$this->l10n->t(
'Error while copying file to target location (copied: %1$s, expected filesize: %2$s)',
@@ -253,7 +253,7 @@ class File extends Node implements IFile {
// if content length is sent by client:
// double check if the file was fully received
// compare expected and actual size
if ($expected >= 0
if ($expected !== null
&& $expected !== $count
&& $this->request->getMethod() === 'PUT'
) {
@@ -0,0 +1,82 @@
<?php
declare(strict_types = 1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\DAV\Connector\Sabre;
use Sabre\DAV\Server as SabreServer;
use Sabre\DAV\ServerPlugin;
use Sabre\HTTP\RequestInterface;
use Sabre\HTTP\ResponseInterface;
/**
* This plugin runs after requests and logs an error if a plugin is detected
* to be doing too many SQL requests.
*/
class PropFindMonitorPlugin extends ServerPlugin {
/**
* A Plugin can scan up to this amount of nodes without an error being
* reported.
*/
public const THRESHOLD_NODES = 50;
/**
* A plugin can use up to this amount of queries per node.
*/
public const THRESHOLD_QUERY_FACTOR = 1;
private SabreServer $server;
public function initialize(SabreServer $server): void {
$this->server = $server;
$this->server->on('afterResponse', [$this, 'afterResponse']);
}
public function afterResponse(
RequestInterface $request,
ResponseInterface $response): void {
if (!$this->server instanceof Server) {
return;
}
$pluginQueries = $this->server->getPluginQueries();
if (empty($pluginQueries)) {
return;
}
$logger = $this->server->getLogger();
foreach ($pluginQueries as $eventName => $eventQueries) {
$maxDepth = max(0, ...array_keys($eventQueries));
// entries at the top are usually not interesting
unset($eventQueries[$maxDepth]);
foreach ($eventQueries as $depth => $propFinds) {
foreach ($propFinds as $pluginName => $propFind) {
[
'queries' => $queries,
'nodes' => $nodes
] = $propFind;
if ($queries === 0 || $nodes > $queries || $nodes < self::THRESHOLD_NODES
|| $queries < $nodes * self::THRESHOLD_QUERY_FACTOR) {
continue;
}
$logger->error(
'{name}:{event} scanned {scans} nodes with {count} queries in depth {depth}/{maxDepth}. This is bad for performance, please report to the plugin developer!',
[
'name' => $pluginName,
'scans' => $nodes,
'count' => $queries,
'depth' => $depth,
'maxDepth' => $maxDepth,
'event' => $eventName,
]
);
}
}
}
}
}
@@ -0,0 +1,55 @@
<?php
declare(strict_types = 1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\DAV\Connector\Sabre;
use Sabre\DAV\ICollection;
use Sabre\DAV\INode;
use Sabre\DAV\PropFind;
use Sabre\DAV\Server;
use Sabre\DAV\ServerPlugin;
/**
* This plugin asks other plugins to preload data for a collection, so that
* subsequent PROPFIND handlers for children do not query the DB on a per-node
* basis.
*/
class PropFindPreloadNotifyPlugin extends ServerPlugin {
private Server $server;
public function initialize(Server $server): void {
$this->server = $server;
$this->server->on('propFind', [$this, 'collectionPreloadNotifier' ], 1);
}
/**
* Uses the server instance to emit a `preloadCollection` event to signal
* to interested plugins that a collection can be preloaded.
*
* NOTE: this can be emitted several times, so ideally every plugin
* should cache what they need and check if a cache exists before
* re-fetching.
*/
public function collectionPreloadNotifier(PropFind $propFind, INode $node): bool {
if (!$this->shouldPreload($propFind, $node)) {
return true;
}
return $this->server->emit('preloadCollection', [$propFind, $node]);
}
private function shouldPreload(
PropFind $propFind,
INode $node,
): bool {
$depth = $propFind->getDepth();
return $node instanceof ICollection
&& ($depth === Server::DEPTH_INFINITY || $depth > 0);
}
}
+122
View File
@@ -7,7 +7,11 @@
*/
namespace OCA\DAV\Connector\Sabre;
use OC\DB\Connection;
use Override;
use Sabre\DAV\Exception;
use Sabre\DAV\INode;
use Sabre\DAV\PropFind;
use Sabre\DAV\Version;
use TypeError;
@@ -21,6 +25,15 @@ use TypeError;
class Server extends \Sabre\DAV\Server {
/** @var CachingTree $tree */
/**
* Tracks queries done by plugins.
* @var array<string, array<int, array<string, array{nodes:int,
* queries:int}>>> The keys represent: event name, depth and plugin name
*/
private array $pluginQueries = [];
public bool $debugEnabled = false;
/**
* @see \Sabre\DAV\Server
*/
@@ -30,6 +43,106 @@ class Server extends \Sabre\DAV\Server {
$this->enablePropfindDepthInfinity = true;
}
#[Override]
public function once(
string $eventName,
callable $callBack,
int $priority = 100,
): void {
$this->debugEnabled ? $this->monitorPropfindQueries(
parent::once(...),
...\func_get_args(),
) : parent::once(...\func_get_args());
}
#[Override]
public function on(
string $eventName,
callable $callBack,
int $priority = 100,
): void {
$this->debugEnabled ? $this->monitorPropfindQueries(
parent::on(...),
...\func_get_args(),
) : parent::on(...\func_get_args());
}
/**
* Wraps the handler $callBack into a query-monitoring function and calls
* $parentFn to register it.
*/
private function monitorPropfindQueries(
callable $parentFn,
string $eventName,
callable $callBack,
int $priority = 100,
): void {
$pluginName = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3)[2]['class'] ?? 'unknown';
// The NotifyPlugin needs to be excluded as it emits the
// `preloadCollection` event, which causes many plugins run queries.
/** @psalm-suppress TypeDoesNotContainType */
if ($pluginName === PropFindPreloadNotifyPlugin::class || ($eventName !== 'propFind'
&& $eventName !== 'preloadCollection')) {
$parentFn($eventName, $callBack, $priority);
return;
}
$callback = $this->getMonitoredCallback($callBack, $pluginName, $eventName);
$parentFn($eventName, $callback, $priority);
}
/**
* Returns a callable that wraps $callBack with code that monitors and
* records queries per plugin.
*/
private function getMonitoredCallback(
callable $callBack,
string $pluginName,
string $eventName,
): callable {
return function (PropFind $propFind, INode $node) use (
$callBack,
$pluginName,
$eventName,
): bool {
$connection = \OCP\Server::get(Connection::class);
$queriesBefore = $connection->getStats()['executed'];
$result = $callBack($propFind, $node);
$queriesAfter = $connection->getStats()['executed'];
$this->trackPluginQueries(
$pluginName,
$eventName,
$queriesAfter - $queriesBefore,
$propFind->getDepth()
);
// many callbacks don't care about returning a bool
return $result ?? true;
};
}
/**
* Tracks the queries executed by a specific plugin.
*/
private function trackPluginQueries(
string $pluginName,
string $eventName,
int $queriesExecuted,
int $depth,
): void {
// report only nodes which cause queries to the DB
if ($queriesExecuted === 0) {
return;
}
$this->pluginQueries[$eventName][$depth][$pluginName]['nodes']
= ($this->pluginQueries[$eventName][$depth][$pluginName]['nodes'] ?? 0) + 1;
$this->pluginQueries[$eventName][$depth][$pluginName]['queries']
= ($this->pluginQueries[$eventName][$depth][$pluginName]['queries'] ?? 0) + $queriesExecuted;
}
/**
*
* @return void
@@ -115,4 +228,13 @@ class Server extends \Sabre\DAV\Server {
$this->sapi->sendResponse($this->httpResponse);
}
}
/**
* Returns queries executed by registered plugins.
* @return array<string, array<int, array<string, array{nodes:int,
* queries:int}>>> The keys represent: event name, depth and plugin name
*/
public function getPluginQueries(): array {
return $this->pluginQueries;
}
}
+12 -2
View File
@@ -14,6 +14,7 @@ use OCA\DAV\CalDAV\DefaultCalendarValidator;
use OCA\DAV\CalDAV\Proxy\ProxyMapper;
use OCA\DAV\DAV\CustomPropertiesBackend;
use OCA\DAV\DAV\ViewOnlyPlugin;
use OCA\DAV\Db\PropertyMapper;
use OCA\DAV\Files\BrowserErrorPagePlugin;
use OCA\DAV\Files\Sharing\RootCollection;
use OCA\DAV\Upload\CleanupService;
@@ -68,6 +69,7 @@ class ServerFactory {
Plugin $authPlugin,
callable $viewCallBack,
): Server {
$debugEnabled = $this->config->getSystemValue('debug', false);
// Fire up server
if ($isPublicShare) {
$rootCollection = new SimpleCollection('root');
@@ -89,6 +91,12 @@ class ServerFactory {
));
$server->addPlugin(new AnonymousOptionsPlugin());
$server->addPlugin($authPlugin);
if ($debugEnabled) {
$server->debugEnabled = $debugEnabled;
$server->addPlugin(new PropFindMonitorPlugin());
}
$server->addPlugin(new PropFindPreloadNotifyPlugin());
// FIXME: The following line is a workaround for legacy components relying on being able to send a GET to /
$server->addPlugin(new DummyGetResponsePlugin());
$server->addPlugin(new ExceptionLoggerPlugin('webdav', $this->logger));
@@ -117,7 +125,8 @@ class ServerFactory {
}
// wait with registering these until auth is handled and the filesystem is setup
$server->on('beforeMethod:*', function () use ($server, $tree, $viewCallBack, $isPublicShare, $rootCollection): void {
$server->on('beforeMethod:*', function () use ($server, $tree,
$viewCallBack, $isPublicShare, $rootCollection, $debugEnabled): void {
// ensure the skeleton is copied
$userFolder = \OC::$server->getUserFolder();
@@ -181,7 +190,7 @@ class ServerFactory {
\OCP\Server::get(IFilenameValidator::class),
\OCP\Server::get(IAccountManager::class),
false,
!$this->config->getSystemValue('debug', false)
!$debugEnabled
)
);
$server->addPlugin(new QuotaPlugin($view));
@@ -220,6 +229,7 @@ class ServerFactory {
$tree,
$this->databaseConnection,
$this->userSession->getUser(),
\OCP\Server::get(PropertyMapper::class),
\OCP\Server::get(DefaultCalendarValidator::class),
)
)
+37 -25
View File
@@ -15,6 +15,7 @@ use OCP\Files\NotFoundException;
use OCP\IUserSession;
use OCP\Share\IManager;
use OCP\Share\IShare;
use Sabre\DAV\ICollection;
use Sabre\DAV\PropFind;
use Sabre\DAV\Server;
use Sabre\DAV\Tree;
@@ -38,7 +39,14 @@ class SharesPlugin extends \Sabre\DAV\ServerPlugin {
/** @var IShare[][] */
private array $cachedShares = [];
/** @var string[] */
/**
* Tracks which folders have been cached.
* When a folder is cached, it will appear with its path as key and true
* as value.
*
* @var bool[]
*/
private array $cachedFolders = [];
public function __construct(
@@ -67,6 +75,7 @@ class SharesPlugin extends \Sabre\DAV\ServerPlugin {
$server->protectedProperties[] = self::SHAREES_PROPERTYNAME;
$this->server = $server;
$this->server->on('preloadCollection', $this->preloadCollection(...));
$this->server->on('propFind', [$this, 'handleGetProperties']);
}
@@ -89,28 +98,28 @@ class SharesPlugin extends \Sabre\DAV\ServerPlugin {
];
foreach ($requestedShareTypes as $requestedShareType) {
$result = array_merge($result, $this->shareManager->getSharesBy(
$result[] = $this->shareManager->getSharesBy(
$this->userId,
$requestedShareType,
$node,
false,
-1
));
);
// Also check for shares where the user is the recipient
try {
$result = array_merge($result, $this->shareManager->getSharedWith(
$result[] = $this->shareManager->getSharedWith(
$this->userId,
$requestedShareType,
$node,
-1
));
);
} catch (BackendError $e) {
// ignore
}
}
return $result;
return array_merge(...$result);
}
/**
@@ -141,7 +150,7 @@ class SharesPlugin extends \Sabre\DAV\ServerPlugin {
// if we already cached the folder containing this file
// then we already know there are no shares here.
if (array_search($parentPath, $this->cachedFolders) === false) {
if (!isset($this->cachedFolders[$parentPath])) {
try {
$node = $sabreNode->getNode();
} catch (NotFoundException $e) {
@@ -156,6 +165,27 @@ class SharesPlugin extends \Sabre\DAV\ServerPlugin {
return [];
}
private function preloadCollection(PropFind $propFind, ICollection $collection): void {
if (!$collection instanceof Directory
|| isset($this->cachedFolders[$collection->getPath()])
|| (
$propFind->getStatus(self::SHARETYPES_PROPERTYNAME) === null
&& $propFind->getStatus(self::SHAREES_PROPERTYNAME) === null
)
) {
return;
}
// If the node is a directory and we are requesting share types or sharees
// then we get all the shares in the folder and cache them.
// This is more performant than iterating each files afterwards.
$folderNode = $collection->getNode();
$this->cachedFolders[$collection->getPath()] = true;
foreach ($this->getSharesFolder($folderNode) as $id => $shares) {
$this->cachedShares[$id] = $shares;
}
}
/**
* Adds shares to propfind response
*
@@ -170,24 +200,6 @@ class SharesPlugin extends \Sabre\DAV\ServerPlugin {
return;
}
// If the node is a directory and we are requesting share types or sharees
// then we get all the shares in the folder and cache them.
// This is more performant than iterating each files afterwards.
if ($sabreNode instanceof Directory
&& $propFind->getDepth() !== 0
&& (
!is_null($propFind->getStatus(self::SHARETYPES_PROPERTYNAME))
|| !is_null($propFind->getStatus(self::SHAREES_PROPERTYNAME))
)
) {
$folderNode = $sabreNode->getNode();
$this->cachedFolders[] = $sabreNode->getPath();
$childShares = $this->getSharesFolder($folderNode);
foreach ($childShares as $id => $shares) {
$this->cachedShares[$id] = $shares;
}
}
$propFind->handle(self::SHARETYPES_PROPERTYNAME, function () use ($sabreNode): ShareTypeList {
$shares = $this->getShares($sabreNode);
+26 -15
View File
@@ -31,6 +31,7 @@ use OCP\EventDispatcher\IEventDispatcher;
use OCP\ITagManager;
use OCP\ITags;
use OCP\IUserSession;
use Sabre\DAV\ICollection;
use Sabre\DAV\PropFind;
use Sabre\DAV\PropPatch;
@@ -61,6 +62,7 @@ class TagsPlugin extends \Sabre\DAV\ServerPlugin {
* @var array
*/
private $cachedTags;
private array $cachedDirectories;
/**
* @param \Sabre\DAV\Tree $tree tree
@@ -92,6 +94,7 @@ class TagsPlugin extends \Sabre\DAV\ServerPlugin {
$server->xml->elementMap[self::TAGS_PROPERTYNAME] = TagList::class;
$this->server = $server;
$this->server->on('preloadCollection', $this->preloadCollection(...));
$this->server->on('propFind', [$this, 'handleGetProperties']);
$this->server->on('propPatch', [$this, 'handleUpdateProperties']);
$this->server->on('preloadProperties', [$this, 'handlePreloadProperties']);
@@ -194,6 +197,29 @@ class TagsPlugin extends \Sabre\DAV\ServerPlugin {
}
}
private function preloadCollection(PropFind $propFind, ICollection $collection):
void {
if (!($collection instanceof Node)) {
return;
}
// need prefetch ?
if ($collection instanceof Directory
&& !isset($this->cachedDirectories[$collection->getPath()])
&& (!is_null($propFind->getStatus(self::TAGS_PROPERTYNAME))
|| !is_null($propFind->getStatus(self::FAVORITE_PROPERTYNAME))
)) {
// note: pre-fetching only supported for depth <= 1
$folderContent = $collection->getChildren();
$fileIds = [(int)$collection->getId()];
foreach ($folderContent as $info) {
$fileIds[] = (int)$info->getId();
}
$this->prefetchTagsForFileIds($fileIds);
$this->cachedDirectories[$collection->getPath()] = true;
}
}
/**
* Adds tags and favorites properties to the response,
* if requested.
@@ -210,21 +236,6 @@ class TagsPlugin extends \Sabre\DAV\ServerPlugin {
return;
}
// need prefetch ?
if ($node instanceof Directory
&& $propFind->getDepth() !== 0
&& (!is_null($propFind->getStatus(self::TAGS_PROPERTYNAME))
|| !is_null($propFind->getStatus(self::FAVORITE_PROPERTYNAME))
)) {
// note: pre-fetching only supported for depth <= 1
$folderContent = $node->getChildren();
$fileIds = [(int)$node->getId()];
foreach ($folderContent as $info) {
$fileIds[] = (int)$info->getId();
}
$this->prefetchTagsForFileIds($fileIds);
}
$isFav = null;
$propFind->handle(self::TAGS_PROPERTYNAME, function () use (&$isFav, $node) {
@@ -67,15 +67,16 @@ class ZipFolderPlugin extends ServerPlugin {
// Remove the root path from the filename to make it relative to the requested folder
$filename = str_replace($rootPath, '', $node->getPath());
$mtime = $node->getMTime();
if ($node instanceof NcFile) {
$resource = $node->fopen('rb');
if ($resource === false) {
$this->logger->info('Cannot read file for zip stream', ['filePath' => $node->getPath()]);
throw new \Sabre\DAV\Exception\ServiceUnavailable('Requested file can currently not be accessed.');
}
$streamer->addFileFromStream($resource, $filename, $node->getSize(), $node->getMTime());
$streamer->addFileFromStream($resource, $filename, $node->getSize(), $mtime);
} elseif ($node instanceof NcFolder) {
$streamer->addEmptyDir($filename);
$streamer->addEmptyDir($filename, $mtime);
$content = $node->getDirectoryListing();
foreach ($content as $subNode) {
$this->streamNode($streamer, $subNode, $rootPath);
+122 -44
View File
@@ -9,13 +9,20 @@
namespace OCA\DAV\DAV;
use Exception;
use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\CalDAV\Calendar;
use OCA\DAV\CalDAV\CalendarHome;
use OCA\DAV\CalDAV\CalendarObject;
use OCA\DAV\CalDAV\DefaultCalendarValidator;
use OCA\DAV\CalDAV\Integration\ExternalCalendar;
use OCA\DAV\CalDAV\Outbox;
use OCA\DAV\CalDAV\Trashbin\TrashbinHome;
use OCA\DAV\Connector\Sabre\Directory;
use OCA\DAV\Connector\Sabre\FilesPlugin;
use OCA\DAV\Db\PropertyMapper;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use OCP\IUser;
use Sabre\CalDAV\Schedule\Inbox;
use Sabre\DAV\Exception as DavException;
use Sabre\DAV\PropertyStorage\Backend\BackendInterface;
use Sabre\DAV\PropFind;
@@ -66,38 +73,16 @@ class CustomPropertiesBackend implements BackendInterface {
'{DAV:}getetag',
'{DAV:}quota-used-bytes',
'{DAV:}quota-available-bytes',
'{http://owncloud.org/ns}permissions',
'{http://owncloud.org/ns}downloadURL',
'{http://owncloud.org/ns}dDC',
'{http://owncloud.org/ns}size',
'{http://nextcloud.org/ns}is-encrypted',
];
// Currently, returning null from any propfind handler would still trigger the backend,
// so we add all known Nextcloud custom properties in here to avoid that
// text app
'{http://nextcloud.org/ns}rich-workspace',
'{http://nextcloud.org/ns}rich-workspace-file',
// groupfolders
'{http://nextcloud.org/ns}acl-enabled',
'{http://nextcloud.org/ns}acl-can-manage',
'{http://nextcloud.org/ns}acl-list',
'{http://nextcloud.org/ns}inherited-acl-list',
'{http://nextcloud.org/ns}group-folder-id',
// files_lock
'{http://nextcloud.org/ns}lock',
'{http://nextcloud.org/ns}lock-owner-type',
'{http://nextcloud.org/ns}lock-owner',
'{http://nextcloud.org/ns}lock-owner-displayname',
'{http://nextcloud.org/ns}lock-owner-editor',
'{http://nextcloud.org/ns}lock-time',
'{http://nextcloud.org/ns}lock-timeout',
'{http://nextcloud.org/ns}lock-token',
// photos
'{http://nextcloud.org/ns}realpath',
'{http://nextcloud.org/ns}nbItems',
'{http://nextcloud.org/ns}face-detections',
'{http://nextcloud.org/ns}face-preview-image',
/**
* Allowed properties for the oc/nc namespace, all other properties in the namespace are ignored
*
* @var string[]
*/
private const ALLOWED_NC_PROPERTIES = [
'{http://owncloud.org/ns}calendar-enabled',
'{http://owncloud.org/ns}enabled',
];
/**
@@ -119,11 +104,17 @@ class CustomPropertiesBackend implements BackendInterface {
];
/**
* Properties cache
*
* @var array
* Map of well-known property names to default values
*/
private $userCache = [];
private const PROPERTY_DEFAULT_VALUES = [
'{http://owncloud.org/ns}calendar-enabled' => '1',
];
/**
* Properties cache
*/
private array $userCache = [];
private array $publishedCache = [];
private XmlService $xmlService;
/**
@@ -136,6 +127,7 @@ class CustomPropertiesBackend implements BackendInterface {
private Tree $tree,
private IDBConnection $connection,
private IUser $user,
private PropertyMapper $propertyMapper,
private DefaultCalendarValidator $defaultCalendarValidator,
) {
$this->xmlService = new XmlService();
@@ -155,14 +147,9 @@ class CustomPropertiesBackend implements BackendInterface {
public function propFind($path, PropFind $propFind) {
$requestedProps = $propFind->get404Properties();
// these might appear
$requestedProps = array_diff(
$requestedProps,
self::IGNORED_PROPERTIES,
);
$requestedProps = array_filter(
$requestedProps,
fn ($prop) => !str_starts_with($prop, FilesPlugin::FILE_METADATA_PREFIX),
$this->isPropertyAllowed(...),
);
// substr of calendars/ => path is inside the CalDAV component
@@ -224,6 +211,18 @@ class CustomPropertiesBackend implements BackendInterface {
$this->cacheDirectory($path, $node);
}
if ($node instanceof CalendarHome && $propFind->getDepth() !== 0) {
$backend = $node->getCalDAVBackend();
if ($backend instanceof CalDavBackend) {
$this->cacheCalendars($node, $requestedProps);
}
}
if ($node instanceof CalendarObject) {
// No custom properties supported on individual events
return;
}
// First fetch the published properties (set by another user), then get the ones set by
// the current user. If both are set then the latter as priority.
foreach ($this->getPublishedProperties($path, $requestedProps) as $propName => $propValue) {
@@ -244,6 +243,16 @@ class CustomPropertiesBackend implements BackendInterface {
}
}
private function isPropertyAllowed(string $property): bool {
if (in_array($property, self::IGNORED_PROPERTIES)) {
return false;
}
if (str_starts_with($property, '{http://owncloud.org/ns}') || str_starts_with($property, '{http://nextcloud.org/ns}')) {
return in_array($property, self::ALLOWED_NC_PROPERTIES);
}
return true;
}
/**
* Updates properties for a path
*
@@ -328,6 +337,10 @@ class CustomPropertiesBackend implements BackendInterface {
return [];
}
if (isset($this->publishedCache[$path])) {
return $this->publishedCache[$path];
}
$qb = $this->connection->getQueryBuilder();
$qb->select('*')
->from(self::TABLE_NAME)
@@ -338,6 +351,7 @@ class CustomPropertiesBackend implements BackendInterface {
$props[$row['propertyname']] = $this->decodeValueFromDatabase($row['propertyvalue'], $row['valuetype']);
}
$result->closeCursor();
$this->publishedCache[$path] = $props;
return $props;
}
@@ -376,6 +390,62 @@ class CustomPropertiesBackend implements BackendInterface {
$this->userCache = array_merge($this->userCache, $propsByPath);
}
private function cacheCalendars(CalendarHome $node, array $requestedProperties): void {
$calendars = $node->getChildren();
$users = [];
foreach ($calendars as $calendar) {
if ($calendar instanceof Calendar) {
$user = str_replace('principals/users/', '', $calendar->getPrincipalURI());
if (!isset($users[$user])) {
$users[$user] = ['calendars/' . $user];
}
$users[$user][] = 'calendars/' . $user . '/' . $calendar->getUri();
} elseif ($calendar instanceof Inbox || $calendar instanceof Outbox || $calendar instanceof TrashbinHome || $calendar instanceof ExternalCalendar) {
if ($calendar->getOwner()) {
$user = str_replace('principals/users/', '', $calendar->getOwner());
if (!isset($users[$user])) {
$users[$user] = ['calendars/' . $user];
}
$users[$user][] = 'calendars/' . $user . '/' . $calendar->getName();
}
}
}
// user properties
$properties = $this->propertyMapper->findPropertiesByPathsAndUsers($users);
$propsByPath = [];
foreach ($users as $paths) {
foreach ($paths as $path) {
$propsByPath[$path] = [];
}
}
foreach ($properties as $property) {
$propsByPath[$property->getPropertypath()][$property->getPropertyname()] = $this->decodeValueFromDatabase($property->getPropertyvalue(), $property->getValuetype());
}
$this->userCache = array_merge($this->userCache, $propsByPath);
// published properties
$allowedProps = array_intersect(self::PUBLISHED_READ_ONLY_PROPERTIES, $requestedProperties);
if (empty($allowedProps)) {
return;
}
$paths = [];
foreach ($users as $nestedPaths) {
$paths = array_merge($paths, $nestedPaths);
}
$paths = array_unique($paths);
$propsByPath = array_fill_keys(array_values($paths), []);
$properties = $this->propertyMapper->findPropertiesByPaths($paths, $allowedProps);
foreach ($properties as $property) {
$propsByPath[$property->getPropertypath()][$property->getPropertyname()] = $this->decodeValueFromDatabase($property->getPropertyvalue(), $property->getValuetype());
}
$this->publishedCache = array_merge($this->publishedCache, $propsByPath);
}
/**
* Returns a list of properties for the given path and current user
*
@@ -422,6 +492,14 @@ class CustomPropertiesBackend implements BackendInterface {
return $props;
}
private function isPropertyDefaultValue(string $name, mixed $value): bool {
if (!isset(self::PROPERTY_DEFAULT_VALUES[$name])) {
return false;
}
return self::PROPERTY_DEFAULT_VALUES[$name] === $value;
}
/**
* @throws Exception
*/
@@ -438,8 +516,8 @@ class CustomPropertiesBackend implements BackendInterface {
'propertyName' => $propertyName,
];
// If it was null, we need to delete the property
if (is_null($propertyValue)) {
// If it was null or set to the default value, we need to delete the property
if (is_null($propertyValue) || $this->isPropertyDefaultValue($propertyName, $propertyValue)) {
if (array_key_exists($propertyName, $existing)) {
$deleteQuery = $deleteQuery ?? $this->createDeleteQuery();
$deleteQuery
+20 -14
View File
@@ -16,6 +16,7 @@ use OCP\AppFramework\Http;
use OCP\IConfig;
use OCP\IRequest;
use Sabre\DAV\Exception\NotFound;
use Sabre\DAV\ICollection;
use Sabre\DAV\INode;
use Sabre\DAV\PropFind;
use Sabre\DAV\Server;
@@ -89,6 +90,7 @@ class Plugin extends ServerPlugin {
$this->server->xml->elementMap['{' . Plugin::NS_OWNCLOUD . '}invite'] = Invite::class;
$this->server->on('method:POST', [$this, 'httpPost']);
$this->server->on('preloadCollection', $this->preloadCollection(...));
$this->server->on('propFind', [$this, 'propFind']);
}
@@ -168,6 +170,24 @@ class Plugin extends ServerPlugin {
}
}
private function preloadCollection(PropFind $propFind, ICollection $collection): void {
if (!$collection instanceof CalendarHome || $propFind->getDepth() !== 1) {
return;
}
$backend = $collection->getCalDAVBackend();
if (!$backend instanceof CalDavBackend) {
return;
}
$calendars = $collection->getChildren();
$calendars = array_filter($calendars, static fn (INode $node) => $node instanceof IShareable);
/** @var int[] $resourceIds */
$resourceIds = array_map(
static fn (IShareable $node) => $node->getResourceId(), $calendars);
$backend->preloadShares($resourceIds);
}
/**
* This event is triggered when properties are requested for a certain
* node.
@@ -179,20 +199,6 @@ class Plugin extends ServerPlugin {
* @return void
*/
public function propFind(PropFind $propFind, INode $node) {
if ($node instanceof CalendarHome && $propFind->getDepth() === 1) {
$backend = $node->getCalDAVBackend();
if ($backend instanceof CalDavBackend) {
$calendars = $node->getChildren();
$calendars = array_filter($calendars, function (INode $node) {
return $node instanceof IShareable;
});
/** @var int[] $resourceIds */
$resourceIds = array_map(function (IShareable $node) {
return $node->getResourceId();
}, $calendars);
$backend->preloadShares($resourceIds);
}
}
if ($node instanceof IShareable) {
$propFind->handle('{' . Plugin::NS_OWNCLOUD . '}invite', function () use ($node) {
return new Invite(
+1
View File
@@ -16,6 +16,7 @@ use OCP\AppFramework\Db\Entity;
* @method string getPropertypath()
* @method string getPropertyname()
* @method string getPropertyvalue()
* @method int getValuetype()
*/
class Property extends Entity {
+32 -5
View File
@@ -10,6 +10,7 @@ declare(strict_types=1);
namespace OCA\DAV\Db;
use OCP\AppFramework\Db\QBMapper;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
/**
@@ -39,17 +40,43 @@ class PropertyMapper extends QBMapper {
}
/**
* @param array<string, string[]> $calendars
* @return Property[]
* @throws \OCP\DB\Exception
*/
public function findPropertiesByPath(string $userId, string $path): array {
public function findPropertiesByPathsAndUsers(array $calendars): array {
$selectQb = $this->db->getQueryBuilder();
$selectQb->select('*')
->from(self::TABLE_NAME)
->where(
$selectQb->expr()->eq('userid', $selectQb->createNamedParameter($userId)),
$selectQb->expr()->eq('propertypath', $selectQb->createNamedParameter($path)),
->from(self::TABLE_NAME);
foreach ($calendars as $user => $paths) {
$selectQb->orWhere(
$selectQb->expr()->andX(
$selectQb->expr()->eq('userid', $selectQb->createNamedParameter($user)),
$selectQb->expr()->in('propertypath', $selectQb->createNamedParameter($paths, IQueryBuilder::PARAM_STR_ARRAY)),
)
);
}
return $this->findEntities($selectQb);
}
/**
* @param string[] $calendars
* @param string[] $allowedProperties
* @return Property[]
* @throws \OCP\DB\Exception
*/
public function findPropertiesByPaths(array $calendars, array $allowedProperties = []): array {
$selectQb = $this->db->getQueryBuilder();
$selectQb->select('*')
->from(self::TABLE_NAME)
->where($selectQb->expr()->in('propertypath', $selectQb->createNamedParameter($calendars, IQueryBuilder::PARAM_STR_ARRAY)));
if ($allowedProperties) {
$selectQb->andWhere($selectQb->expr()->in('propertyname', $selectQb->createNamedParameter($allowedProperties, IQueryBuilder::PARAM_STR_ARRAY)));
}
return $this->findEntities($selectQb);
}
}
@@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\DAV\Migration;
use Closure;
use OCP\DB\ISchemaWrapper;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;
use Override;
class Version1034Date20250813093701 extends SimpleMigrationStep {
public function __construct(
private IDBConnection $db,
) {
}
/**
* @param IOutput $output
* @param Closure(): ISchemaWrapper $schemaClosure
* @param array $options
*/
#[Override]
public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
$qb = $this->db->getQueryBuilder();
$qb->delete('properties')
->where($qb->expr()->eq(
'propertyname',
$qb->createNamedParameter(
'{http://owncloud.org/ns}calendar-enabled',
IQueryBuilder::PARAM_STR,
),
IQueryBuilder::PARAM_STR,
))
->andWhere($qb->expr()->eq(
'propertyvalue',
$qb->createNamedParameter(
'1',
IQueryBuilder::PARAM_STR,
),
IQueryBuilder::PARAM_STR,
))
->executeStatement();
}
}
+3
View File
@@ -132,6 +132,7 @@ class RootCollection extends SimpleCollection {
);
$contactsSharingBackend = Server::get(\OCA\DAV\CardDAV\Sharing\Backend::class);
$config = Server::get(IConfig::class);
$pluginManager = new PluginManager(\OC::$server, Server::get(IAppManager::class));
$usersCardDavBackend = new CardDavBackend(
@@ -140,6 +141,7 @@ class RootCollection extends SimpleCollection {
$userManager,
$dispatcher,
$contactsSharingBackend,
$config
);
$usersAddressBookRoot = new AddressBookRoot($userPrincipalBackend, $usersCardDavBackend, $pluginManager, $userSession->getUser(), $groupManager, 'principals/users');
$usersAddressBookRoot->disableListing = $disableListing;
@@ -150,6 +152,7 @@ class RootCollection extends SimpleCollection {
$userManager,
$dispatcher,
$contactsSharingBackend,
$config
);
$systemAddressBookRoot = new AddressBookRoot(new SystemPrincipalBackend(), $systemCardDavBackend, $pluginManager, $userSession->getUser(), $groupManager, 'principals/system');
$systemAddressBookRoot->disableListing = $disableListing;
+11 -1
View File
@@ -45,6 +45,8 @@ use OCA\DAV\Connector\Sabre\FilesReportPlugin;
use OCA\DAV\Connector\Sabre\LockPlugin;
use OCA\DAV\Connector\Sabre\MaintenancePlugin;
use OCA\DAV\Connector\Sabre\PropfindCompressionPlugin;
use OCA\DAV\Connector\Sabre\PropFindMonitorPlugin;
use OCA\DAV\Connector\Sabre\PropFindPreloadNotifyPlugin;
use OCA\DAV\Connector\Sabre\QuotaPlugin;
use OCA\DAV\Connector\Sabre\RequestIdHeaderPlugin;
use OCA\DAV\Connector\Sabre\SharesPlugin;
@@ -53,6 +55,7 @@ use OCA\DAV\Connector\Sabre\ZipFolderPlugin;
use OCA\DAV\DAV\CustomPropertiesBackend;
use OCA\DAV\DAV\PublicAuth;
use OCA\DAV\DAV\ViewOnlyPlugin;
use OCA\DAV\Db\PropertyMapper;
use OCA\DAV\Events\SabrePluginAddEvent;
use OCA\DAV\Events\SabrePluginAuthInitEvent;
use OCA\DAV\Files\BrowserErrorPagePlugin;
@@ -108,6 +111,7 @@ class Server {
private IRequest $request,
private string $baseUri,
) {
$debugEnabled = \OCP\Server::get(IConfig::class)->getSystemValue('debug', false);
$this->profiler = \OCP\Server::get(IProfiler::class);
if ($this->profiler->isEnabled()) {
/** @var IEventLogger $eventLogger */
@@ -120,6 +124,7 @@ class Server {
$root = new RootCollection();
$this->server = new \OCA\DAV\Connector\Sabre\Server(new CachingTree($root));
$this->server->setLogger($logger);
// Add maintenance plugin
$this->server->addPlugin(new MaintenancePlugin(\OCP\Server::get(IConfig::class), \OC::$server->getL10N('dav')));
@@ -167,7 +172,9 @@ class Server {
$authPlugin->addBackend($authBackend);
// debugging
if (\OCP\Server::get(IConfig::class)->getSystemValue('debug', false)) {
if ($debugEnabled) {
$this->server->debugEnabled = true;
$this->server->addPlugin(new PropFindMonitorPlugin());
$this->server->addPlugin(new \Sabre\DAV\Browser\Plugin());
} else {
$this->server->addPlugin(new DummyGetResponsePlugin());
@@ -232,6 +239,7 @@ class Server {
\OCP\Server::get(IUserSession::class)
));
// performance improvement plugins
$this->server->addPlugin(new CopyEtagHeaderPlugin());
$this->server->addPlugin(new RequestIdHeaderPlugin(\OCP\Server::get(IRequest::class)));
$this->server->addPlugin(new UploadAutoMkcolPlugin());
@@ -243,6 +251,7 @@ class Server {
$eventDispatcher,
));
$this->server->addPlugin(\OCP\Server::get(PaginatePlugin::class));
$this->server->addPlugin(new PropFindPreloadNotifyPlugin());
// allow setup of additional plugins
$eventDispatcher->dispatch('OCA\DAV\Connector\Sabre::addPlugin', $event);
@@ -301,6 +310,7 @@ class Server {
$this->server->tree,
\OCP\Server::get(IDBConnection::class),
\OCP\Server::get(IUserSession::class)->getUser(),
\OCP\Server::get(PropertyMapper::class),
\OCP\Server::get(DefaultCalendarValidator::class),
)
)
+36 -23
View File
@@ -27,6 +27,7 @@ use Sabre\DAV\Exception\BadRequest;
use Sabre\DAV\Exception\Conflict;
use Sabre\DAV\Exception\Forbidden;
use Sabre\DAV\Exception\UnsupportedMediaType;
use Sabre\DAV\ICollection;
use Sabre\DAV\PropFind;
use Sabre\DAV\PropPatch;
use Sabre\HTTP\RequestInterface;
@@ -94,6 +95,7 @@ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin {
$server->protectedProperties[] = self::ID_PROPERTYNAME;
$server->on('preloadCollection', $this->preloadCollection(...));
$server->on('propFind', [$this, 'handleGetProperties']);
$server->on('propPatch', [$this, 'handleUpdateProperties']);
$server->on('method:POST', [$this, 'httpPost']);
@@ -199,6 +201,40 @@ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin {
}
}
private function preloadCollection(
PropFind $propFind,
ICollection $collection,
): void {
if (!$collection instanceof Node) {
return;
}
if ($collection instanceof Directory
&& !isset($this->cachedTagMappings[$collection->getId()])
&& $propFind->getStatus(
self::SYSTEM_TAGS_PROPERTYNAME
) !== null) {
$fileIds = [$collection->getId()];
// note: pre-fetching only supported for depth <= 1
$folderContent = $collection->getChildren();
foreach ($folderContent as $info) {
if ($info instanceof Node) {
$fileIds[] = $info->getId();
}
}
$tags = $this->tagMapper->getTagIdsForObjects($fileIds, 'files');
$this->cachedTagMappings += $tags;
$emptyFileIds = array_diff($fileIds, array_keys($tags));
// also cache the ones that were not found
foreach ($emptyFileIds as $fileId) {
$this->cachedTagMappings[$fileId] = [];
}
}
}
/**
* Retrieves system tag properties
@@ -297,29 +333,6 @@ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin {
}
private function propfindForFile(PropFind $propFind, Node $node): void {
if ($node instanceof Directory
&& $propFind->getDepth() !== 0
&& !is_null($propFind->getStatus(self::SYSTEM_TAGS_PROPERTYNAME))) {
$fileIds = [$node->getId()];
// note: pre-fetching only supported for depth <= 1
$folderContent = $node->getChildren();
foreach ($folderContent as $info) {
if ($info instanceof Node) {
$fileIds[] = $info->getId();
}
}
$tags = $this->tagMapper->getTagIdsForObjects($fileIds, 'files');
$this->cachedTagMappings = $this->cachedTagMappings + $tags;
$emptyFileIds = array_diff($fileIds, array_keys($tags));
// also cache the ones that were not found
foreach ($emptyFileIds as $fileId) {
$this->cachedTagMappings[$fileId] = [];
}
}
$propFind->handle(self::SYSTEM_TAGS_PROPERTYNAME, function () use ($node) {
$user = $this->userSession->getUser();
@@ -241,14 +241,15 @@ class AddressBookImplTest extends TestCase {
public static function dataTestGetPermissions(): array {
return [
[[], 0],
[[['privilege' => '{DAV:}read']], 1],
[[['privilege' => '{DAV:}write']], 6],
[[['privilege' => '{DAV:}all']], 31],
[[['privilege' => '{DAV:}read'],['privilege' => '{DAV:}write']], 7],
[[['privilege' => '{DAV:}read'],['privilege' => '{DAV:}all']], 31],
[[['privilege' => '{DAV:}all'],['privilege' => '{DAV:}write']], 31],
[[['privilege' => '{DAV:}read'],['privilege' => '{DAV:}write'],['privilege' => '{DAV:}all']], 31],
[[['privilege' => '{DAV:}all'],['privilege' => '{DAV:}read'],['privilege' => '{DAV:}write']], 31],
[[['privilege' => '{DAV:}read', 'principal' => 'principals/system/system']], 1],
[[['privilege' => '{DAV:}read', 'principal' => 'principals/system/system'], ['privilege' => '{DAV:}write', 'principal' => 'principals/someone/else']], 1],
[[['privilege' => '{DAV:}write', 'principal' => 'principals/system/system']], 6],
[[['privilege' => '{DAV:}all', 'principal' => 'principals/system/system']], 31],
[[['privilege' => '{DAV:}read', 'principal' => 'principals/system/system'],['privilege' => '{DAV:}write', 'principal' => 'principals/system/system']], 7],
[[['privilege' => '{DAV:}read', 'principal' => 'principals/system/system'],['privilege' => '{DAV:}all', 'principal' => 'principals/system/system']], 31],
[[['privilege' => '{DAV:}all', 'principal' => 'principals/system/system'],['privilege' => '{DAV:}write', 'principal' => 'principals/system/system']], 31],
[[['privilege' => '{DAV:}read', 'principal' => 'principals/system/system'],['privilege' => '{DAV:}write', 'principal' => 'principals/system/system'],['privilege' => '{DAV:}all', 'principal' => 'principals/system/system']], 31],
[[['privilege' => '{DAV:}all', 'principal' => 'principals/system/system'],['privilege' => '{DAV:}read', 'principal' => 'principals/system/system'],['privilege' => '{DAV:}write', 'principal' => 'principals/system/system']], 31],
];
}
@@ -50,6 +50,7 @@ class CardDavBackendTest extends TestCase {
private IUserManager&MockObject $userManager;
private IGroupManager&MockObject $groupManager;
private IEventDispatcher&MockObject $dispatcher;
private IConfig&MockObject $config;
private Backend $sharingBackend;
private IDBConnection $db;
private CardDavBackend $backend;
@@ -96,6 +97,7 @@ class CardDavBackendTest extends TestCase {
$this->userManager = $this->createMock(IUserManager::class);
$this->groupManager = $this->createMock(IGroupManager::class);
$this->config = $this->createMock(IConfig::class);
$this->principal = $this->getMockBuilder(Principal::class)
->setConstructorArgs([
$this->userManager,
@@ -106,7 +108,7 @@ class CardDavBackendTest extends TestCase {
$this->createMock(IAppManager::class),
$this->createMock(ProxyMapper::class),
$this->createMock(KnownUserService::class),
$this->createMock(IConfig::class),
$this->config,
$this->createMock(IFactory::class)
])
->onlyMethods(['getPrincipalByPath', 'getGroupMembership', 'findByUri'])
@@ -135,6 +137,7 @@ class CardDavBackendTest extends TestCase {
$this->userManager,
$this->dispatcher,
$this->sharingBackend,
$this->config,
);
// start every test with a empty cards_properties and cards table
$query = $this->db->getQueryBuilder();
@@ -231,7 +234,7 @@ class CardDavBackendTest extends TestCase {
public function testCardOperations(): void {
/** @var CardDavBackend&MockObject $backend */
$backend = $this->getMockBuilder(CardDavBackend::class)
->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend])
->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend,$this->config])
->onlyMethods(['updateProperties', 'purgeProperties'])
->getMock();
@@ -291,7 +294,7 @@ class CardDavBackendTest extends TestCase {
public function testMultiCard(): void {
$this->backend = $this->getMockBuilder(CardDavBackend::class)
->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend])
->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend,$this->config])
->onlyMethods(['updateProperties'])
->getMock();
@@ -345,7 +348,7 @@ class CardDavBackendTest extends TestCase {
public function testMultipleUIDOnDifferentAddressbooks(): void {
$this->backend = $this->getMockBuilder(CardDavBackend::class)
->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend])
->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend,$this->config])
->onlyMethods(['updateProperties'])
->getMock();
@@ -368,7 +371,7 @@ class CardDavBackendTest extends TestCase {
public function testMultipleUIDDenied(): void {
$this->backend = $this->getMockBuilder(CardDavBackend::class)
->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend])
->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend, $this->config])
->onlyMethods(['updateProperties'])
->getMock();
@@ -390,7 +393,7 @@ class CardDavBackendTest extends TestCase {
public function testNoValidUID(): void {
$this->backend = $this->getMockBuilder(CardDavBackend::class)
->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend])
->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend, $this->config])
->onlyMethods(['updateProperties'])
->getMock();
@@ -408,7 +411,7 @@ class CardDavBackendTest extends TestCase {
public function testDeleteWithoutCard(): void {
$this->backend = $this->getMockBuilder(CardDavBackend::class)
->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend])
->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend, $this->config])
->onlyMethods([
'getCardId',
'addChange',
@@ -453,7 +456,7 @@ class CardDavBackendTest extends TestCase {
public function testSyncSupport(): void {
$this->backend = $this->getMockBuilder(CardDavBackend::class)
->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend])
->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend, $this->config])
->onlyMethods(['updateProperties'])
->getMock();
@@ -522,7 +525,7 @@ class CardDavBackendTest extends TestCase {
$cardId = 2;
$backend = $this->getMockBuilder(CardDavBackend::class)
->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend])
->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend, $this->config])
->onlyMethods(['getCardId'])->getMock();
$backend->expects($this->any())->method('getCardId')->willReturn($cardId);
@@ -108,7 +108,7 @@ class SyncServiceTest extends TestCase {
'1',
'principals/system/system',
[]
);
)[0];
$this->assertEquals('http://sabre.io/ns/sync/1', $token);
}
@@ -179,7 +179,7 @@ END:VCARD';
'1',
'principals/system/system',
[]
);
)[0];
$this->assertEquals('http://sabre.io/ns/sync/2', $token);
}
@@ -250,7 +250,7 @@ END:VCARD';
'1',
'principals/system/system',
[]
);
)[0];
$this->assertEquals('http://sabre.io/ns/sync/3', $token);
}
@@ -291,7 +291,7 @@ END:VCARD';
'1',
'principals/system/system',
[]
);
)[0];
$this->assertEquals('http://sabre.io/ns/sync/4', $token);
}
@@ -12,6 +12,7 @@ use OCA\DAV\CalDAV\DefaultCalendarValidator;
use OCA\DAV\Connector\Sabre\Directory;
use OCA\DAV\Connector\Sabre\File;
use OCA\DAV\DAV\CustomPropertiesBackend;
use OCA\DAV\Db\PropertyMapper;
use OCP\IDBConnection;
use OCP\IUser;
use OCP\Server;
@@ -52,6 +53,7 @@ class CustomPropertiesBackendTest extends \Test\TestCase {
$this->tree,
Server::get(IDBConnection::class),
$this->user,
Server::get(PropertyMapper::class),
$this->defaultCalendarValidator,
);
}
@@ -0,0 +1,133 @@
<?php
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace unit\Connector\Sabre;
use OCA\DAV\Connector\Sabre\PropFindMonitorPlugin;
use OCA\DAV\Connector\Sabre\Server;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;
use Sabre\HTTP\Request;
use Sabre\HTTP\Response;
use Test\TestCase;
class PropFindMonitorPluginTest extends TestCase {
private PropFindMonitorPlugin $plugin;
private Server&MockObject $server;
private LoggerInterface&MockObject $logger;
private Request&MockObject $request;
private Response&MockObject $response;
public static function dataTest(): array {
$minQueriesTrigger = PropFindMonitorPlugin::THRESHOLD_QUERY_FACTOR
* PropFindMonitorPlugin::THRESHOLD_NODES;
return [
'No queries logged' => [[], 0],
'Plugins with queries in less than threshold nodes should not be logged' => [
[
'propFind' => [
[
'PluginName' => [
'queries' => 100,
'nodes' => PropFindMonitorPlugin::THRESHOLD_NODES - 1]
],
[],
]
],
0
],
'Plugins with query-to-node ratio less than threshold should not be logged' => [
[
'propFind' => [
[
'PluginName' => [
'queries' => $minQueriesTrigger - 1,
'nodes' => PropFindMonitorPlugin::THRESHOLD_NODES ],
],
[],
]
],
0
],
'Plugins with more nodes scanned than queries executed should not be logged' => [
[
'propFind' => [
[
'PluginName' => [
'queries' => $minQueriesTrigger,
'nodes' => PropFindMonitorPlugin::THRESHOLD_NODES * 2],
],
[],]
],
0
],
'Plugins with queries only in highest depth level should not be logged' => [
[
'propFind' => [
[
'PluginName' => [
'queries' => $minQueriesTrigger,
'nodes' => PropFindMonitorPlugin::THRESHOLD_NODES - 1
]
],
[
'PluginName' => [
'queries' => $minQueriesTrigger * 2,
'nodes' => PropFindMonitorPlugin::THRESHOLD_NODES
]
],
]
],
0
],
'Plugins with too many queries should be logged' => [
[
'propFind' => [
[
'FirstPlugin' => [
'queries' => $minQueriesTrigger,
'nodes' => PropFindMonitorPlugin::THRESHOLD_NODES,
],
'SecondPlugin' => [
'queries' => $minQueriesTrigger,
'nodes' => PropFindMonitorPlugin::THRESHOLD_NODES,
]
],
[],
]
],
2
]
];
}
/**
* @dataProvider dataTest
*/
public function test(array $queries, $expectedLogCalls): void {
$this->plugin->initialize($this->server);
$this->server->expects($this->once())->method('getPluginQueries')
->willReturn($queries);
$this->server->expects(empty($queries) ? $this->never() : $this->once())
->method('getLogger')
->willReturn($this->logger);
$this->logger->expects($this->exactly($expectedLogCalls))->method('error');
$this->plugin->afterResponse($this->request, $this->response);
}
protected function setUp(): void {
parent::setUp();
$this->plugin = new PropFindMonitorPlugin();
$this->server = $this->createMock(Server::class);
$this->logger = $this->createMock(LoggerInterface::class);
$this->request = $this->createMock(Request::class);
$this->response = $this->createMock(Response::class);
}
}
@@ -0,0 +1,92 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\DAV\Tests\unit\Connector\Sabre;
use OCA\DAV\Connector\Sabre\PropFindPreloadNotifyPlugin;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\MockObject\MockObject;
use Sabre\DAV\ICollection;
use Sabre\DAV\IFile;
use Sabre\DAV\PropFind;
use Sabre\DAV\Server;
use Test\TestCase;
class PropFindPreloadNotifyPluginTest extends TestCase {
private Server&MockObject $server;
private PropFindPreloadNotifyPlugin $plugin;
protected function setUp(): void {
parent::setUp();
$this->server = $this->createMock(Server::class);
$this->plugin = new PropFindPreloadNotifyPlugin();
}
public function testInitialize(): void {
$this->server
->expects(self::once())
->method('on')
->with('propFind',
$this->anything(), 1);
$this->plugin->initialize($this->server);
}
public static function dataTestCollectionPreloadNotifier(): array {
return [
'When node is not a collection, should not emit' => [
IFile::class,
1,
false,
true
],
'When node is a collection but depth is zero, should not emit' => [
ICollection::class,
0,
false,
true
],
'When node is a collection, and depth > 0, should emit' => [
ICollection::class,
1,
true,
true
],
'When node is a collection, and depth is infinite, should emit'
=> [
ICollection::class,
Server::DEPTH_INFINITY,
true,
true
],
'When called called handler returns false, it should be returned'
=> [
ICollection::class,
1,
true,
false
]
];
}
#[DataProvider(methodName: 'dataTestCollectionPreloadNotifier')]
public function testCollectionPreloadNotifier(string $nodeType, int $depth, bool $shouldEmit, bool $emitReturns):
void {
$this->plugin->initialize($this->server);
$propFind = $this->createMock(PropFind::class);
$propFind->expects(self::any())->method('getDepth')->willReturn($depth);
$node = $this->createMock($nodeType);
$expectation = $shouldEmit ? self::once() : self::never();
$this->server->expects($expectation)->method('emit')->with('preloadCollection',
[$propFind, $node])->willReturn($emitReturns);
$return = $this->plugin->collectionPreloadNotifier($propFind, $node);
$this->assertEquals($emitReturns, $return);
}
}
@@ -223,6 +223,7 @@ class SharesPluginTest extends \Test\TestCase {
0
);
$this->server->emit('preloadCollection', [$propFindRoot, $sabreNode]);
$this->plugin->handleGetProperties(
$propFindRoot,
$sabreNode
@@ -147,6 +147,8 @@ class TagsPluginTest extends \Test\TestCase {
0
);
$this->server->emit('preloadCollection', [$propFindRoot, $node]);
$this->plugin->handleGetProperties(
$propFindRoot,
$node
@@ -10,6 +10,7 @@ namespace OCA\DAV\Tests\unit\DAV;
use OCA\DAV\CalDAV\Calendar;
use OCA\DAV\CalDAV\DefaultCalendarValidator;
use OCA\DAV\DAV\CustomPropertiesBackend;
use OCA\DAV\Db\PropertyMapper;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use OCP\IUser;
@@ -36,6 +37,7 @@ class CustomPropertiesBackendTest extends TestCase {
private IUser&MockObject $user;
private DefaultCalendarValidator&MockObject $defaultCalendarValidator;
private CustomPropertiesBackend $backend;
private PropertyMapper $propertyMapper;
protected function setUp(): void {
parent::setUp();
@@ -49,6 +51,7 @@ class CustomPropertiesBackendTest extends TestCase {
->with()
->willReturn('dummy_user_42');
$this->dbConnection = \OCP\Server::get(IDBConnection::class);
$this->propertyMapper = \OCP\Server::get(PropertyMapper::class);
$this->defaultCalendarValidator = $this->createMock(DefaultCalendarValidator::class);
$this->backend = new CustomPropertiesBackend(
@@ -56,6 +59,7 @@ class CustomPropertiesBackendTest extends TestCase {
$this->tree,
$this->dbConnection,
$this->user,
$this->propertyMapper,
$this->defaultCalendarValidator,
);
}
@@ -129,6 +133,7 @@ class CustomPropertiesBackendTest extends TestCase {
$this->tree,
$db,
$this->user,
$this->propertyMapper,
$this->defaultCalendarValidator,
);
+1 -1
View File
@@ -39,7 +39,7 @@ OC.L10N.register(
"In order to use this encryption module you need to enable server-side encryption in the admin settings. Once enabled this module will encrypt all your files transparently. The encryption is based on AES 256 keys.\nThe module will not touch existing files, only new files will be encrypted after server-side encryption was enabled. It is also not possible to disable the encryption again and switch back to an unencrypted system.\nPlease read the documentation to know all implications before you decide to enable server-side encryption." : "De manera de usar este módulo de cifrado necesita activar el cifrado del lado del servidor en la configuraciones de administrador. Una vez que esté habilitado este módulo cifrará todos tus archivos de manera transparente. El cifrado está basado en llaves AES-256\nEl módulo no tocará los archivos existentes, solo los archivos nuevos serán cifrados una vez que el cifrado del lado del servidor se habilite. Además, no es posible deshabilitar el cifrado de nuevo y cambiar a un sistema sin cifrado.\nPor favor lea la documentación para que entienda todas las implicaciones antes de que decida habilitar el cifrado del lado del servidor.",
"Encryption app is enabled but your keys are not initialized, please log-out and log-in again" : "La app de cifrado está habilitada pero sus claves no se han inicializado, por favor, cierre la sesión y vuelva a iniciarla de nuevo.",
"Encrypt the home storage" : "Encriptar el almacenamiento personal",
"Enabling this option encrypts all files stored on the main storage, otherwise only files on external storage will be encrypted" : "Al activar esta opción se encriptarán todos los archivos almacenados en la memoria principal, de lo contrario serán cifrados sólo los archivos de almacenamiento externo",
"Enabling this option encrypts all files stored on the main storage, otherwise only files on external storage will be encrypted" : "Al activar esta opción se encriptarán todos los archivos almacenados en la memoria principal, de lo contrario, serán cifrados sólo los archivos de almacenamiento externo",
"Enable recovery key" : "Activa la clave de recuperación",
"Disable recovery key" : "Desactiva la clave de recuperación",
"The recovery key is an additional encryption key used to encrypt files. It is used to recover files from an account if the password is forgotten." : "La llave de recuperación es una llave de cifrado adicional utilizada para cifrar archivos. Es utilizada para recuperar los archivos de una cuenta si la contraseña fuese olvidada.",
+1 -1
View File
@@ -37,7 +37,7 @@
"In order to use this encryption module you need to enable server-side encryption in the admin settings. Once enabled this module will encrypt all your files transparently. The encryption is based on AES 256 keys.\nThe module will not touch existing files, only new files will be encrypted after server-side encryption was enabled. It is also not possible to disable the encryption again and switch back to an unencrypted system.\nPlease read the documentation to know all implications before you decide to enable server-side encryption." : "De manera de usar este módulo de cifrado necesita activar el cifrado del lado del servidor en la configuraciones de administrador. Una vez que esté habilitado este módulo cifrará todos tus archivos de manera transparente. El cifrado está basado en llaves AES-256\nEl módulo no tocará los archivos existentes, solo los archivos nuevos serán cifrados una vez que el cifrado del lado del servidor se habilite. Además, no es posible deshabilitar el cifrado de nuevo y cambiar a un sistema sin cifrado.\nPor favor lea la documentación para que entienda todas las implicaciones antes de que decida habilitar el cifrado del lado del servidor.",
"Encryption app is enabled but your keys are not initialized, please log-out and log-in again" : "La app de cifrado está habilitada pero sus claves no se han inicializado, por favor, cierre la sesión y vuelva a iniciarla de nuevo.",
"Encrypt the home storage" : "Encriptar el almacenamiento personal",
"Enabling this option encrypts all files stored on the main storage, otherwise only files on external storage will be encrypted" : "Al activar esta opción se encriptarán todos los archivos almacenados en la memoria principal, de lo contrario serán cifrados sólo los archivos de almacenamiento externo",
"Enabling this option encrypts all files stored on the main storage, otherwise only files on external storage will be encrypted" : "Al activar esta opción se encriptarán todos los archivos almacenados en la memoria principal, de lo contrario, serán cifrados sólo los archivos de almacenamiento externo",
"Enable recovery key" : "Activa la clave de recuperación",
"Disable recovery key" : "Desactiva la clave de recuperación",
"The recovery key is an additional encryption key used to encrypt files. It is used to recover files from an account if the password is forgotten." : "La llave de recuperación es una llave de cifrado adicional utilizada para cifrar archivos. Es utilizada para recuperar los archivos de una cuenta si la contraseña fuese olvidada.",
+4 -1
View File
@@ -3,7 +3,7 @@ OC.L10N.register(
{
"Missing recovery key password" : "Muuda taastevõtme salasõna",
"Please repeat the recovery key password" : "Palun korda uut taastevõtme salasõna",
"Repeated recovery key password does not match the provided recovery key password" : "Sisestatud taastevõtme salasõna ei kattu",
"Repeated recovery key password does not match the provided recovery key password" : "Teistkorda sisestatud taastevõtme salasõna ei kattu sisestatud taastevõtme salasõnaga",
"Recovery key successfully enabled" : "Taastevõtme lubamine õnnestus",
"Could not enable recovery key. Please check your recovery key password!" : "Ei suutnud taastevõtit kasutusele võtta. Palun kontrolli oma taastevõtme salasõna!",
"Recovery key successfully disabled" : "Taastevõtme keelamine õnnestus",
@@ -21,6 +21,8 @@ OC.L10N.register(
"The current log-in password was not correct, please try again." : "Sisselogimise senine salasõna polnud õige, palun proovi uuesti.",
"Private key password successfully updated." : "Privaatvõtme salasõna uuendamine õnnestus.",
"Invalid private key for encryption app. Please update your private key password in your personal settings to recover access to your encrypted files." : "Krüptimisrakenduse vigane privaatvõti. Taastamaks ligipääsu krüptitud failidele palun uuenda oma isiklikest seadistustest privaatvõtme salasõna.",
"Encryption App is enabled, but your keys are not initialized. Please log-out and log-in again." : "Krüptimisrakendus on kasutusel, kuid krüptovõtmed pole valmendatud. Palun logi välja ning uuesti sisse.",
"Encryption app is enabled and ready" : "Krüptimisrakendus on kasutusel ja töövalmis",
"Bad Signature" : "Vigane allkiri",
"Missing Signature" : "Allkiri puudub",
"one-time password for server-side-encryption" : "ühekordne salasõna serveripoolse krüptimise jaoks",
@@ -29,6 +31,7 @@ OC.L10N.register(
"The administration enabled server-side-encryption. Your files were encrypted using the password \"%s\"." : "Serveri peakasutaja lülitas sisse serveripoolse krüptimise. Sinu failid on krüptitud salasõnaga „%s“.",
"Default encryption module" : "Vaikimisi krüptimismoodul",
"Default encryption module for server-side encryption" : "Vaikimisi krüptimismoodul serveripoolse krüptimise jaoks",
"Encryption app is enabled but your keys are not initialized, please log-out and log-in again" : "Krüptimisrakendus on kasutusel, kuid krüptovõtmed pole valmendatud. Palun logi välja ning uuesti sisse.",
"Encrypt the home storage" : "Krüpti ka sisemine andmeruum",
"Enabling this option encrypts all files stored on the main storage, otherwise only files on external storage will be encrypted" : "Selle valiku kasutamisel krüptitakse failid sisemises ja välises andmeruumis. Vastasel juhul vaid välises andmeruumis.",
"Enable recovery key" : "Luba taastevõtme kasutamine",
+4 -1
View File
@@ -1,7 +1,7 @@
{ "translations": {
"Missing recovery key password" : "Muuda taastevõtme salasõna",
"Please repeat the recovery key password" : "Palun korda uut taastevõtme salasõna",
"Repeated recovery key password does not match the provided recovery key password" : "Sisestatud taastevõtme salasõna ei kattu",
"Repeated recovery key password does not match the provided recovery key password" : "Teistkorda sisestatud taastevõtme salasõna ei kattu sisestatud taastevõtme salasõnaga",
"Recovery key successfully enabled" : "Taastevõtme lubamine õnnestus",
"Could not enable recovery key. Please check your recovery key password!" : "Ei suutnud taastevõtit kasutusele võtta. Palun kontrolli oma taastevõtme salasõna!",
"Recovery key successfully disabled" : "Taastevõtme keelamine õnnestus",
@@ -19,6 +19,8 @@
"The current log-in password was not correct, please try again." : "Sisselogimise senine salasõna polnud õige, palun proovi uuesti.",
"Private key password successfully updated." : "Privaatvõtme salasõna uuendamine õnnestus.",
"Invalid private key for encryption app. Please update your private key password in your personal settings to recover access to your encrypted files." : "Krüptimisrakenduse vigane privaatvõti. Taastamaks ligipääsu krüptitud failidele palun uuenda oma isiklikest seadistustest privaatvõtme salasõna.",
"Encryption App is enabled, but your keys are not initialized. Please log-out and log-in again." : "Krüptimisrakendus on kasutusel, kuid krüptovõtmed pole valmendatud. Palun logi välja ning uuesti sisse.",
"Encryption app is enabled and ready" : "Krüptimisrakendus on kasutusel ja töövalmis",
"Bad Signature" : "Vigane allkiri",
"Missing Signature" : "Allkiri puudub",
"one-time password for server-side-encryption" : "ühekordne salasõna serveripoolse krüptimise jaoks",
@@ -27,6 +29,7 @@
"The administration enabled server-side-encryption. Your files were encrypted using the password \"%s\"." : "Serveri peakasutaja lülitas sisse serveripoolse krüptimise. Sinu failid on krüptitud salasõnaga „%s“.",
"Default encryption module" : "Vaikimisi krüptimismoodul",
"Default encryption module for server-side encryption" : "Vaikimisi krüptimismoodul serveripoolse krüptimise jaoks",
"Encryption app is enabled but your keys are not initialized, please log-out and log-in again" : "Krüptimisrakendus on kasutusel, kuid krüptovõtmed pole valmendatud. Palun logi välja ning uuesti sisse.",
"Encrypt the home storage" : "Krüpti ka sisemine andmeruum",
"Enabling this option encrypts all files stored on the main storage, otherwise only files on external storage will be encrypted" : "Selle valiku kasutamisel krüptitakse failid sisemises ja välises andmeruumis. Vastasel juhul vaid välises andmeruumis.",
"Enable recovery key" : "Luba taastevõtme kasutamine",
+5 -5
View File
@@ -2,16 +2,16 @@ OC.L10N.register(
"encryption",
{
"Missing recovery key password" : "Pazudusi atkopšanas atslēgas parole",
"Please repeat the recovery key password" : "Lūdzu atkārtot atgūšanas atslēgas paroli",
"Please repeat the recovery key password" : "Lūgums atkārtot atkopes atslēgas paroli",
"Repeated recovery key password does not match the provided recovery key password" : "Atkārtota atkopšanas atslēgas parole nesakrīt ar izsniegto atkopšanas atslēgu paroli",
"Recovery key successfully enabled" : "Atkopšanas atslēga ir veiksmīgi iespējota",
"Could not enable recovery key. Please check your recovery key password!" : "Atkopšanas atslēgu nevarēja iespējot. Lūdzu, pārbaudiet atkopšanas atslēgas paroli!",
"Could not enable recovery key. Please check your recovery key password!" : "Atkopes atslēgu nevarēja iespējot. Lūgums pārbaudīt atkopes atslēgas paroli.",
"Recovery key successfully disabled" : "Atkopšanas atslēga ir veiksmīgi deaktivizēta",
"Could not disable recovery key. Please check your recovery key password!" : "Atkopšanas atslēgu nevarēja atspējot. Lūdzu, pārbaudiet atkopšanas atslēgas paroli!",
"Could not disable recovery key. Please check your recovery key password!" : "Atkopes atslēgu nevarēja atspējot. Lūgums pārbaudīt atkopes atslēgas paroli.",
"Missing parameters" : "Trūkstošos parametrs",
"Please provide the old recovery password" : "Lūgums norādīt iepriekšējo atkopes paroli",
"Please provide a new recovery password" : "Lūdzu, ievadiet jaunu paroli",
"Please repeat the new recovery password" : "Lūdzu, atkārtojiet jauno atkopšanas paroli",
"Please provide a new recovery password" : "Lūgums norādīt jaunu atkopes paroli",
"Please repeat the new recovery password" : "Lūgums atkārtot jauno atkopes paroli",
"Password successfully changed." : "Parole veiksmīgi nomainīta.",
"Could not change the password. Maybe the old password was not correct." : "Nevarēja mainīt paroli. Varbūt vecā parole nav pareiza.",
"Recovery Key disabled" : "Atkopšanas atslēga deaktivizēta",
+5 -5
View File
@@ -1,15 +1,15 @@
{ "translations": {
"Missing recovery key password" : "Pazudusi atkopšanas atslēgas parole",
"Please repeat the recovery key password" : "Lūdzu atkārtot atgūšanas atslēgas paroli",
"Please repeat the recovery key password" : "Lūgums atkārtot atkopes atslēgas paroli",
"Repeated recovery key password does not match the provided recovery key password" : "Atkārtota atkopšanas atslēgas parole nesakrīt ar izsniegto atkopšanas atslēgu paroli",
"Recovery key successfully enabled" : "Atkopšanas atslēga ir veiksmīgi iespējota",
"Could not enable recovery key. Please check your recovery key password!" : "Atkopšanas atslēgu nevarēja iespējot. Lūdzu, pārbaudiet atkopšanas atslēgas paroli!",
"Could not enable recovery key. Please check your recovery key password!" : "Atkopes atslēgu nevarēja iespējot. Lūgums pārbaudīt atkopes atslēgas paroli.",
"Recovery key successfully disabled" : "Atkopšanas atslēga ir veiksmīgi deaktivizēta",
"Could not disable recovery key. Please check your recovery key password!" : "Atkopšanas atslēgu nevarēja atspējot. Lūdzu, pārbaudiet atkopšanas atslēgas paroli!",
"Could not disable recovery key. Please check your recovery key password!" : "Atkopes atslēgu nevarēja atspējot. Lūgums pārbaudīt atkopes atslēgas paroli.",
"Missing parameters" : "Trūkstošos parametrs",
"Please provide the old recovery password" : "Lūgums norādīt iepriekšējo atkopes paroli",
"Please provide a new recovery password" : "Lūdzu, ievadiet jaunu paroli",
"Please repeat the new recovery password" : "Lūdzu, atkārtojiet jauno atkopšanas paroli",
"Please provide a new recovery password" : "Lūgums norādīt jaunu atkopes paroli",
"Please repeat the new recovery password" : "Lūgums atkārtot jauno atkopes paroli",
"Password successfully changed." : "Parole veiksmīgi nomainīta.",
"Could not change the password. Maybe the old password was not correct." : "Nevarēja mainīt paroli. Varbūt vecā parole nav pareiza.",
"Recovery Key disabled" : "Atkopšanas atslēga deaktivizēta",
+15 -8
View File
@@ -21,22 +21,28 @@ OC.L10N.register(
"The old password was not correct, please try again." : "Het oude wachtwoord was onjuist, probeer het opnieuw.",
"The current log-in password was not correct, please try again." : "Het huidige inlogwachtwoord was niet juist, probeer het opnieuw.",
"Private key password successfully updated." : "Privésleutel succesvol bijgewerkt.",
"Invalid private key for encryption app. Please update your private key password in your personal settings to recover access to your encrypted files." : "Ongeldige privésleutel voor de crypto app. Werk het privésleutel wachtwoord bij in je persoonlijke instellingen om opnieuw toegang te krijgen tot je versleutelde bestanden.",
"Encryption App is enabled, but your keys are not initialized. Please log-out and log-in again." : "Crypto app is ingeschakeld, maar je sleutels werden niet geïnitialiseerd. Log uit en log daarna opnieuw in.",
"Please enable server side encryption in the admin settings in order to use the encryption module." : "Activeer de server-encryptie in de beheerdersinstellingen om de encryptiemodule te kunnen gebruiken.",
"Encryption app is enabled and ready" : "Encryptie app is ingeschakeld en gereed",
"Invalid private key for encryption app. Please update your private key password in your personal settings to recover access to your encrypted files." : "Ongeldige persoonlijke sleutel voor de versleutelingsapp. Werk de persoonlijke wachtwoordsleutel bij in je persoonlijke instellingen om opnieuw toegang te krijgen tot je versleutelde bestanden.",
"Encryption App is enabled, but your keys are not initialized. Please log-out and log-in again." : "Versleutelingsapp is ingeschakeld, maar je sleutels werden niet geïnitialiseerd. Log uit en log daarna opnieuw in.",
"Please enable server side encryption in the admin settings in order to use the encryption module." : "Schakel versleuteling aan de serverzijde in de beheerdersinstellingen in om de versleutelingsmodule te gebruiken.",
"Encryption app is enabled and ready" : "Versleutelingsapp is ingeschakeld en gereed",
"Bad Signature" : "Verkeerde handtekening",
"Missing Signature" : "Missende ondertekening",
"one-time password for server-side-encryption" : "eenmalig wachtwoord voor server-side versleuteling",
"one-time password for server-side-encryption" : "eenmalig wachtwoord voor versleuteling aan de serverzijde",
"Encryption password" : "Versleutelingswachtwoord",
"The administration enabled server-side-encryption. Your files were encrypted using the password <strong>%s</strong>." : "De beheerder heeft versleuteling aan de serverzijde mogelijk gemaakt. Je bestanden zijn gecodeerd met behulp van het wachtwoord <strong>%s</strong>.",
"The administration enabled server-side-encryption. Your files were encrypted using the password \"%s\"." : "De beheerder heeft versleuteling aan de serverzijde mogelijk gemaakt. Je bestanden zijn gecodeerd met behulp van het wachtwoord \"%s\".",
"Please login to the web interface, go to the \"Security\" section of your personal settings and update your encryption password by entering this password into the \"Old login password\" field and your current login password." : "Log in op de webinterface, ga naar het gedeelte 'Beveiliging' van je persoonlijke instellingen en update je coderingswachtwoord door dit wachtwoord in te voeren in het veld 'Oud inlogwachtwoord' en je huidige inlogwachtwoord.",
"Cannot decrypt this file, probably this is a shared file. Please ask the file owner to reshare the file with you." : "Kan dit bestand niet ontcijferen, waarschijnlijk is het een gedeeld bestand, Vraag de eigenaar om het bestand opnieuw met je te delen.",
"Cannot read this file, probably this is a shared file. Please ask the file owner to reshare the file with you." : "Kan dit bestand niet lezen, waarschijnlijk is het een gedeeld bestand. Vraag de eigenaar om het bestand opnieuw met je te delen.",
"Default encryption module" : "Standaard cryptomodule",
"Default encryption module for server-side encryption" : "Standaard cryptomodule voor server-side versleuteling",
"Default encryption module" : "Standaard versleutelingsmodule",
"Default encryption module for server-side encryption" : "Standaard module voor versleuteling aan de serverzijde",
"In order to use this encryption module you need to enable server-side encryption in the admin settings. Once enabled this module will encrypt all your files transparently. The encryption is based on AES 256 keys.\nThe module will not touch existing files, only new files will be encrypted after server-side encryption was enabled. It is also not possible to disable the encryption again and switch back to an unencrypted system.\nPlease read the documentation to know all implications before you decide to enable server-side encryption." : "Om deze versleutelingsmodule te kunnen gebruiken, moet je versleuteling aan de serverzijde inschakelen in de beheerdersinstellingen. Eenmaal ingeschakeld zal deze module al je bestanden transparant versleutelen. De versleuteling is gebaseerd op AES 256-sleutels.\nDe module doet niets met bestaande bestanden, alleen nieuwe bestanden worden gecodeerd nadat versleuteling aan de serverzijde is ingeschakeld. Het is ook niet mogelijk om de codering weer uit te schakelen en terug te schakelen naar een niet-gecodeerd systeem.\nLees de documentatie om alle implicaties te kennen voordat je besluit versleuteling aan de serverzijde in te schakelen.",
"Encryption app is enabled but your keys are not initialized, please log-out and log-in again" : "Crypto app is ingeschakeld, maar je sleutels werden niet geïnitialiseerd. Log uit en log daarna opnieuw in.",
"Encrypt the home storage" : "Versleutel de eigen serveropslag",
"Enabling this option encrypts all files stored on the main storage, otherwise only files on external storage will be encrypted" : "Het inschakelen van deze optie zorgt voor versleutelen van alle bestanden op de hoofdopslag, anders worden alleen bestanden op externe opslag versleuteld",
"Enable recovery key" : "Activeer herstelsleutel",
"Disable recovery key" : "Deactiveer herstelsleutel",
"The recovery key is an additional encryption key used to encrypt files. It is used to recover files from an account if the password is forgotten." : "De herstelsleutel is een extra coderingssleutel die wordt gebruikt om bestanden te versleutelen. Het wordt gebruikt om bestanden van een account te herstellen als het wachtwoord wordt vergeten.",
"Recovery key password" : "Wachtwoord herstelsleulel",
"Repeat recovery key password" : "Herhaal wachtwoord herstelsleutel",
"Change recovery key password:" : "Wijzig wachtwoord herstelsleutel:",
@@ -44,9 +50,10 @@ OC.L10N.register(
"New recovery key password" : "Nieuwe wachtwoord herstelsleutel",
"Repeat new recovery key password" : "Herhaal nieuwe wachtwoord herstelsleutel",
"Change Password" : "Wijzigen wachtwoord",
"Basic encryption module" : "Basis versleutelingsmodule",
"Basic encryption module" : "Basis-versleutelingsmodule",
"Your private key password no longer matches your log-in password." : "Het wachtwoord van je privésleutel komt niet meer overeen met je inlogwachtwoord.",
"Set your old private key password to your current log-in password:" : "Stel het wachtwoord van je oude privésleutel in op je huidige inlogwachtwoord.",
"If you do not remember your old password you can ask your administrator to recover your files." : "Als je je oude wachtwoord niet meer weet, kun je de beheerder vragen je bestanden te herstellen.",
"Old log-in password" : "Oude wachtwoord",
"Current log-in password" : "Huidige wachtwoord",
"Update Private Key Password" : "Bijwerken wachtwoord Privésleutel",
+15 -8
View File
@@ -19,22 +19,28 @@
"The old password was not correct, please try again." : "Het oude wachtwoord was onjuist, probeer het opnieuw.",
"The current log-in password was not correct, please try again." : "Het huidige inlogwachtwoord was niet juist, probeer het opnieuw.",
"Private key password successfully updated." : "Privésleutel succesvol bijgewerkt.",
"Invalid private key for encryption app. Please update your private key password in your personal settings to recover access to your encrypted files." : "Ongeldige privésleutel voor de crypto app. Werk het privésleutel wachtwoord bij in je persoonlijke instellingen om opnieuw toegang te krijgen tot je versleutelde bestanden.",
"Encryption App is enabled, but your keys are not initialized. Please log-out and log-in again." : "Crypto app is ingeschakeld, maar je sleutels werden niet geïnitialiseerd. Log uit en log daarna opnieuw in.",
"Please enable server side encryption in the admin settings in order to use the encryption module." : "Activeer de server-encryptie in de beheerdersinstellingen om de encryptiemodule te kunnen gebruiken.",
"Encryption app is enabled and ready" : "Encryptie app is ingeschakeld en gereed",
"Invalid private key for encryption app. Please update your private key password in your personal settings to recover access to your encrypted files." : "Ongeldige persoonlijke sleutel voor de versleutelingsapp. Werk de persoonlijke wachtwoordsleutel bij in je persoonlijke instellingen om opnieuw toegang te krijgen tot je versleutelde bestanden.",
"Encryption App is enabled, but your keys are not initialized. Please log-out and log-in again." : "Versleutelingsapp is ingeschakeld, maar je sleutels werden niet geïnitialiseerd. Log uit en log daarna opnieuw in.",
"Please enable server side encryption in the admin settings in order to use the encryption module." : "Schakel versleuteling aan de serverzijde in de beheerdersinstellingen in om de versleutelingsmodule te gebruiken.",
"Encryption app is enabled and ready" : "Versleutelingsapp is ingeschakeld en gereed",
"Bad Signature" : "Verkeerde handtekening",
"Missing Signature" : "Missende ondertekening",
"one-time password for server-side-encryption" : "eenmalig wachtwoord voor server-side versleuteling",
"one-time password for server-side-encryption" : "eenmalig wachtwoord voor versleuteling aan de serverzijde",
"Encryption password" : "Versleutelingswachtwoord",
"The administration enabled server-side-encryption. Your files were encrypted using the password <strong>%s</strong>." : "De beheerder heeft versleuteling aan de serverzijde mogelijk gemaakt. Je bestanden zijn gecodeerd met behulp van het wachtwoord <strong>%s</strong>.",
"The administration enabled server-side-encryption. Your files were encrypted using the password \"%s\"." : "De beheerder heeft versleuteling aan de serverzijde mogelijk gemaakt. Je bestanden zijn gecodeerd met behulp van het wachtwoord \"%s\".",
"Please login to the web interface, go to the \"Security\" section of your personal settings and update your encryption password by entering this password into the \"Old login password\" field and your current login password." : "Log in op de webinterface, ga naar het gedeelte 'Beveiliging' van je persoonlijke instellingen en update je coderingswachtwoord door dit wachtwoord in te voeren in het veld 'Oud inlogwachtwoord' en je huidige inlogwachtwoord.",
"Cannot decrypt this file, probably this is a shared file. Please ask the file owner to reshare the file with you." : "Kan dit bestand niet ontcijferen, waarschijnlijk is het een gedeeld bestand, Vraag de eigenaar om het bestand opnieuw met je te delen.",
"Cannot read this file, probably this is a shared file. Please ask the file owner to reshare the file with you." : "Kan dit bestand niet lezen, waarschijnlijk is het een gedeeld bestand. Vraag de eigenaar om het bestand opnieuw met je te delen.",
"Default encryption module" : "Standaard cryptomodule",
"Default encryption module for server-side encryption" : "Standaard cryptomodule voor server-side versleuteling",
"Default encryption module" : "Standaard versleutelingsmodule",
"Default encryption module for server-side encryption" : "Standaard module voor versleuteling aan de serverzijde",
"In order to use this encryption module you need to enable server-side encryption in the admin settings. Once enabled this module will encrypt all your files transparently. The encryption is based on AES 256 keys.\nThe module will not touch existing files, only new files will be encrypted after server-side encryption was enabled. It is also not possible to disable the encryption again and switch back to an unencrypted system.\nPlease read the documentation to know all implications before you decide to enable server-side encryption." : "Om deze versleutelingsmodule te kunnen gebruiken, moet je versleuteling aan de serverzijde inschakelen in de beheerdersinstellingen. Eenmaal ingeschakeld zal deze module al je bestanden transparant versleutelen. De versleuteling is gebaseerd op AES 256-sleutels.\nDe module doet niets met bestaande bestanden, alleen nieuwe bestanden worden gecodeerd nadat versleuteling aan de serverzijde is ingeschakeld. Het is ook niet mogelijk om de codering weer uit te schakelen en terug te schakelen naar een niet-gecodeerd systeem.\nLees de documentatie om alle implicaties te kennen voordat je besluit versleuteling aan de serverzijde in te schakelen.",
"Encryption app is enabled but your keys are not initialized, please log-out and log-in again" : "Crypto app is ingeschakeld, maar je sleutels werden niet geïnitialiseerd. Log uit en log daarna opnieuw in.",
"Encrypt the home storage" : "Versleutel de eigen serveropslag",
"Enabling this option encrypts all files stored on the main storage, otherwise only files on external storage will be encrypted" : "Het inschakelen van deze optie zorgt voor versleutelen van alle bestanden op de hoofdopslag, anders worden alleen bestanden op externe opslag versleuteld",
"Enable recovery key" : "Activeer herstelsleutel",
"Disable recovery key" : "Deactiveer herstelsleutel",
"The recovery key is an additional encryption key used to encrypt files. It is used to recover files from an account if the password is forgotten." : "De herstelsleutel is een extra coderingssleutel die wordt gebruikt om bestanden te versleutelen. Het wordt gebruikt om bestanden van een account te herstellen als het wachtwoord wordt vergeten.",
"Recovery key password" : "Wachtwoord herstelsleulel",
"Repeat recovery key password" : "Herhaal wachtwoord herstelsleutel",
"Change recovery key password:" : "Wijzig wachtwoord herstelsleutel:",
@@ -42,9 +48,10 @@
"New recovery key password" : "Nieuwe wachtwoord herstelsleutel",
"Repeat new recovery key password" : "Herhaal nieuwe wachtwoord herstelsleutel",
"Change Password" : "Wijzigen wachtwoord",
"Basic encryption module" : "Basis versleutelingsmodule",
"Basic encryption module" : "Basis-versleutelingsmodule",
"Your private key password no longer matches your log-in password." : "Het wachtwoord van je privésleutel komt niet meer overeen met je inlogwachtwoord.",
"Set your old private key password to your current log-in password:" : "Stel het wachtwoord van je oude privésleutel in op je huidige inlogwachtwoord.",
"If you do not remember your old password you can ask your administrator to recover your files." : "Als je je oude wachtwoord niet meer weet, kun je de beheerder vragen je bestanden te herstellen.",
"Old log-in password" : "Oude wachtwoord",
"Current log-in password" : "Huidige wachtwoord",
"Update Private Key Password" : "Bijwerken wachtwoord Privésleutel",
+10 -13
View File
@@ -12,6 +12,7 @@ use OC\Files\View;
use OCA\Encryption\KeyManager;
use OCA\Encryption\Users\Setup;
use OCA\Encryption\Util;
use OCP\Files\FileInfo;
use OCP\IConfig;
use OCP\IL10N;
use OCP\IUser;
@@ -202,15 +203,19 @@ class EncryptAll {
while ($root = array_pop($directories)) {
$content = $this->rootView->getDirectoryContent($root);
foreach ($content as $file) {
$path = $root . '/' . $file['name'];
if ($this->rootView->is_dir($path)) {
$path = $root . '/' . $file->getName();
if ($file->isShared()) {
$progress->setMessage("Skip shared file/folder $path");
$progress->advance();
continue;
} elseif ($file->getType() === FileInfo::TYPE_FOLDER) {
$directories[] = $path;
continue;
} else {
$progress->setMessage("encrypt files for user $userCount: $path");
$progress->advance();
try {
if ($this->encryptFile($path) === false) {
if ($this->encryptFile($file, $path) === false) {
$progress->setMessage("encrypt files for user $userCount: $path (already encrypted)");
$progress->advance();
}
@@ -231,17 +236,9 @@ class EncryptAll {
}
}
/**
* encrypt file
*
* @param string $path
* @return bool
*/
protected function encryptFile($path) {
protected function encryptFile(FileInfo $fileInfo, string $path): bool {
// skip already encrypted files
$fileInfo = $this->rootView->getFileInfo($path);
if ($fileInfo !== false && $fileInfo->isEncrypted()) {
if ($fileInfo->isEncrypted()) {
return true;
}
+15 -18
View File
@@ -82,7 +82,7 @@ class EncryptAllTest extends TestCase {
/**
* We need format method to return a string
* @var OutputFormatterInterface|\PHPUnit\Framework\MockObject\MockObject
* @var OutputFormatterInterface&MockObject
*/
$outputFormatter = $this->createMock(OutputFormatterInterface::class);
$outputFormatter->method('isDecorated')->willReturn(false);
@@ -114,6 +114,13 @@ class EncryptAllTest extends TestCase {
);
}
protected function createFileInfoMock($type, string $name): FileInfo&MockObject {
$fileInfo = $this->createMock(FileInfo::class);
$fileInfo->method('getType')->willReturn($type);
$fileInfo->method('getName')->willReturn($name);
return $fileInfo;
}
public function testEncryptAll(): void {
/** @var EncryptAll&MockObject $encryptAll */
$encryptAll = $this->getMockBuilder(EncryptAll::class)
@@ -299,8 +306,8 @@ class EncryptAllTest extends TestCase {
'',
null,
[
['name' => 'foo', 'type' => 'dir'],
['name' => 'bar', 'type' => 'file'],
$this->createFileInfoMock(FileInfo::TYPE_FOLDER, 'foo'),
$this->createFileInfoMock(FileInfo::TYPE_FILE, 'bar'),
],
],
[
@@ -308,26 +315,17 @@ class EncryptAllTest extends TestCase {
'',
null,
[
['name' => 'subfile', 'type' => 'file']
$this->createFileInfoMock(FileInfo::TYPE_FILE, 'subfile'),
],
],
]);
$this->view->expects($this->any())->method('is_dir')
->willReturnCallback(
function ($path) {
if ($path === '/user1/files/foo') {
return true;
}
return false;
}
);
$encryptAllCalls = [];
$encryptAll->expects($this->exactly(2))
->method('encryptFile')
->willReturnCallback(function (string $path) use (&$encryptAllCalls): void {
->willReturnCallback(function (FileInfo $file, string $path) use (&$encryptAllCalls): bool {
$encryptAllCalls[] = $path;
return true;
});
$outputFormatter = $this->createMock(OutputFormatterInterface::class);
@@ -362,8 +360,7 @@ class EncryptAllTest extends TestCase {
$fileInfo = $this->createMock(FileInfo::class);
$fileInfo->expects($this->any())->method('isEncrypted')
->willReturn($isEncrypted);
$this->view->expects($this->any())->method('getFileInfo')
->willReturn($fileInfo);
$this->view->expects($this->never())->method('getFileInfo');
if ($isEncrypted) {
@@ -375,7 +372,7 @@ class EncryptAllTest extends TestCase {
}
$this->assertTrue(
$this->invokePrivate($this->encryptAll, 'encryptFile', ['foo.txt'])
$this->invokePrivate($this->encryptAll, 'encryptFile', [$fileInfo, 'foo.txt'])
);
}
+4 -3
View File
@@ -47,8 +47,7 @@ OC.L10N.register(
"Share with me through my #Nextcloud Federated Cloud ID, see {url}" : "شارك معي عبر #مُعرّف سحابة نكست كلاود الاتحادية، أنظُر {url} ",
"Share with me through my #Nextcloud Federated Cloud ID" : "شارك معي عبر #مُعرّف سحابة نكست كلاود الاتحادية",
"Share with me via Nextcloud" : "شاركه معي عبر النكست كلاود",
"Cloud ID copied to the clipboard" : "تمّ نسخ مُعرِّف السحابة إلى الحافظة",
"Copy to clipboard" : "نسخ الرابط",
"Copy" : "إنسَخ",
"Clipboard not available. Please copy the cloud ID manually." : "الحافظة غير متوفرة. رجاءً، قم بنسخ مُعرِّف السحابة يدوياً.",
"Copied!" : "تمَّ النسخ!",
"Federated Cloud" : "السحابة الاتحادية",
@@ -66,6 +65,8 @@ OC.L10N.register(
"Remote share" : "مُشاركة بعيدة remote",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "هل ترغب في إضافة مُشاركة بعيدة remote ـ {name} من {owner}@{remote}؟",
"Remote share password" : "كلمة مرور المشاركة البعيدة remote",
"Incoming share could not be processed" : "لا يمكن معالجة المشاركة الواردة"
"Incoming share could not be processed" : "لا يمكن معالجة المشاركة الواردة",
"Cloud ID copied to the clipboard" : "تمّ نسخ مُعرِّف السحابة إلى الحافظة",
"Copy to clipboard" : "نسخ الرابط"
},
"nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;");
+4 -3
View File
@@ -45,8 +45,7 @@
"Share with me through my #Nextcloud Federated Cloud ID, see {url}" : "شارك معي عبر #مُعرّف سحابة نكست كلاود الاتحادية، أنظُر {url} ",
"Share with me through my #Nextcloud Federated Cloud ID" : "شارك معي عبر #مُعرّف سحابة نكست كلاود الاتحادية",
"Share with me via Nextcloud" : "شاركه معي عبر النكست كلاود",
"Cloud ID copied to the clipboard" : "تمّ نسخ مُعرِّف السحابة إلى الحافظة",
"Copy to clipboard" : "نسخ الرابط",
"Copy" : "إنسَخ",
"Clipboard not available. Please copy the cloud ID manually." : "الحافظة غير متوفرة. رجاءً، قم بنسخ مُعرِّف السحابة يدوياً.",
"Copied!" : "تمَّ النسخ!",
"Federated Cloud" : "السحابة الاتحادية",
@@ -64,6 +63,8 @@
"Remote share" : "مُشاركة بعيدة remote",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "هل ترغب في إضافة مُشاركة بعيدة remote ـ {name} من {owner}@{remote}؟",
"Remote share password" : "كلمة مرور المشاركة البعيدة remote",
"Incoming share could not be processed" : "لا يمكن معالجة المشاركة الواردة"
"Incoming share could not be processed" : "لا يمكن معالجة المشاركة الواردة",
"Cloud ID copied to the clipboard" : "تمّ نسخ مُعرِّف السحابة إلى الحافظة",
"Copy to clipboard" : "نسخ الرابط"
},"pluralForm" :"nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;"
}
+4 -3
View File
@@ -34,8 +34,7 @@ OC.L10N.register(
"Share with me through my #Nextcloud Federated Cloud ID, see {url}" : "Compartir conmigo pente la mio ID de nube federada de #Nextcloud, mira {url}",
"Share with me through my #Nextcloud Federated Cloud ID" : "Compartir conmigo pente la mio ID de nube federada de #Nextcloud",
"Share with me via Nextcloud" : "Compartir conmigo per Nextcloud",
"Cloud ID copied to the clipboard" : "La ID de la nube copióse nel cartafueyu",
"Copy to clipboard" : "Copiar nel cartafueyu",
"Copy" : "Copiar",
"Copied!" : "¡Copióse!",
"Federated Cloud" : "Nube federada",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Pues compartir conteníu con cualesquier persona qu'use un sirvidor de Nextcloud o otros sirvidores y servicios compatibles con Open Cloud Mesh (OCM). Namás indica la ID de na nube federada nel cuadru de diálogu d'usu compartíu. Aseméyase a persona@nube.exemplu.com",
@@ -49,6 +48,8 @@ OC.L10N.register(
"Add remote share" : "Amestar un elementu compartíu remotu",
"Remote share" : "Compartición remota",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "¿Quies amestar la compartición remota «{name}» de {owner}@{remote}?",
"Remote share password" : "Contraseña de la compartición remota"
"Remote share password" : "Contraseña de la compartición remota",
"Cloud ID copied to the clipboard" : "La ID de la nube copióse nel cartafueyu",
"Copy to clipboard" : "Copiar nel cartafueyu"
},
"nplurals=2; plural=(n != 1);");
+4 -3
View File
@@ -32,8 +32,7 @@
"Share with me through my #Nextcloud Federated Cloud ID, see {url}" : "Compartir conmigo pente la mio ID de nube federada de #Nextcloud, mira {url}",
"Share with me through my #Nextcloud Federated Cloud ID" : "Compartir conmigo pente la mio ID de nube federada de #Nextcloud",
"Share with me via Nextcloud" : "Compartir conmigo per Nextcloud",
"Cloud ID copied to the clipboard" : "La ID de la nube copióse nel cartafueyu",
"Copy to clipboard" : "Copiar nel cartafueyu",
"Copy" : "Copiar",
"Copied!" : "¡Copióse!",
"Federated Cloud" : "Nube federada",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Pues compartir conteníu con cualesquier persona qu'use un sirvidor de Nextcloud o otros sirvidores y servicios compatibles con Open Cloud Mesh (OCM). Namás indica la ID de na nube federada nel cuadru de diálogu d'usu compartíu. Aseméyase a persona@nube.exemplu.com",
@@ -47,6 +46,8 @@
"Add remote share" : "Amestar un elementu compartíu remotu",
"Remote share" : "Compartición remota",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "¿Quies amestar la compartición remota «{name}» de {owner}@{remote}?",
"Remote share password" : "Contraseña de la compartición remota"
"Remote share password" : "Contraseña de la compartición remota",
"Cloud ID copied to the clipboard" : "La ID de la nube copióse nel cartafueyu",
"Copy to clipboard" : "Copiar nel cartafueyu"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}
+4 -3
View File
@@ -26,8 +26,7 @@ OC.L10N.register(
"Share with me through my #Nextcloud Federated Cloud ID, see {url}" : "Споделете с мен чрез моя #Nextcloud Federated Cloud ID, вижте {url}",
"Share with me through my #Nextcloud Federated Cloud ID" : "Споделете с мен, чрез моя #Nextcloud Federated Cloud ID",
"Share with me via Nextcloud" : "Споделете с мен, чрез Nextcloud",
"Cloud ID copied to the clipboard" : "Cloud идентификатора е копиран в клипборда",
"Copy to clipboard" : "Копиране в клипборда",
"Copy" : "Копие",
"Copied!" : "Копирано!",
"Federated Cloud" : "Федериран облак",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Можете да споделяте с всеки, който използва сървър Nextcloud или други сървъри и услуги, съвместими с Open Cloud Mesh (OCM)! Просто поставете техния идентификатор за Федериран облак в диалоговия прозорец за споделяне. Изглежда като person@cloud.example.com",
@@ -40,6 +39,8 @@ OC.L10N.register(
"Add remote share" : "Добави отдалечено споделяне",
"Remote share" : "Отдалечено споделяне",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "Искате ли да добавите отдалечено споделяне {name} от {owner}@{remote}?",
"Remote share password" : "Парола за отдалечено споделяне"
"Remote share password" : "Парола за отдалечено споделяне",
"Cloud ID copied to the clipboard" : "Cloud идентификатора е копиран в клипборда",
"Copy to clipboard" : "Копиране в клипборда"
},
"nplurals=2; plural=(n != 1);");
+4 -3
View File
@@ -24,8 +24,7 @@
"Share with me through my #Nextcloud Federated Cloud ID, see {url}" : "Споделете с мен чрез моя #Nextcloud Federated Cloud ID, вижте {url}",
"Share with me through my #Nextcloud Federated Cloud ID" : "Споделете с мен, чрез моя #Nextcloud Federated Cloud ID",
"Share with me via Nextcloud" : "Споделете с мен, чрез Nextcloud",
"Cloud ID copied to the clipboard" : "Cloud идентификатора е копиран в клипборда",
"Copy to clipboard" : "Копиране в клипборда",
"Copy" : "Копие",
"Copied!" : "Копирано!",
"Federated Cloud" : "Федериран облак",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Можете да споделяте с всеки, който използва сървър Nextcloud или други сървъри и услуги, съвместими с Open Cloud Mesh (OCM)! Просто поставете техния идентификатор за Федериран облак в диалоговия прозорец за споделяне. Изглежда като person@cloud.example.com",
@@ -38,6 +37,8 @@
"Add remote share" : "Добави отдалечено споделяне",
"Remote share" : "Отдалечено споделяне",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "Искате ли да добавите отдалечено споделяне {name} от {owner}@{remote}?",
"Remote share password" : "Парола за отдалечено споделяне"
"Remote share password" : "Парола за отдалечено споделяне",
"Cloud ID copied to the clipboard" : "Cloud идентификатора е копиран в клипборда",
"Copy to clipboard" : "Копиране в клипборда"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}
+4 -3
View File
@@ -37,8 +37,7 @@ OC.L10N.register(
"Share with me through my #Nextcloud Federated Cloud ID, see {url}" : "Comparteix contingut amb mi amb el meu ID del núvol federat del #Nextcloud, consulta {url}",
"Share with me through my #Nextcloud Federated Cloud ID" : "Comparteix contingut amb mi amb el meu ID del núvol federat del #Nextcloud",
"Share with me via Nextcloud" : "Comparteix contingut amb mi a través del Nextcloud",
"Cloud ID copied to the clipboard" : "S'ha copiat l'ID del núvol al porta-retalls",
"Copy to clipboard" : "Copia-ho al porta-retalls",
"Copy" : "Còpia",
"Clipboard not available. Please copy the cloud ID manually." : "Porta-retalls no disponible. Copieu l'identificador del núvol manualment.",
"Copied!" : "S'ha copiat!",
"Federated Cloud" : "Núvol federat",
@@ -56,6 +55,8 @@ OC.L10N.register(
"Remote share" : "Element compartit remot",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "Voleu afegir l'element compartit remot {name} de {owner}@{remote}?",
"Remote share password" : "Contrasenya de l'element compartit remot",
"Incoming share could not be processed" : "No s'ha pogut processar la compartició entrant"
"Incoming share could not be processed" : "No s'ha pogut processar la compartició entrant",
"Cloud ID copied to the clipboard" : "S'ha copiat l'ID del núvol al porta-retalls",
"Copy to clipboard" : "Copia-ho al porta-retalls"
},
"nplurals=2; plural=(n != 1);");
+4 -3
View File
@@ -35,8 +35,7 @@
"Share with me through my #Nextcloud Federated Cloud ID, see {url}" : "Comparteix contingut amb mi amb el meu ID del núvol federat del #Nextcloud, consulta {url}",
"Share with me through my #Nextcloud Federated Cloud ID" : "Comparteix contingut amb mi amb el meu ID del núvol federat del #Nextcloud",
"Share with me via Nextcloud" : "Comparteix contingut amb mi a través del Nextcloud",
"Cloud ID copied to the clipboard" : "S'ha copiat l'ID del núvol al porta-retalls",
"Copy to clipboard" : "Copia-ho al porta-retalls",
"Copy" : "Còpia",
"Clipboard not available. Please copy the cloud ID manually." : "Porta-retalls no disponible. Copieu l'identificador del núvol manualment.",
"Copied!" : "S'ha copiat!",
"Federated Cloud" : "Núvol federat",
@@ -54,6 +53,8 @@
"Remote share" : "Element compartit remot",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "Voleu afegir l'element compartit remot {name} de {owner}@{remote}?",
"Remote share password" : "Contrasenya de l'element compartit remot",
"Incoming share could not be processed" : "No s'ha pogut processar la compartició entrant"
"Incoming share could not be processed" : "No s'ha pogut processar la compartició entrant",
"Cloud ID copied to the clipboard" : "S'ha copiat l'ID del núvol al porta-retalls",
"Copy to clipboard" : "Copia-ho al porta-retalls"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}
+4 -3
View File
@@ -47,8 +47,7 @@ OC.L10N.register(
"Share with me through my #Nextcloud Federated Cloud ID, see {url}" : "Sdílejte se mnou prostřednictvím mého #Nextcloud identifikátoru v rámci federovaného cloudu více na {url}",
"Share with me through my #Nextcloud Federated Cloud ID" : "Sdílejte se mnou pomocí mého #Nextcloud identifikátoru v rámci federovaného cloudu",
"Share with me via Nextcloud" : "Sdílet se mnou přes Nextcloud",
"Cloud ID copied to the clipboard" : "Cloudový identifikátor zkopírován do schránky",
"Copy to clipboard" : "Zkopírovat do schránky",
"Copy" : "Zkopírovat",
"Clipboard not available. Please copy the cloud ID manually." : "Schránka není k dispozici. Zkopírujte cloudový identifikátor ručně.",
"Copied!" : "Zkopírováno",
"Federated Cloud" : "Federovaný cloud",
@@ -66,6 +65,8 @@ OC.L10N.register(
"Remote share" : "Vzdálené sdílení",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "Chcete přidat vzdálené sdílení {name} od {owner}@{remote}?",
"Remote share password" : "Heslo ke vzdálenému sdílení",
"Incoming share could not be processed" : "Příchozí sdílení se nepodařilo zpracovat"
"Incoming share could not be processed" : "Příchozí sdílení se nepodařilo zpracovat",
"Cloud ID copied to the clipboard" : "Cloudový identifikátor zkopírován do schránky",
"Copy to clipboard" : "Zkopírovat do schránky"
},
"nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;");
+4 -3
View File
@@ -45,8 +45,7 @@
"Share with me through my #Nextcloud Federated Cloud ID, see {url}" : "Sdílejte se mnou prostřednictvím mého #Nextcloud identifikátoru v rámci federovaného cloudu více na {url}",
"Share with me through my #Nextcloud Federated Cloud ID" : "Sdílejte se mnou pomocí mého #Nextcloud identifikátoru v rámci federovaného cloudu",
"Share with me via Nextcloud" : "Sdílet se mnou přes Nextcloud",
"Cloud ID copied to the clipboard" : "Cloudový identifikátor zkopírován do schránky",
"Copy to clipboard" : "Zkopírovat do schránky",
"Copy" : "Zkopírovat",
"Clipboard not available. Please copy the cloud ID manually." : "Schránka není k dispozici. Zkopírujte cloudový identifikátor ručně.",
"Copied!" : "Zkopírováno",
"Federated Cloud" : "Federovaný cloud",
@@ -64,6 +63,8 @@
"Remote share" : "Vzdálené sdílení",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "Chcete přidat vzdálené sdílení {name} od {owner}@{remote}?",
"Remote share password" : "Heslo ke vzdálenému sdílení",
"Incoming share could not be processed" : "Příchozí sdílení se nepodařilo zpracovat"
"Incoming share could not be processed" : "Příchozí sdílení se nepodařilo zpracovat",
"Cloud ID copied to the clipboard" : "Cloudový identifikátor zkopírován do schránky",
"Copy to clipboard" : "Zkopírovat do schránky"
},"pluralForm" :"nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;"
}
+4 -3
View File
@@ -47,8 +47,7 @@ OC.L10N.register(
"Share with me through my #Nextcloud Federated Cloud ID, see {url}" : "Del med mig gennem min #Nextcloud Sammenkoblings Cloud ID, se {url}",
"Share with me through my #Nextcloud Federated Cloud ID" : "Del med mig gennem min #Nextcloud sammenkoblings Cloud ID",
"Share with me via Nextcloud" : "Del med mig gennem Nextcloud",
"Cloud ID copied to the clipboard" : "Cloud ID er kopieret til udklipsholderen.",
"Copy to clipboard" : "Kopier til udklipsholder",
"Copy" : "Kopiér",
"Clipboard not available. Please copy the cloud ID manually." : "Udklipsholder ikke tilgængelig. Kopier venligst Cloud ID'et manuelt.",
"Copied!" : "Kopieret!",
"Federated Cloud" : "Sammenkoblet Cloud",
@@ -66,6 +65,8 @@ OC.L10N.register(
"Remote share" : "Eksterne drev",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "Ønsker du at tilføje det eksterne drev {name} fra {owner}@{remote}?",
"Remote share password" : "Fjerndrev adgangskode",
"Incoming share could not be processed" : "Indgående deling kunne ikke behandles"
"Incoming share could not be processed" : "Indgående deling kunne ikke behandles",
"Cloud ID copied to the clipboard" : "Cloud ID er kopieret til udklipsholderen.",
"Copy to clipboard" : "Kopier til udklipsholder"
},
"nplurals=2; plural=(n != 1);");
+4 -3
View File
@@ -45,8 +45,7 @@
"Share with me through my #Nextcloud Federated Cloud ID, see {url}" : "Del med mig gennem min #Nextcloud Sammenkoblings Cloud ID, se {url}",
"Share with me through my #Nextcloud Federated Cloud ID" : "Del med mig gennem min #Nextcloud sammenkoblings Cloud ID",
"Share with me via Nextcloud" : "Del med mig gennem Nextcloud",
"Cloud ID copied to the clipboard" : "Cloud ID er kopieret til udklipsholderen.",
"Copy to clipboard" : "Kopier til udklipsholder",
"Copy" : "Kopiér",
"Clipboard not available. Please copy the cloud ID manually." : "Udklipsholder ikke tilgængelig. Kopier venligst Cloud ID'et manuelt.",
"Copied!" : "Kopieret!",
"Federated Cloud" : "Sammenkoblet Cloud",
@@ -64,6 +63,8 @@
"Remote share" : "Eksterne drev",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "Ønsker du at tilføje det eksterne drev {name} fra {owner}@{remote}?",
"Remote share password" : "Fjerndrev adgangskode",
"Incoming share could not be processed" : "Indgående deling kunne ikke behandles"
"Incoming share could not be processed" : "Indgående deling kunne ikke behandles",
"Cloud ID copied to the clipboard" : "Cloud ID er kopieret til udklipsholderen.",
"Copy to clipboard" : "Kopier til udklipsholder"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}
+6 -3
View File
@@ -47,8 +47,8 @@ OC.L10N.register(
"Share with me through my #Nextcloud Federated Cloud ID, see {url}" : "Teile mit mir über meine #Nextcloud Federated-Cloud-ID, siehe {url}",
"Share with me through my #Nextcloud Federated Cloud ID" : "Teile mit mir über meine #Nextcloud Federated-Cloud-ID",
"Share with me via Nextcloud" : "Teile mit mir über Nextcloud",
"Cloud ID copied to the clipboard" : "Cloud-ID in die Zwischenablage kopiert",
"Copy to clipboard" : "In die Zwischenablage kopieren",
"Cloud ID copied" : "Cloud-ID kopiert",
"Copy" : "Kopieren",
"Clipboard not available. Please copy the cloud ID manually." : "Zwischenablage nicht verfügbar. Bitte die Cloud-ID manuell kopieren.",
"Copied!" : "Kopiert!",
"Federated Cloud" : "Federated Cloud",
@@ -59,6 +59,7 @@ OC.L10N.register(
"X (formerly Twitter)" : "X (früher Twitter)",
"formerly Twitter" : "früher Twitter",
"Mastodon" : "Mastodon",
"Bluesky" : "Bluesky",
"Add to your website" : "Zu deiner Webseite hinzufügen",
"HTML Code:" : "HTML-Code:",
"Cancel" : "Abbrechen",
@@ -66,6 +67,8 @@ OC.L10N.register(
"Remote share" : "Externe Freigabe",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "Soll die externe Freigabe {name} von {owner}@{remote} hinzugefügt werden?",
"Remote share password" : "Passwort für die externe Freigabe",
"Incoming share could not be processed" : "Eingehende Freigabe konnte nicht verarbeitet werden"
"Incoming share could not be processed" : "Eingehende Freigabe konnte nicht verarbeitet werden",
"Cloud ID copied to the clipboard" : "Cloud-ID in die Zwischenablage kopiert",
"Copy to clipboard" : "In die Zwischenablage kopieren"
},
"nplurals=2; plural=(n != 1);");
+6 -3
View File
@@ -45,8 +45,8 @@
"Share with me through my #Nextcloud Federated Cloud ID, see {url}" : "Teile mit mir über meine #Nextcloud Federated-Cloud-ID, siehe {url}",
"Share with me through my #Nextcloud Federated Cloud ID" : "Teile mit mir über meine #Nextcloud Federated-Cloud-ID",
"Share with me via Nextcloud" : "Teile mit mir über Nextcloud",
"Cloud ID copied to the clipboard" : "Cloud-ID in die Zwischenablage kopiert",
"Copy to clipboard" : "In die Zwischenablage kopieren",
"Cloud ID copied" : "Cloud-ID kopiert",
"Copy" : "Kopieren",
"Clipboard not available. Please copy the cloud ID manually." : "Zwischenablage nicht verfügbar. Bitte die Cloud-ID manuell kopieren.",
"Copied!" : "Kopiert!",
"Federated Cloud" : "Federated Cloud",
@@ -57,6 +57,7 @@
"X (formerly Twitter)" : "X (früher Twitter)",
"formerly Twitter" : "früher Twitter",
"Mastodon" : "Mastodon",
"Bluesky" : "Bluesky",
"Add to your website" : "Zu deiner Webseite hinzufügen",
"HTML Code:" : "HTML-Code:",
"Cancel" : "Abbrechen",
@@ -64,6 +65,8 @@
"Remote share" : "Externe Freigabe",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "Soll die externe Freigabe {name} von {owner}@{remote} hinzugefügt werden?",
"Remote share password" : "Passwort für die externe Freigabe",
"Incoming share could not be processed" : "Eingehende Freigabe konnte nicht verarbeitet werden"
"Incoming share could not be processed" : "Eingehende Freigabe konnte nicht verarbeitet werden",
"Cloud ID copied to the clipboard" : "Cloud-ID in die Zwischenablage kopiert",
"Copy to clipboard" : "In die Zwischenablage kopieren"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}
+6 -3
View File
@@ -47,8 +47,8 @@ OC.L10N.register(
"Share with me through my #Nextcloud Federated Cloud ID, see {url}" : "Teilen Sie mit mir über meine #Nextcloud Federated-Cloud-ID, siehe {url}",
"Share with me through my #Nextcloud Federated Cloud ID" : "Teilen Sie mit mir über meine #Nextcloud Federated-Cloud-ID",
"Share with me via Nextcloud" : "Teilen Sie mit mir über Nextcloud",
"Cloud ID copied to the clipboard" : "Cloud-ID wurde in die Zwischenablage kopiert",
"Copy to clipboard" : "In die Zwischenablage kopieren",
"Cloud ID copied" : "Cloud-ID kopiert",
"Copy" : "Kopieren",
"Clipboard not available. Please copy the cloud ID manually." : "Zwischenablage nicht verfügbar. Bitte die Cloud-ID manuell kopieren.",
"Copied!" : "Kopiert!",
"Federated Cloud" : "Federated Cloud",
@@ -59,6 +59,7 @@ OC.L10N.register(
"X (formerly Twitter)" : "X (früher Twitter)",
"formerly Twitter" : "früher Twitter",
"Mastodon" : "Mastodon",
"Bluesky" : "Bluesky",
"Add to your website" : "Zu Ihrer Webseite hinzufügen",
"HTML Code:" : "HTML-Code:",
"Cancel" : "Abbrechen",
@@ -66,6 +67,8 @@ OC.L10N.register(
"Remote share" : "Externe Freigabe",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "Möchten Sie die externe Freigabe {name} von {owner}@{remote} hinzufügen?",
"Remote share password" : "Passwort für die externe Freigabe",
"Incoming share could not be processed" : "Eingehende Freigabe konnte nicht verarbeitet werden"
"Incoming share could not be processed" : "Eingehende Freigabe konnte nicht verarbeitet werden",
"Cloud ID copied to the clipboard" : "Cloud-ID wurde in die Zwischenablage kopiert",
"Copy to clipboard" : "In die Zwischenablage kopieren"
},
"nplurals=2; plural=(n != 1);");
+6 -3
View File
@@ -45,8 +45,8 @@
"Share with me through my #Nextcloud Federated Cloud ID, see {url}" : "Teilen Sie mit mir über meine #Nextcloud Federated-Cloud-ID, siehe {url}",
"Share with me through my #Nextcloud Federated Cloud ID" : "Teilen Sie mit mir über meine #Nextcloud Federated-Cloud-ID",
"Share with me via Nextcloud" : "Teilen Sie mit mir über Nextcloud",
"Cloud ID copied to the clipboard" : "Cloud-ID wurde in die Zwischenablage kopiert",
"Copy to clipboard" : "In die Zwischenablage kopieren",
"Cloud ID copied" : "Cloud-ID kopiert",
"Copy" : "Kopieren",
"Clipboard not available. Please copy the cloud ID manually." : "Zwischenablage nicht verfügbar. Bitte die Cloud-ID manuell kopieren.",
"Copied!" : "Kopiert!",
"Federated Cloud" : "Federated Cloud",
@@ -57,6 +57,7 @@
"X (formerly Twitter)" : "X (früher Twitter)",
"formerly Twitter" : "früher Twitter",
"Mastodon" : "Mastodon",
"Bluesky" : "Bluesky",
"Add to your website" : "Zu Ihrer Webseite hinzufügen",
"HTML Code:" : "HTML-Code:",
"Cancel" : "Abbrechen",
@@ -64,6 +65,8 @@
"Remote share" : "Externe Freigabe",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "Möchten Sie die externe Freigabe {name} von {owner}@{remote} hinzufügen?",
"Remote share password" : "Passwort für die externe Freigabe",
"Incoming share could not be processed" : "Eingehende Freigabe konnte nicht verarbeitet werden"
"Incoming share could not be processed" : "Eingehende Freigabe konnte nicht verarbeitet werden",
"Cloud ID copied to the clipboard" : "Cloud-ID wurde in die Zwischenablage kopiert",
"Copy to clipboard" : "In die Zwischenablage kopieren"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}
+3 -2
View File
@@ -22,7 +22,7 @@ OC.L10N.register(
"Provide federated file sharing across servers" : "Παρέχει κοινής χρήσης αρχεία μεταξύ διακομιστών",
"Share with me through my #Nextcloud Federated Cloud ID" : "Διαμοιρασμός με εμένα μέσω του #Nextcloud Federated Cloud ID μου",
"Share with me via Nextcloud" : "Διαμοιραστείτε με εμένα μέσω του Nextcloud",
"Copy to clipboard" : "Αντιγραφή στο πρόχειρο",
"Copy" : "Αντιγραφή",
"Copied!" : "Αντιγράφτηκε!",
"Federated Cloud" : "Federated Cloud",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Μπορείτε να διαμοιράζεστε με οποιονδήποτε χρησιμοποιεί Nextcloud ή άλλο συμβατό διακομιστή και υπηρεσιών Open Cloud Mesh (OCM)! Απλά προσθέστε το Federated Cloud ID στο πλαίσιο διαλόγου διαμοιρασμού. Θα μοιάζει με person@cloud.example.com",
@@ -35,6 +35,7 @@ OC.L10N.register(
"Add remote share" : "Προσθήκη απομακρυσμένου κοινόχρηστου φακέλου",
"Remote share" : "Απομακρυσμένος κοινόχρηστος φάκελος",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "Θέλετε να προσθέσουμε τον απομακρυσμένο κοινόχρηστο φάκελο {name} από {owner}@{remote}?",
"Remote share password" : "Συνθηματικό απομακρυσμένου κοινόχρηστου φακέλου"
"Remote share password" : "Συνθηματικό απομακρυσμένου κοινόχρηστου φακέλου",
"Copy to clipboard" : "Αντιγραφή στο πρόχειρο"
},
"nplurals=2; plural=(n != 1);");
+3 -2
View File
@@ -20,7 +20,7 @@
"Provide federated file sharing across servers" : "Παρέχει κοινής χρήσης αρχεία μεταξύ διακομιστών",
"Share with me through my #Nextcloud Federated Cloud ID" : "Διαμοιρασμός με εμένα μέσω του #Nextcloud Federated Cloud ID μου",
"Share with me via Nextcloud" : "Διαμοιραστείτε με εμένα μέσω του Nextcloud",
"Copy to clipboard" : "Αντιγραφή στο πρόχειρο",
"Copy" : "Αντιγραφή",
"Copied!" : "Αντιγράφτηκε!",
"Federated Cloud" : "Federated Cloud",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Μπορείτε να διαμοιράζεστε με οποιονδήποτε χρησιμοποιεί Nextcloud ή άλλο συμβατό διακομιστή και υπηρεσιών Open Cloud Mesh (OCM)! Απλά προσθέστε το Federated Cloud ID στο πλαίσιο διαλόγου διαμοιρασμού. Θα μοιάζει με person@cloud.example.com",
@@ -33,6 +33,7 @@
"Add remote share" : "Προσθήκη απομακρυσμένου κοινόχρηστου φακέλου",
"Remote share" : "Απομακρυσμένος κοινόχρηστος φάκελος",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "Θέλετε να προσθέσουμε τον απομακρυσμένο κοινόχρηστο φάκελο {name} από {owner}@{remote}?",
"Remote share password" : "Συνθηματικό απομακρυσμένου κοινόχρηστου φακέλου"
"Remote share password" : "Συνθηματικό απομακρυσμένου κοινόχρηστου φακέλου",
"Copy to clipboard" : "Αντιγραφή στο πρόχειρο"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}
+6 -3
View File
@@ -47,8 +47,8 @@ OC.L10N.register(
"Share with me through my #Nextcloud Federated Cloud ID, see {url}" : "Share with me through my #Nextcloud Federated Cloud ID, see {url}",
"Share with me through my #Nextcloud Federated Cloud ID" : "Share with me through my #Nextcloud Federated Cloud ID",
"Share with me via Nextcloud" : "Share with me via Nextcloud",
"Cloud ID copied to the clipboard" : "Cloud ID copied to the clipboard",
"Copy to clipboard" : "Copy to clipboard",
"Cloud ID copied" : "Cloud ID copied",
"Copy" : "Copy",
"Clipboard not available. Please copy the cloud ID manually." : "Clipboard not available. Please copy the cloud ID manually.",
"Copied!" : "Copied!",
"Federated Cloud" : "Federated Cloud",
@@ -59,6 +59,7 @@ OC.L10N.register(
"X (formerly Twitter)" : "X (formerly Twitter)",
"formerly Twitter" : "formerly Twitter",
"Mastodon" : "Mastodon",
"Bluesky" : "Bluesky",
"Add to your website" : "Add to your website",
"HTML Code:" : "HTML Code:",
"Cancel" : "Cancel",
@@ -66,6 +67,8 @@ OC.L10N.register(
"Remote share" : "Remote share",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "Do you want to add the remote share {name} from {owner}@{remote}?",
"Remote share password" : "Remote share password",
"Incoming share could not be processed" : "Incoming share could not be processed"
"Incoming share could not be processed" : "Incoming share could not be processed",
"Cloud ID copied to the clipboard" : "Cloud ID copied to the clipboard",
"Copy to clipboard" : "Copy to clipboard"
},
"nplurals=2; plural=(n != 1);");
+6 -3
View File
@@ -45,8 +45,8 @@
"Share with me through my #Nextcloud Federated Cloud ID, see {url}" : "Share with me through my #Nextcloud Federated Cloud ID, see {url}",
"Share with me through my #Nextcloud Federated Cloud ID" : "Share with me through my #Nextcloud Federated Cloud ID",
"Share with me via Nextcloud" : "Share with me via Nextcloud",
"Cloud ID copied to the clipboard" : "Cloud ID copied to the clipboard",
"Copy to clipboard" : "Copy to clipboard",
"Cloud ID copied" : "Cloud ID copied",
"Copy" : "Copy",
"Clipboard not available. Please copy the cloud ID manually." : "Clipboard not available. Please copy the cloud ID manually.",
"Copied!" : "Copied!",
"Federated Cloud" : "Federated Cloud",
@@ -57,6 +57,7 @@
"X (formerly Twitter)" : "X (formerly Twitter)",
"formerly Twitter" : "formerly Twitter",
"Mastodon" : "Mastodon",
"Bluesky" : "Bluesky",
"Add to your website" : "Add to your website",
"HTML Code:" : "HTML Code:",
"Cancel" : "Cancel",
@@ -64,6 +65,8 @@
"Remote share" : "Remote share",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "Do you want to add the remote share {name} from {owner}@{remote}?",
"Remote share password" : "Remote share password",
"Incoming share could not be processed" : "Incoming share could not be processed"
"Incoming share could not be processed" : "Incoming share could not be processed",
"Cloud ID copied to the clipboard" : "Cloud ID copied to the clipboard",
"Copy to clipboard" : "Copy to clipboard"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}
-38
View File
@@ -1,38 +0,0 @@
OC.L10N.register(
"federatedfilesharing",
{
"Invalid Federated Cloud ID" : "Nevalida federnuba identigilo",
"Server to server sharing is not enabled on this server" : "Interservila kunhavo ne estas ebligita en ĉi tiu servilo",
"Couldn't establish a federated share." : "Ne povis fari federan kunhavon.",
"Couldn't establish a federated share, maybe the password was wrong." : "Ne povis fari federan kunhavon, eble la pasvorto ne ĝustas.",
"Federated Share request sent, you will receive an invitation. Check your notifications." : "Federkunhava peto sendita, vi ricevos inviton. Kontrolu viajn sciigojn.",
"Couldn't establish a federated share, it looks like the server to federate with is too old (Nextcloud <= 9)." : "Ne povis fari federan kunhavon, ŝajnas, ke la servilo federota estas tro malnova (Nextcloud ⩽ 9).",
"It is not allowed to send federated group shares from this server." : "Ne estas permesita sendi federajn grupajn kuhavojn el tiu ĉi servilo. ",
"File is already shared with %s" : "Dosiero jam kuhaviĝas kun %s",
"Sharing %1$s failed, could not find %2$s, maybe the server is currently unreachable or uses a self-signed certificate." : "Kunhavigo de %1$s malsukcesis, ne eblis trovi %2$s; eble la servilo estas provizore neatingebla aŭ uzas memsubskribitan atestilon.",
"Could not find share" : "Ne eblis trovi kunhavon",
"Federated sharing" : "Federa kunhavado",
"You received {share} as a remote share from {user} (on behalf of {behalf})" : "Vi ricevis „{share}“ kiel foran kunhavon el {user} (nome de {behalf})",
"You received {share} as a remote share from {user}" : "Vi ricevis „{share}“ kiel foran kunhavon el {user}",
"Accept" : "Akcepti",
"Decline" : "Malakcepti",
"Federated Cloud Sharing" : "Federnuba kunhavigo",
"Sharing" : "Kunhavigo",
"Federated file sharing" : "Federa dosierkunhavado",
"Provide federated file sharing across servers" : "Provizas federan dosierkunhavigon inter serviloj",
"Share with me through my #Nextcloud Federated Cloud ID" : "Kunhavigi kun mi per mia #Nextcloud-federnuba identigilo",
"Share with me via Nextcloud" : "Kunhavigi kun mi per Nextcloud",
"Copy to clipboard" : "Kopii tondejen",
"Copied!" : "Kopiita!",
"Federated Cloud" : "Federa nubo",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Vi povas kunhavigi kun iu, kiu uzas Nextcloud aŭ aliaj serviloj kaj servoj kungruaj kun „Open Cloud Mesh (OCM)“! Entajpu simple lian aŭ ŝian federnuban identigilon en la kunhaviga dialogo. Federnuba identigilo similas al persono@nubo.example.com",
"Share it so your friends can share files with you:" : "Kunhavigu ĝin, por ke viaj amikoj povu kunhavigi dosierojn kun vi:",
"Add to your website" : "Aldoni al via TTT-ejo",
"HTML Code:" : "HTML-kodo:",
"Cancel" : "Nuligi",
"Add remote share" : "Aldoni foran kunhavon",
"Remote share" : "Foran kunhavo",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "Ĉu vi volas aldoni la foran kunhavon {name} de {owner}@{remote}?",
"Remote share password" : "Pasvorto de fora kunhavo"
},
"nplurals=2; plural=(n != 1);");
-36
View File
@@ -1,36 +0,0 @@
{ "translations": {
"Invalid Federated Cloud ID" : "Nevalida federnuba identigilo",
"Server to server sharing is not enabled on this server" : "Interservila kunhavo ne estas ebligita en ĉi tiu servilo",
"Couldn't establish a federated share." : "Ne povis fari federan kunhavon.",
"Couldn't establish a federated share, maybe the password was wrong." : "Ne povis fari federan kunhavon, eble la pasvorto ne ĝustas.",
"Federated Share request sent, you will receive an invitation. Check your notifications." : "Federkunhava peto sendita, vi ricevos inviton. Kontrolu viajn sciigojn.",
"Couldn't establish a federated share, it looks like the server to federate with is too old (Nextcloud <= 9)." : "Ne povis fari federan kunhavon, ŝajnas, ke la servilo federota estas tro malnova (Nextcloud ⩽ 9).",
"It is not allowed to send federated group shares from this server." : "Ne estas permesita sendi federajn grupajn kuhavojn el tiu ĉi servilo. ",
"File is already shared with %s" : "Dosiero jam kuhaviĝas kun %s",
"Sharing %1$s failed, could not find %2$s, maybe the server is currently unreachable or uses a self-signed certificate." : "Kunhavigo de %1$s malsukcesis, ne eblis trovi %2$s; eble la servilo estas provizore neatingebla aŭ uzas memsubskribitan atestilon.",
"Could not find share" : "Ne eblis trovi kunhavon",
"Federated sharing" : "Federa kunhavado",
"You received {share} as a remote share from {user} (on behalf of {behalf})" : "Vi ricevis „{share}“ kiel foran kunhavon el {user} (nome de {behalf})",
"You received {share} as a remote share from {user}" : "Vi ricevis „{share}“ kiel foran kunhavon el {user}",
"Accept" : "Akcepti",
"Decline" : "Malakcepti",
"Federated Cloud Sharing" : "Federnuba kunhavigo",
"Sharing" : "Kunhavigo",
"Federated file sharing" : "Federa dosierkunhavado",
"Provide federated file sharing across servers" : "Provizas federan dosierkunhavigon inter serviloj",
"Share with me through my #Nextcloud Federated Cloud ID" : "Kunhavigi kun mi per mia #Nextcloud-federnuba identigilo",
"Share with me via Nextcloud" : "Kunhavigi kun mi per Nextcloud",
"Copy to clipboard" : "Kopii tondejen",
"Copied!" : "Kopiita!",
"Federated Cloud" : "Federa nubo",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Vi povas kunhavigi kun iu, kiu uzas Nextcloud aŭ aliaj serviloj kaj servoj kungruaj kun „Open Cloud Mesh (OCM)“! Entajpu simple lian aŭ ŝian federnuban identigilon en la kunhaviga dialogo. Federnuba identigilo similas al persono@nubo.example.com",
"Share it so your friends can share files with you:" : "Kunhavigu ĝin, por ke viaj amikoj povu kunhavigi dosierojn kun vi:",
"Add to your website" : "Aldoni al via TTT-ejo",
"HTML Code:" : "HTML-kodo:",
"Cancel" : "Nuligi",
"Add remote share" : "Aldoni foran kunhavon",
"Remote share" : "Foran kunhavo",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "Ĉu vi volas aldoni la foran kunhavon {name} de {owner}@{remote}?",
"Remote share password" : "Pasvorto de fora kunhavo"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}
+6 -3
View File
@@ -47,8 +47,8 @@ OC.L10N.register(
"Share with me through my #Nextcloud Federated Cloud ID, see {url}" : "Comparte conmigo a través de mi ID de Nube Federada #Nextcloud, ve {url}",
"Share with me through my #Nextcloud Federated Cloud ID" : "Compartirlo conmigo a través de mi ID de Nube Federada #Nextcloud",
"Share with me via Nextcloud" : "Compartirlo conmigo vía Nextcloud",
"Cloud ID copied to the clipboard" : "ID de nube copiado al portapapeles",
"Copy to clipboard" : "Copiar al portapapeles",
"Cloud ID copied" : "ID de Nube copiado",
"Copy" : "Copiar",
"Clipboard not available. Please copy the cloud ID manually." : "Portapapeles no disponible. Por favor, copia el ID de nube manualmente.",
"Copied!" : "¡Copiado!",
"Federated Cloud" : "Nube Federada",
@@ -59,6 +59,7 @@ OC.L10N.register(
"X (formerly Twitter)" : "X (anteriormente Twitter)",
"formerly Twitter" : "anteriormente Twitter",
"Mastodon" : "Mastodon",
"Bluesky" : "Bluesky",
"Add to your website" : "Añadir a su sitio web",
"HTML Code:" : "Código HTML:",
"Cancel" : "Cancelar",
@@ -66,6 +67,8 @@ OC.L10N.register(
"Remote share" : "Recurso compartido remoto",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "¿Deseas añadir el recurso compartido remoto {name} de {owner}@{remote}?",
"Remote share password" : "Contraseña del compartido remoto",
"Incoming share could not be processed" : "Elemento compartido entrante no pudo ser procesado"
"Incoming share could not be processed" : "Elemento compartido entrante no pudo ser procesado",
"Cloud ID copied to the clipboard" : "ID de nube copiado al portapapeles",
"Copy to clipboard" : "Copiar al portapapeles"
},
"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
+6 -3
View File
@@ -45,8 +45,8 @@
"Share with me through my #Nextcloud Federated Cloud ID, see {url}" : "Comparte conmigo a través de mi ID de Nube Federada #Nextcloud, ve {url}",
"Share with me through my #Nextcloud Federated Cloud ID" : "Compartirlo conmigo a través de mi ID de Nube Federada #Nextcloud",
"Share with me via Nextcloud" : "Compartirlo conmigo vía Nextcloud",
"Cloud ID copied to the clipboard" : "ID de nube copiado al portapapeles",
"Copy to clipboard" : "Copiar al portapapeles",
"Cloud ID copied" : "ID de Nube copiado",
"Copy" : "Copiar",
"Clipboard not available. Please copy the cloud ID manually." : "Portapapeles no disponible. Por favor, copia el ID de nube manualmente.",
"Copied!" : "¡Copiado!",
"Federated Cloud" : "Nube Federada",
@@ -57,6 +57,7 @@
"X (formerly Twitter)" : "X (anteriormente Twitter)",
"formerly Twitter" : "anteriormente Twitter",
"Mastodon" : "Mastodon",
"Bluesky" : "Bluesky",
"Add to your website" : "Añadir a su sitio web",
"HTML Code:" : "Código HTML:",
"Cancel" : "Cancelar",
@@ -64,6 +65,8 @@
"Remote share" : "Recurso compartido remoto",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "¿Deseas añadir el recurso compartido remoto {name} de {owner}@{remote}?",
"Remote share password" : "Contraseña del compartido remoto",
"Incoming share could not be processed" : "Elemento compartido entrante no pudo ser procesado"
"Incoming share could not be processed" : "Elemento compartido entrante no pudo ser procesado",
"Cloud ID copied to the clipboard" : "ID de nube copiado al portapapeles",
"Copy to clipboard" : "Copiar al portapapeles"
},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
}
+4 -3
View File
@@ -25,8 +25,7 @@ OC.L10N.register(
"Share with me through my #Nextcloud Federated Cloud ID, see {url}" : "Comparte conmigo a través de mi ID de Nube Federada de #Nextcloud, consulta {url}",
"Share with me through my #Nextcloud Federated Cloud ID" : "Compartir conmigo a través de mi ID de Nube Federada #Nextcloud",
"Share with me via Nextcloud" : "Compartir conmigo vía Nextcloud",
"Cloud ID copied to the clipboard" : "ID de Nube copiado al portapapeles",
"Copy to clipboard" : "Copiar al portapapeles",
"Copy" : "Copiar",
"Copied!" : "¡Copiado!",
"Federated Cloud" : "Nube Federada",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "¡Puedes compartir con cualquier persona que utilice un servidor Nextcloud u otros servidores y servicios compatibles con Open Cloud Mesh (OCM)! Simplemente ingresa su ID de Nube Federada en el diálogo de compartición. Se ve como person@cloud.example.com",
@@ -39,6 +38,8 @@ OC.L10N.register(
"Add remote share" : "Agregar elemento compartido remoto",
"Remote share" : "Elemento compartido remoto",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "¿Desea agregar el elemento compartido remoto {name} de {owner}@{remote}?",
"Remote share password" : "Contraseña del elemento compartido remoto"
"Remote share password" : "Contraseña del elemento compartido remoto",
"Cloud ID copied to the clipboard" : "ID de Nube copiado al portapapeles",
"Copy to clipboard" : "Copiar al portapapeles"
},
"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
+4 -3
View File
@@ -23,8 +23,7 @@
"Share with me through my #Nextcloud Federated Cloud ID, see {url}" : "Comparte conmigo a través de mi ID de Nube Federada de #Nextcloud, consulta {url}",
"Share with me through my #Nextcloud Federated Cloud ID" : "Compartir conmigo a través de mi ID de Nube Federada #Nextcloud",
"Share with me via Nextcloud" : "Compartir conmigo vía Nextcloud",
"Cloud ID copied to the clipboard" : "ID de Nube copiado al portapapeles",
"Copy to clipboard" : "Copiar al portapapeles",
"Copy" : "Copiar",
"Copied!" : "¡Copiado!",
"Federated Cloud" : "Nube Federada",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "¡Puedes compartir con cualquier persona que utilice un servidor Nextcloud u otros servidores y servicios compatibles con Open Cloud Mesh (OCM)! Simplemente ingresa su ID de Nube Federada en el diálogo de compartición. Se ve como person@cloud.example.com",
@@ -37,6 +36,8 @@
"Add remote share" : "Agregar elemento compartido remoto",
"Remote share" : "Elemento compartido remoto",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "¿Desea agregar el elemento compartido remoto {name} de {owner}@{remote}?",
"Remote share password" : "Contraseña del elemento compartido remoto"
"Remote share password" : "Contraseña del elemento compartido remoto",
"Cloud ID copied to the clipboard" : "ID de Nube copiado al portapapeles",
"Copy to clipboard" : "Copiar al portapapeles"
},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
}
+4 -3
View File
@@ -34,8 +34,7 @@ OC.L10N.register(
"Share with me through my #Nextcloud Federated Cloud ID, see {url}" : "Comparte conmigo a través de mi identificador de nube federada de #Nextcloud, vea {url}",
"Share with me through my #Nextcloud Federated Cloud ID" : "Compartir conmigo a través de mi ID de Nube Federada #Nextcloud",
"Share with me via Nextcloud" : "Compartir conmigo vía Nextcloud",
"Cloud ID copied to the clipboard" : "Identificador de nube copiado al portapapeles",
"Copy to clipboard" : "Copiar al portapapeles",
"Copy" : "Copiar",
"Copied!" : "¡Copiado!",
"Federated Cloud" : "Nube Federada",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "¡Puede compartir con cualquier persona que use un servidor Nextcloud u otros servidores y servicios compatibles con Open Cloud Mesh (OCM)!. Sólo ponga el identificador de nube federada en el diálogo de compartir. Tiene la forma: persona@nube.ejemplo.com",
@@ -48,6 +47,8 @@ OC.L10N.register(
"Add remote share" : "Agregar elemento compartido remoto",
"Remote share" : "Elemento compartido remoto",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "¿Desea agregar el elemento compartido remoto {name} de {owner}@{remote}?",
"Remote share password" : "Contraseña del elemento compartido remoto"
"Remote share password" : "Contraseña del elemento compartido remoto",
"Cloud ID copied to the clipboard" : "Identificador de nube copiado al portapapeles",
"Copy to clipboard" : "Copiar al portapapeles"
},
"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
+4 -3
View File
@@ -32,8 +32,7 @@
"Share with me through my #Nextcloud Federated Cloud ID, see {url}" : "Comparte conmigo a través de mi identificador de nube federada de #Nextcloud, vea {url}",
"Share with me through my #Nextcloud Federated Cloud ID" : "Compartir conmigo a través de mi ID de Nube Federada #Nextcloud",
"Share with me via Nextcloud" : "Compartir conmigo vía Nextcloud",
"Cloud ID copied to the clipboard" : "Identificador de nube copiado al portapapeles",
"Copy to clipboard" : "Copiar al portapapeles",
"Copy" : "Copiar",
"Copied!" : "¡Copiado!",
"Federated Cloud" : "Nube Federada",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "¡Puede compartir con cualquier persona que use un servidor Nextcloud u otros servidores y servicios compatibles con Open Cloud Mesh (OCM)!. Sólo ponga el identificador de nube federada en el diálogo de compartir. Tiene la forma: persona@nube.ejemplo.com",
@@ -46,6 +45,8 @@
"Add remote share" : "Agregar elemento compartido remoto",
"Remote share" : "Elemento compartido remoto",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "¿Desea agregar el elemento compartido remoto {name} de {owner}@{remote}?",
"Remote share password" : "Contraseña del elemento compartido remoto"
"Remote share password" : "Contraseña del elemento compartido remoto",
"Cloud ID copied to the clipboard" : "Identificador de nube copiado al portapapeles",
"Copy to clipboard" : "Copiar al portapapeles"
},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
}
+6 -3
View File
@@ -47,8 +47,8 @@ OC.L10N.register(
"Share with me through my #Nextcloud Federated Cloud ID, see {url}" : "Jaga minuga minu #Nextcloudi kasutajatunnuse abil liitpilves, vaata {url}",
"Share with me through my #Nextcloud Federated Cloud ID" : "Jaga minuga minu #Nextcloudi kasutajatunnuse abil liitpilves",
"Share with me via Nextcloud" : "Jaga minuga Nextcloudi vahendusel",
"Cloud ID copied to the clipboard" : "Kasutajatunnus liitpilves on kopeeritud lõikelauale",
"Copy to clipboard" : "Kopeeri lõikelauale",
"Cloud ID copied" : "Kasutajatunnus liitpilves on kopeeritud",
"Copy" : "Kopeeri",
"Clipboard not available. Please copy the cloud ID manually." : "Lõikelaud pole saadaval. Palun kopeeri kasutajatunnus liitpilves käsitsi.",
"Copied!" : "Kopeeritud!",
"Federated Cloud" : "Liitpilv",
@@ -59,6 +59,7 @@ OC.L10N.register(
"X (formerly Twitter)" : "X (varasemalt Twitter)",
"formerly Twitter" : "varasemalt Twitter",
"Mastodon" : "Mastodon",
"Bluesky" : "Bluesky",
"Add to your website" : "Lisa oma veebisaidile",
"HTML Code:" : "HTML kood:",
"Cancel" : "Tühista",
@@ -66,6 +67,8 @@ OC.L10N.register(
"Remote share" : "Kaugjagamine",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "Soovid lisada kaugjagamise {name} asukohast {owner}@{remote}?",
"Remote share password" : "Kaugjagamise salasõna",
"Incoming share could not be processed" : "Sissetulevat kausta ei saanud töödelda"
"Incoming share could not be processed" : "Sissetulevat kausta ei saanud töödelda",
"Cloud ID copied to the clipboard" : "Kasutajatunnus liitpilves on kopeeritud lõikelauale",
"Copy to clipboard" : "Kopeeri lõikelauale"
},
"nplurals=2; plural=(n != 1);");
+6 -3
View File
@@ -45,8 +45,8 @@
"Share with me through my #Nextcloud Federated Cloud ID, see {url}" : "Jaga minuga minu #Nextcloudi kasutajatunnuse abil liitpilves, vaata {url}",
"Share with me through my #Nextcloud Federated Cloud ID" : "Jaga minuga minu #Nextcloudi kasutajatunnuse abil liitpilves",
"Share with me via Nextcloud" : "Jaga minuga Nextcloudi vahendusel",
"Cloud ID copied to the clipboard" : "Kasutajatunnus liitpilves on kopeeritud lõikelauale",
"Copy to clipboard" : "Kopeeri lõikelauale",
"Cloud ID copied" : "Kasutajatunnus liitpilves on kopeeritud",
"Copy" : "Kopeeri",
"Clipboard not available. Please copy the cloud ID manually." : "Lõikelaud pole saadaval. Palun kopeeri kasutajatunnus liitpilves käsitsi.",
"Copied!" : "Kopeeritud!",
"Federated Cloud" : "Liitpilv",
@@ -57,6 +57,7 @@
"X (formerly Twitter)" : "X (varasemalt Twitter)",
"formerly Twitter" : "varasemalt Twitter",
"Mastodon" : "Mastodon",
"Bluesky" : "Bluesky",
"Add to your website" : "Lisa oma veebisaidile",
"HTML Code:" : "HTML kood:",
"Cancel" : "Tühista",
@@ -64,6 +65,8 @@
"Remote share" : "Kaugjagamine",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "Soovid lisada kaugjagamise {name} asukohast {owner}@{remote}?",
"Remote share password" : "Kaugjagamise salasõna",
"Incoming share could not be processed" : "Sissetulevat kausta ei saanud töödelda"
"Incoming share could not be processed" : "Sissetulevat kausta ei saanud töödelda",
"Cloud ID copied to the clipboard" : "Kasutajatunnus liitpilves on kopeeritud lõikelauale",
"Copy to clipboard" : "Kopeeri lõikelauale"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}
+4 -3
View File
@@ -36,8 +36,7 @@ OC.L10N.register(
"Share with me through my #Nextcloud Federated Cloud ID, see {url}" : "Partekatu nirekin, nire federatutako #Nextcloud hodei IDa erabiliz, ikus {url}",
"Share with me through my #Nextcloud Federated Cloud ID" : "Partekatu nirekin, nire federatutako #Nextcloud hodei IDa erabiliz",
"Share with me via Nextcloud" : "Partekatu nirekin Nextcloud bidez",
"Cloud ID copied to the clipboard" : "Hodei IDa arbelean kopiatu da",
"Copy to clipboard" : "Kopiatu arbelera",
"Copy" : "Kopiatu",
"Clipboard not available. Please copy the cloud ID manually." : "Arbela ez dago eskuragarri, mesedez kopiatu hodei IDa eskuz.",
"Copied!" : "Kopiatuta!",
"Federated Cloud" : "Hodei Federatua",
@@ -55,6 +54,8 @@ OC.L10N.register(
"Remote share" : "Urruneko partekatzea",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "{owner}@{remote}(r)en {name} urruneko partekatzea gehitu nahi duzu?",
"Remote share password" : "Urruneko partekatzearen pasahitza",
"Incoming share could not be processed" : "Sarrerako partekatzea ezin izan da prozesatu"
"Incoming share could not be processed" : "Sarrerako partekatzea ezin izan da prozesatu",
"Cloud ID copied to the clipboard" : "Hodei IDa arbelean kopiatu da",
"Copy to clipboard" : "Kopiatu arbelera"
},
"nplurals=2; plural=(n != 1);");
+4 -3
View File
@@ -34,8 +34,7 @@
"Share with me through my #Nextcloud Federated Cloud ID, see {url}" : "Partekatu nirekin, nire federatutako #Nextcloud hodei IDa erabiliz, ikus {url}",
"Share with me through my #Nextcloud Federated Cloud ID" : "Partekatu nirekin, nire federatutako #Nextcloud hodei IDa erabiliz",
"Share with me via Nextcloud" : "Partekatu nirekin Nextcloud bidez",
"Cloud ID copied to the clipboard" : "Hodei IDa arbelean kopiatu da",
"Copy to clipboard" : "Kopiatu arbelera",
"Copy" : "Kopiatu",
"Clipboard not available. Please copy the cloud ID manually." : "Arbela ez dago eskuragarri, mesedez kopiatu hodei IDa eskuz.",
"Copied!" : "Kopiatuta!",
"Federated Cloud" : "Hodei Federatua",
@@ -53,6 +52,8 @@
"Remote share" : "Urruneko partekatzea",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "{owner}@{remote}(r)en {name} urruneko partekatzea gehitu nahi duzu?",
"Remote share password" : "Urruneko partekatzearen pasahitza",
"Incoming share could not be processed" : "Sarrerako partekatzea ezin izan da prozesatu"
"Incoming share could not be processed" : "Sarrerako partekatzea ezin izan da prozesatu",
"Cloud ID copied to the clipboard" : "Hodei IDa arbelean kopiatu da",
"Copy to clipboard" : "Kopiatu arbelera"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}
+4 -3
View File
@@ -26,8 +26,7 @@ OC.L10N.register(
"Share with me through my #Nextcloud Federated Cloud ID, see {url}" : "از طریق شناسه ابری فدرال #Nextcloud با من به اشتراک بگذارید، به {url} مراجعه کنید",
"Share with me through my #Nextcloud Federated Cloud ID" : "از طریق شناسه ابری فدرال #Nextcloud با من به اشتراک بگذارید",
"Share with me via Nextcloud" : "هم‌رسانی با من روی نسکت‌کلود",
"Cloud ID copied to the clipboard" : "Cloud ID در کلیپ بورد کپی شد",
"Copy to clipboard" : "رونوشت به تخته‌گیره",
"Copy" : "کپی",
"Copied!" : "رونوشت شد!",
"Federated Cloud" : "ابر خودگردان",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "می‌توانید با هر کسی که از سرور Nextcloud یا سایر سرورها و سرویس‌های سازگار با Open Cloud Mesh (OCM) استفاده می‌کند، اشتراک‌گذاری کنید! فقط شناسه ابری فدرال آنها را در گفتگوی اشتراک گذاری قرار دهید. به نظر می رسد person@cloud.example.com",
@@ -40,6 +39,8 @@ OC.L10N.register(
"Add remote share" : "افزودن هم‌رسانی دوردست",
"Remote share" : "هم‌رسانی دوردست",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "می‌خواهید هم‌رسانی دوردست {name} را از {owner}@{remote} بیفزایید؟",
"Remote share password" : "گذرواژهٔ هم‌رسانی دوردست"
"Remote share password" : "گذرواژهٔ هم‌رسانی دوردست",
"Cloud ID copied to the clipboard" : "Cloud ID در کلیپ بورد کپی شد",
"Copy to clipboard" : "رونوشت به تخته‌گیره"
},
"nplurals=2; plural=(n > 1);");
+4 -3
View File
@@ -24,8 +24,7 @@
"Share with me through my #Nextcloud Federated Cloud ID, see {url}" : "از طریق شناسه ابری فدرال #Nextcloud با من به اشتراک بگذارید، به {url} مراجعه کنید",
"Share with me through my #Nextcloud Federated Cloud ID" : "از طریق شناسه ابری فدرال #Nextcloud با من به اشتراک بگذارید",
"Share with me via Nextcloud" : "هم‌رسانی با من روی نسکت‌کلود",
"Cloud ID copied to the clipboard" : "Cloud ID در کلیپ بورد کپی شد",
"Copy to clipboard" : "رونوشت به تخته‌گیره",
"Copy" : "کپی",
"Copied!" : "رونوشت شد!",
"Federated Cloud" : "ابر خودگردان",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "می‌توانید با هر کسی که از سرور Nextcloud یا سایر سرورها و سرویس‌های سازگار با Open Cloud Mesh (OCM) استفاده می‌کند، اشتراک‌گذاری کنید! فقط شناسه ابری فدرال آنها را در گفتگوی اشتراک گذاری قرار دهید. به نظر می رسد person@cloud.example.com",
@@ -38,6 +37,8 @@
"Add remote share" : "افزودن هم‌رسانی دوردست",
"Remote share" : "هم‌رسانی دوردست",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "می‌خواهید هم‌رسانی دوردست {name} را از {owner}@{remote} بیفزایید؟",
"Remote share password" : "گذرواژهٔ هم‌رسانی دوردست"
"Remote share password" : "گذرواژهٔ هم‌رسانی دوردست",
"Cloud ID copied to the clipboard" : "Cloud ID در کلیپ بورد کپی شد",
"Copy to clipboard" : "رونوشت به تخته‌گیره"
},"pluralForm" :"nplurals=2; plural=(n > 1);"
}
+3 -2
View File
@@ -22,7 +22,7 @@ OC.L10N.register(
"Provide federated file sharing across servers" : "Mahdollistaa federoidun tiedostojaon palvelinten välillä",
"Share with me through my #Nextcloud Federated Cloud ID" : "Jaa kanssani käyttäen #Nextcloud ja federoitua pilvitunnistetta",
"Share with me via Nextcloud" : "Jaa kanssani Nextcloudin kautta",
"Copy to clipboard" : "Kopioi leikepöydälle",
"Copy" : "Kopioi",
"Copied!" : "Kopioitu!",
"Federated Cloud" : "Federoitu pilvi",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Voit jakaa kenelle tahansa, joka käyttää Nextcloud-palvelinta tai muuta Open Cloud Mesh (OCM) -yhteensopivaa palvelinta tai palvelua! Kirjoita heidän federoidun pilven tunniste jaon kohteeksi. Se on muodossa henkilö@cloud.example.com",
@@ -36,6 +36,7 @@ OC.L10N.register(
"Add remote share" : "Lisää etäjako",
"Remote share" : "Etäjako",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "Haluatko lisätä etäjaon {name} kohteesta {owner}@{remote}?",
"Remote share password" : "Etäjaon salasana"
"Remote share password" : "Etäjaon salasana",
"Copy to clipboard" : "Kopioi leikepöydälle"
},
"nplurals=2; plural=(n != 1);");
+3 -2
View File
@@ -20,7 +20,7 @@
"Provide federated file sharing across servers" : "Mahdollistaa federoidun tiedostojaon palvelinten välillä",
"Share with me through my #Nextcloud Federated Cloud ID" : "Jaa kanssani käyttäen #Nextcloud ja federoitua pilvitunnistetta",
"Share with me via Nextcloud" : "Jaa kanssani Nextcloudin kautta",
"Copy to clipboard" : "Kopioi leikepöydälle",
"Copy" : "Kopioi",
"Copied!" : "Kopioitu!",
"Federated Cloud" : "Federoitu pilvi",
"You can share with anyone who uses a Nextcloud server or other Open Cloud Mesh (OCM) compatible servers and services! Just put their Federated Cloud ID in the share dialog. It looks like person@cloud.example.com" : "Voit jakaa kenelle tahansa, joka käyttää Nextcloud-palvelinta tai muuta Open Cloud Mesh (OCM) -yhteensopivaa palvelinta tai palvelua! Kirjoita heidän federoidun pilven tunniste jaon kohteeksi. Se on muodossa henkilö@cloud.example.com",
@@ -34,6 +34,7 @@
"Add remote share" : "Lisää etäjako",
"Remote share" : "Etäjako",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "Haluatko lisätä etäjaon {name} kohteesta {owner}@{remote}?",
"Remote share password" : "Etäjaon salasana"
"Remote share password" : "Etäjaon salasana",
"Copy to clipboard" : "Kopioi leikepöydälle"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}
+4 -3
View File
@@ -47,8 +47,7 @@ OC.L10N.register(
"Share with me through my #Nextcloud Federated Cloud ID, see {url}" : "Partagez avec moi grâce à mon ID de Cloud Fédéré #Nextcloud, voir {url}.",
"Share with me through my #Nextcloud Federated Cloud ID" : "Partagez avec moi grâce à mon ID de Cloud Fédéré #Nextcloud",
"Share with me via Nextcloud" : "Partagez avec moi via Nextcloud",
"Cloud ID copied to the clipboard" : "ID de Cloud Fédéré copié dans le presse-papiers",
"Copy to clipboard" : "Copier dans le presse-papiers",
"Copy" : "Copier",
"Clipboard not available. Please copy the cloud ID manually." : "Presse-papiers non disponible. Veuillez copier l'ID cloud manuellement.",
"Copied!" : "Copié !",
"Federated Cloud" : "Cloud Fédéré",
@@ -66,6 +65,8 @@ OC.L10N.register(
"Remote share" : "Partage distant",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "Voulez-vous ajouter le partage distant {name} depuis {owner}@{remote} ?",
"Remote share password" : "Mot de passe du partage distant",
"Incoming share could not be processed" : "Le partage entrant n'a pas pu être traité"
"Incoming share could not be processed" : "Le partage entrant n'a pas pu être traité",
"Cloud ID copied to the clipboard" : "ID de Cloud Fédéré copié dans le presse-papiers",
"Copy to clipboard" : "Copier dans le presse-papiers"
},
"nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
+4 -3
View File
@@ -45,8 +45,7 @@
"Share with me through my #Nextcloud Federated Cloud ID, see {url}" : "Partagez avec moi grâce à mon ID de Cloud Fédéré #Nextcloud, voir {url}.",
"Share with me through my #Nextcloud Federated Cloud ID" : "Partagez avec moi grâce à mon ID de Cloud Fédéré #Nextcloud",
"Share with me via Nextcloud" : "Partagez avec moi via Nextcloud",
"Cloud ID copied to the clipboard" : "ID de Cloud Fédéré copié dans le presse-papiers",
"Copy to clipboard" : "Copier dans le presse-papiers",
"Copy" : "Copier",
"Clipboard not available. Please copy the cloud ID manually." : "Presse-papiers non disponible. Veuillez copier l'ID cloud manuellement.",
"Copied!" : "Copié !",
"Federated Cloud" : "Cloud Fédéré",
@@ -64,6 +63,8 @@
"Remote share" : "Partage distant",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "Voulez-vous ajouter le partage distant {name} depuis {owner}@{remote} ?",
"Remote share password" : "Mot de passe du partage distant",
"Incoming share could not be processed" : "Le partage entrant n'a pas pu être traité"
"Incoming share could not be processed" : "Le partage entrant n'a pas pu être traité",
"Cloud ID copied to the clipboard" : "ID de Cloud Fédéré copié dans le presse-papiers",
"Copy to clipboard" : "Copier dans le presse-papiers"
},"pluralForm" :"nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
}
+5 -3
View File
@@ -47,8 +47,7 @@ OC.L10N.register(
"Share with me through my #Nextcloud Federated Cloud ID, see {url}" : "Roinn liom trí m'aitheantas scamall #Nextcloud Federated Cloud, féach {url}",
"Share with me through my #Nextcloud Federated Cloud ID" : "Roinn liom trí m'aitheantas scamall #Nextcloud Federated Cloud",
"Share with me via Nextcloud" : "Roinn liom trí Nextcloud",
"Cloud ID copied to the clipboard" : "Cóipeáladh Cloud ID chuig an ngearrthaisce",
"Copy to clipboard" : "Cóipeáil chuig an ngearrthaisce",
"Copy" : "Cóipeáil",
"Clipboard not available. Please copy the cloud ID manually." : "Níl an gearrthaisce ar fáil. Cóipeáil an t-aitheantas néil de láimh.",
"Copied!" : "Cóipeáladh!",
"Federated Cloud" : "Scamall Cónaidhme",
@@ -59,6 +58,7 @@ OC.L10N.register(
"X (formerly Twitter)" : "X (Twitter roimhe seo)",
"formerly Twitter" : "Twitter roimhe seo",
"Mastodon" : "Mastodon",
"Bluesky" : "Bluesky",
"Add to your website" : "Cuir le do láithreán gréasáin",
"HTML Code:" : "Cód HTML:",
"Cancel" : "Cealaigh",
@@ -66,6 +66,8 @@ OC.L10N.register(
"Remote share" : "Comhroinnt iargúlta",
"Do you want to add the remote share {name} from {owner}@{remote}?" : "Ar mhaith leat an sciar cianda {name} ó {owner}@{remote} a chur leis?",
"Remote share password" : "Pasfhocal comhroinnte cianda",
"Incoming share could not be processed" : "Níorbh fhéidir an sciar isteach a phróiseáil"
"Incoming share could not be processed" : "Níorbh fhéidir an sciar isteach a phróiseáil",
"Cloud ID copied to the clipboard" : "Cóipeáladh Cloud ID chuig an ngearrthaisce",
"Copy to clipboard" : "Cóipeáil chuig an ngearrthaisce"
},
"nplurals=5; plural=(n==1 ? 0 : n==2 ? 1 : n<7 ? 2 : n<11 ? 3 : 4);");

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