Compare commits

..

277 Commits

Author SHA1 Message Date
Josh edbb1e2821 chore(Setup/AbstractDB): fixup for cs and psalm
Signed-off-by: Josh <josh.t.richards@gmail.com>
2026-02-18 22:32:10 -05:00
Josh 047d464240 chore(setup/AbstractDB): drop fixed psalm-baseline entries
Signed-off-by: Josh <josh.t.richards@gmail.com>
2026-02-18 22:15:47 -05:00
Josh 46b6185202 refactor(setup/AbstractDB): add logging and docs to runMigrations
Signed-off-by: Josh <josh.t.richards@gmail.com>
2026-02-18 22:04:40 -05:00
Josh d3ce30b46a feat(setup/AbstractDB): add param debug logging to connect()
Signed-off-by: Josh <josh.t.richards@gmail.com>
2026-02-18 16:56:01 -05:00
Josh c2067e1889 refactor(setup/AbstractDB): extract port/socket handling logic
Also:

- add some helpful inline comments
- simplify primary/replica param merging (same outcome)

Signed-off-by: Josh <josh.t.richards@gmail.com>
2026-02-18 16:46:55 -05:00
Josh 1d33bbbda7 refactor(setup/AbstractDB): split initialize() logic up into helpers
- Improved readability
- Easier child class customization

Also added for certain config keys: a couple clarifying comments and additional error checking

Signed-off-by: Josh <josh.t.richards@gmail.com>
2026-02-18 11:26:27 -05:00
Josh 55268455a6 refactor(setup/AbstractDB): early return + override comment in validateDatabaseName()
Signed-off-by: Josh <josh.t.richards@gmail.com>
2026-02-18 10:23:04 -05:00
Josh a5c4640be7 chore(setup/PostgreSQL): switch from $dbprettyname to getDisplayName
Signed-off-by: Josh <josh.t.richards@gmail.com>
2026-02-18 10:08:17 -05:00
Josh 63e1a49c0a chore(setup/Sqlite): switch from $dbprettyname to getDisplayName
And canonicalize "SQLite" properly

Signed-off-by: Josh <josh.t.richards@gmail.com>
2026-02-18 10:06:45 -05:00
Josh f9ae23b064 chore(setup/OCI): switch from $dbprettyname to getDisplayName
Signed-off-by: Josh <josh.t.richards@gmail.com>
2026-02-18 10:04:48 -05:00
Josh b0294cf397 chore(setup/MySQL): switch from $dbprettyname to getDisplayName
Signed-off-by: Josh <josh.t.richards@gmail.com>
2026-02-18 10:03:22 -05:00
Josh 41c9b48346 refactor(setup/AbstractDB): eliminate magic property $this->dbprettyname
- Makes the undefined property explicit and required (less psalm baseline noise)
- Forces child classes to implement it
- Better type safety

Signed-off-by: Josh <josh.t.richards@gmail.com>
2026-02-18 10:00:18 -05:00
Josh 28449695f7 refactor(setup/OCI): override override validateDatabaseName() to skip dot check
Signed-off-by: Josh <josh.t.richards@gmail.com>
2026-02-18 09:35:31 -05:00
Josh 57e54fd867 refactor(setup/AbstractDB): Extract validation methods
- Child classes can override specific validation rules more selectively (e.g. OCI override becomes simpler to avoid the dot check)
- Improved code readability

Signed-off-by: Josh <josh.t.richards@gmail.com>
2026-02-18 09:28:11 -05:00
Josh 5aba60adab fix(setup/AbstractDB): handle missing dbname in dots check
Also:

- Switch to more modern str_contains()
- Tidy up code formatting a bit

Signed-off-by: Josh <josh.t.richards@gmail.com>
2026-02-18 09:20:24 -05:00
Joas Schilling a6d1fa1fa6 Merge pull request #58399 from nextcloud/followup/58282/restore-typing-on-middle-class
fix(migrations): Restore type definitions for API class used in apps
2026-02-18 09:24:57 +01:00
Joas Schilling 06f35ec4f8 fix(migrations): Restore type definitions for API class used in apps
Signed-off-by: Joas Schilling <coding@schilljs.com>
2026-02-18 08:36:08 +01:00
Nextcloud bot 16d3cee3a2 fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2026-02-18 00:20:52 +00:00
Ferdinand Thiessen 48d6d8df2b Merge pull request #54048 from nextcloud/files-external-optional-dependencies
fix(files_external): ignore unsatisfied optional dependencies
2026-02-17 22:58:42 +01:00
Ferdinand Thiessen 252449023f Merge pull request #58379 from nextcloud/backport/58285/master
fix(cache): filter out invalid entries in `OC\Files\Cache\Wrapper\CacheWrapper::getFolderContentsById`
2026-02-17 18:11:04 +01:00
Jan-Philipp Litza 0f4d6b8027 fix(files_external): ignore unsatisfied optional dependencies
Signed-off-by: Jan-Philipp Litza <janphilipp@litza.de>
2026-02-17 18:03:56 +01:00
Ferdinand Thiessen 88bb82ccfe fix(cache): filter out invalid entries in OC\Files\Cache\Wrapper\CacheWrapper::getFolderContentsById
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2026-02-17 17:20:46 +01:00
Ferdinand Thiessen 560df4a73c fix(cache): filter out invalid entries in OC\Files\Cache\Wrapper\CacheWrapper::getFolderContentsById
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>

[skip ci]
2026-02-17 17:17:02 +01:00
Benjamin Gaussorgues f2907f133c Merge pull request #58282 from nextcloud/carl/refactor-migration
refactor(psalm): Modernize migration doc
2026-02-17 14:58:03 +01:00
Ferdinand Thiessen b6d22f384b Merge pull request #57648 from nextcloud/chore/drop-jquery
chore!: drop jQuery UI and jQuery
2026-02-17 14:14:12 +01:00
Robin Appelman 867ef52cfb Merge pull request #57523 from nextcloud/authoritative-share-provider
Make share mount provider authoritative
2026-02-17 13:31:10 +01:00
Kate e9bba6a610 Merge pull request #58286 from nextcloud/fix/groupfolders-phpstan
Fix a bunch of typing issues to make PHPStan level 10 happy on groupfolders
2026-02-17 13:30:46 +01:00
Ferdinand Thiessen 0b37d37884 chore: compile assets
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2026-02-17 12:53:15 +01:00
Ferdinand Thiessen b0df06d533 chore!(core): remove deprecated jQuery and jQuery UI
"jQuery is deprecated and will be removed with Nextcloud 19"
Well... At least it is removed now 😉

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2026-02-17 12:51:42 +01:00
Ferdinand Thiessen 00a5c028a4 chore!(core): remove legacy Backbone
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2026-02-17 12:50:49 +01:00
Ferdinand Thiessen a32168b01e chore!(core): remove legacy OC.Files.Client
Use the DAV client from `@nextcloud/files/dav` instead.

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2026-02-17 12:50:49 +01:00
Ferdinand Thiessen fd5108b68b chore!(core): remove global snapper.js handling
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2026-02-17 12:50:49 +01:00
Ferdinand Thiessen c89939fbd6 chore!(core): remove handling of .live-relative-timestamp use @nextcloud/vue/components/NcDateTime instead
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2026-02-17 12:50:49 +01:00
Ferdinand Thiessen 503fad2b16 chore!(core): remove legacy OC.registerMenu and other menu helpers
Instead use Vue components.

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2026-02-17 12:50:49 +01:00
Ferdinand Thiessen e14d97b95a chore!(core): remove deprecated OC.Apps
Instead use components from `@nextcloud/vue`

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2026-02-17 12:50:49 +01:00
Ferdinand Thiessen b66292a0d4 chore!(core): drop deprecated OC.Notification
Deprecated since 17.0.0, use the `@nextcloud/dialogs` package instead.

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2026-02-17 12:50:49 +01:00
Ferdinand Thiessen 16f4ff98e4 chore!(core): drop deprecated OC.Dialogs.fileexists
It is deprecated since 29.0.0.
Instead use `openConflictPicker` from the `@nextcloud/upload` (Vue 2).
Or from `@nextcloud/dialogs` (every other framework like Vue 3 etc).

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2026-02-17 12:50:49 +01:00
Ferdinand Thiessen 9c8f80102a chore!(core): drop deprecated OC.Settings
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2026-02-17 12:50:49 +01:00
Ferdinand Thiessen b0e99d0293 Merge pull request #58327 from nextcloud/feat/1698/recent-files-include-recently-uploaded
feat: add upload_time support to file search and recent files
2026-02-17 11:48:12 +01:00
Kate 3a80443508 Merge pull request #58278 from nextcloud/techdebt/noid/openapi-for-workflow-engine
feat(workflowengine): Add openapi for workflow engine
2026-02-17 08:34:45 +01:00
provokateurin 4eada2d804 refactor(Files): Modernize Wrapper
Signed-off-by: provokateurin <kate@provokateurin.de>
2026-02-17 06:43:16 +01:00
provokateurin 672a29aa0c refactor(Files): Remove deprecated and unused getMimeType and searchByMime methods
Signed-off-by: provokateurin <kate@provokateurin.de>
2026-02-17 06:43:16 +01:00
provokateurin 5f4a5d8c67 refactor(Files): Remove deprecated streamCopy method
Signed-off-by: provokateurin <kate@provokateurin.de>
2026-02-17 06:43:16 +01:00
provokateurin c953ad9bcd fix(Storage): Use correct return types for getMetaData and getDirectoryContent
Signed-off-by: provokateurin <kate@provokateurin.de>
2026-02-17 06:43:16 +01:00
provokateurin 7ceddb3ffb fix(IStorage): Use correct return type for stat
Signed-off-by: provokateurin <kate@provokateurin.de>
2026-02-17 06:43:16 +01:00
provokateurin 2d486c5629 fix(IGroupManager): Use correct return type for displayNamesInGroup
Signed-off-by: provokateurin <kate@provokateurin.de>
2026-02-17 06:43:16 +01:00
provokateurin e454cc6765 refactor(settings): Modernize authorized group classes
Signed-off-by: provokateurin <kate@provokateurin.de>
2026-02-17 06:43:16 +01:00
provokateurin 3693cbeaf9 fix(IMetadataVersion): Use correct return type for getMetadata
Signed-off-by: provokateurin <kate@provokateurin.de>
2026-02-17 06:43:16 +01:00
provokateurin e40d6b1c6b refactor(dav): Modernize Node
Signed-off-by: provokateurin <kate@provokateurin.de>
2026-02-17 06:43:16 +01:00
provokateurin bf71461bee fix(psalm-strict): Supress errors related to internal classes, methods and properties
Signed-off-by: provokateurin <kate@provokateurin.de>
2026-02-17 06:43:16 +01:00
Nextcloud bot 045a41625a fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2026-02-17 00:20:47 +00:00
Robin Appelman 3ec7bd2ee1 Merge pull request #58207 from nextcloud/share-verify-mount-event-improvements
feat: improve VerifyMountPointEvent event
2026-02-16 20:49:39 +01:00
Joas Schilling 4134df9eda feat(workflowengine): Add openapi for workflow engine
Signed-off-by: Joas Schilling <coding@schilljs.com>
2026-02-16 17:51:18 +01:00
Kent Delante 91946d549f Merge pull request #58262 from nextcloud/leftybournes/fix/external_storage_forbid_local
fix(external_storage): hide local storage as an option for users
2026-02-16 21:23:52 +08:00
Kent Delante 2d2139ab94 fix(external_storage): hide local storage as an option for users
Signed-off-by: Kent Delante <kent.delante@proton.me>
2026-02-16 20:53:30 +08:00
Kate 35f24bcd38 Merge pull request #58355 from nextcloud/fix/ocp/container
fix(IRegistrationContext): Use IContainer in registerService factory
2026-02-16 13:36:37 +01:00
provokateurin d92c2de741 fix(IRegistrationContext): Use IContainer in registerService factory
Signed-off-by: provokateurin <kate@provokateurin.de>
2026-02-16 10:45:13 +01:00
provokateurin 931d15e476 feat(IContainer): Undeprecate class
Signed-off-by: provokateurin <kate@provokateurin.de>
2026-02-16 10:45:13 +01:00
provokateurin 935c2416b5 fix(IContainer): Override get method with better type annotations
Signed-off-by: provokateurin <kate@provokateurin.de>
2026-02-16 10:45:13 +01:00
provokateurin 9dc1d6372f fix(IContainer): Fix parameter and return types
Signed-off-by: provokateurin <kate@provokateurin.de>
2026-02-16 10:45:13 +01:00
Git'Fellow f06133bf13 Merge pull request #58350 from nextcloud/regressionEmptyPort
fix(sftp): Handle empty port parameter to allow host-defined ports
2026-02-16 10:13:38 +01:00
Nextcloud bot 891ec7bed5 fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2026-02-16 00:20:46 +00:00
github-actions[bot] 53ac6d62b5 Merge pull request #58307 from nextcloud/dependabot/npm_and_yarn/nextcloud/dialogs-7.3.0
chore(deps): Bump @nextcloud/dialogs from 7.2.0 to 7.3.0
2026-02-15 16:07:39 +01:00
nextcloud-command b812040314 chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2026-02-15 12:39:59 +00:00
dependabot[bot] d4416f10fe chore(deps): Bump @nextcloud/dialogs from 7.2.0 to 7.3.0
Bumps [@nextcloud/dialogs](https://github.com/nextcloud-libraries/nextcloud-dialogs) from 7.2.0 to 7.3.0.
- [Release notes](https://github.com/nextcloud-libraries/nextcloud-dialogs/releases)
- [Changelog](https://github.com/nextcloud-libraries/nextcloud-dialogs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/nextcloud-libraries/nextcloud-dialogs/compare/v7.2.0...v7.3.0)

---
updated-dependencies:
- dependency-name: "@nextcloud/dialogs"
  dependency-version: 7.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-15 12:08:37 +00:00
github-actions[bot] eebcc7b5bb Merge pull request #58314 from nextcloud/dependabot/npm_and_yarn/build/frontend-legacy/nextcloud/upload-1.11.1
chore(deps): Bump @nextcloud/upload from 1.11.0 to 1.11.1 in /build/frontend-legacy
2026-02-15 11:13:37 +00:00
nextcloud-command 88b6c54d6b chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2026-02-15 08:27:14 +00:00
dependabot[bot] ee65e8e79c chore(deps): Bump @nextcloud/upload in /build/frontend-legacy
Bumps [@nextcloud/upload](https://github.com/nextcloud-libraries/nextcloud-upload) from 1.11.0 to 1.11.1.
- [Release notes](https://github.com/nextcloud-libraries/nextcloud-upload/releases)
- [Changelog](https://github.com/nextcloud-libraries/nextcloud-upload/blob/main/CHANGELOG.md)
- [Commits](https://github.com/nextcloud-libraries/nextcloud-upload/compare/v1.11.0...v1.11.1)

---
updated-dependencies:
- dependency-name: "@nextcloud/upload"
  dependency-version: 1.11.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-15 08:19:12 +00:00
github-actions[bot] 15f1ec22f4 Merge pull request #58312 from nextcloud/dependabot/npm_and_yarn/build/frontend-legacy/webpack-5.105.2
chore(deps-dev): Bump webpack from 5.105.0 to 5.105.2 in /build/frontend-legacy
2026-02-15 09:17:15 +01:00
github-actions[bot] a04d6d6de2 Merge pull request #58310 from nextcloud/dependabot/npm_and_yarn/sortablejs-1.15.7
chore(deps): Bump sortablejs from 1.15.6 to 1.15.7
2026-02-15 09:02:37 +01:00
Nextcloud bot ba2ddc1392 fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2026-02-15 00:20:42 +00:00
nextcloud-command 72d825a7dc chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2026-02-14 23:16:00 +00:00
nextcloud-command 096bff5fed chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2026-02-14 23:15:51 +00:00
dependabot[bot] e0e37fcac4 chore(deps): Bump sortablejs from 1.15.6 to 1.15.7
Bumps [sortablejs](https://github.com/SortableJS/Sortable) from 1.15.6 to 1.15.7.
- [Release notes](https://github.com/SortableJS/Sortable/releases)
- [Commits](https://github.com/SortableJS/Sortable/compare/1.15.6...1.15.7)

---
updated-dependencies:
- dependency-name: sortablejs
  dependency-version: 1.15.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-14 23:07:24 +00:00
dependabot[bot] eec2e93d3a chore(deps-dev): Bump webpack in /build/frontend-legacy
Bumps [webpack](https://github.com/webpack/webpack) from 5.105.0 to 5.105.2.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Changelog](https://github.com/webpack/webpack/blob/main/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack/compare/v5.105.0...v5.105.2)

---
updated-dependencies:
- dependency-name: webpack
  dependency-version: 5.105.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-14 23:07:05 +00:00
github-actions[bot] f6dc6ec3e3 Merge pull request #58346 from nextcloud/dependabot/npm_and_yarn/build/frontend-legacy/marked-17.0.2
chore(deps): Bump marked from 17.0.1 to 17.0.2 in /build/frontend-legacy
2026-02-14 22:53:59 +00:00
Ferdinand Thiessen 66c1e0ab61 Merge pull request #58330 from nextcloud/fix/view-order
fix(files): correctly sort views
2026-02-14 22:59:27 +01:00
nextcloud-command 93d6087eb6 chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2026-02-14 21:37:23 +00:00
Ferdinand Thiessen 2007491e11 fix(files): correctly sort views
The condition to sort by order was missing, so the views were only
sorted by name. Added the most important order condition and proper
tests for this.

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2026-02-14 22:29:46 +01:00
nextcloud-command 0263a7f093 chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2026-02-14 20:53:35 +01:00
dependabot[bot] d16a7b3653 chore(deps): Bump marked from 17.0.1 to 17.0.2 in /build/frontend-legacy
Bumps [marked](https://github.com/markedjs/marked) from 17.0.1 to 17.0.2.
- [Release notes](https://github.com/markedjs/marked/releases)
- [Commits](https://github.com/markedjs/marked/compare/v17.0.1...v17.0.2)

---
updated-dependencies:
- dependency-name: marked
  dependency-version: 17.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-14 20:53:35 +01:00
github-actions[bot] a69d56d1b7 Merge pull request #58348 from nextcloud/dependabot/npm_and_yarn/build/frontend-legacy/multi-d31b51a7f2
chore(deps): Bump qs and body-parser in /build/frontend-legacy
2026-02-14 20:38:35 +01:00
nextcloud-command eb4194a973 chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2026-02-14 16:46:10 +00:00
Git'Fellow b1e71ae646 fix(sftp): Handle empty port parameter to allow host-defined ports
Signed-off-by: Git'Fellow <12234510+solracsf@users.noreply.github.com>
2026-02-14 12:29:38 +01:00
dependabot[bot] 161b1098c0 chore(deps): Bump qs and body-parser in /build/frontend-legacy
Bumps [qs](https://github.com/ljharb/qs) and [body-parser](https://github.com/expressjs/body-parser). These dependencies needed to be updated together.

Updates `qs` from 6.14.1 to 6.14.2
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/qs/compare/v6.14.1...v6.14.2)

Updates `body-parser` from 1.20.3 to 1.20.4
- [Release notes](https://github.com/expressjs/body-parser/releases)
- [Changelog](https://github.com/expressjs/body-parser/blob/master/HISTORY.md)
- [Commits](https://github.com/expressjs/body-parser/compare/1.20.3...1.20.4)

---
updated-dependencies:
- dependency-name: qs
  dependency-version: 6.14.2
  dependency-type: indirect
- dependency-name: body-parser
  dependency-version: 1.20.4
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-14 07:08:25 +00:00
github-actions[bot] cf1c70ca62 Merge pull request #58345 from nextcloud/dependabot/npm_and_yarn/stylelint-17.3.0
chore(deps-dev): Bump stylelint from 17.1.1 to 17.3.0
2026-02-14 07:07:17 +00:00
dependabot[bot] e9ec5026a6 chore(deps-dev): Bump stylelint from 17.1.1 to 17.3.0
Bumps [stylelint](https://github.com/stylelint/stylelint) from 17.1.1 to 17.3.0.
- [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/17.1.1...17.3.0)

---
updated-dependencies:
- dependency-name: stylelint
  dependency-version: 17.3.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-14 02:05:13 +00:00
Nextcloud bot 1439a2caab fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2026-02-14 00:21:13 +00:00
github-actions[bot] f9f35151c0 Merge pull request #58301 from nextcloud/dependabot/npm_and_yarn/zip.js/zip.js-2.8.20
chore(deps-dev): Bump @zip.js/zip.js from 2.8.17 to 2.8.20
2026-02-13 18:34:50 +00:00
github-actions[bot] 7ed3134160 Merge pull request #58304 from nextcloud/dependabot/npm_and_yarn/msw-2.12.10
chore(deps-dev): Bump msw from 2.12.9 to 2.12.10
2026-02-13 18:19:11 +00:00
Robin Appelman eb6f78e26d feat: implement authoritative mount provider for share provider
Signed-off-by: Robin Appelman <robin@icewind.nl>
2026-02-13 19:02:05 +01:00
Cristian Scheid e964351389 feat: add upload_time support to file search and recent files
Signed-off-by: Cristian Scheid <cristianscheid@gmail.com>
2026-02-13 14:38:51 -03:00
Sebastian Krupinski c56938f6a9 Merge pull request #58203 from nextcloud/fix/obey-x-nc-scheduling-flag-on-delete
fix: obey x-nc-scheduling flag on delete
2026-02-13 12:07:25 -05:00
github-actions[bot] 8443c71d3e Merge pull request #58302 from nextcloud/dependabot/npm_and_yarn/eslint-plugin-cypress-5.3.0
chore(deps-dev): Bump eslint-plugin-cypress from 5.2.1 to 5.3.0
2026-02-13 16:25:09 +00:00
SebastianKrupinski b91dfee54a fix: obey x-nc-scheduling flag on delete
Signed-off-by: SebastianKrupinski <krupinskis05@gmail.com>
2026-02-13 09:58:26 -05:00
Sebastian Krupinski 0cd375532d Merge pull request #50843 from nextcloud/fix/noid/fix-itipbroker-messages
fix(CalDAV): iTipBroker message generation and testing
2026-02-13 09:51:52 -05:00
Carl Schwan 2391d58eb0 Merge pull request #58107 from nextcloud/local-preview-mkdir-exists
fix: don't try to create preview folder if it already exists
2026-02-13 15:10:24 +01:00
github-actions[bot] 44e7082e5a Merge pull request #58318 from nextcloud/dependabot/npm_and_yarn/qs-6.14.2
chore(deps): Bump qs from 6.14.1 to 6.14.2
2026-02-13 12:39:13 +01:00
Ferdinand Thiessen fee431f9c0 Merge pull request #58003 from nextcloud/fix/57669/show-theming-image-upload-errors
fix(theming): display error messages on image upload and delete previous image only after validation
2026-02-13 11:49:38 +01:00
nextcloud-command 0f91aec1bd chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2026-02-13 10:03:50 +00:00
Nextcloud bot bf83faf7cd fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2026-02-13 00:41:19 +00:00
github-actions[bot] a793dac97c Merge pull request #58317 from nextcloud/dependabot/github_actions/github-actions-ee4cefd656
chore(deps): Bump the github-actions group with 3 updates
2026-02-13 00:12:39 +00:00
github-actions[bot] d6842d720e Merge pull request #58316 from nextcloud/dependabot/npm_and_yarn/build/frontend-legacy/nextcloud/dialogs-7.3.0
chore(deps): Bump @nextcloud/dialogs from 7.2.0 to 7.3.0 in /build/frontend-legacy
2026-02-12 23:11:48 +00:00
nextcloud-command 742f8717e3 chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2026-02-12 22:55:26 +00:00
dependabot[bot] acb9698012 chore(deps): Bump @nextcloud/dialogs in /build/frontend-legacy
Bumps [@nextcloud/dialogs](https://github.com/nextcloud-libraries/nextcloud-dialogs) from 7.2.0 to 7.3.0.
- [Release notes](https://github.com/nextcloud-libraries/nextcloud-dialogs/releases)
- [Changelog](https://github.com/nextcloud-libraries/nextcloud-dialogs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/nextcloud-libraries/nextcloud-dialogs/compare/v7.2.0...v7.3.0)

---
updated-dependencies:
- dependency-name: "@nextcloud/dialogs"
  dependency-version: 7.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-12 22:43:30 +00:00
dependabot[bot] ac4c74156a chore(deps): Bump qs from 6.14.1 to 6.14.2
Bumps [qs](https://github.com/ljharb/qs) from 6.14.1 to 6.14.2.
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/qs/compare/v6.14.1...v6.14.2)

---
updated-dependencies:
- dependency-name: qs
  dependency-version: 6.14.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-12 22:42:25 +00:00
github-actions[bot] e00c98cf67 Merge pull request #58315 from nextcloud/dependabot/npm_and_yarn/build/frontend-legacy/msw-2.12.10
chore(deps-dev): Bump msw from 2.12.9 to 2.12.10 in /build/frontend-legacy
2026-02-12 23:41:12 +01:00
SebastianKrupinski 70d051f602 fix: iTipBroker message generation and testing
Signed-off-by: SebastianKrupinski <krupinskis05@gmail.com>
2026-02-12 15:20:36 -05:00
dependabot[bot] bd4899fc42 chore(deps): Bump the github-actions group with 3 updates
Bumps the github-actions group with 3 updates: [github/codeql-action](https://github.com/github/codeql-action), [cypress-io/github-action](https://github.com/cypress-io/github-action) and [LizardByte/actions](https://github.com/lizardbyte/actions).


Updates `github/codeql-action` from 4.32.0 to 4.32.2
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/b20883b0cd1f46c72ae0ba6d1090936928f9fa30...45cbd0c69e560cd9e7cd7f8c32362050c9b7ded2)

Updates `cypress-io/github-action` from 7.1.1 to 7.1.2
- [Release notes](https://github.com/cypress-io/github-action/releases)
- [Changelog](https://github.com/cypress-io/github-action/blob/master/CHANGELOG.md)
- [Commits](https://github.com/cypress-io/github-action/compare/0f330ebf0d60f87608ed72f1d6232e5644aa3171...84d178e4bbce871e23f2ffa3085898cde0e4f0ec)

Updates `LizardByte/actions` from 2026.129.194351 to 2026.212.22356
- [Release notes](https://github.com/lizardbyte/actions/releases)
- [Commits](https://github.com/lizardbyte/actions/compare/09a6e10dc8175f2933c20bdf35fde0a193a9c00e...9bf3ef783775e17fe6b8dde3585d94ec570b93c2)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.32.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: cypress-io/github-action
  dependency-version: 7.1.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: LizardByte/actions
  dependency-version: 2026.212.22356
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-12 18:52:19 +00:00
dependabot[bot] af1d11434b chore(deps-dev): Bump msw in /build/frontend-legacy
Bumps [msw](https://github.com/mswjs/msw) from 2.12.9 to 2.12.10.
- [Release notes](https://github.com/mswjs/msw/releases)
- [Changelog](https://github.com/mswjs/msw/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mswjs/msw/compare/v2.12.9...v2.12.10)

---
updated-dependencies:
- dependency-name: msw
  dependency-version: 2.12.10
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-12 18:39:16 +00:00
dependabot[bot] 0578b0dd9c chore(deps-dev): Bump msw from 2.12.9 to 2.12.10
Bumps [msw](https://github.com/mswjs/msw) from 2.12.9 to 2.12.10.
- [Release notes](https://github.com/mswjs/msw/releases)
- [Changelog](https://github.com/mswjs/msw/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mswjs/msw/compare/v2.12.9...v2.12.10)

---
updated-dependencies:
- dependency-name: msw
  dependency-version: 2.12.10
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-12 18:36:34 +00:00
dependabot[bot] 178517847c chore(deps-dev): Bump eslint-plugin-cypress from 5.2.1 to 5.3.0
Bumps [eslint-plugin-cypress](https://github.com/cypress-io/eslint-plugin-cypress) from 5.2.1 to 5.3.0.
- [Release notes](https://github.com/cypress-io/eslint-plugin-cypress/releases)
- [Commits](https://github.com/cypress-io/eslint-plugin-cypress/compare/v5.2.1...v5.3.0)

---
updated-dependencies:
- dependency-name: eslint-plugin-cypress
  dependency-version: 5.3.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-12 18:36:05 +00:00
dependabot[bot] 50c163f9a9 chore(deps-dev): Bump @zip.js/zip.js from 2.8.17 to 2.8.20
Bumps [@zip.js/zip.js](https://github.com/gildas-lormeau/zip.js) from 2.8.17 to 2.8.20.
- [Release notes](https://github.com/gildas-lormeau/zip.js/releases)
- [Commits](https://github.com/gildas-lormeau/zip.js/compare/v2.8.17...v2.8.20)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-12 18:35:36 +00:00
Andy Scherzinger 4cc2bb26ca Merge pull request #58292 from nextcloud/ci/noid/dependabot-v31
Drop dependabot for stable31
2026-02-12 19:32:27 +01:00
Ferdinand Thiessen 202124cd28 Merge pull request #58287 from nextcloud/fix/comments-action
fix(comments): properly handle opening the sidebar when Activity integration is used
2026-02-12 19:10:58 +01:00
Andy Scherzinger 6fb0072a00 ci(deps): Drop dependabot for stable31
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
2026-02-12 18:53:50 +01:00
Joas Schilling 8c5fb9c751 Merge pull request #58289 from nextcloud/bugfix/noid/fix-dead-docs-link
fix(docs): Fix dead documentation link for Windows file names
2026-02-12 18:32:36 +01:00
nextcloud-command eb07c52f95 chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2026-02-12 17:23:17 +00:00
Joas Schilling bb1b12b218 fix(docs): Fix dead documentation link for Windows file names
Signed-off-by: Joas Schilling <coding@schilljs.com>
2026-02-12 17:47:37 +01:00
Ferdinand Thiessen f2574fdf34 fix(files): fallback to action id if displayname is empty
This fixes invalid error messages if the action has an empty
displayname, often the case for inline actions

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2026-02-12 17:20:30 +01:00
Ferdinand Thiessen cd15bb0679 fix(comments): properly handle opening the sidebar when Activity integration is used
When the activity integration is used we need to open the `activity` tab
not the comments tab.

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2026-02-12 17:19:04 +01:00
Cristian Scheid 8a703c95ec Merge branch 'master' into fix/57669/show-theming-image-upload-errors
Signed-off-by: Cristian Scheid <74515775+cristianscheid@users.noreply.github.com>
2026-02-12 11:16:47 -03:00
Kate b80816dfb7 Merge pull request #58098 from nextcloud/fix/appframework/types-phpstan
fix(AppFramework): Adjust types so PHPStan understands them
2026-02-12 14:00:47 +01:00
Louis 286d0d23a3 Merge pull request #58283 from nextcloud/chore/files-v4
chore(deps): update `@nextcloud/files` to v4.0.0
2026-02-12 13:56:47 +01:00
nextcloud-command c524562c88 chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2026-02-12 12:15:28 +00:00
nextcloud-command 7ab1c0f212 chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
Signed-off-by: Cristian Scheid <cristianscheid@gmail.com>
2026-02-12 08:08:44 -03:00
Cristian Scheid 347c509308 refactor(theming): use isAxiosError for error handling
Signed-off-by: Cristian Scheid <cristianscheid@gmail.com>
2026-02-12 08:03:03 -03:00
Cristian Scheid 0a5f75e719 fix(theming): display error messages on image upload and delete previous image only after validation
Signed-off-by: Cristian Scheid <cristianscheid@gmail.com>
2026-02-12 08:03:03 -03:00
Ferdinand Thiessen bb1d814e04 fix(files_sharing): adjust for @nextcloud/files v4
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2026-02-12 11:50:07 +01:00
Ferdinand Thiessen 7ef66a6199 fix(files_external): use root id property instead of internal attributes
Root attributes must not be accessed by the internal `attributes`.
Trying to do so will return `undefined`, so instead use the root `id`
property of nodes.

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2026-02-12 11:49:41 +01:00
Ferdinand Thiessen 85c0dcf136 chore(deps): update @nextcloud/files to v4.0.0
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2026-02-12 11:49:21 +01:00
Carl Schwan f6c839d125 refactor(psalm): Modernize migration doc
And remove all the duplication in the subclasses

Signed-off-by: Carl Schwan <carlschwan@kde.org>
2026-02-12 11:25:55 +01:00
Kate 4d6959da27 Merge pull request #58261 from nextcloud/fix/simplecontainer/get-return-type 2026-02-12 10:56:53 +01:00
Andy Scherzinger 3d4774edb6 Merge pull request #58273 from nextcloud/fix/folder-tree-2
perf(files): initialize folder tree from current state and store
2026-02-12 07:35:50 +01:00
nextcloud-command e38f96af72 chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2026-02-12 03:31:28 +01:00
Carl Schwan 0e66ed2233 perf(files): Only request directories from the database
Signed-off-by: Carl Schwan <carlschwan@kde.org>
2026-02-12 03:31:27 +01:00
Ferdinand Thiessen 95d7b5608b perf(files): initialize folder tree from current path and store
Initialize the folder tree based on the current directory.
Also only include views needed.

If possible reuse nodes from files store to prevent API call.

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2026-02-12 03:31:27 +01:00
Ferdinand Thiessen e5c1d80a00 perf(files): only emit initialization once
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2026-02-12 03:31:27 +01:00
Ferdinand Thiessen 69f2c17675 refactor(files): only load navigation views needed
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2026-02-12 03:31:27 +01:00
Ferdinand Thiessen 2f29ad8ff8 feat(files): allow to include parents in folder tree API
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2026-02-12 01:28:46 +01:00
Carl Schwan d35773b99a Merge pull request #57891 from nextcloud/carl/getDirectoryContent-mimetypefilter
perf: Allow filtering the directory content by mimetype
2026-02-12 01:22:10 +01:00
Nextcloud bot 55fb2f395e fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2026-02-12 00:20:46 +00:00
Carl Schwan 3da621d7fb Merge pull request #58230 from nextcloud/carl/fix-appdata-scanning
fix(preview): Fix files:app-data-scan for previews
2026-02-12 00:57:28 +01:00
Carl Schwan edd37d349b perf(file-cache): Add mimetype filter on getFolderContents
Signed-off-by: Carl Schwan <carlschwan@kde.org>
2026-02-12 00:36:18 +01:00
Carl Schwan 9741f5f17d perf: Allow filtering the directory content by mimetype
Signed-off-by: Carl Schwan <carlschwan@kde.org>
2026-02-12 00:35:48 +01:00
Carl Schwan 89a20598e5 fix: Skip test requiring local storage on object store setup
Signed-off-by: Carl Schwan <carlschwan@kde.org>
2026-02-12 00:26:22 +01:00
Carl Schwan 3d18cd7cc5 fix(preview): Fix files:app-data-scan for previews
And add unit tests for both migrated files and non-migrated files

Signed-off-by: Carl Schwan <carlschwan@kde.org>
2026-02-12 00:26:20 +01:00
github-actions[bot] fdf200e343 Merge pull request #58272 from nextcloud/dependabot/npm_and_yarn/axios-1.13.5
chore(deps): Bump axios from 1.13.2 to 1.13.5
2026-02-11 22:50:07 +01:00
nextcloud-command 5e6c8b6134 chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2026-02-11 21:03:02 +00:00
dependabot[bot] ce99b0f7ed chore(deps): Bump axios from 1.13.2 to 1.13.5
Bumps [axios](https://github.com/axios/axios) from 1.13.2 to 1.13.5.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.13.2...v1.13.5)

---
updated-dependencies:
- dependency-name: axios
  dependency-version: 1.13.5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-11 20:35:30 +00:00
Andy Scherzinger 47b314067c Merge pull request #58216 from nextcloud/carl/preview-unique-constraints
fix(preview): Handle unique constraints
2026-02-11 20:20:31 +01:00
Carl Schwan 4722f56778 fix(preview): Handle unique constraints
Signed-off-by: Carl Schwan <carlschwan@kde.org>
2026-02-11 19:56:11 +01:00
Kate 0fc06b3b87 Merge pull request #58237 from nextcloud/share-target-repair-circles 2026-02-11 17:58:51 +01:00
Robin Appelman f216bf5798 fix: handle circle shares in share target repair
Signed-off-by: Robin Appelman <robin@icewind.nl>
2026-02-11 15:39:11 +01:00
Louis d21351701a Merge pull request #58140 from nextcloud/artonge/fix/ignore_abort_error
fix(files): Do not show abort error to the user
2026-02-11 15:03:02 +01:00
nextcloud-command 6c04307e13 chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2026-02-11 13:42:45 +00:00
Louis Chmn a4f396e648 fix(files): Do not show abort error to the user
Signed-off-by: Louis Chmn <louis@chmn.me>
2026-02-11 13:40:33 +00:00
provokateurin 5c47ce9b40 fix(SimpleContainer): Adjust return type for PHPStan
Signed-off-by: provokateurin <kate@provokateurin.de>
2026-02-11 11:42:37 +01:00
provokateurin e8d4d435ed fix(IRegistrationContext): Use SimpleContainer in registerService factory
Signed-off-by: provokateurin <kate@provokateurin.de>
2026-02-11 11:42:37 +01:00
Kate 615d343d96 Merge pull request #58256 from nextcloud/fix/iresult/fetch-all-conditional-return-type 2026-02-11 09:26:55 +01:00
provokateurin 83fbc64c99 fix(IResult): Use more accurate conditional return type for fetchAll
Signed-off-by: provokateurin <kate@provokateurin.de>
2026-02-11 08:50:13 +01:00
Nextcloud bot 4a9e04962c fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2026-02-11 00:20:55 +00:00
Anna feaebeb97e Merge pull request #58223 from nextcloud/fix/oracle-truncate-table
fix: quote tablenames for truncating in oracle
2026-02-10 23:29:34 +01:00
grnd-alt bc5771b0ff fix: correctly quote tablenames for truncating with oracle
Signed-off-by: grnd-alt <git@belakkaf.net>
2026-02-10 22:48:02 +01:00
Sebastian Krupinski 63eb9679c2 Merge pull request #58228 from nextcloud/fix/group-shares-cleanup
fix: delete CalDav and CardDav shares upon group deletion
2026-02-10 11:49:15 -05:00
Kate 98cb8b6155 Merge pull request #55632 from nextcloud/feat/preview/expire-previews
feat(preview): Expire previews
2026-02-10 17:35:28 +01:00
Maximilian Martin 6e5baa6928 fix: delete CalDav and CardDav shares upon group deletion
Signed-off-by: Maximilian Martin <maximilian_martin@gmx.de>
2026-02-10 11:14:40 -05:00
Maksim Sukharev a096c89c66 Merge pull request #58218 from nextcloud/fix/57804/clear-status-this-week 2026-02-10 16:56:23 +01:00
nextcloud-command 6563214204 chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2026-02-10 15:36:54 +00:00
Cristian Scheid 2ddf73f89f fix(user_status): use getFirstDay() from @nextcloud/l10n
Signed-off-by: Cristian Scheid <cristianscheid@gmail.com>
2026-02-10 16:27:51 +01:00
provokateurin fe9e43c165 feat(preview): Expire previews
Signed-off-by: provokateurin <kate@provokateurin.de>
2026-02-10 15:35:38 +01:00
Ferdinand Thiessen 1a5679b176 Merge pull request #58208 from nextcloud/chore/update-files-4rc3
chore: update `@nextcloud/files` to v4.0.0-rc.3
2026-02-10 15:22:34 +01:00
Ferdinand Thiessen 7b7d74fda2 chore: compile assets
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2026-02-10 14:53:19 +01:00
Ferdinand Thiessen f075051f4a test: remove testing internal of libraries and test only app code
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2026-02-10 14:50:41 +01:00
Ferdinand Thiessen 0e361550f1 fix(files): add legacy wrapper for upload library
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2026-02-10 14:50:41 +01:00
Ferdinand Thiessen 38644873f2 refactor(files): migrate to files registry for reactive file actions
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2026-02-10 14:50:41 +01:00
Ferdinand Thiessen 643a815557 refactor(files): migrate file list actions to new files registry
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2026-02-10 14:50:41 +01:00
Ferdinand Thiessen c73b85aecb refactor(files): port file list headers to new files registry
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2026-02-10 14:50:41 +01:00
Ferdinand Thiessen 4a284f61e6 refactor(files): port filters store to new files registry
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2026-02-10 14:50:41 +01:00
Ferdinand Thiessen e088473929 refactor(files_sharing): adjust note to recipient files header
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2026-02-10 14:50:41 +01:00
Ferdinand Thiessen 29b47c93ab chore(deps): update @nextcloud/files to v4.0.0-rc.3
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2026-02-10 14:50:40 +01:00
github-actions[bot] 935cd2910f Merge pull request #58213 from nextcloud/dependabot/npm_and_yarn/build/frontend-legacy/axios-1.13.5
chore(deps): Bump axios from 1.12.2 to 1.13.5 in /build/frontend-legacy
2026-02-10 13:21:20 +00:00
nextcloud-command 422bca31bf chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2026-02-10 12:39:26 +00:00
dependabot[bot] adce834b4f chore(deps): Bump axios from 1.12.2 to 1.13.5 in /build/frontend-legacy
Bumps [axios](https://github.com/axios/axios) from 1.12.2 to 1.13.5.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.12.2...v1.13.5)

---
updated-dependencies:
- dependency-name: axios
  dependency-version: 1.13.5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-10 13:23:36 +01:00
Andy Scherzinger 7da7f50203 Merge pull request #58209 from nextcloud/carl/local-preview-fix
fix(preview): Fix scanning preview
2026-02-10 12:38:58 +01:00
Anna 8c01737a63 Merge pull request #58206 from nextcloud/fix/preview-637/cast-snowflake-id-to-int
fix(snowflake): cast lastId to string
2026-02-10 11:46:07 +01:00
Kate e0c282d531 Merge pull request #58195 from nextcloud/ci/rector-apply
ci: Add workflow to apply rector changes weekly
2026-02-10 10:36:25 +01:00
Nextcloud bot d65aa0b7c3 fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2026-02-10 00:21:48 +00:00
Carl Schwan 543b46f3aa fix(preview): Fix scanning preview
Make sure we set the mimetype string representation and not int
representation.

Signed-off-by: Carl Schwan <carlschwan@kde.org>
2026-02-09 22:49:59 +01:00
Anna Larch 99a1150ec2 fix(snowflake): cast lastId to int
Signed-off-by: Anna Larch <anna@nextcloud.com>
2026-02-09 21:36:20 +01:00
Robin Appelman a3cc9754f8 feat: improve VerifyMountPointEvent event
Signed-off-by: Robin Appelman <robin@icewind.nl>
2026-02-09 18:42:30 +01:00
Robin Appelman 9cd337bebe Merge pull request #57745 from nextcloud/share-transfered-event
Share mount validation fixes
2026-02-09 16:46:15 +01:00
Robin Appelman 83810aec99 Merge pull request #57926 from nextcloud/share-target-repair-no-providers
fix: don't rely on share providers being avaiable in CleanupShareTarget
2026-02-09 16:44:01 +01:00
Sebastian Krupinski c373b8e614 Merge pull request #58197 from nextcloud/fix/calendar-federation-additional-properties
fix: federated calendar additional properties
2026-02-09 10:19:57 -05:00
Andy Scherzinger 39ff32a33b Merge pull request #58134 from nextcloud/carl/lock-propagator-order
fix(propagator): Improve lock behavior of propagator
2026-02-09 16:04:38 +01:00
SebastianKrupinski 566e598a4e fix: federated calendar additional properties
Signed-off-by: SebastianKrupinski <krupinskis05@gmail.com>
2026-02-09 09:26:33 -05:00
provokateurin 0469f57a3a ci: Add workflow to apply rector changes weekly
Signed-off-by: provokateurin <kate@provokateurin.de>
2026-02-09 14:55:54 +01:00
provokateurin 97c09753c3 fix(AppFramework): Adjust types so PHPStan understands them
Signed-off-by: provokateurin <kate@provokateurin.de>
2026-02-09 14:08:42 +01:00
Kate 431a37e71e Merge pull request #58194 from nextcloud/feat/rector/safe-declare-strict-types
feat(rector): Enable SafeDeclareStrictTypesRector
2026-02-09 12:38:21 +01:00
Carl Schwan 95b3bd98d9 fix(propagator): Lock rows also in propagateChange
Signed-off-by: Carl Schwan <carlschwan@kde.org>
2026-02-09 12:11:21 +01:00
Carl Schwan f3778bc9c7 fix(propagator): Improve lock behavior of propagator
Fix possible dead locks when running the propagator caused by two
requests updating the same amount rows in transactions.

- Lock rows always in the same deterministic order by sorting the
  path_hash first

- On all database outside of sqlite, also do first a SELECT FOR UPDATE
  to lock all the rows used in batch UPDATE calls, afterward to decrease
  the risk of two requests trying to lock the same rows

Signed-off-by: Carl Schwan <carlschwan@kde.org>
2026-02-09 12:10:56 +01:00
provokateurin f12cecb684 feat(rector): Enable SafeDeclareStrictTypesRector
Signed-off-by: provokateurin <kate@provokateurin.de>
2026-02-09 10:59:31 +01:00
Nextcloud bot 53a67966c0 fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2026-02-09 00:21:30 +00:00
Robin Appelman 7efdfd161b Merge pull request #58121 from nextcloud/move-share-out-share-reshare
fix: don't trigger moveShareInOrOutOfShare for reshares
2026-02-08 19:37:59 +01:00
Nextcloud bot eb08dddcf5 fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2026-02-08 00:20:46 +00:00
github-actions[bot] 823d0dcb7c Merge pull request #58161 from nextcloud/dependabot/npm_and_yarn/build/frontend-legacy/nextcloud/vue-8.36.0
chore(deps): Bump @nextcloud/vue from 8.35.3 to 8.36.0 in /build/frontend-legacy
2026-02-07 18:14:53 +00:00
nextcloud-command 36f4423887 chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2026-02-07 15:27:03 +00:00
dependabot[bot] f70f7be416 chore(deps): Bump @nextcloud/vue in /build/frontend-legacy
Bumps [@nextcloud/vue](https://github.com/nextcloud-libraries/nextcloud-vue) from 8.35.3 to 8.36.0.
- [Release notes](https://github.com/nextcloud-libraries/nextcloud-vue/releases)
- [Changelog](https://github.com/nextcloud-libraries/nextcloud-vue/blob/v8.36.0/CHANGELOG.md)
- [Commits](https://github.com/nextcloud-libraries/nextcloud-vue/compare/v8.35.3...v8.36.0)

---
updated-dependencies:
- dependency-name: "@nextcloud/vue"
  dependency-version: 8.36.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-07 16:15:53 +01:00
github-actions[bot] e586ca0071 Merge pull request #58163 from nextcloud/dependabot/npm_and_yarn/build/frontend-legacy/libphonenumber-js-1.12.36
chore(deps): Bump libphonenumber-js from 1.12.35 to 1.12.36 in /build/frontend-legacy
2026-02-07 15:14:07 +00:00
nextcloud-command b4c7dcbf58 chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2026-02-07 14:58:52 +00:00
dependabot[bot] 35e315bd91 chore(deps): Bump libphonenumber-js in /build/frontend-legacy
Bumps [libphonenumber-js](https://gitlab.com/catamphetamine/libphonenumber-js) from 1.12.35 to 1.12.36.
- [Changelog](https://gitlab.com/catamphetamine/libphonenumber-js/blob/master/CHANGELOG.md)
- [Commits](https://gitlab.com/catamphetamine/libphonenumber-js/compare/v1.12.35...v1.12.36)

---
updated-dependencies:
- dependency-name: libphonenumber-js
  dependency-version: 1.12.36
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-07 14:56:40 +00:00
github-actions[bot] 21fff5160a Merge pull request #58158 from nextcloud/dependabot/npm_and_yarn/build/frontend-legacy/webdav-5.9.0
chore(deps): Bump webdav from 5.8.0 to 5.9.0 in /build/frontend-legacy
2026-02-07 14:43:40 +00:00
nextcloud-command 5fd4b9fe52 chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2026-02-07 14:26:57 +00:00
dependabot[bot] 6a2e9814af chore(deps): Bump webdav from 5.8.0 to 5.9.0 in /build/frontend-legacy
Bumps [webdav](https://github.com/perry-mitchell/webdav-client) from 5.8.0 to 5.9.0.
- [Changelog](https://github.com/perry-mitchell/webdav-client/blob/master/CHANGELOG.md)
- [Commits](https://github.com/perry-mitchell/webdav-client/compare/v5.8.0...v5.9.0)

---
updated-dependencies:
- dependency-name: webdav
  dependency-version: 5.9.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-07 15:19:33 +01:00
github-actions[bot] 79600b0c9f Merge pull request #58151 from nextcloud/dependabot/npm_and_yarn/jsdom-28.0.0
chore(deps-dev): Bump jsdom from 27.4.0 to 28.0.0
2026-02-07 14:17:41 +00:00
dependabot[bot] 38f1063be7 chore(deps-dev): Bump jsdom from 27.4.0 to 28.0.0
Bumps [jsdom](https://github.com/jsdom/jsdom) from 27.4.0 to 28.0.0.
- [Release notes](https://github.com/jsdom/jsdom/releases)
- [Changelog](https://github.com/jsdom/jsdom/blob/main/Changelog.md)
- [Commits](https://github.com/jsdom/jsdom/compare/27.4.0...28.0.0)

---
updated-dependencies:
- dependency-name: jsdom
  dependency-version: 28.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-07 14:02:13 +00:00
github-actions[bot] 878cb525a3 Merge pull request #58147 from nextcloud/dependabot/composer/vendor-bin/rector/rector/rector-2.3.6
chore(deps-dev): Bump rector/rector from 2.3.4 to 2.3.6 in /vendor-bin/rector
2026-02-07 14:58:19 +01:00
github-actions[bot] 0b36e05965 Merge pull request #58157 from nextcloud/dependabot/npm_and_yarn/build/frontend-legacy/babel/node-7.29.0
chore(deps-dev): Bump @babel/node from 7.28.6 to 7.29.0 in /build/frontend-legacy
2026-02-07 13:36:27 +00:00
github-actions[bot] 1081e32e55 Merge pull request #58154 from nextcloud/dependabot/npm_and_yarn/webdav-5.9.0
chore(deps): Bump webdav from 5.8.0 to 5.9.0
2026-02-07 13:24:07 +00:00
github-actions[bot] 9770eeae66 Merge pull request #58149 from nextcloud/dependabot/npm_and_yarn/cypress-split-1.24.28
chore(deps-dev): Bump cypress-split from 1.24.25 to 1.24.28
2026-02-07 13:10:26 +00:00
nextcloud-command caf1c669f1 chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2026-02-07 13:07:05 +00:00
dependabot[bot] ec60d74b70 chore(deps): Bump webdav from 5.8.0 to 5.9.0
Bumps [webdav](https://github.com/perry-mitchell/webdav-client) from 5.8.0 to 5.9.0.
- [Changelog](https://github.com/perry-mitchell/webdav-client/blob/master/CHANGELOG.md)
- [Commits](https://github.com/perry-mitchell/webdav-client/compare/v5.8.0...v5.9.0)

---
updated-dependencies:
- dependency-name: webdav
  dependency-version: 5.9.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-07 13:58:57 +01:00
github-actions[bot] 06fed8d96b Merge pull request #58153 from nextcloud/dependabot/npm_and_yarn/cypress-15.10.0
chore(deps-dev): Bump cypress from 15.9.0 to 15.10.0
2026-02-07 12:54:42 +00:00
github-actions[bot] 481bf459b8 Merge pull request #58152 from nextcloud/dependabot/npm_and_yarn/msw-2.12.9
chore(deps-dev): Bump msw from 2.12.7 to 2.12.9
2026-02-07 12:54:21 +00:00
github-actions[bot] db9f03c1bb Merge pull request #58146 from nextcloud/dependabot/composer/bamarni/composer-bin-plugin-1.9.1
chore(deps-dev): Bump bamarni/composer-bin-plugin from 1.8.3 to 1.9.1
2026-02-07 12:52:26 +00:00
github-actions[bot] 2f57e9f520 Merge pull request #58162 from nextcloud/dependabot/npm_and_yarn/build/frontend-legacy/webpack-5.105.0
chore(deps-dev): Bump webpack from 5.104.1 to 5.105.0 in /build/frontend-legacy
2026-02-07 12:52:12 +00:00
github-actions[bot] 559c04662e Merge pull request #58159 from nextcloud/dependabot/npm_and_yarn/build/frontend-legacy/msw-2.12.9
chore(deps-dev): Bump msw from 2.12.7 to 2.12.9 in /build/frontend-legacy
2026-02-07 13:50:33 +01:00
github-actions[bot] 639f8ec974 Merge pull request #58155 from nextcloud/dependabot/npm_and_yarn/zip.js/zip.js-2.8.17
chore(deps-dev): Bump @zip.js/zip.js from 2.8.16 to 2.8.17
2026-02-07 12:47:19 +00:00
github-actions[bot] 4d6d4bd579 Merge pull request #58156 from nextcloud/dependabot/npm_and_yarn/nextcloud/stylelint-config-3.2.1
chore(deps-dev): Bump @nextcloud/stylelint-config from 3.2.0 to 3.2.1
2026-02-07 12:46:31 +00:00
github-actions[bot] 50e85a1622 Merge pull request #58150 from nextcloud/dependabot/npm_and_yarn/stylelint-17.1.1
chore(deps-dev): Bump stylelint from 17.1.0 to 17.1.1
2026-02-07 13:35:30 +01:00
nextcloud-command 85ab6a683a chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2026-02-07 12:32:02 +00:00
dependabot[bot] 4bf784459d chore(deps-dev): Bump webpack in /build/frontend-legacy
Bumps [webpack](https://github.com/webpack/webpack) from 5.104.1 to 5.105.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Changelog](https://github.com/webpack/webpack/blob/main/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack/compare/v5.104.1...v5.105.0)

---
updated-dependencies:
- dependency-name: webpack
  dependency-version: 5.105.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-07 13:24:04 +01:00
github-actions[bot] 1c299fef9c Merge pull request #58164 from nextcloud/dependabot/npm_and_yarn/build/frontend-legacy/p-limit-7.3.0
chore(deps): Bump p-limit from 7.2.0 to 7.3.0 in /build/frontend-legacy
2026-02-07 08:06:52 +00:00
nextcloud-command 6b36b15c2f chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2026-02-07 07:51:27 +00:00
dependabot[bot] fa26f4f6ab chore(deps): Bump p-limit from 7.2.0 to 7.3.0 in /build/frontend-legacy
Bumps [p-limit](https://github.com/sindresorhus/p-limit) from 7.2.0 to 7.3.0.
- [Release notes](https://github.com/sindresorhus/p-limit/releases)
- [Commits](https://github.com/sindresorhus/p-limit/compare/v7.2.0...v7.3.0)

---
updated-dependencies:
- dependency-name: p-limit
  dependency-version: 7.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-07 02:15:16 +00:00
dependabot[bot] c353e0bc3a chore(deps-dev): Bump msw in /build/frontend-legacy
Bumps [msw](https://github.com/mswjs/msw) from 2.12.7 to 2.12.9.
- [Release notes](https://github.com/mswjs/msw/releases)
- [Changelog](https://github.com/mswjs/msw/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mswjs/msw/compare/v2.12.7...v2.12.9)

---
updated-dependencies:
- dependency-name: msw
  dependency-version: 2.12.9
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-07 02:13:14 +00:00
dependabot[bot] 6a56f766cf chore(deps-dev): Bump @babel/node in /build/frontend-legacy
Bumps [@babel/node](https://github.com/babel/babel/tree/HEAD/packages/babel-node) from 7.28.6 to 7.29.0.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.29.0/packages/babel-node)

---
updated-dependencies:
- dependency-name: "@babel/node"
  dependency-version: 7.29.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-07 02:12:35 +00:00
dependabot[bot] 1292b552a3 chore(deps-dev): Bump @nextcloud/stylelint-config from 3.2.0 to 3.2.1
Bumps [@nextcloud/stylelint-config](https://github.com/nextcloud-libraries/stylelint-config) from 3.2.0 to 3.2.1.
- [Release notes](https://github.com/nextcloud-libraries/stylelint-config/releases)
- [Changelog](https://github.com/nextcloud-libraries/stylelint-config/blob/main/CHANGELOG.md)
- [Commits](https://github.com/nextcloud-libraries/stylelint-config/compare/v3.2.0...v3.2.1)

---
updated-dependencies:
- dependency-name: "@nextcloud/stylelint-config"
  dependency-version: 3.2.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-07 02:12:00 +00:00
dependabot[bot] ddfd854d76 chore(deps-dev): Bump @zip.js/zip.js from 2.8.16 to 2.8.17
Bumps [@zip.js/zip.js](https://github.com/gildas-lormeau/zip.js) from 2.8.16 to 2.8.17.
- [Release notes](https://github.com/gildas-lormeau/zip.js/releases)
- [Commits](https://github.com/gildas-lormeau/zip.js/compare/v2.8.16...v2.8.17)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-07 02:11:45 +00:00
dependabot[bot] c1f2108c4b chore(deps-dev): Bump cypress from 15.9.0 to 15.10.0
Bumps [cypress](https://github.com/cypress-io/cypress) from 15.9.0 to 15.10.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/v15.9.0...v15.10.0)

---
updated-dependencies:
- dependency-name: cypress
  dependency-version: 15.10.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-07 02:11:13 +00:00
dependabot[bot] 2909f66d47 chore(deps-dev): Bump msw from 2.12.7 to 2.12.9
Bumps [msw](https://github.com/mswjs/msw) from 2.12.7 to 2.12.9.
- [Release notes](https://github.com/mswjs/msw/releases)
- [Changelog](https://github.com/mswjs/msw/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mswjs/msw/compare/v2.12.7...v2.12.9)

---
updated-dependencies:
- dependency-name: msw
  dependency-version: 2.12.9
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-07 02:10:52 +00:00
dependabot[bot] daf1ee91c1 chore(deps-dev): Bump stylelint from 17.1.0 to 17.1.1
Bumps [stylelint](https://github.com/stylelint/stylelint) from 17.1.0 to 17.1.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/17.1.0...17.1.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-07 02:10:14 +00:00
dependabot[bot] 34558b1f9a chore(deps-dev): Bump cypress-split from 1.24.25 to 1.24.28
Bumps [cypress-split](https://github.com/bahmutov/cypress-split) from 1.24.25 to 1.24.28.
- [Release notes](https://github.com/bahmutov/cypress-split/releases)
- [Commits](https://github.com/bahmutov/cypress-split/compare/v1.24.25...v1.24.28)

---
updated-dependencies:
- dependency-name: cypress-split
  dependency-version: 1.24.28
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-07 02:09:53 +00:00
dependabot[bot] 8b17197245 chore(deps-dev): Bump rector/rector in /vendor-bin/rector
Bumps [rector/rector](https://github.com/rectorphp/rector) from 2.3.4 to 2.3.6.
- [Release notes](https://github.com/rectorphp/rector/releases)
- [Commits](https://github.com/rectorphp/rector/compare/2.3.4...2.3.6)

---
updated-dependencies:
- dependency-name: rector/rector
  dependency-version: 2.3.6
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-07 02:08:22 +00:00
dependabot[bot] 4f23d5e49d chore(deps-dev): Bump bamarni/composer-bin-plugin from 1.8.3 to 1.9.1
Bumps [bamarni/composer-bin-plugin](https://github.com/bamarni/composer-bin-plugin) from 1.8.3 to 1.9.1.
- [Release notes](https://github.com/bamarni/composer-bin-plugin/releases)
- [Commits](https://github.com/bamarni/composer-bin-plugin/compare/1.8.3...1.9.1)

---
updated-dependencies:
- dependency-name: bamarni/composer-bin-plugin
  dependency-version: 1.9.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-07 02:08:05 +00:00
Nextcloud bot cc45cf6138 fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2026-02-07 00:20:59 +00:00
Ferdinand Thiessen c10f35333e Merge pull request #57797 from nextcloud/fix/proper-handling-404
fix(FileDisplayResponse): return 404 if not found
2026-02-06 20:07:23 +01:00
Andy Scherzinger b0c02b91b4 Merge pull request #58059 from nextcloud/fix/add-missing-user-id-header
fix: add X-User-Id header to logout response
2026-02-06 18:17:39 +01:00
Ferdinand Thiessen 762ae4520a fix(InMemoryFile): allow to stream read the contents
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2026-02-06 17:29:26 +01:00
Ferdinand Thiessen 6eddda147b fix(FileDisplayResponse): return 404 if not found
If the linked file is not found (anymore) return proper 404
status code.

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2026-02-06 17:29:26 +01:00
Robin Appelman 6c9418880f Merge pull request #57757 from nextcloud/getowner-no-mountpoints
fix: don't include the mountpoints when getting fileinfo for getOwner
2026-02-06 17:22:51 +01:00
Robin Appelman 9c10593021 fix: prevent recursion in SharesUpdatedListener
Signed-off-by: Robin Appelman <robin@icewind.nl>
2026-02-06 16:55:14 +01:00
Robin Appelman 3b70d0fcf8 fix: remove validate-user-shares-once optimization
Signed-off-by: Robin Appelman <robin@icewind.nl>
2026-02-06 16:55:14 +01:00
Robin Appelman c49665f5cd feat: add event for tranfered shares
Signed-off-by: Robin Appelman <robin@icewind.nl>
2026-02-06 16:55:12 +01:00
F. E Noel Nfebe 3e25c28d68 Merge pull request #58087 from nextcloud/fix/54700/file-list-scroll-jump
fix(files): Prevent file list from jumping when selecting visible files
2026-02-06 16:15:21 +01:00
Carl Schwan 1dbaf178c3 Merge pull request #56496 from nextcloud/carl/rector
Run rector on lib/private
2026-02-06 16:09:55 +01:00
Robin Appelman 3c7124d670 fix: don't trigger moveShareInOrOutOfShare for reshares
Signed-off-by: Robin Appelman <robin@icewind.nl>
2026-02-06 15:41:49 +01:00
Robin Appelman c801d63e5a fix: don't include the mountpoints when getting fileinfo for getOwner
Signed-off-by: Robin Appelman <robin@icewind.nl>
2026-02-06 15:19:30 +01:00
Robin Appelman da6bf8b116 fix: target-repair: handle cases where the parent folder doesn't exist
Signed-off-by: Robin Appelman <robin@icewind.nl>
2026-02-06 14:41:29 +01:00
Robin Appelman cfe19dbb5a fix: don't stop the entire share target repair on an error
Signed-off-by: Robin Appelman <robin@icewind.nl>
2026-02-06 14:39:06 +01:00
Robin Appelman 70607a0d11 fix: don't rely on share providers being avaiable in CleanupShareTarget
Signed-off-by: Robin Appelman <robin@icewind.nl>
2026-02-06 14:39:06 +01:00
Carl Schwan 9a26169323 fix(WatcherTest): Properly mock getCache and getScanner in IStorage
Otherwise we end up with trying to assign null to a property which has
strict types.

Signed-off-by: Carl Schwan <carlschwan@kde.org>
2026-02-06 14:21:41 +01:00
Carl Schwan e56e42e7e7 refactor(navigation-manager): Cleanup implementation and add type hinting
Signed-off-by: Carl Schwan <carl.schwan@nextcloud.com>
2026-02-06 13:55:40 +01:00
Carl Schwan c96ece0bcb refactor: Add more typing
- repairs job
- database
- redis

And remove Helpertest which was unused outside of some tests.

Signed-off-by: Carl Schwan <carl.schwan@nextcloud.com>
2026-02-06 13:55:39 +01:00
Carl Schwan fb3f9fe2de refactor: Update repairs jobs
Signed-off-by: Carl Schwan <carl.schwan@nextcloud.com>
2026-02-06 13:54:24 +01:00
Carl Schwan 3979c493f9 refactor: Apply second batch of comments
Signed-off-by: Carl Schwan <carl.schwan@nextcloud.com>
2026-02-06 13:52:51 +01:00
Carl Schwan acda4ff072 fix(ShareAPIControllerTest): Fix return types of DateTimeZone
Signed-off-by: Carl Schwan <carl.schwan@nextcloud.com>
2026-02-06 13:50:47 +01:00
Carl Schwan fa75c1b659 fix(ExpressionBuilderTest): Fix test and improve typing
Signed-off-by: Carl Schwan <carl.schwan@nextcloud.com>
2026-02-06 13:50:47 +01:00
Carl Schwan 65e769a861 refactor: Apply comments
Signed-off-by: Carl Schwan <carl.schwan@nextcloud.com>
2026-02-06 13:50:46 +01:00
Carl Schwan 6e48f9fedb fix(IAppContainer): Expose more correct type
OC\ServerContainer is not inherinting from IServerContainer so expose
the most near type.

Signed-off-by: Carl Schwan <carl.schwan@nextcloud.com>
2026-02-06 13:50:23 +01:00
Carl Schwan 7b6078875b refactor: Run rector on lib/private
Signed-off-by: Carl Schwan <carl.schwan@nextcloud.com>
2026-02-06 13:50:18 +01:00
Carl Schwan 5d0d0c17e5 chore(rector): Run rector on apps, core and tests directory
Signed-off-by: Carl Schwan <carl.schwan@nextcloud.com>
2026-02-06 13:23:23 +01:00
nextcloud-command 80a10068d0 chore(assets): Recompile assets
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
2026-02-06 12:12:48 +00:00
nfebe b0bf531c19 test(files): Update scrolling tests for new visible buffer behavior
Adjust test assertions to match the new behavior where scrolling is
skipped for files already in the visible buffer. Tests now verify file
existence rather than full visibility for files within the buffer.

Signed-off-by: nfebe <fenn25.fn@gmail.com>
2026-02-06 13:04:42 +01:00
nfebe f296e5e652 fix(files): Prevent file list from jumping when selecting visible files
Skip scrolling when a file is clicked if it's already within the
visible viewport, avoiding the confusing list jump behavior.

Fixes #54700

Signed-off-by: nfebe <fenn25.fn@gmail.com>
2026-02-06 12:47:17 +01:00
Robin Appelman 2e9f6c37d5 fix: don't try to create preview folder if it already exists
Signed-off-by: Robin Appelman <robin@icewind.nl>
2026-02-05 15:22:54 +01:00
Salvatore Martire 7d95a88958 fix: add X-User-Id header to logout response before clearing the user session
Signed-off-by: Salvatore Martire <4652631+salmart-dev@users.noreply.github.com>
2026-02-04 18:18:39 +01:00
2167 changed files with 23198 additions and 19872 deletions
-43
View File
@@ -165,46 +165,3 @@ updates:
# no major updates on stable branches
- dependency-name: "*"
update-types: ["version-update:semver-major"]
# Composer dependencies for linting and testing
- package-ecosystem: composer
target-branch: stable31
directories:
- "/"
- "/build/integration"
- "/vendor-bin/cs-fixer"
- "/vendor-bin/openapi-extractor"
- "/vendor-bin/phpunit"
- "/vendor-bin/psalm"
schedule:
interval: weekly
day: saturday
time: "04:00"
timezone: Europe/Paris
labels:
- "3. to review"
- "feature: dependencies"
ignore:
# only patch updates on stable branches
- dependency-name: "*"
update-types: ["version-update:semver-major", "version-update:semver-minor"]
# frontend dependencies
- package-ecosystem: npm
target-branch: stable31
directory: "/"
schedule:
interval: weekly
day: saturday
time: "04:00"
timezone: Europe/Paris
open-pull-requests-limit: 20
labels:
- "3. to review"
- "feature: dependencies"
# Disable automatic rebasing because without a build CI will likely fail anyway
rebase-strategy: "disabled"
ignore:
# no major updates on stable branches
- dependency-name: "*"
update-types: ["version-update:semver-major"]
+2 -2
View File
@@ -37,13 +37,13 @@ jobs:
persist-credentials: false
- name: Initialize CodeQL
uses: github/codeql-action/init@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4.32.0
uses: github/codeql-action/init@45cbd0c69e560cd9e7cd7f8c32362050c9b7ded2 # v4.32.2
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
config-file: ./.github/codeql-config.yml
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4.32.0
uses: github/codeql-action/analyze@45cbd0c69e560cd9e7cd7f8c32362050c9b7ded2 # v4.32.2
with:
category: "/language:${{matrix.language}}"
+1 -1
View File
@@ -171,7 +171,7 @@ jobs:
run: ./node_modules/cypress/bin/cypress install
- name: Run ${{ matrix.containers == 'component' && 'component' || 'E2E' }} cypress tests
uses: cypress-io/github-action@0f330ebf0d60f87608ed72f1d6232e5644aa3171 # v7.1.1
uses: cypress-io/github-action@84d178e4bbce871e23f2ffa3085898cde0e4f0ec # v7.1.2
with:
# We already installed the dependencies in the init job
install: false
+1 -1
View File
@@ -71,7 +71,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Python
uses: LizardByte/actions/actions/setup_python@09a6e10dc8175f2933c20bdf35fde0a193a9c00e # v2026.129.194351
uses: LizardByte/actions/actions/setup_python@9bf3ef783775e17fe6b8dde3585d94ec570b93c2 # v2026.212.22356
with:
python-version: '2.7'
+68
View File
@@ -0,0 +1,68 @@
# This workflow is provided via the organization template repository
#
# https://github.com/nextcloud/.github
# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization
#
# SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
# SPDX-License-Identifier: MIT
name: Apply rector changes
on:
workflow_dispatch:
schedule:
# At 14:30 on Sundays
- cron: '30 14 * * 0'
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
name: rector-apply
steps:
- name: Checkout
id: checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
ref: ${{ github.event.repository.default_branch }}
- name: Get php version
id: versions
uses: icewind1991/nextcloud-version-matrix@58becf3b4bb6dc6cef677b15e2fd8e7d48c0908f # v1.3.1
- name: Set up php${{ steps.versions.outputs.php-min }}
uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # v2.36.0
with:
php-version: ${{ steps.versions.outputs.php-min }}
extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite
coverage: none
ini-file: development
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install dependencies
run: |
composer remove nextcloud/ocp --dev --no-scripts
composer i
- name: Rector
run: composer run rector
- name: Create Pull Request
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
with:
token: ${{ secrets.COMMAND_BOT_PAT }}
commit-message: 'refactor: Apply rector changes'
committer: GitHub <noreply@github.com>
author: nextcloud-command <nextcloud-command@users.noreply.github.com>
signoff: true
branch: automated/noid/rector-changes
title: 'Apply rector changes'
labels: |
technical debt
3. to review
+1 -1
View File
@@ -88,7 +88,7 @@ jobs:
- name: Upload Security Analysis results to GitHub
if: always()
uses: github/codeql-action/upload-sarif@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v3
uses: github/codeql-action/upload-sarif@45cbd0c69e560cd9e7cd7f8c32362050c9b7ded2 # v3
with:
sarif_file: results.sarif
+2
View File
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
+2 -2
View File
@@ -1,8 +1,8 @@
OC.L10N.register(
"cloud_federation_api",
{
"Cloud Federation API" : "Asl faylni o'chirishda kutilmagan xatolik yuz berdi.",
"Cloud Federation API" : "Jamg'armaning bulutli APIsi",
"Enable clouds to communicate with each other and exchange data" : "Bulutlar bir-biri bilan aloqa qilish va ma'lumot almashish imkonini beradi",
"The Cloud Federation API enables various Nextcloud instances to communicate with each other and to exchange data." : "Cloud Federation API turli xil Nextcloud misollariga bir-biri bilan muloqot qilish va ma'lumotlarni almashish imkonini beradi."
"The Cloud Federation API enables various Nextcloud instances to communicate with each other and to exchange data." : "Cloud Jamoada API turli xil Nextcloud misollariga bir-biri bilan muloqot qilish va ma'lumotlarni almashish imkonini beradi."
},
"nplurals=1; plural=0;");
+2 -2
View File
@@ -1,6 +1,6 @@
{ "translations": {
"Cloud Federation API" : "Asl faylni o'chirishda kutilmagan xatolik yuz berdi.",
"Cloud Federation API" : "Jamg'armaning bulutli APIsi",
"Enable clouds to communicate with each other and exchange data" : "Bulutlar bir-biri bilan aloqa qilish va ma'lumot almashish imkonini beradi",
"The Cloud Federation API enables various Nextcloud instances to communicate with each other and to exchange data." : "Cloud Federation API turli xil Nextcloud misollariga bir-biri bilan muloqot qilish va ma'lumotlarni almashish imkonini beradi."
"The Cloud Federation API enables various Nextcloud instances to communicate with each other and to exchange data." : "Cloud Jamoada API turli xil Nextcloud misollariga bir-biri bilan muloqot qilish va ma'lumotlarni almashish imkonini beradi."
},"pluralForm" :"nplurals=1; plural=0;"
}
+2
View File
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+1
View File
@@ -17,6 +17,7 @@ OC.L10N.register(
"Delete comment" : "Zmazať komentár",
"Cancel edit" : "Zrušiť upravovanie",
"New comment" : "Nový komentár",
"Write a comment …" : "Napísať komentár ...",
"Post comment" : "Odoslať komentár",
"@ for mentions, : for emoji, / for smart picker" : "@ pre spomienky, : pre emotikony, / pre inteligentný výber",
"Could not reload comments" : "Nepodarilo sa obnoviť komentáre",
+1
View File
@@ -15,6 +15,7 @@
"Delete comment" : "Zmazať komentár",
"Cancel edit" : "Zrušiť upravovanie",
"New comment" : "Nový komentár",
"Write a comment …" : "Napísať komentár ...",
"Post comment" : "Odoslať komentár",
"@ for mentions, : for emoji, / for smart picker" : "@ pre spomienky, : pre emotikony, / pre inteligentný výber",
"Could not reload comments" : "Nepodarilo sa obnoviť komentáre",
+37
View File
@@ -0,0 +1,37 @@
OC.L10N.register(
"comments",
{
"Comments" : "Izohlar",
"You commented" : "Siz fikr bildirgansiz",
"{author} commented" : "{author} izoh qoldirdi",
"You commented on %1$s" : "Siz %1$s haqida fikr bildirdingiz",
"You commented on {file}" : "Siz {file} ga izoh qoldirdingiz",
"%1$s commented on %2$s" : "%1$s %2$s haqida fikr bildirdi",
"{author} commented on {file}" : "{author} {file} ga izoh qoldirdi",
"<strong>Comments</strong> for files" : "Fayllar uchun <strong>Izohlar</strong>",
"Files" : "Fayllar",
"You were mentioned on \"{file}\", in a comment by an account that has since been deleted" : "Siz \"{file}\"da, keyinchalik o'chirilgan hisob tomonidan izohda tilga olingansiz",
"{user} mentioned you in a comment on \"{file}\"" : "{user} sizni \"{file}\" dagi izohda tilga oldi",
"Files app plugin to add comments to files" : "Fayllarga izohlar qo'shish ilova plagini",
"Edit comment" : "Izohni tahrirlash",
"Delete comment" : "Izohni o'chirish",
"Cancel edit" : "Tahrirni bekor qilish",
"New comment" : "Yangi izoh",
"Write a comment …" : "Fikr yozing…",
"Post comment" : "Fikr qoldirish",
"@ for mentions, : for emoji, / for smart picker" : "@ eslatmalar uchun, : emojilar uchun, / aqlli tanlovclar uchun",
"Could not reload comments" : "Izohlarni qayta yuklab bo'lmadi",
"Failed to mark comments as read" : "Izohlarni o'qilgan deb belgilashda xatolik yuz berdi",
"Unable to load the comments list" : "Izohlar ro'yxatini yuklab bo'lmadi",
"No comments yet, start the conversation!" : "Hali izohlar yo'q, suhbatni boshlang!",
"No more messages" : "Boshqa xabarlar yo'q",
"Retry" : "Qayta urinish",
"_1 new comment_::_{unread} new comments_" : ["{unread} ta yangi izoh"],
"Comment" : "Izoh",
"An error occurred while trying to edit the comment" : "Izohni tahrirlashda xatolik yuz berdi",
"Comment deleted" : "Izoh o'chirildi",
"An error occurred while trying to delete the comment" : "Izohni o'chirishda xatolik yuz berdi",
"An error occurred while trying to create the comment" : "Izoh yaratishda xatolik yuz berdi",
"Write a comment …" : "Izoh yozing..."
},
"nplurals=1; plural=0;");
+35
View File
@@ -0,0 +1,35 @@
{ "translations": {
"Comments" : "Izohlar",
"You commented" : "Siz fikr bildirgansiz",
"{author} commented" : "{author} izoh qoldirdi",
"You commented on %1$s" : "Siz %1$s haqida fikr bildirdingiz",
"You commented on {file}" : "Siz {file} ga izoh qoldirdingiz",
"%1$s commented on %2$s" : "%1$s %2$s haqida fikr bildirdi",
"{author} commented on {file}" : "{author} {file} ga izoh qoldirdi",
"<strong>Comments</strong> for files" : "Fayllar uchun <strong>Izohlar</strong>",
"Files" : "Fayllar",
"You were mentioned on \"{file}\", in a comment by an account that has since been deleted" : "Siz \"{file}\"da, keyinchalik o'chirilgan hisob tomonidan izohda tilga olingansiz",
"{user} mentioned you in a comment on \"{file}\"" : "{user} sizni \"{file}\" dagi izohda tilga oldi",
"Files app plugin to add comments to files" : "Fayllarga izohlar qo'shish ilova plagini",
"Edit comment" : "Izohni tahrirlash",
"Delete comment" : "Izohni o'chirish",
"Cancel edit" : "Tahrirni bekor qilish",
"New comment" : "Yangi izoh",
"Write a comment …" : "Fikr yozing…",
"Post comment" : "Fikr qoldirish",
"@ for mentions, : for emoji, / for smart picker" : "@ eslatmalar uchun, : emojilar uchun, / aqlli tanlovclar uchun",
"Could not reload comments" : "Izohlarni qayta yuklab bo'lmadi",
"Failed to mark comments as read" : "Izohlarni o'qilgan deb belgilashda xatolik yuz berdi",
"Unable to load the comments list" : "Izohlar ro'yxatini yuklab bo'lmadi",
"No comments yet, start the conversation!" : "Hali izohlar yo'q, suhbatni boshlang!",
"No more messages" : "Boshqa xabarlar yo'q",
"Retry" : "Qayta urinish",
"_1 new comment_::_{unread} new comments_" : ["{unread} ta yangi izoh"],
"Comment" : "Izoh",
"An error occurred while trying to edit the comment" : "Izohni tahrirlashda xatolik yuz berdi",
"Comment deleted" : "Izoh o'chirildi",
"An error occurred while trying to delete the comment" : "Izohni o'chirishda xatolik yuz berdi",
"An error occurred while trying to create the comment" : "Izoh yaratishda xatolik yuz berdi",
"Write a comment …" : "Izoh yozing..."
},"pluralForm" :"nplurals=1; plural=0;"
}
+2
View File
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
+2
View File
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -9,6 +9,7 @@ import CommentProcessingSvg from '@mdi/svg/svg/comment-processing.svg?raw'
import { getSidebar } from '@nextcloud/files'
import { n, t } from '@nextcloud/l10n'
import logger from '../logger.js'
import { isUsingActivityIntegration } from '../utils/activity.js'
export const action: IFileAction = {
id: 'comments-unread',
@@ -38,7 +39,13 @@ export const action: IFileAction = {
try {
const sidebar = getSidebar()
sidebar.open(nodes[0], 'comments')
const sidebarTabId = isUsingActivityIntegration() ? 'activity' : 'comments'
if (sidebar.isOpen && sidebar.node?.source === nodes[0].source) {
logger.debug('Sidebar already open for this node, just activating comments tab')
sidebar.setActiveTab(sidebarTabId)
return null
}
sidebar.open(nodes[0], sidebarTabId)
return null
} catch (error) {
logger.error('Error while opening sidebar', { error })
+3 -3
View File
@@ -1,4 +1,4 @@
/**
/*!
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
@@ -6,18 +6,18 @@
import MessageReplyText from '@mdi/svg/svg/message-reply-text.svg?raw'
import { getCSPNonce } from '@nextcloud/auth'
import { registerSidebarTab } from '@nextcloud/files'
import { loadState } from '@nextcloud/initial-state'
import { t } from '@nextcloud/l10n'
import wrap from '@vue/web-component-wrapper'
import { createPinia, PiniaVuePlugin } from 'pinia'
import Vue from 'vue'
import { registerCommentsPlugins } from './comments-activity-tab.ts'
import { isUsingActivityIntegration } from './utils/activity.ts'
__webpack_nonce__ = getCSPNonce()
const tagName = 'comments_files-sidebar-tab'
if (loadState('comments', 'activityEnabled', false) && OCA?.Activity?.registerSidebarAction !== undefined) {
if (isUsingActivityIntegration()) {
// Do not mount own tab but mount into activity
window.addEventListener('DOMContentLoaded', function() {
registerCommentsPlugins()
+13
View File
@@ -0,0 +1,13 @@
/*!
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { loadState } from '@nextcloud/initial-state'
/**
* Check if the comments app is using the Activity app integration for the sidebar.
*/
export function isUsingActivityIntegration() {
return loadState('comments', 'activityEnabled', false) && window.OCA?.Activity?.registerSidebarAction !== undefined
}
+1
View File
@@ -20,6 +20,7 @@ OC.L10N.register(
"Edit widgets" : "Upraviť miniaplikácie",
"Get more widgets from the App Store" : "Získať viac miniaplikácií v Obchode s aplikáciami",
"Weather service" : "Služba počasie",
"For your privacy, the weather data is requested by your {productName} server on your behalf so the weather service receives no personal information." : "Pre vaše súkromie sú údaje o počasí požadované vaším {productName} serverom vo vašom mene, takže služba počasia neobdrží žiadne osobné informácie.",
"Weather data from Met.no" : "Dáta počasia z Met.no",
"geocoding with Nominatim" : "geokódovanie pomocou Nominatim",
"elevation data from OpenTopoData" : "dáta o nadmorskej výške z OpenTopoData",
+1
View File
@@ -18,6 +18,7 @@
"Edit widgets" : "Upraviť miniaplikácie",
"Get more widgets from the App Store" : "Získať viac miniaplikácií v Obchode s aplikáciami",
"Weather service" : "Služba počasie",
"For your privacy, the weather data is requested by your {productName} server on your behalf so the weather service receives no personal information." : "Pre vaše súkromie sú údaje o počasí požadované vaším {productName} serverom vo vašom mene, takže služba počasia neobdrží žiadne osobné informácie.",
"Weather data from Met.no" : "Dáta počasia z Met.no",
"geocoding with Nominatim" : "geokódovanie pomocou Nominatim",
"elevation data from OpenTopoData" : "dáta o nadmorskej výške z OpenTopoData",
+2
View File
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
+2 -2
View File
@@ -159,9 +159,9 @@ OC.L10N.register(
"Attendees:" : "Asistentes:",
"Title:" : "Título:",
"When:" : "Cando:",
"Location:" : "Lugar:",
"Location:" : "Onde:",
"Link:" : "Ligazón:",
"Occurring:" : "Acaecendo:",
"Occurring:" : "Acaece:",
"Accept" : "Aceptar",
"Decline" : "Declinar",
"More options …" : "Máis opcións…",
+2 -2
View File
@@ -157,9 +157,9 @@
"Attendees:" : "Asistentes:",
"Title:" : "Título:",
"When:" : "Cando:",
"Location:" : "Lugar:",
"Location:" : "Onde:",
"Link:" : "Ligazón:",
"Occurring:" : "Acaecendo:",
"Occurring:" : "Acaece:",
"Accept" : "Aceptar",
"Decline" : "Declinar",
"More options …" : "Máis opcións…",
+18
View File
@@ -75,7 +75,17 @@ OC.L10N.register(
"In the past on %1$s for the entire day" : "W przeszłości w %1$s na cały dzień",
"_In %n minute on %1$s for the entire day_::_In %n minutes on %1$s for the entire day_" : ["Za minutę o %1$s przez cały dzień","Za %n minut o %1$s przez cały dzień","Za %n minut o %1$s przez cały dzień","Za %n minut o %1$s przez cały dzień"],
"_In %n hour on %1$s for the entire day_::_In %n hours on %1$s for the entire day_" : ["Za %n godzinę o %1$s przez cały dzień","Za %n godzin o %1$s przez cały dzień","Za %n godzin o %1$s przez cały dzień","Za %n godziny o %1$s przez cały dzień"],
"_In %n day on %1$s for the entire day_::_In %n days on %1$s for the entire day_" : ["W ciągu %n dnia o %1$s przez cały dzień","W ciągu %n dni o %1$s przez cały dzień","W ciągu %n dni o %1$s przez cały dzień","W ciągu %n dni o %1$s przez cały dzień"],
"_In %n week on %1$s for the entire day_::_In %n weeks on %1$s for the entire day_" : ["Za %n tydzień o %1$s przez cały dzień","W ciągu %n tygodni o %1$s przez cały dzień","W ciągu %n tygodni o %1$s przez cały dzień","W ciągu %n tygodni o %1$s przez cały dzień"],
"_In %n month on %1$s for the entire day_::_In %n months on %1$s for the entire day_" : ["Za %n miesiąc, o %1$s przez cały dzień","W ciągu %n miesięcy, o %1$s przez cały dzień","W ciągu %n miesięcy, o %1$s przez cały dzień","W ciągu %n miesięcy, o %1$s przez cały dzień"],
"_In %n year on %1$s for the entire day_::_In %n years on %1$s for the entire day_" : ["Za %n rok o %1$s przez cały dzień","Za %n lat o %1$s przez cały dzień","Za %n lat o %1$s przez cały dzień","Za %n lat o %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 %n minute on %1$s between %2$s - %3$s_::_In %n minutes on %1$s between %2$s - %3$s_" : ["Za %n minutę o %1$s pomiędzy %2$s - %3$s","Za %n minut o %1$s pomiędzy %2$s - %3$s","Za %n minut o %1$s pomiędzy %2$s - %3$s","Za %n minut o %1$s pomiędzy %2$s - %3$s"],
"_In %n hour on %1$s between %2$s - %3$s_::_In %n hours on %1$s between %2$s - %3$s_" : ["Za %n godzinę o %1$s między %2$s - %3$s","Za %n godzin o %1$s między %2$s - %3$s","Za %n godzin o %1$s między %2$s - %3$s","Za %n godzin o %1$s między %2$s - %3$s"],
"_In %n day on %1$s between %2$s - %3$s_::_In %n days on %1$s between %2$s - %3$s_" : ["Za %n dzień o %1$s między %2$s - %3$s","Za %n dni o %1$s między %2$s - %3$s","Za %n dni o %1$s między %2$s - %3$s","Za %n dni o %1$s między %2$s - %3$s"],
"_In %n week on %1$s between %2$s - %3$s_::_In %n weeks on %1$s between %2$s - %3$s_" : ["Za %n tydzień o %1$s między %2$s - %3$s","Za %n tygodni o %1$s między %2$s - %3$s","Za %n tygodni o %1$s między %2$s - %3$s","Za %n tygodnie o %1$s między %2$s - %3$s"],
"_In %n month on %1$s between %2$s - %3$s_::_In %n months on %1$s between %2$s - %3$s_" : ["Za %n miesiąc o %1$s między %2$s - %3$s","Za %n miesięcy o %1$s między %2$s - %3$s","Za %n miesięcy o %1$s między %2$s - %3$s","Za %n miesiące o %1$s między %2$s - %3$s"],
"_In %n year on %1$s between %2$s - %3$s_::_In %n years on %1$s between %2$s - %3$s_" : ["Za %n rok o %1$s między %2$s - %3$s","Za %n lat o %1$s między %2$s - %3$s","Za %n lat o %1$s między %2$s - %3$s","Za %n lata o %1$s między %2$s - %3$s"],
"Could not generate when statement" : "Nie można wygenerować instrukcji when",
"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",
@@ -113,6 +123,12 @@ OC.L10N.register(
"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 %n minute on %1$s_::_In %n minutes on %1$s_" : ["Za %n minutę o %1$s","Za %n minuty o %1$s","Za %n minuty o %1$s","Za %n minuty o %1$s"],
"_In %n hour on %1$s_::_In %n hours on %1$s_" : ["Za %n godzinę o %1$s","Za %n godzin o %1$s","Za %n godzin o %1$s","Za %n godzin o %1$s"],
"_In %n day on %1$s_::_In %n days on %1$s_" : ["Za %n dzień o %1$s","Za %n dni o %1$s","Za %n dni o %1$s","Za %n dni o %1$s"],
"_In %n week on %1$s_::_In %n weeks on %1$s_" : ["Za %n tydzień o %1$s","Za %n tygodnie o %1$s","Za %n tygodnie o %1$s","Za %n tygodnie o %1$s"],
"_In %n month on %1$s_::_In %n months on %1$s_" : ["Za %n miesiąc o %1$s","W ciągu %n miesięcy o %1$s","W ciągu %n miesięcy o %1$s","W ciągu %n miesięcy o %1$s"],
"_In %n year on %1$s_::_In %n years on %1$s_" : ["Za %n rok o %1$s","Za %n lat o %1$s","Za %n lat o %1$s","Za %n lat o %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 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",
"Could not generate next recurrence statement" : "Nie można wygenerować następnej instrukcji powtarzania",
@@ -206,6 +222,8 @@ OC.L10N.register(
"Could not rename part file to final file, canceled by hook" : "Nie można zmienić nazwy pliku podzielonego na plik końcowy, anulowane przez hook",
"Could not rename part file to final file" : "Nie można zmienić nazwy pliku podzielonego na plik końcowy",
"Failed to check file size: %1$s" : "Nie udało się sprawdzić rozmiaru pliku: %1$s",
"Could not open file: %1$s (%2$d), file does seem to exist" : "Nie można otworzyć: %1$s (%2$d), zdaje się, że plik istnieje",
"Could not open file: %1$s (%2$d), file doesn't seem to exist" : "Nie można otworzyć: %1$s (%2$d), zdaje się, że plik nie istnieje",
"Encryption not ready: %1$s" : "Szyfrowanie nie jest gotowe: %1$s",
"Failed to open file: %1$s" : "Nie udało się otworzyć pliku: %1$s",
"Failed to unlink: %1$s" : "Nie udało się odłączyć: %1$s",
+18
View File
@@ -73,7 +73,17 @@
"In the past on %1$s for the entire day" : "W przeszłości w %1$s na cały dzień",
"_In %n minute on %1$s for the entire day_::_In %n minutes on %1$s for the entire day_" : ["Za minutę o %1$s przez cały dzień","Za %n minut o %1$s przez cały dzień","Za %n minut o %1$s przez cały dzień","Za %n minut o %1$s przez cały dzień"],
"_In %n hour on %1$s for the entire day_::_In %n hours on %1$s for the entire day_" : ["Za %n godzinę o %1$s przez cały dzień","Za %n godzin o %1$s przez cały dzień","Za %n godzin o %1$s przez cały dzień","Za %n godziny o %1$s przez cały dzień"],
"_In %n day on %1$s for the entire day_::_In %n days on %1$s for the entire day_" : ["W ciągu %n dnia o %1$s przez cały dzień","W ciągu %n dni o %1$s przez cały dzień","W ciągu %n dni o %1$s przez cały dzień","W ciągu %n dni o %1$s przez cały dzień"],
"_In %n week on %1$s for the entire day_::_In %n weeks on %1$s for the entire day_" : ["Za %n tydzień o %1$s przez cały dzień","W ciągu %n tygodni o %1$s przez cały dzień","W ciągu %n tygodni o %1$s przez cały dzień","W ciągu %n tygodni o %1$s przez cały dzień"],
"_In %n month on %1$s for the entire day_::_In %n months on %1$s for the entire day_" : ["Za %n miesiąc, o %1$s przez cały dzień","W ciągu %n miesięcy, o %1$s przez cały dzień","W ciągu %n miesięcy, o %1$s przez cały dzień","W ciągu %n miesięcy, o %1$s przez cały dzień"],
"_In %n year on %1$s for the entire day_::_In %n years on %1$s for the entire day_" : ["Za %n rok o %1$s przez cały dzień","Za %n lat o %1$s przez cały dzień","Za %n lat o %1$s przez cały dzień","Za %n lat o %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 %n minute on %1$s between %2$s - %3$s_::_In %n minutes on %1$s between %2$s - %3$s_" : ["Za %n minutę o %1$s pomiędzy %2$s - %3$s","Za %n minut o %1$s pomiędzy %2$s - %3$s","Za %n minut o %1$s pomiędzy %2$s - %3$s","Za %n minut o %1$s pomiędzy %2$s - %3$s"],
"_In %n hour on %1$s between %2$s - %3$s_::_In %n hours on %1$s between %2$s - %3$s_" : ["Za %n godzinę o %1$s między %2$s - %3$s","Za %n godzin o %1$s między %2$s - %3$s","Za %n godzin o %1$s między %2$s - %3$s","Za %n godzin o %1$s między %2$s - %3$s"],
"_In %n day on %1$s between %2$s - %3$s_::_In %n days on %1$s between %2$s - %3$s_" : ["Za %n dzień o %1$s między %2$s - %3$s","Za %n dni o %1$s między %2$s - %3$s","Za %n dni o %1$s między %2$s - %3$s","Za %n dni o %1$s między %2$s - %3$s"],
"_In %n week on %1$s between %2$s - %3$s_::_In %n weeks on %1$s between %2$s - %3$s_" : ["Za %n tydzień o %1$s między %2$s - %3$s","Za %n tygodni o %1$s między %2$s - %3$s","Za %n tygodni o %1$s między %2$s - %3$s","Za %n tygodnie o %1$s między %2$s - %3$s"],
"_In %n month on %1$s between %2$s - %3$s_::_In %n months on %1$s between %2$s - %3$s_" : ["Za %n miesiąc o %1$s między %2$s - %3$s","Za %n miesięcy o %1$s między %2$s - %3$s","Za %n miesięcy o %1$s między %2$s - %3$s","Za %n miesiące o %1$s między %2$s - %3$s"],
"_In %n year on %1$s between %2$s - %3$s_::_In %n years on %1$s between %2$s - %3$s_" : ["Za %n rok o %1$s między %2$s - %3$s","Za %n lat o %1$s między %2$s - %3$s","Za %n lat o %1$s między %2$s - %3$s","Za %n lata o %1$s między %2$s - %3$s"],
"Could not generate when statement" : "Nie można wygenerować instrukcji when",
"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",
@@ -111,6 +121,12 @@
"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 %n minute on %1$s_::_In %n minutes on %1$s_" : ["Za %n minutę o %1$s","Za %n minuty o %1$s","Za %n minuty o %1$s","Za %n minuty o %1$s"],
"_In %n hour on %1$s_::_In %n hours on %1$s_" : ["Za %n godzinę o %1$s","Za %n godzin o %1$s","Za %n godzin o %1$s","Za %n godzin o %1$s"],
"_In %n day on %1$s_::_In %n days on %1$s_" : ["Za %n dzień o %1$s","Za %n dni o %1$s","Za %n dni o %1$s","Za %n dni o %1$s"],
"_In %n week on %1$s_::_In %n weeks on %1$s_" : ["Za %n tydzień o %1$s","Za %n tygodnie o %1$s","Za %n tygodnie o %1$s","Za %n tygodnie o %1$s"],
"_In %n month on %1$s_::_In %n months on %1$s_" : ["Za %n miesiąc o %1$s","W ciągu %n miesięcy o %1$s","W ciągu %n miesięcy o %1$s","W ciągu %n miesięcy o %1$s"],
"_In %n year on %1$s_::_In %n years on %1$s_" : ["Za %n rok o %1$s","Za %n lat o %1$s","Za %n lat o %1$s","Za %n lat o %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 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",
"Could not generate next recurrence statement" : "Nie można wygenerować następnej instrukcji powtarzania",
@@ -204,6 +220,8 @@
"Could not rename part file to final file, canceled by hook" : "Nie można zmienić nazwy pliku podzielonego na plik końcowy, anulowane przez hook",
"Could not rename part file to final file" : "Nie można zmienić nazwy pliku podzielonego na plik końcowy",
"Failed to check file size: %1$s" : "Nie udało się sprawdzić rozmiaru pliku: %1$s",
"Could not open file: %1$s (%2$d), file does seem to exist" : "Nie można otworzyć: %1$s (%2$d), zdaje się, że plik istnieje",
"Could not open file: %1$s (%2$d), file doesn't seem to exist" : "Nie można otworzyć: %1$s (%2$d), zdaje się, że plik nie istnieje",
"Encryption not ready: %1$s" : "Szyfrowanie nie jest gotowe: %1$s",
"Failed to open file: %1$s" : "Nie udało się otworzyć pliku: %1$s",
"Failed to unlink: %1$s" : "Nie udało się odłączyć: %1$s",
+50
View File
@@ -73,7 +73,19 @@ OC.L10N.register(
"Where: %s" : "Kde: %s",
"%1$s via %2$s" : "%1$s cez %2$s",
"In the past on %1$s for the entire day" : "V minulosti %1$s na celý deň",
"_In %n minute on %1$s for the entire day_::_In %n minutes on %1$s for the entire day_" : ["Za %n minútu %1$s na celý deň","Za %n minúty %1$s na celý deň","Za %n minút %1$s na celý deň","Za %n minút %1$s na celý deň"],
"_In %n hour on %1$s for the entire day_::_In %n hours on %1$s for the entire day_" : ["Za %n hodinu %1$s na celý deň","Za %n hodiny %1$s na celý deň","Za %n hodín %1$s na celý deň","Za %n hodín %1$s na celý deň"],
"_In %n day on %1$s for the entire day_::_In %n days on %1$s for the entire day_" : ["Za %n deň %1$s na celý deň","Za %n dni %1$s na celý deň","Za %n dní %1$s na celý deň","Za %n dní %1$s na celý deň"],
"_In %n week on %1$s for the entire day_::_In %n weeks on %1$s for the entire day_" : ["Za %n týždeň %1$s na celý deň","Za %n týždne %1$s na celý deň","Za %n týždňov %1$s na celý deň","Za %n týždňov %1$s na celý deň"],
"_In %n month on %1$s for the entire day_::_In %n months on %1$s for the entire day_" : ["Za %n mesiac %1$s na celý deň","Za %n mesiace %1$s na celý deň","Za %n mesiacov %1$s na celý deň","Za %n mesiacov %1$s na celý deň"],
"_In %n year on %1$s for the entire day_::_In %n years on %1$s for the entire day_" : ["Za %n rok %1$s na celý deň","Za %n roky %1$s na celý deň","Za %n rokov %1$s na celý deň","Za %n rokov %1$s na celý deň"],
"In the past on %1$s between %2$s - %3$s" : "V minulosti %1$s medzi %2$s - %3$s",
"_In %n minute on %1$s between %2$s - %3$s_::_In %n minutes on %1$s between %2$s - %3$s_" : ["Za %n minútu %1$s %2$s medzi %2$s - %3$s","Za %n minúty %1$s %2$s medzi %2$s - %3$s","Za %n minút %1$s %2$s medzi %2$s - %3$s","Za %n minút %1$s %2$s medzi %2$s - %3$s"],
"_In %n hour on %1$s between %2$s - %3$s_::_In %n hours on %1$s between %2$s - %3$s_" : ["Za %n hodinu %1$s %2$s medzi %2$s - %3$s","Za %n hodiny %1$s %2$s medzi %2$s - %3$s","Za %n hodín %1$s %2$s medzi %2$s - %3$s","Za %n hodín %1$s %2$s medzi %2$s - %3$s"],
"_In %n day on %1$s between %2$s - %3$s_::_In %n days on %1$s between %2$s - %3$s_" : ["Za %n deň %1$s %2$s medzi %2$s - %3$s","Za %n dni %1$s %2$s medzi %2$s - %3$s","Za %n dní %1$s %2$s medzi %2$s - %3$s","Za %n dní %1$s %2$s medzi %2$s - %3$s"],
"_In %n week on %1$s between %2$s - %3$s_::_In %n weeks on %1$s between %2$s - %3$s_" : ["Za %n týždeň %1$s %2$s medzi %2$s - %3$s","Za %n týždne %1$s %2$s medzi %2$s - %3$s","Za %n týždňov %1$s %2$s medzi %2$s - %3$s","Za %n týždňov %1$s %2$s medzi %2$s - %3$s"],
"_In %n month on %1$s between %2$s - %3$s_::_In %n months on %1$s between %2$s - %3$s_" : ["Za %n mesiac %1$s %2$s medzi %2$s - %3$s","Za %n mesiace %1$s %2$s medzi %2$s - %3$s","Za %n mesiacov %1$s %2$s medzi %2$s - %3$s","Za %n mesiacov %1$s %2$s medzi %2$s - %3$s"],
"_In %n year on %1$s between %2$s - %3$s_::_In %n years on %1$s between %2$s - %3$s_" : ["Za %n rok %1$s %2$s medzi %2$s - %3$s","Za %n roky %1$s %2$s medzi %2$s - %3$s","Za %n rokov %1$s %2$s medzi %2$s - %3$s","Za %n rokov %1$s %2$s medzi %2$s - %3$s"],
"Could not generate when statement" : "Nepodarilo sa vygenerovať vyhlásenie kedy",
"Every Day for the entire day" : "Každý deň, na celý deň",
"Every Day for the entire day until %1$s" : "Každý deň, na celý deň, do %1$s",
@@ -111,8 +123,26 @@ OC.L10N.register(
"On specific dates for the entire day until %1$s" : "V konkrétnych dátumoch na celý deň %1$s",
"On specific dates between %1$s - %2$s until %3$s" : "V konkrétnych dátumoch medzi %1$s - %2$s do %3$s",
"In the past on %1$s" : "V minulosti %1$s",
"_In %n minute on %1$s_::_In %n minutes on %1$s_" : ["Za %n minútu na %1$s","Za %n minúty na %1$s","Za %n minút na %1$s","Za %n minút na %1$s"],
"_In %n hour on %1$s_::_In %n hours on %1$s_" : ["Za %n hodinu na %1$s","Za %n hodiny na %1$s","Za %n hodín na %1$s","Za %n hodín na%1$s"],
"_In %n day on %1$s_::_In %n days on %1$s_" : ["Za %n deň na %1$s","Za %n dni na %1$s","Za %n dní na %1$s","Za %n dní na %1$s"],
"_In %n week on %1$s_::_In %n weeks on %1$s_" : ["Za %n týždeň %1$s","Za %n týždne na %1$s","Za %n týždňov na %1$s","Za %n týždňov na %1$s"],
"_In %n month on %1$s_::_In %n months on %1$s_" : ["Za %n mesiac na%1$s","Za %n mesiace na%1$s","Za %n mesiacov na%1$s","Za %n mesiacov na %1$s"],
"_In %n year on %1$s_::_In %n years on %1$s_" : ["Za %n rok na %1$s","Za %n roky na %1$s","Za %n rokov na %1$s","Za %n rokov na %1$s"],
"In the past on %1$s then on %2$s" : "V minulosti %1$s a potom %2$s",
"_In %n minute on %1$s then on %2$s_::_In %n minutes on %1$s then on %2$s_" : ["Za %n minútu %1$s a potom %2$s","Za %n minúty %1$s a potom %2$s","Za %n minút %1$s a potom %2$s","Za %n minút %1$s a potom %2$s"],
"_In %n hour on %1$s then on %2$s_::_In %n hours on %1$s then on %2$s_" : ["Za %n hodinu %1$s a potom %2$s","Za %n hodiny %1$s a potom %2$s","Za %n hodín %1$s a potom %2$s","Za %n hodín %1$s a potom %2$s"],
"_In %n day on %1$s then on %2$s_::_In %n days on %1$s then on %2$s_" : ["Za %n deň %1$s a potom %2$s","Za %n dni %1$s a potom %2$s","Za %n dní %1$s a potom %2$s","Za %n dní %1$s a potom %2$s"],
"_In %n week on %1$s then on %2$s_::_In %n weeks on %1$s then on %2$s_" : ["Za %n týždeň %1$s a potom %2$s","Za %n týždne %1$s a potom %2$s","Za %n týždňov %1$s a potom %2$s","Za %n týždňov %1$s a potom %2$s"],
"_In %n month on %1$s then on %2$s_::_In %n months on %1$s then on %2$s_" : ["Za %n mesiac %1$s a potom %2$s","Za %n mesiace %1$s a potom %2$s","Za %n mesiacov %1$s a potom %2$s","Za %n mesiacov %1$s a potom %2$s"],
"_In %n year on %1$s then on %2$s_::_In %n years on %1$s then on %2$s_" : ["Za %n rok %1$s a potom %2$s","Za %n roky %1$s a potom %2$s","Za %n rokov %1$s a potom %2$s","Za %n rokov %1$s a potom %2$s"],
"In the past on %1$s then on %2$s and %3$s" : "V minulosti %1$s potom %2$s a %3$s",
"_In %n minute on %1$s then on %2$s and %3$s_::_In %n minutes on %1$s then on %2$s and %3$s_" : ["Za %n minútu %1$s potom %2$s a %3$s","Za %n minúty %1$s potom %2$s a %3$s","Za %n minút %1$s potom %2$s a %3$s","Za %n minút %1$s potom %2$s a %3$s"],
"_In %n hour on %1$s then on %2$s and %3$s_::_In %n hours on %1$s then on %2$s and %3$s_" : ["Za %n hodinu %1$s potom %2$s a %3$s","Za %n hodiny %1$s potom %2$s a %3$s","Za %n hodín %1$s potom %2$s a %3$s","Za %nhodín %1$s potom %2$s a %3$s"],
"_In %n day on %1$s then on %2$s and %3$s_::_In %n days on %1$s then on %2$s and %3$s_" : ["Za %n deň %1$s potom %2$s a %3$s","Za %n dni %1$s potom %2$s a %3$s","Za %n dní %1$s potom %2$s a %3$s","Za %n dní %1$s potom %2$s a %3$s"],
"_In %n week on %1$s then on %2$s and %3$s_::_In %n weeks on %1$s then on %2$s and %3$s_" : ["Za %n týždeň %1$s potom %2$s a %3$s","Za %n týždne %1$s potom %2$s a %3$s","Za %n týždňov %1$s potom %2$s a %3$s","Za %n týždňov %1$s potom %2$s a %3$s"],
"_In %n month on %1$s then on %2$s and %3$s_::_In %n months on %1$s then on %2$s and %3$s_" : ["Za %n mesiac %1$s potom %2$s a %3$s","Za %n mesiace %1$s potom %2$s a %3$s","Za %n mesiacov %1$s potom %2$s a %3$s","Za %n mesiacov %1$s potom %2$s a %3$s"],
"_In %n year on %1$s then on %2$s and %3$s_::_In %n years on %1$s then on %2$s and %3$s_" : ["Za %n rok %1$s potom %2$s a %3$s","Za %n roky %1$s potom %2$s a %3$s","Za %n rokov %1$s potom %2$s a %3$s","Za %n rokov %1$s potom %2$s a %3$s"],
"Could not generate next recurrence statement" : "Nepodarilo sa vygenerovať ďalšie opakovanie",
"Cancelled: %1$s" : "Zrušené: %1$s",
"\"%1$s\" has been canceled" : "\"%1$s\" bolo zrušené",
@@ -190,6 +220,8 @@ OC.L10N.register(
"{actor} updated contact {card} in address book {addressbook}" : "{actor} upravil kontakt {card} v adresári {addressbook}",
"You updated contact {card} in address book {addressbook}" : "Upravili ste kontakt {card} v adresári {addressbook}",
"A <strong>contact</strong> or <strong>address book</strong> was modified" : "<strong>kontakt</strong> alebo <strong>adresár</strong> bol upravený",
"System address book disabled" : "Systémový adresár je vypnutý",
"The system contacts address book has been automatically disabled during upgrade. This means that the address book will no longer be available to users in the contacts app or other clients. The system contacts address book was disabled because the amount of contacts in the address book exceeded the maximum recommended number of contacts. This limit is set to prevent performance issues. You can re-enable the system address book with the following command {command}" : "Systémový adresár kontaktov bol automaticky vypnutý počas aktualizácie. To znamená, že adresár kontaktov už nebude dostupný pre používateľov v aplikácii Kontakty ani v iných klientoch. Systémový adresár kontaktov bol vypnutý, pretože počet kontaktov v adresári prekročil maximálny odporúčaný počet kontaktov. Tento limit je nastavený na prevenciu problémov s výkonom. Systémový adresár kontaktov môžete znovu povoliť pomocou nasledujúcej príkazu {command}.",
"Accounts" : "Účty",
"System address book which holds all accounts" : "Systémový adresár, ktorý obsahuje všetky účty.",
"File is not updatable: %1$s" : "Súbor nie je možné aktualizovať: %1$s",
@@ -202,6 +234,8 @@ OC.L10N.register(
"Could not rename part file to final file, canceled by hook" : "Nepodarilo sa premenovať dočasný súbor na finálny, zrušené háčikom (hook)",
"Could not rename part file to final file" : "Nepodarilo sa premenovať dočasný súbor na finálny.",
"Failed to check file size: %1$s" : "Kontrola veľkosti súboru zlyhala: %1$s",
"Could not open file: %1$s (%2$d), file does seem to exist" : "Nie je možné otvoriť súbor: %1$s (%2$d), zdá sa že súbor existuje",
"Could not open file: %1$s (%2$d), file doesn't seem to exist" : "Nie je možné otvoriť súbor: %1$s (%2$d), nezdá sa že súbor existuje",
"Encryption not ready: %1$s" : "Šifrovanie nie je dostupné: %1$s",
"Failed to open file: %1$s" : "Otvorenie súboru zlyhalo: %1$s",
"Failed to unlink: %1$s" : "Odpojenie zlyhalo: %1$s",
@@ -218,12 +252,18 @@ OC.L10N.register(
"Completed on %s" : "Dokončené %s",
"Due on %s by %s" : "Termín od %s do %s",
"Due on %s" : "Termín do %s",
"Welcome to Nextcloud Calendar!\n\nThis is a sample event - explore the flexibility of planning with Nextcloud Calendar by making any edits you want!\n\nWith Nextcloud Calendar, you can:\n- Create, edit, and manage events effortlessly.\n- Create multiple calendars and share them with teammates, friends, or family.\n- Check availability and display your busy times to others.\n- Seamlessly integrate with apps and devices via CalDAV.\n- Customize your experience: schedule recurring events, adjust notifications and other settings." : "Vitajte v Nextcloud Calendar!\n\nToto je ukážková udalosť preskúmajte flexibilitu plánovania s Nextcloud Calendar tým, že vykonáte akékoľvek úpravy, ktoré chcete!\n\nS Nextcloud Calendar môžete:\n- Jednoducho vytvárať, upravovať a spravovať udalosti.\n- Vytvárať viacero kalendárov a zdieľať ich s tímom, priateľmi alebo rodinou.\n- Skontrolovať dostupnosť a zobraziť svoje zaneprázdnené časy pre ostatných.\n- Bezproblémovo integrovať s aplikáciami a zariadeniami cez CalDAV.\n- Prispôsobiť si skúsenosti: naplánovať opakujúce sa udalosti, nastaviť notifikácie a iné nastavenia.",
"Example event - open me!" : "Príklad udalosti - otvorte ma!",
"System Address Book" : "Systémový Adresár",
"The system address book contains contact information for all users in your instance." : "Systémový adresár obsahuje kontaktné informácie o všetkých užívateľov vo vašej inštancii.",
"Enable System Address Book" : "Povoliť systémový Adresár",
"DAV system address book" : "Systémový DAV adresár",
"No outstanding DAV system address book sync." : "Žiadna zostávajúca synchronizácia adresára systému DAV.",
"The DAV system address book sync has not run yet as your instance has more than 1000 users or because an error occurred. Please run it manually by calling \"occ dav:sync-system-addressbook\"." : "DAV synchronizácia systémového adresára ešte nebola spustená, pretože vaša inštancia má viac ako 1000 užívateľov alebo sa vyskytla chyba. Prosím, spustite ju manuálne volaním \"occ dav:sync-system-addressbook\".",
"DAV system address book size" : "Veľkosť adresára systému DAV",
"The system address book is disabled" : "Systémový adresár je zakázaný",
"The system address book is enabled, but contains more than the configured limit of %d contacts" : "Adresár systému je povolený, ale obsahuje viac kontaktov, než je nastavený limit %dkontaktov.",
"The system address book is enabled and contains less than the configured limit of %d contacts" : "Adresár systému je povolený a obsahuje menej kontaktov, než je nastavený limit %dkontaktov.",
"WebDAV endpoint" : "Koncový bod WebDAV",
"Could not check that your web server is properly set up to allow file synchronization over WebDAV. Please check manually." : "Nepodarilo sa skontrolovať, či je váš webový server správne nastavený tak, aby umožňoval synchronizáciu súborov cez WebDAV. Skontrolujte prosím manuálne.",
"Your web server is not yet properly set up to allow file synchronization, because the WebDAV interface seems to be broken." : "Váš webový server nie je zatiaľ správne nastavený, aby umožnil synchronizáciu súborov, pretože rozhranie WebDAV sa zdá byť nefunkčné.",
@@ -266,6 +306,14 @@ OC.L10N.register(
"Reset to default" : "Nastaviť predvolené",
"Import contacts" : "Importovať kontakty",
"Importing a new .vcf file will delete the existing default contact and replace it with the new one. Do you want to continue?" : "Importovaním nového súboru .vcf sa vymaže existujúci predvolený kontakt a nahradí sa novým. Chcete pokračovať?",
"Failed to save example event creation setting" : "Nepodarilo sa uložiť nastavenie vytvorenia príkladu udalosti",
"Failed to upload the example event" : "Nezvládlo sa nahrať príklad udalosti",
"Custom example event was saved successfully" : "Vlastný príklad udalosti bol úspešne uložený",
"Failed to delete the custom example event" : "Nepodarilo sa odstrániť vlastnú ukážkovú udalosť",
"Custom example event was deleted successfully" : "Vlastný príklad udalosti bol úspešne odstránený",
"Import calendar event" : "Importovať udalosť kalendára",
"Uploading a new event will overwrite the existing one." : "Nahrávanie novej udalosti prepíše existujúcu.",
"Upload event" : "Nahratie udalosti",
"Also install the {calendarappstoreopen}Calendar app{linkclose}, or {calendardocopen}connect your desktop & mobile for syncing ↗{linkclose}." : "Tiež nainštalujte {calendarappstoreopen}apku Kalendár{linkclose} alebo {calendardocopen}pripojte svoj počítač a smartfón pre synchronizáciu ↗{linkclose}.",
"Please make sure to properly set up {emailopen}the email server{linkclose}." : "Uistite sa, že ste správne nastavili {emailopen}e-mailový server{linkclose}.",
"Calendar server" : "Kalendárový server",
@@ -278,6 +326,8 @@ OC.L10N.register(
"Send reminder notifications to calendar sharees as well" : "Posielať upozornenia na pripomienky aj zdieľaným osobám v kalendári",
"Reminders are always sent to organizers and attendees." : "Upozornenia sa vždy posielajú organizátorom a účastníkom.",
"Enable notifications for events via push" : "Zapnúť oznámenia o udalostiach prostredníctvom technológie push.",
"Example content" : "Príklad obsahu",
"Example content serves to showcase the features of Nextcloud. Default content is shipped with Nextcloud, and can be replaced by custom content." : "Príklad obsahu slúži na predvedenie funkcií Nextcloud. Predvolený obsah je dodávaný s Nextcloudom a môže byť nahradený vlastným obsahom.",
"Availability" : "Dostupnosť",
"If you configure your working hours, other people will see when you are out of office when they book a meeting." : "Ak nakonfigurujete svoj pracovný čas, ostatní užívatelia vás uvidia ako neprítomného, keď si rezervujete schôdzku",
"Absence" : "Neprítomnosť",
+50
View File
@@ -71,7 +71,19 @@
"Where: %s" : "Kde: %s",
"%1$s via %2$s" : "%1$s cez %2$s",
"In the past on %1$s for the entire day" : "V minulosti %1$s na celý deň",
"_In %n minute on %1$s for the entire day_::_In %n minutes on %1$s for the entire day_" : ["Za %n minútu %1$s na celý deň","Za %n minúty %1$s na celý deň","Za %n minút %1$s na celý deň","Za %n minút %1$s na celý deň"],
"_In %n hour on %1$s for the entire day_::_In %n hours on %1$s for the entire day_" : ["Za %n hodinu %1$s na celý deň","Za %n hodiny %1$s na celý deň","Za %n hodín %1$s na celý deň","Za %n hodín %1$s na celý deň"],
"_In %n day on %1$s for the entire day_::_In %n days on %1$s for the entire day_" : ["Za %n deň %1$s na celý deň","Za %n dni %1$s na celý deň","Za %n dní %1$s na celý deň","Za %n dní %1$s na celý deň"],
"_In %n week on %1$s for the entire day_::_In %n weeks on %1$s for the entire day_" : ["Za %n týždeň %1$s na celý deň","Za %n týždne %1$s na celý deň","Za %n týždňov %1$s na celý deň","Za %n týždňov %1$s na celý deň"],
"_In %n month on %1$s for the entire day_::_In %n months on %1$s for the entire day_" : ["Za %n mesiac %1$s na celý deň","Za %n mesiace %1$s na celý deň","Za %n mesiacov %1$s na celý deň","Za %n mesiacov %1$s na celý deň"],
"_In %n year on %1$s for the entire day_::_In %n years on %1$s for the entire day_" : ["Za %n rok %1$s na celý deň","Za %n roky %1$s na celý deň","Za %n rokov %1$s na celý deň","Za %n rokov %1$s na celý deň"],
"In the past on %1$s between %2$s - %3$s" : "V minulosti %1$s medzi %2$s - %3$s",
"_In %n minute on %1$s between %2$s - %3$s_::_In %n minutes on %1$s between %2$s - %3$s_" : ["Za %n minútu %1$s %2$s medzi %2$s - %3$s","Za %n minúty %1$s %2$s medzi %2$s - %3$s","Za %n minút %1$s %2$s medzi %2$s - %3$s","Za %n minút %1$s %2$s medzi %2$s - %3$s"],
"_In %n hour on %1$s between %2$s - %3$s_::_In %n hours on %1$s between %2$s - %3$s_" : ["Za %n hodinu %1$s %2$s medzi %2$s - %3$s","Za %n hodiny %1$s %2$s medzi %2$s - %3$s","Za %n hodín %1$s %2$s medzi %2$s - %3$s","Za %n hodín %1$s %2$s medzi %2$s - %3$s"],
"_In %n day on %1$s between %2$s - %3$s_::_In %n days on %1$s between %2$s - %3$s_" : ["Za %n deň %1$s %2$s medzi %2$s - %3$s","Za %n dni %1$s %2$s medzi %2$s - %3$s","Za %n dní %1$s %2$s medzi %2$s - %3$s","Za %n dní %1$s %2$s medzi %2$s - %3$s"],
"_In %n week on %1$s between %2$s - %3$s_::_In %n weeks on %1$s between %2$s - %3$s_" : ["Za %n týždeň %1$s %2$s medzi %2$s - %3$s","Za %n týždne %1$s %2$s medzi %2$s - %3$s","Za %n týždňov %1$s %2$s medzi %2$s - %3$s","Za %n týždňov %1$s %2$s medzi %2$s - %3$s"],
"_In %n month on %1$s between %2$s - %3$s_::_In %n months on %1$s between %2$s - %3$s_" : ["Za %n mesiac %1$s %2$s medzi %2$s - %3$s","Za %n mesiace %1$s %2$s medzi %2$s - %3$s","Za %n mesiacov %1$s %2$s medzi %2$s - %3$s","Za %n mesiacov %1$s %2$s medzi %2$s - %3$s"],
"_In %n year on %1$s between %2$s - %3$s_::_In %n years on %1$s between %2$s - %3$s_" : ["Za %n rok %1$s %2$s medzi %2$s - %3$s","Za %n roky %1$s %2$s medzi %2$s - %3$s","Za %n rokov %1$s %2$s medzi %2$s - %3$s","Za %n rokov %1$s %2$s medzi %2$s - %3$s"],
"Could not generate when statement" : "Nepodarilo sa vygenerovať vyhlásenie kedy",
"Every Day for the entire day" : "Každý deň, na celý deň",
"Every Day for the entire day until %1$s" : "Každý deň, na celý deň, do %1$s",
@@ -109,8 +121,26 @@
"On specific dates for the entire day until %1$s" : "V konkrétnych dátumoch na celý deň %1$s",
"On specific dates between %1$s - %2$s until %3$s" : "V konkrétnych dátumoch medzi %1$s - %2$s do %3$s",
"In the past on %1$s" : "V minulosti %1$s",
"_In %n minute on %1$s_::_In %n minutes on %1$s_" : ["Za %n minútu na %1$s","Za %n minúty na %1$s","Za %n minút na %1$s","Za %n minút na %1$s"],
"_In %n hour on %1$s_::_In %n hours on %1$s_" : ["Za %n hodinu na %1$s","Za %n hodiny na %1$s","Za %n hodín na %1$s","Za %n hodín na%1$s"],
"_In %n day on %1$s_::_In %n days on %1$s_" : ["Za %n deň na %1$s","Za %n dni na %1$s","Za %n dní na %1$s","Za %n dní na %1$s"],
"_In %n week on %1$s_::_In %n weeks on %1$s_" : ["Za %n týždeň %1$s","Za %n týždne na %1$s","Za %n týždňov na %1$s","Za %n týždňov na %1$s"],
"_In %n month on %1$s_::_In %n months on %1$s_" : ["Za %n mesiac na%1$s","Za %n mesiace na%1$s","Za %n mesiacov na%1$s","Za %n mesiacov na %1$s"],
"_In %n year on %1$s_::_In %n years on %1$s_" : ["Za %n rok na %1$s","Za %n roky na %1$s","Za %n rokov na %1$s","Za %n rokov na %1$s"],
"In the past on %1$s then on %2$s" : "V minulosti %1$s a potom %2$s",
"_In %n minute on %1$s then on %2$s_::_In %n minutes on %1$s then on %2$s_" : ["Za %n minútu %1$s a potom %2$s","Za %n minúty %1$s a potom %2$s","Za %n minút %1$s a potom %2$s","Za %n minút %1$s a potom %2$s"],
"_In %n hour on %1$s then on %2$s_::_In %n hours on %1$s then on %2$s_" : ["Za %n hodinu %1$s a potom %2$s","Za %n hodiny %1$s a potom %2$s","Za %n hodín %1$s a potom %2$s","Za %n hodín %1$s a potom %2$s"],
"_In %n day on %1$s then on %2$s_::_In %n days on %1$s then on %2$s_" : ["Za %n deň %1$s a potom %2$s","Za %n dni %1$s a potom %2$s","Za %n dní %1$s a potom %2$s","Za %n dní %1$s a potom %2$s"],
"_In %n week on %1$s then on %2$s_::_In %n weeks on %1$s then on %2$s_" : ["Za %n týždeň %1$s a potom %2$s","Za %n týždne %1$s a potom %2$s","Za %n týždňov %1$s a potom %2$s","Za %n týždňov %1$s a potom %2$s"],
"_In %n month on %1$s then on %2$s_::_In %n months on %1$s then on %2$s_" : ["Za %n mesiac %1$s a potom %2$s","Za %n mesiace %1$s a potom %2$s","Za %n mesiacov %1$s a potom %2$s","Za %n mesiacov %1$s a potom %2$s"],
"_In %n year on %1$s then on %2$s_::_In %n years on %1$s then on %2$s_" : ["Za %n rok %1$s a potom %2$s","Za %n roky %1$s a potom %2$s","Za %n rokov %1$s a potom %2$s","Za %n rokov %1$s a potom %2$s"],
"In the past on %1$s then on %2$s and %3$s" : "V minulosti %1$s potom %2$s a %3$s",
"_In %n minute on %1$s then on %2$s and %3$s_::_In %n minutes on %1$s then on %2$s and %3$s_" : ["Za %n minútu %1$s potom %2$s a %3$s","Za %n minúty %1$s potom %2$s a %3$s","Za %n minút %1$s potom %2$s a %3$s","Za %n minút %1$s potom %2$s a %3$s"],
"_In %n hour on %1$s then on %2$s and %3$s_::_In %n hours on %1$s then on %2$s and %3$s_" : ["Za %n hodinu %1$s potom %2$s a %3$s","Za %n hodiny %1$s potom %2$s a %3$s","Za %n hodín %1$s potom %2$s a %3$s","Za %nhodín %1$s potom %2$s a %3$s"],
"_In %n day on %1$s then on %2$s and %3$s_::_In %n days on %1$s then on %2$s and %3$s_" : ["Za %n deň %1$s potom %2$s a %3$s","Za %n dni %1$s potom %2$s a %3$s","Za %n dní %1$s potom %2$s a %3$s","Za %n dní %1$s potom %2$s a %3$s"],
"_In %n week on %1$s then on %2$s and %3$s_::_In %n weeks on %1$s then on %2$s and %3$s_" : ["Za %n týždeň %1$s potom %2$s a %3$s","Za %n týždne %1$s potom %2$s a %3$s","Za %n týždňov %1$s potom %2$s a %3$s","Za %n týždňov %1$s potom %2$s a %3$s"],
"_In %n month on %1$s then on %2$s and %3$s_::_In %n months on %1$s then on %2$s and %3$s_" : ["Za %n mesiac %1$s potom %2$s a %3$s","Za %n mesiace %1$s potom %2$s a %3$s","Za %n mesiacov %1$s potom %2$s a %3$s","Za %n mesiacov %1$s potom %2$s a %3$s"],
"_In %n year on %1$s then on %2$s and %3$s_::_In %n years on %1$s then on %2$s and %3$s_" : ["Za %n rok %1$s potom %2$s a %3$s","Za %n roky %1$s potom %2$s a %3$s","Za %n rokov %1$s potom %2$s a %3$s","Za %n rokov %1$s potom %2$s a %3$s"],
"Could not generate next recurrence statement" : "Nepodarilo sa vygenerovať ďalšie opakovanie",
"Cancelled: %1$s" : "Zrušené: %1$s",
"\"%1$s\" has been canceled" : "\"%1$s\" bolo zrušené",
@@ -188,6 +218,8 @@
"{actor} updated contact {card} in address book {addressbook}" : "{actor} upravil kontakt {card} v adresári {addressbook}",
"You updated contact {card} in address book {addressbook}" : "Upravili ste kontakt {card} v adresári {addressbook}",
"A <strong>contact</strong> or <strong>address book</strong> was modified" : "<strong>kontakt</strong> alebo <strong>adresár</strong> bol upravený",
"System address book disabled" : "Systémový adresár je vypnutý",
"The system contacts address book has been automatically disabled during upgrade. This means that the address book will no longer be available to users in the contacts app or other clients. The system contacts address book was disabled because the amount of contacts in the address book exceeded the maximum recommended number of contacts. This limit is set to prevent performance issues. You can re-enable the system address book with the following command {command}" : "Systémový adresár kontaktov bol automaticky vypnutý počas aktualizácie. To znamená, že adresár kontaktov už nebude dostupný pre používateľov v aplikácii Kontakty ani v iných klientoch. Systémový adresár kontaktov bol vypnutý, pretože počet kontaktov v adresári prekročil maximálny odporúčaný počet kontaktov. Tento limit je nastavený na prevenciu problémov s výkonom. Systémový adresár kontaktov môžete znovu povoliť pomocou nasledujúcej príkazu {command}.",
"Accounts" : "Účty",
"System address book which holds all accounts" : "Systémový adresár, ktorý obsahuje všetky účty.",
"File is not updatable: %1$s" : "Súbor nie je možné aktualizovať: %1$s",
@@ -200,6 +232,8 @@
"Could not rename part file to final file, canceled by hook" : "Nepodarilo sa premenovať dočasný súbor na finálny, zrušené háčikom (hook)",
"Could not rename part file to final file" : "Nepodarilo sa premenovať dočasný súbor na finálny.",
"Failed to check file size: %1$s" : "Kontrola veľkosti súboru zlyhala: %1$s",
"Could not open file: %1$s (%2$d), file does seem to exist" : "Nie je možné otvoriť súbor: %1$s (%2$d), zdá sa že súbor existuje",
"Could not open file: %1$s (%2$d), file doesn't seem to exist" : "Nie je možné otvoriť súbor: %1$s (%2$d), nezdá sa že súbor existuje",
"Encryption not ready: %1$s" : "Šifrovanie nie je dostupné: %1$s",
"Failed to open file: %1$s" : "Otvorenie súboru zlyhalo: %1$s",
"Failed to unlink: %1$s" : "Odpojenie zlyhalo: %1$s",
@@ -216,12 +250,18 @@
"Completed on %s" : "Dokončené %s",
"Due on %s by %s" : "Termín od %s do %s",
"Due on %s" : "Termín do %s",
"Welcome to Nextcloud Calendar!\n\nThis is a sample event - explore the flexibility of planning with Nextcloud Calendar by making any edits you want!\n\nWith Nextcloud Calendar, you can:\n- Create, edit, and manage events effortlessly.\n- Create multiple calendars and share them with teammates, friends, or family.\n- Check availability and display your busy times to others.\n- Seamlessly integrate with apps and devices via CalDAV.\n- Customize your experience: schedule recurring events, adjust notifications and other settings." : "Vitajte v Nextcloud Calendar!\n\nToto je ukážková udalosť preskúmajte flexibilitu plánovania s Nextcloud Calendar tým, že vykonáte akékoľvek úpravy, ktoré chcete!\n\nS Nextcloud Calendar môžete:\n- Jednoducho vytvárať, upravovať a spravovať udalosti.\n- Vytvárať viacero kalendárov a zdieľať ich s tímom, priateľmi alebo rodinou.\n- Skontrolovať dostupnosť a zobraziť svoje zaneprázdnené časy pre ostatných.\n- Bezproblémovo integrovať s aplikáciami a zariadeniami cez CalDAV.\n- Prispôsobiť si skúsenosti: naplánovať opakujúce sa udalosti, nastaviť notifikácie a iné nastavenia.",
"Example event - open me!" : "Príklad udalosti - otvorte ma!",
"System Address Book" : "Systémový Adresár",
"The system address book contains contact information for all users in your instance." : "Systémový adresár obsahuje kontaktné informácie o všetkých užívateľov vo vašej inštancii.",
"Enable System Address Book" : "Povoliť systémový Adresár",
"DAV system address book" : "Systémový DAV adresár",
"No outstanding DAV system address book sync." : "Žiadna zostávajúca synchronizácia adresára systému DAV.",
"The DAV system address book sync has not run yet as your instance has more than 1000 users or because an error occurred. Please run it manually by calling \"occ dav:sync-system-addressbook\"." : "DAV synchronizácia systémového adresára ešte nebola spustená, pretože vaša inštancia má viac ako 1000 užívateľov alebo sa vyskytla chyba. Prosím, spustite ju manuálne volaním \"occ dav:sync-system-addressbook\".",
"DAV system address book size" : "Veľkosť adresára systému DAV",
"The system address book is disabled" : "Systémový adresár je zakázaný",
"The system address book is enabled, but contains more than the configured limit of %d contacts" : "Adresár systému je povolený, ale obsahuje viac kontaktov, než je nastavený limit %dkontaktov.",
"The system address book is enabled and contains less than the configured limit of %d contacts" : "Adresár systému je povolený a obsahuje menej kontaktov, než je nastavený limit %dkontaktov.",
"WebDAV endpoint" : "Koncový bod WebDAV",
"Could not check that your web server is properly set up to allow file synchronization over WebDAV. Please check manually." : "Nepodarilo sa skontrolovať, či je váš webový server správne nastavený tak, aby umožňoval synchronizáciu súborov cez WebDAV. Skontrolujte prosím manuálne.",
"Your web server is not yet properly set up to allow file synchronization, because the WebDAV interface seems to be broken." : "Váš webový server nie je zatiaľ správne nastavený, aby umožnil synchronizáciu súborov, pretože rozhranie WebDAV sa zdá byť nefunkčné.",
@@ -264,6 +304,14 @@
"Reset to default" : "Nastaviť predvolené",
"Import contacts" : "Importovať kontakty",
"Importing a new .vcf file will delete the existing default contact and replace it with the new one. Do you want to continue?" : "Importovaním nového súboru .vcf sa vymaže existujúci predvolený kontakt a nahradí sa novým. Chcete pokračovať?",
"Failed to save example event creation setting" : "Nepodarilo sa uložiť nastavenie vytvorenia príkladu udalosti",
"Failed to upload the example event" : "Nezvládlo sa nahrať príklad udalosti",
"Custom example event was saved successfully" : "Vlastný príklad udalosti bol úspešne uložený",
"Failed to delete the custom example event" : "Nepodarilo sa odstrániť vlastnú ukážkovú udalosť",
"Custom example event was deleted successfully" : "Vlastný príklad udalosti bol úspešne odstránený",
"Import calendar event" : "Importovať udalosť kalendára",
"Uploading a new event will overwrite the existing one." : "Nahrávanie novej udalosti prepíše existujúcu.",
"Upload event" : "Nahratie udalosti",
"Also install the {calendarappstoreopen}Calendar app{linkclose}, or {calendardocopen}connect your desktop & mobile for syncing ↗{linkclose}." : "Tiež nainštalujte {calendarappstoreopen}apku Kalendár{linkclose} alebo {calendardocopen}pripojte svoj počítač a smartfón pre synchronizáciu ↗{linkclose}.",
"Please make sure to properly set up {emailopen}the email server{linkclose}." : "Uistite sa, že ste správne nastavili {emailopen}e-mailový server{linkclose}.",
"Calendar server" : "Kalendárový server",
@@ -276,6 +324,8 @@
"Send reminder notifications to calendar sharees as well" : "Posielať upozornenia na pripomienky aj zdieľaným osobám v kalendári",
"Reminders are always sent to organizers and attendees." : "Upozornenia sa vždy posielajú organizátorom a účastníkom.",
"Enable notifications for events via push" : "Zapnúť oznámenia o udalostiach prostredníctvom technológie push.",
"Example content" : "Príklad obsahu",
"Example content serves to showcase the features of Nextcloud. Default content is shipped with Nextcloud, and can be replaced by custom content." : "Príklad obsahu slúži na predvedenie funkcií Nextcloud. Predvolený obsah je dodávaný s Nextcloudom a môže byť nahradený vlastným obsahom.",
"Availability" : "Dostupnosť",
"If you configure your working hours, other people will see when you are out of office when they book a meeting." : "Ak nakonfigurujete svoj pracovný čas, ostatní užívatelia vás uvidia ako neprítomného, keď si rezervujete schôdzku",
"Absence" : "Neprítomnosť",
+2
View File
@@ -89,6 +89,7 @@ use OCP\Contacts\IManager as IContactsManager;
use OCP\DB\Events\AddMissingIndicesEvent;
use OCP\Federation\Events\TrustedServerRemovedEvent;
use OCP\Federation\ICloudFederationProviderManager;
use OCP\Group\Events\GroupDeletedEvent;
use OCP\IURLGenerator;
use OCP\IUserSession;
use OCP\Server;
@@ -205,6 +206,7 @@ class Application extends App implements IBootstrap {
$context->registerEventListener(UserCreatedEvent::class, UserEventsListener::class);
$context->registerEventListener(UserChangedEvent::class, UserEventsListener::class);
$context->registerEventListener(UserUpdatedEvent::class, UserEventsListener::class);
$context->registerEventListener(GroupDeletedEvent::class, UserEventsListener::class);
$context->registerEventListener(SabrePluginAuthInitEvent::class, SabrePluginAuthInitListener::class);
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
+1 -1
View File
@@ -67,7 +67,7 @@ class CalendarProvider implements ICalendarProvider {
$user = str_replace('principals/users/', '', $calendarInfo['principaluri']);
$path = 'calendars/' . $user . '/' . $calendarInfo['uri'];
if (isset($additionalFederatedProps[$path])) {
$calendarInfo = array_merge($calendarInfo, $additionalProperties[$path]);
$calendarInfo = array_merge($calendarInfo, $additionalFederatedProps[$path]);
}
$iCalendars[] = new FederatedCalendarImpl($calendarInfo, $this->calDavBackend);
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
+2
View File
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
+2
View File
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
+8
View File
@@ -165,6 +165,7 @@ class Plugin extends \Sabre\CalDAV\Schedule\Plugin {
// Do not generate iTip and iMip messages if scheduling is disabled for this message
if ($request->getHeader('x-nc-scheduling') === 'false') {
$this->logger->debug('Skipping scheduling messages for calendar object change because x-nc-scheduling header is set to false');
return;
}
@@ -212,6 +213,13 @@ class Plugin extends \Sabre\CalDAV\Schedule\Plugin {
* @inheritDoc
*/
public function beforeUnbind($path): void {
// Do not generate iTip and iMip messages if scheduling is disabled for this message
if ($this->server->httpRequest->getHeader('x-nc-scheduling') === 'false') {
$this->logger->debug('Skipping scheduling messages for calendar object delete because x-nc-scheduling header is set to false');
return;
}
try {
parent::beforeUnbind($path);
} catch (SameOrganizerForAllComponentsException $e) {
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -24,17 +24,14 @@ use function explode;
class RateLimitingPlugin extends ServerPlugin {
private Limiter $limiter;
public function __construct(
Limiter $limiter,
private Limiter $limiter,
private IUserManager $userManager,
private CalDavBackend $calDavBackend,
private LoggerInterface $logger,
private IAppConfig $config,
private ?string $userId,
) {
$this->limiter = $limiter;
}
public function initialize(DAV\Server $server): void {
+239 -126
View File
@@ -9,6 +9,7 @@ declare(strict_types=1);
namespace OCA\DAV\CalDAV;
use Sabre\VObject\Component;
use Sabre\VObject\Component\VCalendar;
use Sabre\VObject\ITip\Broker;
use Sabre\VObject\ITip\Message;
@@ -27,9 +28,56 @@ class TipBroker extends Broker {
'SUMMARY',
'DESCRIPTION',
'LOCATION',
];
/**
* Processes incoming CANCEL messages.
*
* This is a message from an organizer, and means that either an
* attendee got removed from an event, or an event got cancelled
* altogether.
*
* @param VCalendar $existingObject
*
* @return VCalendar|null
*/
protected function processMessageCancel(Message $itipMessage, ?VCalendar $existingObject = null) {
if ($existingObject === null) {
return null;
}
$componentType = $itipMessage->component;
$instances = [];
foreach ($itipMessage->message->$componentType as $component) {
$instanceId = isset($component->{'RECURRENCE-ID'}) ? $component->{'RECURRENCE-ID'}->getValue() : 'base';
$instances[$instanceId] = $component;
}
// any existing instances should be marked as cancelled
foreach ($existingObject->$componentType as $component) {
$instanceId = isset($component->{'RECURRENCE-ID'}) ? $component->{'RECURRENCE-ID'}->getValue() : 'base';
if (isset($instances[$instanceId])) {
if (isset($component->STATUS)) {
$component->STATUS->setValue('CANCELLED');
} else {
$component->add('STATUS', 'CANCELLED');
}
if (isset($component->SEQUENCE)) {
$component->SEQUENCE->setValue($itipMessage->sequence);
} else {
$component->add('SEQUENCE', $itipMessage->sequence);
}
unset($instances[$instanceId]);
}
}
// any remaining instances are new and should be added
foreach ($instances as $instance) {
$existingObject->add($instance);
}
return $existingObject;
}
/**
* This method is used in cases where an event got updated, and we
* potentially need to send emails to attendees to let them know of updates
@@ -38,154 +86,219 @@ class TipBroker extends Broker {
* We will detect which attendees got added, which got removed and create
* specific messages for these situations.
*
* @return array
* @return array<int,Message>
*/
protected function parseEventForOrganizer(VCalendar $calendar, array $eventInfo, array $oldEventInfo) {
// Merging attendee lists.
$attendees = [];
foreach ($oldEventInfo['attendees'] as $attendee) {
$attendees[$attendee['href']] = [
'href' => $attendee['href'],
'oldInstances' => $attendee['instances'],
'newInstances' => [],
'name' => $attendee['name'],
'forceSend' => null,
];
}
foreach ($eventInfo['attendees'] as $attendee) {
if (isset($attendees[$attendee['href']])) {
$attendees[$attendee['href']]['name'] = $attendee['name'];
$attendees[$attendee['href']]['newInstances'] = $attendee['instances'];
$attendees[$attendee['href']]['forceSend'] = $attendee['forceSend'];
} else {
$attendees[$attendee['href']] = [
'href' => $attendee['href'],
'oldInstances' => [],
'newInstances' => $attendee['instances'],
'name' => $attendee['name'],
'forceSend' => $attendee['forceSend'],
];
}
}
$messages = [];
// construct template calendar from original calendar without components
$template = new VCalendar();
foreach ($template->children() as $property) {
$template->remove($property);
}
foreach ($calendar->children() as $property) {
if (in_array($property->name, ['METHOD', 'VEVENT', 'VTODO', 'VJOURNAL', 'VFREEBUSY'], true) === false) {
$template->add(clone $property);
}
}
// extract event information
$objectId = $eventInfo['uid'];
if ($calendar->getBaseComponent() === null) {
$objectType = $calendar->getComponents()[0]->name;
} else {
$objectType = $calendar->getBaseComponent()->name;
}
$objectSequence = $eventInfo['sequence'] ?? 1;
$organizerHref = $eventInfo['organizer'] ?? $oldEventInfo['organizer'];
if ($eventInfo['organizerName'] instanceof \Sabre\VObject\Parameter) {
$organizerName = $eventInfo['organizerName']->getValue();
} else {
$organizerName = $eventInfo['organizerName'];
}
// detect if the singleton or recurring base instance was converted to non-scheduling
if (count($eventInfo['instances']) === 0 && count($oldEventInfo['instances']) > 0) {
foreach ($oldEventInfo['attendees'] as $attendee) {
$messages[] = $this->generateMessage(
$oldEventInfo['instances'], $organizerHref, $organizerName, $attendee, $objectId, $objectType, $objectSequence, 'CANCEL', $template
);
}
return $messages;
}
// detect if the singleton or recurring base instance was cancelled
if ($eventInfo['instances']['master']?->STATUS?->getValue() === 'CANCELLED' && $oldEventInfo['instances']['master']?->STATUS?->getValue() !== 'CANCELLED') {
foreach ($eventInfo['attendees'] as $attendee) {
$messages[] = $this->generateMessage(
$eventInfo['instances'], $organizerHref, $organizerName, $attendee, $objectId, $objectType, $objectSequence, 'CANCEL', $template
);
}
return $messages;
}
// detect if a new cancelled instance was created
$cancelledNewInstances = [];
if (isset($oldEventInfo['instances'])) {
$instancesDelta = array_diff_key($eventInfo['instances'], $oldEventInfo['instances']);
foreach ($instancesDelta as $id => $instance) {
if ($instance->STATUS?->getValue() === 'CANCELLED') {
$cancelledNewInstances[] = $id;
foreach ($eventInfo['attendees'] as $attendee) {
$messages[] = $this->generateMessage(
[$id => $instance], $organizerHref, $organizerName, $attendee, $objectId, $objectType, $objectSequence, 'CANCEL', $template
);
}
}
}
}
// detect attendee mutations
$attendees = array_unique(
array_merge(
array_keys($eventInfo['attendees']),
array_keys($oldEventInfo['attendees'])
)
);
foreach ($attendees as $attendee) {
// An organizer can also be an attendee. We should not generate any
// messages for those.
if ($attendee['href'] === $eventInfo['organizer']) {
// Skip organizer
if ($attendee === $organizerHref) {
continue;
}
$message = new Message();
$message->uid = $eventInfo['uid'];
$message->component = 'VEVENT';
$message->sequence = $eventInfo['sequence'];
$message->sender = $eventInfo['organizer'];
$message->senderName = $eventInfo['organizerName'];
$message->recipient = $attendee['href'];
$message->recipientName = $attendee['name'];
// Creating the new iCalendar body.
$icalMsg = new VCalendar();
foreach ($calendar->select('VTIMEZONE') as $timezone) {
$icalMsg->add(clone $timezone);
// Skip if SCHEDULE-AGENT=CLIENT (respect RFC 6638)
if ($this->scheduleAgentServerRules
&& isset($eventInfo['attendees'][$attendee]['scheduleAgent'])
&& strtoupper($eventInfo['attendees'][$attendee]['scheduleAgent']) === 'CLIENT') {
continue;
}
// If there are no instances the attendee is a part of, it means
// the attendee was removed and we need to send them a CANCEL message.
// Also If the meeting STATUS property was changed to CANCELLED
// we need to send the attendee a CANCEL message.
if (!$attendee['newInstances'] || $eventInfo['status'] === 'CANCELLED') {
$message->method = $icalMsg->METHOD = 'CANCEL';
$message->significantChange = true;
// clone base event
if (isset($eventInfo['instances']['master'])) {
$event = clone $eventInfo['instances']['master'];
} else {
$event = clone $oldEventInfo['instances']['master'];
}
// alter some properties
unset($event->ATTENDEE);
$event->add('ATTENDEE', $attendee['href'], ['CN' => $attendee['name'],]);
$event->DTSTAMP = gmdate('Ymd\\THis\\Z');
$event->SEQUENCE = $message->sequence;
$icalMsg->add($event);
// detect if attendee was removed and send cancel message
if (!isset($eventInfo['attendees'][$attendee]) && isset($oldEventInfo['attendees'][$attendee])) {
//get all instances of the attendee was removed from.
$instances = array_intersect_key($oldEventInfo['instances'], array_flip(array_keys($oldEventInfo['attendees'][$attendee]['instances'])));
$messages[] = $this->generateMessage(
$instances, $organizerHref, $organizerName, $oldEventInfo['attendees'][$attendee], $objectId, $objectType, $objectSequence, 'CANCEL', $template
);
continue;
}
// otherwise any created or modified instances will be sent as REQUEST
$instances = array_intersect_key($eventInfo['instances'], array_flip(array_keys($eventInfo['attendees'][$attendee]['instances'])));
} else {
// The attendee gets the updated event body
$message->method = $icalMsg->METHOD = 'REQUEST';
// Remove already-cancelled new instances from REQUEST
if (!empty($cancelledNewInstances)) {
$instances = array_diff_key($instances, array_flip($cancelledNewInstances));
}
// We need to find out that this change is significant. If it's
// not, systems may opt to not send messages.
//
// We do this based on the 'significantChangeHash' which is
// some value that changes if there's a certain set of
// properties changed in the event, or simply if there's a
// difference in instances that the attendee is invited to.
// Skip if no instances left to send
if (empty($instances)) {
continue;
}
$oldAttendeeInstances = array_keys($attendee['oldInstances']);
$newAttendeeInstances = array_keys($attendee['newInstances']);
// Add EXDATE for instances the attendee is NOT part of (only for recurring events with master)
if (isset($instances['master']) && count($eventInfo['instances']) > 1) {
$masterInstance = clone $instances['master'];
$excludedDates = [];
$message->significantChange
= $attendee['forceSend'] === 'REQUEST'
|| count($oldAttendeeInstances) !== count($newAttendeeInstances)
|| count(array_diff($oldAttendeeInstances, $newAttendeeInstances)) > 0
|| $oldEventInfo['significantChangeHash'] !== $eventInfo['significantChangeHash'];
foreach ($attendee['newInstances'] as $instanceId => $instanceInfo) {
$currentEvent = clone $eventInfo['instances'][$instanceId];
if ($instanceId === 'master') {
// We need to find a list of events that the attendee
// is not a part of to add to the list of exceptions.
$exceptions = [];
foreach ($eventInfo['instances'] as $instanceId => $vevent) {
if (!isset($attendee['newInstances'][$instanceId])) {
$exceptions[] = $instanceId;
}
}
// If there were exceptions, we need to add it to an
// existing EXDATE property, if it exists.
if ($exceptions) {
if (isset($currentEvent->EXDATE)) {
$currentEvent->EXDATE->setParts(array_merge(
$currentEvent->EXDATE->getParts(),
$exceptions
));
} else {
$currentEvent->EXDATE = $exceptions;
}
}
// Cleaning up any scheduling information that
// shouldn't be sent along.
unset($currentEvent->ORGANIZER['SCHEDULE-FORCE-SEND']);
unset($currentEvent->ORGANIZER['SCHEDULE-STATUS']);
foreach ($currentEvent->ATTENDEE as $attendee) {
unset($attendee['SCHEDULE-FORCE-SEND']);
unset($attendee['SCHEDULE-STATUS']);
// We're adding PARTSTAT=NEEDS-ACTION to ensure that
// iOS shows an "Inbox Item"
if (!isset($attendee['PARTSTAT'])) {
$attendee['PARTSTAT'] = 'NEEDS-ACTION';
}
}
foreach ($eventInfo['instances'] as $instanceId => $instance) {
if ($instanceId !== 'master' && !isset($eventInfo['attendees'][$attendee]['instances'][$instanceId])) {
$excludedDates[] = $instance->{'RECURRENCE-ID'}->getValue();
}
}
$currentEvent->DTSTAMP = gmdate('Ymd\\THis\\Z');
$icalMsg->add($currentEvent);
if (!empty($excludedDates)) {
if (isset($masterInstance->EXDATE)) {
$currentExdates = $masterInstance->EXDATE->getParts();
$masterInstance->EXDATE->setParts(array_merge($currentExdates, $excludedDates));
} else {
$masterInstance->EXDATE = $excludedDates;
}
$instances['master'] = $masterInstance;
}
}
$message->message = $icalMsg;
$messages[] = $message;
$messages[] = $this->generateMessage(
$instances, $organizerHref, $organizerName, $eventInfo['attendees'][$attendee], $objectId, $objectType, $objectSequence, 'REQUEST', $template
);
}
return $messages;
}
/**
* Generates an iTip message for a specific attendee
*
* @param array<string, Component> $instances Array of event instances to include, keyed by instance ID:
* - 'master' => Component: The master/base event
* - '{RECURRENCE-ID}' => Component: Exception instances
* @param string $organizerHref The organizer's calendar-user address (e.g., 'mailto:user@example.com')
* @param string|null $organizerName The organizer's display name
* @param array $attendee The attendee information containing:
* - 'href' (string): The attendee's calendar-user address
* - 'name' (string): The attendee's display name
* - 'scheduleAgent' (string|null): SCHEDULE-AGENT parameter
* - 'instances' (array): Instances this attendee is part of
* @param string $objectId The UID of the event
* @param string $objectType The component type ('VEVENT', 'VTODO', etc.)
* @param int $objectSequence The sequence number of the event
* @param string $method The iTip method ('REQUEST', 'CANCEL', 'REPLY', etc.)
* @param VCalendar $template The template calendar object (without event components)
* @return Message The generated iTip message ready to be sent
*/
protected function generateMessage(
array $instances,
string $organizerHref,
?string $organizerName,
array $attendee,
string $objectId,
string $objectType,
int $objectSequence,
string $method,
VCalendar $template,
): Message {
$recipientAddress = $attendee['href'] ?? '';
$recipientName = $attendee['name'] ?? '';
$vObject = clone $template;
if ($vObject->METHOD && $vObject->METHOD->getValue() !== $method) {
$vObject->METHOD->setValue($method);
} else {
$vObject->add('METHOD', $method);
}
foreach ($instances as $instance) {
$vObject->add($this->componentSanitizeScheduling(clone $instance));
}
$message = new Message();
$message->method = $method;
$message->uid = $objectId;
$message->component = $objectType;
$message->sequence = $objectSequence;
$message->sender = $organizerHref;
$message->senderName = $organizerName;
$message->recipient = $recipientAddress;
$message->recipientName = $recipientName;
$message->significantChange = true;
$message->message = $vObject;
return $message;
}
protected function componentSanitizeScheduling(Component $component): Component {
// Cleaning up any scheduling information that should not be sent or is missing
unset($component->ORGANIZER['SCHEDULE-FORCE-SEND'], $component->ORGANIZER['SCHEDULE-STATUS']);
foreach ($component->ATTENDEE as $attendee) {
unset($attendee['SCHEDULE-FORCE-SEND'], $attendee['SCHEDULE-STATUS']);
if (!isset($attendee['PARTSTAT'])) {
$attendee['PARTSTAT'] = 'NEEDS-ACTION';
}
}
// Sequence is a required property, default is 0
// https://datatracker.ietf.org/doc/html/rfc5545#section-3.8.7.4
if ($component->SEQUENCE === null) {
$component->add('SEQUENCE', 0);
}
return $component;
}
}
+2
View File
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2016 ownCloud GmbH
* SPDX-License-Identifier: AGPL-3.0-only
+2
View File
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-only
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+7 -1
View File
@@ -236,7 +236,13 @@ class File extends Node implements IFile {
// because we have no clue about the cause we can only throw back a 500/Internal Server Error
throw new Exception($this->l10n->t('Could not write file contents'));
}
[$count, $result] = Files::streamCopy($data, $target, true);
$count = stream_copy_to_stream($data, $target);
if ($count === false) {
$result = false;
$count = 0;
} else {
$result = true;
}
fclose($target);
}
if ($result === false && $expected !== null) {
@@ -166,11 +166,6 @@ class FilesPlugin extends ServerPlugin {
return;
}
// Ensure source exists
$sourceNodeFileInfo = $sourceNode->getFileInfo();
if ($sourceNodeFileInfo === null) {
throw new NotFound($source . ' does not exist');
}
// Ensure the target name is valid
try {
[$targetPath, $targetName] = \Sabre\Uri\split($target);
@@ -1,10 +1,11 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OCA\DAV\Connector\Sabre;
class MtimeSanitizer {
+85 -93
View File
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
@@ -20,42 +22,48 @@ use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;
use OCP\Files\Storage\ISharedStorage;
use OCP\Files\StorageNotAvailableException;
use OCP\IUser;
use OCP\Lock\ILockingProvider;
use OCP\Lock\LockedException;
use OCP\PreConditionNotMetException;
use OCP\Server;
use OCP\Share\Exceptions\ShareNotFound;
use OCP\Share\IManager;
use RuntimeException;
use Sabre\DAV\Exception;
use Sabre\DAV\Exception\Forbidden;
use Sabre\DAV\INode;
abstract class Node implements \Sabre\DAV\INode {
abstract class Node implements INode {
/**
* The path to the current node
*
* @var string
*/
protected $path;
protected string $path;
protected FileInfo $info;
/**
* @var IManager
*/
protected $shareManager;
protected IManager $shareManager;
protected \OCP\Files\Node $node;
/**
* Sets up the node, expects a full path name
* @throws PreConditionNotMetException
*/
public function __construct(
protected View $fileView,
FileInfo $info,
?IManager $shareManager = null,
) {
$this->path = $this->fileView->getRelativePath($info->getPath());
$this->info = $info;
if ($shareManager) {
$this->shareManager = $shareManager;
} else {
$this->shareManager = Server::get(\OCP\Share\IManager::class);
$relativePath = $this->fileView->getRelativePath($info->getPath());
if ($relativePath === null) {
throw new RuntimeException('Failed to get relative path for ' . $info->getPath());
}
$this->path = $relativePath;
$this->info = $info;
$this->shareManager = $shareManager instanceof IManager ? $shareManager : Server::get(IManager::class);
if ($info instanceof Folder || $info instanceof File) {
$this->node = $info;
} else {
@@ -70,11 +78,16 @@ abstract class Node implements \Sabre\DAV\INode {
}
}
/**
* @throws Exception
* @throws PreConditionNotMetException
*/
protected function refreshInfo(): void {
$info = $this->fileView->getFileInfo($this->path);
if ($info === false) {
throw new \Sabre\DAV\Exception('Failed to get fileinfo for ' . $this->path);
throw new Exception('Failed to get fileinfo for ' . $this->path);
}
$this->info = $info;
$root = Server::get(IRootFolder::class);
$rootView = Server::get(View::class);
@@ -87,19 +100,15 @@ abstract class Node implements \Sabre\DAV\INode {
/**
* Returns the name of the node
*
* @return string
*/
public function getName() {
public function getName(): string {
return $this->info->getName();
}
/**
* Returns the full path
*
* @return string
*/
public function getPath() {
public function getPath(): string {
return $this->path;
}
@@ -107,25 +116,30 @@ abstract class Node implements \Sabre\DAV\INode {
* Renames the node
*
* @param string $name The new name
* @throws \Sabre\DAV\Exception\BadRequest
* @throws \Sabre\DAV\Exception\Forbidden
* @throws Exception
* @throws Forbidden
* @throws InvalidPath
* @throws PreConditionNotMetException
* @throws LockedException
*/
public function setName($name) {
public function setName($name): void {
// rename is only allowed if the delete privilege is granted
// (basically rename is a copy with delete of the original node)
if (!($this->info->isDeletable() || ($this->info->getMountPoint() instanceof MoveableMount && $this->info->getInternalPath() === ''))) {
throw new \Sabre\DAV\Exception\Forbidden();
if (!$this->info->isDeletable() && !($this->info->getMountPoint() instanceof MoveableMount && $this->info->getInternalPath() === '')) {
throw new Forbidden();
}
/** @var string $parentPath */
[$parentPath,] = \Sabre\Uri\split($this->path);
/** @var string $newName */
[, $newName] = \Sabre\Uri\split($name);
$newPath = $parentPath . '/' . $newName;
// verify path of the target
$this->verifyPath($newPath);
if (!$this->fileView->rename($this->path, $newPath)) {
throw new \Sabre\DAV\Exception('Failed to rename ' . $this->path . ' to ' . $newPath);
if ($this->fileView->rename($this->path, $newPath) === false) {
throw new Exception('Failed to rename ' . $this->path . ' to ' . $newPath);
}
$this->path = $newPath;
@@ -138,12 +152,8 @@ abstract class Node implements \Sabre\DAV\INode {
*
* @return int timestamp as integer
*/
public function getLastModified() {
$timestamp = $this->info->getMtime();
if (!empty($timestamp)) {
return (int)$timestamp;
}
return $timestamp;
public function getLastModified(): int {
return $this->info->getMtime();
}
/**
@@ -151,7 +161,7 @@ abstract class Node implements \Sabre\DAV\INode {
* in the second parameter or to now if the second param is empty.
* Even if the modification time is set to a custom value the access time is set to now.
*/
public function touch($mtime) {
public function touch(string $mtime): void {
$mtime = $this->sanitizeMtime($mtime);
$this->fileView->touch($this->path, $mtime);
$this->refreshInfo();
@@ -165,37 +175,29 @@ abstract class Node implements \Sabre\DAV\INode {
* arbitrary string, but MUST be surrounded by double-quotes.
*
* Return null if the ETag can not effectively be determined
*
* @return string
*/
public function getETag() {
public function getETag(): string {
return '"' . $this->info->getEtag() . '"';
}
/**
* Sets the ETag
*
* @param string $etag
*
* @return int file id of updated file or -1 on failure
*/
public function setETag($etag) {
public function setETag(string $etag): int {
return $this->fileView->putFileInfo($this->path, ['etag' => $etag]);
}
public function setCreationTime(int $time) {
public function setCreationTime(int $time): int {
return $this->fileView->putFileInfo($this->path, ['creation_time' => $time]);
}
public function setUploadTime(int $time) {
return $this->fileView->putFileInfo($this->path, ['upload_time' => $time]);
}
/**
* Returns the size of the node, in bytes
*
* @psalm-suppress UnusedPsalmSuppress psalm:strict actually thinks there is no mismatch, idk lol
* @psalm-suppress ImplementedReturnTypeMismatch \Sabre\DAV\IFile::getSize signature does not support 32bit
* @return int|float
*/
public function getSize(): int|float {
return $this->info->getSize();
@@ -203,28 +205,21 @@ abstract class Node implements \Sabre\DAV\INode {
/**
* Returns the cache's file id
*
* @return int
*/
public function getId() {
public function getId(): ?int {
return $this->info->getId();
}
/**
* @return string|null
*/
public function getFileId() {
if ($id = $this->info->getId()) {
public function getFileId(): ?string {
$id = $this->info->getId();
if ($id !== null) {
return DavUtil::getDavFileId($id);
}
return null;
}
/**
* @return integer
*/
public function getInternalFileId() {
public function getInternalFileId(): ?int {
return $this->info->getId();
}
@@ -232,30 +227,24 @@ abstract class Node implements \Sabre\DAV\INode {
return $this->info->getInternalPath();
}
/**
* @param string $user
* @return int
*/
public function getSharePermissions($user) {
public function getSharePermissions(?string $user): int {
// check of we access a federated share
if ($user !== null) {
try {
$share = $this->shareManager->getShareByToken($user);
return $share->getPermissions();
} catch (ShareNotFound $e) {
return $this->shareManager->getShareByToken($user)->getPermissions();
} catch (ShareNotFound) {
// ignore
}
}
try {
$storage = $this->info->getStorage();
} catch (StorageNotAvailableException $e) {
} catch (StorageNotAvailableException) {
$storage = null;
}
if ($storage && $storage->instanceOfStorage(ISharedStorage::class)) {
/** @var ISharedStorage $storage */
$permissions = (int)$storage->getShare()->getPermissions();
$permissions = $storage->getShare()->getPermissions();
} else {
$permissions = $this->info->getPermissions();
}
@@ -266,6 +255,10 @@ abstract class Node implements \Sabre\DAV\INode {
*/
$mountpoint = $this->info->getMountPoint();
if (!($mountpoint instanceof MoveableMount)) {
/**
* @psalm-suppress UnnecessaryVarAnnotation Rector doesn't trust the return type annotation
* @var string $mountpointpath
*/
$mountpointpath = $mountpoint->getMountPoint();
if (str_ends_with($mountpointpath, '/')) {
$mountpointpath = substr($mountpointpath, 0, -1);
@@ -286,25 +279,21 @@ abstract class Node implements \Sabre\DAV\INode {
return $permissions;
}
/**
* @return array
*/
public function getShareAttributes(): array {
try {
$storage = $this->node->getStorage();
} catch (NotFoundException $e) {
} catch (NotFoundException) {
return [];
}
$attributes = [];
if ($storage->instanceOfStorage(ISharedStorage::class)) {
/** @var ISharedStorage $storage */
$attributes = $storage->getShare()->getAttributes();
if ($attributes === null) {
return [];
} else {
return $attributes->toArray();
}
return $attributes->toArray();
}
return $attributes;
@@ -318,63 +307,66 @@ abstract class Node implements \Sabre\DAV\INode {
}
if ($storage->instanceOfStorage(ISharedStorage::class)) {
/** @var ISharedStorage $storage */
$share = $storage->getShare();
if ($user === $share->getShareOwner()) {
// Note is only for recipient not the owner
return null;
}
return $share->getNote();
}
return null;
}
/**
* @return string
*/
public function getDavPermissions() {
public function getDavPermissions(): string {
return DavUtil::getDavPermissions($this->info);
}
public function getOwner() {
public function getOwner(): ?IUser {
return $this->info->getOwner();
}
/**
* @throws InvalidPath
*/
protected function verifyPath(?string $path = null): void {
try {
$path = $path ?? $this->info->getPath();
$path ??= $this->info->getPath();
$this->fileView->verifyPath(
dirname($path),
basename($path),
);
} catch (InvalidPathException $ex) {
throw new InvalidPath($ex->getMessage());
} catch (InvalidPathException $invalidPathException) {
throw new InvalidPath($invalidPathException->getMessage(), false, $invalidPathException);
}
}
/**
* @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
* @param ILockingProvider::LOCK_* $type
* @throws LockedException
*/
public function acquireLock($type) {
public function acquireLock($type): void {
$this->fileView->lockFile($this->path, $type);
}
/**
* @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
* @param ILockingProvider::LOCK_* $type
* @throws LockedException
*/
public function releaseLock($type) {
public function releaseLock($type): void {
$this->fileView->unlockFile($this->path, $type);
}
/**
* @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
* @param ILockingProvider::LOCK_* $type
* @throws LockedException
*/
public function changeLock($type) {
public function changeLock($type): void {
$this->fileView->changeLock($this->path, $type);
}
public function getFileInfo() {
public function getFileInfo(): FileInfo {
return $this->info;
}
+1 -5
View File
@@ -42,9 +42,6 @@ class Principal implements BackendInterface {
/** @var bool */
private $hasCircles;
/** @var KnownUserService */
private $knownUserService;
public function __construct(
private IUserManager $userManager,
private IGroupManager $groupManager,
@@ -53,14 +50,13 @@ class Principal implements BackendInterface {
private IUserSession $userSession,
private IAppManager $appManager,
private ProxyMapper $proxyMapper,
KnownUserService $knownUserService,
private KnownUserService $knownUserService,
private IConfig $config,
private IFactory $languageFactory,
string $principalPrefix = 'principals/users/',
) {
$this->principalPrefix = trim($principalPrefix, '/');
$this->hasGroups = $this->hasCircles = ($principalPrefix === 'principals/users/');
$this->knownUserService = $knownUserService;
}
use PrincipalProxyTrait {
+11 -9
View File
@@ -49,11 +49,7 @@ class TagsPlugin extends \Sabre\DAV\ServerPlugin {
* @var \Sabre\DAV\Server
*/
private $server;
/**
* @var ITags
*/
private $tagger;
private ?ITags $tagger = null;
/**
* Array of file id to tags array
@@ -105,11 +101,17 @@ class TagsPlugin extends \Sabre\DAV\ServerPlugin {
*
* @return ITags tagger
*/
private function getTagger() {
if (!$this->tagger) {
$this->tagger = $this->tagManager->load('files');
private function getTagger(): ITags {
if ($this->tagger) {
return $this->tagger;
}
return $this->tagger;
$tagger = $this->tagManager->load('files');
if ($tagger === null) {
throw new \RuntimeException('Tagger not found for files');
}
$this->tagger = $tagger;
return $tagger;
}
/**
+2
View File
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+5
View File
@@ -86,6 +86,7 @@ class FileSearchBackend implements ISearchBackend {
new SearchPropertyDefinition('{DAV:}displayname', true, true, true),
new SearchPropertyDefinition('{DAV:}getcontenttype', true, true, true),
new SearchPropertyDefinition('{DAV:}getlastmodified', true, true, true, SearchPropertyDefinition::DATATYPE_DATETIME),
new SearchPropertyDefinition('{http://nextcloud.org/ns}upload_time', true, true, true, SearchPropertyDefinition::DATATYPE_DATETIME),
new SearchPropertyDefinition(FilesPlugin::SIZE_PROPERTYNAME, true, true, true, SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER),
new SearchPropertyDefinition(TagsPlugin::FAVORITE_PROPERTYNAME, true, true, true, SearchPropertyDefinition::DATATYPE_BOOLEAN),
new SearchPropertyDefinition(FilesPlugin::INTERNAL_FILEID_PROPERTYNAME, true, true, false, SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER),
@@ -298,6 +299,8 @@ class FileSearchBackend implements ISearchBackend {
return $node->getName();
case '{DAV:}getlastmodified':
return $node->getLastModified();
case '{http://nextcloud.org/ns}upload_time':
return $node->getNode()->getUploadTime();
case FilesPlugin::SIZE_PROPERTYNAME:
return $node->getSize();
case FilesPlugin::INTERNAL_FILEID_PROPERTYNAME:
@@ -458,6 +461,8 @@ class FileSearchBackend implements ISearchBackend {
return 'mimetype';
case '{DAV:}getlastmodified':
return 'mtime';
case '{http://nextcloud.org/ns}upload_time':
return 'upload_time';
case FilesPlugin::SIZE_PROPERTYNAME:
return 'size';
case TagsPlugin::FAVORITE_PROPERTYNAME:
+2 -2
View File
@@ -32,12 +32,12 @@ class FilesHome extends Directory {
throw new Forbidden('Permission denied to delete home folder');
}
public function getName() {
public function getName(): string {
[,$name] = \Sabre\Uri\split($this->principalInfo['uri']);
return $name;
}
public function setName($name) {
public function setName($name): void {
throw new Forbidden('Permission denied to rename this folder');
}
}
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+11 -1
View File
@@ -20,6 +20,8 @@ use OCP\BackgroundJob\IJobList;
use OCP\Defaults;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\Group\Events\BeforeGroupDeletedEvent;
use OCP\Group\Events\GroupDeletedEvent;
use OCP\IUser;
use OCP\IUserManager;
use OCP\User\Events\BeforeUserDeletedEvent;
@@ -32,7 +34,7 @@ use OCP\User\Events\UserIdAssignedEvent;
use OCP\User\Events\UserIdUnassignedEvent;
use Psr\Log\LoggerInterface;
/** @template-implements IEventListener<UserFirstTimeLoggedInEvent|UserIdAssignedEvent|BeforeUserIdUnassignedEvent|UserIdUnassignedEvent|BeforeUserDeletedEvent|UserDeletedEvent|UserCreatedEvent|UserChangedEvent|UserUpdatedEvent> */
/** @template-implements IEventListener<UserFirstTimeLoggedInEvent|UserIdAssignedEvent|BeforeUserIdUnassignedEvent|UserIdUnassignedEvent|BeforeUserDeletedEvent|UserDeletedEvent|UserCreatedEvent|UserChangedEvent|UserUpdatedEvent|BeforeGroupDeletedEvent|GroupDeletedEvent> */
class UserEventsListener implements IEventListener {
/** @var IUser[] */
@@ -77,6 +79,8 @@ class UserEventsListener implements IEventListener {
$this->firstLogin($event->getUser());
} elseif ($event instanceof UserUpdatedEvent) {
$this->updateUser($event->getUser());
} elseif ($event instanceof GroupDeletedEvent) {
$this->postDeleteGroup($event->getGroup()->getGID());
}
}
@@ -135,6 +139,12 @@ class UserEventsListener implements IEventListener {
unset($this->addressBooksToDelete[$uid]);
}
public function postDeleteGroup(string $gid): void {
$encodedGid = urlencode($gid);
$this->calDav->deleteAllSharesByUser('principals/groups/' . $encodedGid);
$this->cardDav->deleteAllSharesByUser('principals/groups/' . $encodedGid);
}
public function changeUser(IUser $user, string $feature): void {
// This case is already covered by the account manager firing up a signal
// later on
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -16,27 +16,16 @@ class RemoveObjectProperties implements IRepairStep {
private const ME_CARD_PROPERTY = '{http://calendarserver.org/ns/}me-card';
private const CALENDAR_TRANSP_PROPERTY = '{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp';
/**
* RemoveObjectProperties constructor.
*
* @param IDBConnection $connection
*/
public function __construct(
private IDBConnection $connection,
private readonly IDBConnection $connection,
) {
}
/**
* @inheritdoc
*/
public function getName() {
public function getName(): string {
return 'Remove invalid object properties';
}
/**
* @inheritdoc
*/
public function run(IOutput $output) {
public function run(IOutput $output): void {
$query = $this->connection->getQueryBuilder();
$updated = $query->delete('properties')
->where($query->expr()->in('propertyname', $query->createNamedParameter([self::RESOURCE_TYPE_PROPERTY, self::ME_CARD_PROPERTY, self::CALENDAR_TRANSP_PROPERTY], IQueryBuilder::PARAM_STR_ARRAY)))
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -17,21 +17,17 @@ use OCP\DB\Types;
use OCP\IDBConnection;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;
use Override;
class Version1025Date20240308063933 extends SimpleMigrationStep {
public function __construct(
private IAppConfig $appConfig,
private IDBConnection $db,
private readonly IAppConfig $appConfig,
private readonly IDBConnection $db,
) {
}
/**
* @param IOutput $output
* @param Closure(): ISchemaWrapper $schemaClosure
* @param array $options
* @return null|ISchemaWrapper
*/
#[Override]
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
@@ -50,6 +46,7 @@ class Version1025Date20240308063933 extends SimpleMigrationStep {
return $schema;
}
#[Override]
public function postSchemaChange(IOutput $output, \Closure $schemaClosure, array $options): void {
// The threshold is higher than the default of \OCA\DAV\BackgroundJob\PruneOutdatedSyncTokensJob
// but small enough to fit into a cluster transaction size.
@@ -11,11 +11,11 @@ namespace OCA\DAV\Migration;
use Closure;
use OCA\DAV\CardDAV\SyncService;
use OCP\DB\ISchemaWrapper;
use OCP\IConfig;
use OCP\IUserManager;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;
use Override;
use Psr\Log\LoggerInterface;
use Throwable;
@@ -27,11 +27,8 @@ class Version1027Date20230504122946 extends SimpleMigrationStep {
private IConfig $config,
) {
}
/**
* @param IOutput $output
* @param Closure(): ISchemaWrapper $schemaClosure
* @param array $options
*/
#[Override]
public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
if ($this->userManager->countSeenUsers() > 100 || $this->userManager->countUsersTotal(100) >= 100) {
$this->config->setAppValue('dav', 'needs_system_address_book_sync', 'yes');
@@ -12,16 +12,14 @@ namespace OCA\DAV\Migration;
use Closure;
use OCP\DB\ISchemaWrapper;
use OCP\DB\Types;
use OCP\Migration\Attributes\CreateTable;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;
use Override;
#[CreateTable(table: 'dav_absence')]
class Version1029Date20231004091403 extends SimpleMigrationStep {
/**
* @param IOutput $output
* @param Closure(): ISchemaWrapper $schemaClosure
* @param array $options
* @return null|ISchemaWrapper
*/
#[Override]
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
@@ -11,17 +11,14 @@ namespace OCA\DAV\Migration;
use Closure;
use OCP\DB\ISchemaWrapper;
use OCP\Migration\Attributes\DropIndex;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;
use Override;
#[DropIndex(table: 'cards')]
class Version1030Date20240205103243 extends SimpleMigrationStep {
/**
* @param IOutput $output
* @param Closure(): ISchemaWrapper $schemaClosure
* @param array $options
* @return null|ISchemaWrapper
*/
#[Override]
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
@@ -17,16 +17,12 @@ use OCP\Migration\Attributes\ColumnType;
use OCP\Migration\Attributes\CreateTable;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;
use Override;
#[AddColumn(table: 'dav_shares', name: 'token', type: ColumnType::STRING)]
#[CreateTable(table: 'calendars_federated', columns: ['id', 'display_name', 'color', 'uri', 'principaluri', 'remote_Url', 'token', 'sync_token', 'last_sync', 'shared_by', 'shared_by_display_name', 'components', 'permissions'], description: 'Supporting Federated Calender')]
class Version1034Date20250605132605 extends SimpleMigrationStep {
/**
* @param IOutput $output
* @param Closure(): ISchemaWrapper $schemaClosure
* @param array $options
* @return null|ISchemaWrapper
*/
#[Override]
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
@@ -10,7 +10,6 @@ declare(strict_types=1);
namespace OCA\DAV\Migration;
use Closure;
use OCP\DB\ISchemaWrapper;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use OCP\Migration\Attributes\DataCleansing;
@@ -25,11 +24,6 @@ class Version1034Date20250813093701 extends SimpleMigrationStep {
) {
}
/**
* @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();
@@ -21,9 +21,6 @@ use Override;
#[ModifyColumn(table: 'calendar_reminders', name: 'uid', type: ColumnType::STRING, description: 'Increase uid length to 512 characters')]
#[ModifyColumn(table: 'calendar_invitations', name: 'uid', type: ColumnType::STRING, description: 'Increase uid length to 512 characters')]
class Version1036Date20251202000000 extends SimpleMigrationStep {
/**
* @param Closure(): ISchemaWrapper $schemaClosure
*/
#[Override]
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
/** @var ISchemaWrapper $schema */
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -22,23 +24,17 @@ class AppleProvisioningPlugin extends ServerPlugin {
*/
protected $server;
/**
* @var \OC_Defaults
*/
protected $themingDefaults;
/**
* AppleProvisioningPlugin constructor.
*/
public function __construct(
protected IUserSession $userSession,
protected IURLGenerator $urlGenerator,
\OC_Defaults $themingDefaults,
protected \OC_Defaults $themingDefaults,
protected IRequest $request,
protected IL10N $l10n,
protected \Closure $uuidClosure,
) {
$this->themingDefaults = $themingDefaults;
}
/**
+2
View File
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-only
@@ -22,18 +22,15 @@ use Sabre\DAV\Exception\NotFound;
use Sabre\DAV\SimpleCollection;
class SystemTagsInUseCollection extends SimpleCollection {
protected SystemTagsInFilesDetector $systemTagsInFilesDetector;
/** @noinspection PhpMissingParentConstructorInspection */
public function __construct(
protected IUserSession $userSession,
protected IRootFolder $rootFolder,
protected ISystemTagManager $systemTagManager,
protected ISystemTagObjectMapper $tagMapper,
SystemTagsInFilesDetector $systemTagsInFilesDetector,
protected SystemTagsInFilesDetector $systemTagsInFilesDetector,
protected string $mediaType = '',
) {
$this->systemTagsInFilesDetector = $systemTagsInFilesDetector;
$this->name = 'systemtags-assigned';
if ($this->mediaType != '') {
$this->name .= '/' . $this->mediaType;
+524 -122
View File
@@ -15,11 +15,19 @@ class TipBrokerTest extends TestCase {
private TipBroker $broker;
private VCalendar $vCalendar1a;
private VCalendar $vCalendar2a;
private array $templateEventInfo;
protected function setUp(): void {
parent::setUp();
$this->broker = new TipBroker();
$this->templateEventInfo = [
'organizer' => null,
'attendees' => [],
'significantChangeHash' => '',
];
// construct calendar with a 1 hour event and same start/end time zones
$this->vCalendar1a = new VCalendar();
/** @var VEvent $vEvent */
@@ -28,7 +36,7 @@ class TipBrokerTest extends TestCase {
$vEvent->add('DTSTAMP', '20240701T000000Z');
$vEvent->add('CREATED', '20240701T000000Z');
$vEvent->add('LAST-MODIFIED', '20240701T000000Z');
$vEvent->add('SEQUENCE', '1');
$vEvent->add('SEQUENCE', 1);
$vEvent->add('STATUS', 'CONFIRMED');
$vEvent->add('DTSTART', '20240701T080000', ['TZID' => 'America/Toronto']);
$vEvent->add('DTEND', '20240701T090000', ['TZID' => 'America/Toronto']);
@@ -41,140 +49,534 @@ class TipBrokerTest extends TestCase {
'ROLE' => 'REQ-PARTICIPANT',
'RSVP' => 'TRUE'
]);
}
public function testParseEventForOrganizerOnCreate(): void {
// construct calendar and generate event info for newly created event with one attendee
$calendar = clone $this->vCalendar1a;
$previousEventInfo = [
'organizer' => null,
'significantChangeHash' => '',
'attendees' => [],
];
$currentEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$calendar]);
// test iTip generation
$messages = $this->invokePrivate($this->broker, 'parseEventForOrganizer', [$calendar, $currentEventInfo, $previousEventInfo]);
$this->assertCount(1, $messages);
$this->assertEquals('REQUEST', $messages[0]->method);
$this->assertEquals($calendar->VEVENT->ORGANIZER->getValue(), $messages[0]->sender);
$this->assertEquals($calendar->VEVENT->ATTENDEE[0]->getValue(), $messages[0]->recipient);
}
public function testParseEventForOrganizerOnModify(): void {
// construct calendar and generate event info for modified event with one attendee
$calendar = clone $this->vCalendar1a;
$previousEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$calendar]);
$calendar->VEVENT->{'LAST-MODIFIED'}->setValue('20240701T020000Z');
$calendar->VEVENT->SEQUENCE->setValue(2);
$calendar->VEVENT->SUMMARY->setValue('Test Event Modified');
$currentEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$calendar]);
// test iTip generation
$messages = $this->invokePrivate($this->broker, 'parseEventForOrganizer', [$calendar, $currentEventInfo, $previousEventInfo]);
$this->assertCount(1, $messages);
$this->assertEquals('REQUEST', $messages[0]->method);
$this->assertEquals($calendar->VEVENT->ORGANIZER->getValue(), $messages[0]->sender);
$this->assertEquals($calendar->VEVENT->ATTENDEE[0]->getValue(), $messages[0]->recipient);
}
public function testParseEventForOrganizerOnDelete(): void {
// construct calendar and generate event info for modified event with one attendee
$calendar = clone $this->vCalendar1a;
$previousEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$calendar]);
$currentEventInfo = $previousEventInfo;
$currentEventInfo['attendees'] = [];
++$currentEventInfo['sequence'];
// test iTip generation
$messages = $this->invokePrivate($this->broker, 'parseEventForOrganizer', [$calendar, $currentEventInfo, $previousEventInfo]);
$this->assertCount(1, $messages);
$this->assertEquals('CANCEL', $messages[0]->method);
$this->assertEquals($calendar->VEVENT->ORGANIZER->getValue(), $messages[0]->sender);
$this->assertEquals($calendar->VEVENT->ATTENDEE[0]->getValue(), $messages[0]->recipient);
}
public function testParseEventForOrganizerOnStatusCancelled(): void {
// construct calendar and generate event info for modified event with one attendee
$calendar = clone $this->vCalendar1a;
$previousEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$calendar]);
$calendar->VEVENT->{'LAST-MODIFIED'}->setValue('20240701T020000Z');
$calendar->VEVENT->SEQUENCE->setValue(2);
$calendar->VEVENT->STATUS->setValue('CANCELLED');
$currentEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$calendar]);
// test iTip generation
$messages = $this->invokePrivate($this->broker, 'parseEventForOrganizer', [$calendar, $currentEventInfo, $previousEventInfo]);
$this->assertCount(1, $messages);
$this->assertEquals('CANCEL', $messages[0]->method);
$this->assertEquals($calendar->VEVENT->ORGANIZER->getValue(), $messages[0]->sender);
$this->assertEquals($calendar->VEVENT->ATTENDEE[0]->getValue(), $messages[0]->recipient);
}
public function testParseEventForOrganizerOnAddAttendee(): void {
// construct calendar and generate event info for modified event with two attendees
$calendar = clone $this->vCalendar1a;
$previousEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$calendar]);
$calendar->VEVENT->{'LAST-MODIFIED'}->setValue('20240701T020000Z');
$calendar->VEVENT->SEQUENCE->setValue(2);
$calendar->VEVENT->add('ATTENDEE', 'mailto:attendee2@testing.com', [
'CN' => 'Attendee Two',
'CUTYPE' => 'INDIVIDUAL',
'PARTSTAT' => 'NEEDS-ACTION',
'ROLE' => 'REQ-PARTICIPANT',
'RSVP' => 'TRUE'
]);
$currentEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$calendar]);
// test iTip generation
$messages = $this->invokePrivate($this->broker, 'parseEventForOrganizer', [$calendar, $currentEventInfo, $previousEventInfo]);
$this->assertCount(2, $messages);
$this->assertEquals('REQUEST', $messages[0]->method);
$this->assertEquals($calendar->VEVENT->ORGANIZER->getValue(), $messages[0]->sender);
$this->assertEquals($calendar->VEVENT->ATTENDEE[0]->getValue(), $messages[0]->recipient);
$this->assertEquals('REQUEST', $messages[1]->method);
$this->assertEquals($calendar->VEVENT->ORGANIZER->getValue(), $messages[1]->sender);
$this->assertEquals($calendar->VEVENT->ATTENDEE[1]->getValue(), $messages[1]->recipient);
}
public function testParseEventForOrganizerOnRemoveAttendee(): void {
// construct calendar and generate event info for modified event with two attendees
$calendar = clone $this->vCalendar1a;
$calendar->VEVENT->add('ATTENDEE', 'mailto:attendee2@testing.com', [
'CN' => 'Attendee Two',
'CUTYPE' => 'INDIVIDUAL',
'PARTSTAT' => 'NEEDS-ACTION',
'ROLE' => 'REQ-PARTICIPANT',
'RSVP' => 'TRUE'
]);
$previousEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$calendar]);
$calendar->VEVENT->{'LAST-MODIFIED'}->setValue('20240701T020000Z');
$calendar->VEVENT->SEQUENCE->setValue(2);
$calendar->VEVENT->remove('ATTENDEE');
$calendar->VEVENT->add('ATTENDEE', 'mailto:attendee1@testing.com', [
// construct calendar with a 1 hour event and same start/end time zones
// recurring every week on Monday for 12 weeks
$this->vCalendar2a = new VCalendar();
/** @var VEvent $vEvent */
$vEvent = $this->vCalendar2a->add('VEVENT', []);
$vEvent->add('UID', '96a0e6b1-d886-4a55-a60d-152b31401dcc');
$vEvent->add('DTSTAMP', '20240701T000000Z');
$vEvent->add('CREATED', '20240701T000000Z');
$vEvent->add('LAST-MODIFIED', '20240701T000000Z');
$vEvent->add('SEQUENCE', 1);
$vEvent->add('STATUS', 'CONFIRMED');
$vEvent->add('DTSTART', '20240701T080000', ['TZID' => 'America/Toronto']);
$vEvent->add('DTEND', '20240701T090000', ['TZID' => 'America/Toronto']);
$vEvent->add('RRULE', 'FREQ=WEEKLY;COUNT=12;BYDAY=MO');
$vEvent->add('SUMMARY', 'Test Event');
$vEvent->add('ORGANIZER', 'mailto:organizer@testing.com', ['CN' => 'Organizer']);
$vEvent->add('ATTENDEE', 'mailto:attendee1@testing.com', [
'CN' => 'Attendee One',
'CUTYPE' => 'INDIVIDUAL',
'PARTSTAT' => 'NEEDS-ACTION',
'ROLE' => 'REQ-PARTICIPANT',
'RSVP' => 'TRUE'
]);
$currentEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$calendar]);
}
/**
* Tests user creating a new singleton or recurring event
*/
public function testParseEventForOrganizerCreated(): void {
// construct calendar and generate event info for newly created event with one attendee
$mutatedCalendar = clone $this->vCalendar1a;
$originalEventInfo = $this->templateEventInfo;
$mutatedEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$mutatedCalendar]);
// test iTip generation
$messages = $this->invokePrivate($this->broker, 'parseEventForOrganizer', [$calendar, $currentEventInfo, $previousEventInfo]);
$messages = $this->invokePrivate($this->broker, 'parseEventForOrganizer', [$mutatedCalendar, $mutatedEventInfo, $originalEventInfo]);
$this->assertCount(1, $messages);
$this->assertEquals('REQUEST', $messages[0]->method);
$this->assertEquals($mutatedCalendar->VEVENT->ORGANIZER->getValue(), $messages[0]->sender);
$this->assertEquals($mutatedCalendar->VEVENT->ATTENDEE[0]->getValue(), $messages[0]->recipient);
}
/**
* Tests user modifying an existing singleton or recurring (base) event
*/
public function testParseEventForOrganizerModified(): void {
// construct calendar and generate event info for modified event with one attendee
$originalCalendar = clone $this->vCalendar1a;
$originalEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$originalCalendar]);
$mutatedCalendar = clone $this->vCalendar1a;
$mutatedCalendar->VEVENT->{'LAST-MODIFIED'}->setValue('20240701T020000Z');
$mutatedCalendar->VEVENT->SEQUENCE->setValue(2);
$mutatedCalendar->VEVENT->SUMMARY->setValue('Test Event Modified');
$mutatedEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$mutatedCalendar]);
// test iTip generation
$messages = $this->invokePrivate($this->broker, 'parseEventForOrganizer', [$mutatedCalendar, $mutatedEventInfo, $originalEventInfo]);
$this->assertCount(1, $messages);
$this->assertEquals('REQUEST', $messages[0]->method);
$this->assertEquals($mutatedCalendar->VEVENT->ORGANIZER->getValue(), $messages[0]->sender);
$this->assertEquals($mutatedCalendar->VEVENT->ATTENDEE[0]->getValue(), $messages[0]->recipient);
}
/**
* Tests user deleting an existing singleton or recurring (base) event
*/
public function testParseEventForOrganizerDeleted(): void {
// construct calendar and generate event info for modified event with one attendee
$originalCalendar = clone $this->vCalendar1a;
$originalEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$originalCalendar]);
$mutatedEventInfo = $originalEventInfo;
$mutatedEventInfo['attendees'] = [];
++$mutatedEventInfo['sequence'];
// test iTip generation
$messages = $this->invokePrivate($this->broker, 'parseEventForOrganizer', [$originalCalendar, $mutatedEventInfo, $originalEventInfo]);
$this->assertCount(1, $messages);
$this->assertEquals('CANCEL', $messages[0]->method);
$this->assertEquals($originalCalendar->VEVENT->ORGANIZER->getValue(), $messages[0]->sender);
$this->assertEquals($originalCalendar->VEVENT->ATTENDEE[0]->getValue(), $messages[0]->recipient);
}
/**
* Tests user cancelling an existing singleton or recurring (base) event
*/
public function testParseEventForOrganizerStatusCancelled(): void {
// construct calendar and generate event info for modified event with one attendee
$originalCalendar = clone $this->vCalendar1a;
$originalEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$originalCalendar]);
$mutatedCalendar = clone $this->vCalendar1a;
$mutatedCalendar->VEVENT->{'LAST-MODIFIED'}->setValue('20240701T020000Z');
$mutatedCalendar->VEVENT->SEQUENCE->setValue(2);
$mutatedCalendar->VEVENT->STATUS->setValue('CANCELLED');
$mutatedEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$mutatedCalendar]);
// test iTip generation
$messages = $this->invokePrivate($this->broker, 'parseEventForOrganizer', [$mutatedCalendar, $mutatedEventInfo, $originalEventInfo]);
$this->assertCount(1, $messages);
$this->assertEquals('CANCEL', $messages[0]->method);
$this->assertEquals($mutatedCalendar->VEVENT->ORGANIZER->getValue(), $messages[0]->sender);
$this->assertEquals($mutatedCalendar->VEVENT->ATTENDEE[0]->getValue(), $messages[0]->recipient);
}
/**
* Tests user adding an attendee to an existing singleton or recurring (base) event
*/
public function testParseEventForOrganizerAddAttendee(): void {
// construct calendar and generate event info for modified event with two attendees
$originalCalendar = clone $this->vCalendar1a;
$originalEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$originalCalendar]);
$mutatedCalendar = clone $this->vCalendar1a;
$mutatedCalendar->VEVENT->{'LAST-MODIFIED'}->setValue('20240701T020000Z');
$mutatedCalendar->VEVENT->SEQUENCE->setValue(2);
$mutatedCalendar->VEVENT->add('ATTENDEE', 'mailto:attendee2@testing.com', [
'CN' => 'Attendee Two',
'CUTYPE' => 'INDIVIDUAL',
'PARTSTAT' => 'NEEDS-ACTION',
'ROLE' => 'REQ-PARTICIPANT',
'RSVP' => 'TRUE'
]);
$mutatedEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$mutatedCalendar]);
// test iTip generation
$messages = $this->invokePrivate($this->broker, 'parseEventForOrganizer', [$mutatedCalendar, $mutatedEventInfo, $originalEventInfo]);
// attendee modifications get generated in order of Added, Removed, Existing
$this->assertCount(2, $messages);
$this->assertEquals('REQUEST', $messages[0]->method);
$this->assertEquals($calendar->VEVENT->ORGANIZER->getValue(), $messages[0]->sender);
$this->assertEquals($calendar->VEVENT->ATTENDEE[0]->getValue(), $messages[0]->recipient);
$this->assertEquals($mutatedCalendar->VEVENT->ORGANIZER->getValue(), $messages[0]->sender);
$this->assertEquals($mutatedCalendar->VEVENT->ATTENDEE[0]->getValue(), $messages[0]->recipient);
$this->assertEquals('REQUEST', $messages[1]->method);
$this->assertEquals($mutatedCalendar->VEVENT->ORGANIZER->getValue(), $messages[1]->sender);
$this->assertEquals($mutatedCalendar->VEVENT->ATTENDEE[1]->getValue(), $messages[1]->recipient);
}
/**
* Tests user removing an attendee from an existing singleton or recurring (base) event
*/
public function testParseEventForOrganizerRemoveAttendee(): void {
// construct calendar and generate event info for modified event with two attendees
$originalCalendar = clone $this->vCalendar1a;
$originalCalendar->VEVENT->add('ATTENDEE', 'mailto:attendee2@testing.com', [
'CN' => 'Attendee Two',
'CUTYPE' => 'INDIVIDUAL',
'PARTSTAT' => 'NEEDS-ACTION',
'ROLE' => 'REQ-PARTICIPANT',
'RSVP' => 'TRUE'
]);
$originalEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$originalCalendar]);
$mutatedCalendar = clone $this->vCalendar1a;
$mutatedCalendar->VEVENT->{'LAST-MODIFIED'}->setValue('20240701T020000Z');
$mutatedCalendar->VEVENT->SEQUENCE->setValue(2);
$mutatedCalendar->VEVENT->remove('ATTENDEE');
$mutatedCalendar->VEVENT->add('ATTENDEE', 'mailto:attendee1@testing.com', [
'CN' => 'Attendee One',
'CUTYPE' => 'INDIVIDUAL',
'PARTSTAT' => 'NEEDS-ACTION',
'ROLE' => 'REQ-PARTICIPANT',
'RSVP' => 'TRUE'
]);
$mutatedEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$mutatedCalendar]);
// test iTip generation
$messages = $this->invokePrivate($this->broker, 'parseEventForOrganizer', [$mutatedCalendar, $mutatedEventInfo, $originalEventInfo]);
// attendee modifications get generated in order of Added, Removed, Existing
$this->assertCount(2, $messages);
$this->assertEquals('REQUEST', $messages[0]->method);
$this->assertEquals($mutatedCalendar->VEVENT->ORGANIZER->getValue(), $messages[0]->sender);
$this->assertEquals($mutatedCalendar->VEVENT->ATTENDEE[0]->getValue(), $messages[0]->recipient);
$this->assertEquals('CANCEL', $messages[1]->method);
$this->assertEquals($calendar->VEVENT->ORGANIZER->getValue(), $messages[1]->sender);
$this->assertEquals($mutatedCalendar->VEVENT->ORGANIZER->getValue(), $messages[1]->sender);
$this->assertEquals('mailto:attendee2@testing.com', $messages[1]->recipient);
}
/**
* Tests user converts existing singleton or recurring (base) event from attended to attendeless
*/
public function testParseEventForOrganizerRemoveOrganizerAndAttendees(): void {
// construct calendar and generate event info for modified event with two attendees
$originalCalendar = clone $this->vCalendar1a;
$originalEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$originalCalendar]);
$mutatedCalendar = clone $this->vCalendar1a;
$mutatedCalendar->VEVENT->{'LAST-MODIFIED'}->setValue('20240701T020000Z');
$mutatedCalendar->VEVENT->SEQUENCE->setValue(2);
$mutatedCalendar->VEVENT->remove('ORGANIZER');
$mutatedCalendar->VEVENT->remove('ATTENDEE');
$mutatedEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$mutatedCalendar]);
// test iTip generation
$messages = $this->invokePrivate($this->broker, 'parseEventForOrganizer', [$mutatedCalendar, $mutatedEventInfo, $originalEventInfo]);
// attendee modifications get generated in order of Added, Removed, Existing
$this->assertCount(1, $messages);
$this->assertEquals('CANCEL', $messages[0]->method);
$this->assertEquals(2, $messages[0]->sequence);
$this->assertEquals($originalCalendar->VEVENT->ORGANIZER->getValue(), $messages[0]->sender);
$this->assertEquals($originalCalendar->VEVENT->ATTENDEE[0]->getValue(), $messages[0]->recipient);
}
/**
* Tests user modifying recurring (base) event by moving instance to a new date
*/
public function testParseEventForOrganizerCreatedInstance(): void {
// construct calendar and generate event info for modified event with two attendees
$originalCalendar = clone $this->vCalendar2a;
$originalEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$originalCalendar]);
$mutatedInstance = clone $originalCalendar->VEVENT;
$mutatedInstance->add('RECURRENCE-ID', '20240715T080000', ['TZID' => 'America/Toronto']);
$mutatedInstance->SEQUENCE->setValue(0);
$mutatedInstance->DTSTART->setValue('20240717T080000');
$mutatedInstance->DTEND->setValue('20240717T090000');
$mutatedCalendar = clone $this->vCalendar2a;
$mutatedCalendar->add($mutatedInstance);
$mutatedEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$mutatedCalendar]);
// test iTip generation
$messages = $this->invokePrivate($this->broker, 'parseEventForOrganizer', [$mutatedCalendar, $mutatedEventInfo, $originalEventInfo]);
// attendee modifications get generated in order of Added, Removed, Existing
$this->assertCount(1, $messages);
$this->assertEquals('REQUEST', $messages[0]->method);
$this->assertEquals(1, $messages[0]->sequence);
$this->assertEquals($mutatedCalendar->VEVENT->ORGANIZER->getValue(), $messages[0]->sender);
$this->assertEquals($mutatedCalendar->VEVENT->ATTENDEE[0]->getValue(), $messages[0]->recipient);
$this->assertCount(2, $messages[0]->message->VEVENT);
$this->assertEquals('20240715T080000', $messages[0]->message->VEVENT[1]->{'RECURRENCE-ID'}->getValue());
}
/**
* Tests user modifying recurring (base) event by cancelling a single instance
*/
public function testParseEventForOrganizerCreatedInstanceCancelled(): void {
// construct calendar and generate event info for modified event with two attendees
$originalCalendar = clone $this->vCalendar2a;
$originalEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$originalCalendar]);
$mutatedInstance = clone $originalCalendar->VEVENT;
$mutatedInstance->add('RECURRENCE-ID', '20240715T080000', ['TZID' => 'America/Toronto']);
$mutatedInstance->SEQUENCE->setValue(0);
$mutatedInstance->STATUS->setValue('CANCELLED');
$mutatedCalendar = clone $this->vCalendar2a;
$mutatedCalendar->add($mutatedInstance);
$mutatedEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$mutatedCalendar]);
// test iTip generation
$messages = $this->invokePrivate($this->broker, 'parseEventForOrganizer', [$mutatedCalendar, $mutatedEventInfo, $originalEventInfo]);
// attendee modifications get generated in order of Added, Removed, Existing
$this->assertCount(2, $messages);
$this->assertEquals('CANCEL', $messages[0]->method);
$this->assertEquals(1, $messages[0]->sequence);
$this->assertEquals($mutatedCalendar->VEVENT[1]->ORGANIZER->getValue(), $messages[0]->sender);
$this->assertEquals($mutatedCalendar->VEVENT[1]->ATTENDEE[0]->getValue(), $messages[0]->recipient);
$this->assertCount(1, $messages[0]->message->VEVENT);
$this->assertEquals('20240715T080000', $messages[0]->message->VEVENT->{'RECURRENCE-ID'}->getValue());
}
/**
* Tests user modifying recurring (instance) event with non status or attendee changes
*/
public function testParseEventForOrganizerModifyInstance(): void {
// construct calendar and generate event info for modified event with two attendees
$originalCalendar = clone $this->vCalendar2a;
$originalInstance = clone $originalCalendar->VEVENT;
$originalInstance->add('RECURRENCE-ID', '20240715T080000', ['TZID' => 'America/Toronto']);
$originalInstance->SEQUENCE->setValue(1);
$originalInstance->DTSTART->setValue('20240717T080000');
$originalInstance->DTEND->setValue('20240717T090000');
$originalCalendar->add($originalInstance);
$originalEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$originalCalendar]);
$mutatedInstance = clone $originalInstance;
$mutatedInstance->SEQUENCE->setValue(2);
$mutatedInstance->DTSTART->setValue('20240718T080000');
$mutatedInstance->DTEND->setValue('20240718T090000');
$mutatedCalendar = clone $this->vCalendar2a;
$mutatedCalendar->add($mutatedInstance);
$mutatedEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$mutatedCalendar]);
// test iTip generation
$messages = $this->invokePrivate($this->broker, 'parseEventForOrganizer', [$mutatedCalendar, $mutatedEventInfo, $originalEventInfo]);
// attendee modifications get generated in order of Added, Removed, Existing
$this->assertCount(1, $messages);
$this->assertEquals('REQUEST', $messages[0]->method);
$this->assertEquals(1, $messages[0]->sequence);
$this->assertEquals($mutatedCalendar->VEVENT[1]->ORGANIZER->getValue(), $messages[0]->sender);
$this->assertEquals($mutatedCalendar->VEVENT[1]->ATTENDEE[0]->getValue(), $messages[0]->recipient);
$this->assertCount(2, $messages[0]->message->VEVENT);
$this->assertEquals('20240715T080000', $messages[0]->message->VEVENT[1]->{'RECURRENCE-ID'}->getValue());
}
/**
* Tests user modifying recurring (instance) event by setting status to cancelled
*/
public function testParseEventForOrganizerModifyInstanceStatus(): void {
// construct calendar and generate event info for modified event with two attendees
$originalCalendar = clone $this->vCalendar2a;
$originalInstance = clone $originalCalendar->VEVENT;
$originalInstance->add('RECURRENCE-ID', '20240715T080000', ['TZID' => 'America/Toronto']);
$originalInstance->SEQUENCE->setValue(1);
$originalInstance->DTSTART->setValue('20240717T080000');
$originalInstance->DTEND->setValue('20240717T090000');
$originalCalendar->add($originalInstance);
$originalEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$originalCalendar]);
$mutatedInstance = clone $originalInstance;
$mutatedInstance->SEQUENCE->setValue(2);
$mutatedInstance->STATUS->setValue('CANCELLED');
$mutatedCalendar = clone $this->vCalendar2a;
$mutatedCalendar->add($mutatedInstance);
$mutatedEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$mutatedCalendar]);
// test iTip generation
$messages = $this->invokePrivate($this->broker, 'parseEventForOrganizer', [$mutatedCalendar, $mutatedEventInfo, $originalEventInfo]);
// attendee modifications get generated in order of Added, Removed, Existing
$this->assertCount(1, $messages);
$this->assertEquals('REQUEST', $messages[0]->method);
$this->assertEquals(1, $messages[0]->sequence);
$this->assertEquals($mutatedCalendar->VEVENT[1]->ORGANIZER->getValue(), $messages[0]->sender);
$this->assertEquals($mutatedCalendar->VEVENT[1]->ATTENDEE[0]->getValue(), $messages[0]->recipient);
$this->assertCount(2, $messages[0]->message->VEVENT);
$this->assertEquals('20240715T080000', $messages[0]->message->VEVENT[1]->{'RECURRENCE-ID'}->getValue());
}
/**
* Tests user modifying recurring (instance) event by adding attendee
*/
public function testParseEventForOrganizerModifyInstanceAddAttendee(): void {
// construct calendar and generate event info for modified event with two attendees
$originalCalendar = clone $this->vCalendar2a;
$originalInstance = clone $originalCalendar->VEVENT;
$originalInstance->add('RECURRENCE-ID', '20240715T080000', ['TZID' => 'America/Toronto']);
$originalInstance->SEQUENCE->setValue(1);
$originalInstance->DTSTART->setValue('20240717T080000');
$originalInstance->DTEND->setValue('20240717T090000');
$originalCalendar->add($originalInstance);
$originalEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$originalCalendar]);
$mutatedInstance = clone $originalInstance;
$mutatedInstance->SEQUENCE->setValue(2);
$mutatedInstance->add('ATTENDEE', 'mailto:attendee2@testing.com', [
'CN' => 'Attendee Two',
'CUTYPE' => 'INDIVIDUAL',
'PARTSTAT' => 'NEEDS-ACTION',
'ROLE' => 'REQ-PARTICIPANT',
'RSVP' => 'TRUE'
]);
$mutatedCalendar = clone $this->vCalendar2a;
$mutatedCalendar->add($mutatedInstance);
$mutatedEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$mutatedCalendar]);
// test iTip generation
$messages = $this->invokePrivate($this->broker, 'parseEventForOrganizer', [$mutatedCalendar, $mutatedEventInfo, $originalEventInfo]);
// attendee modifications get generated in order of Added, Removed, Existing
$this->assertCount(2, $messages);
$this->assertEquals('REQUEST', $messages[0]->method);
$this->assertEquals(1, $messages[0]->sequence);
$this->assertEquals($mutatedCalendar->VEVENT[1]->ORGANIZER->getValue(), $messages[0]->sender);
$this->assertEquals($mutatedCalendar->VEVENT[1]->ATTENDEE[0]->getValue(), $messages[0]->recipient);
$this->assertCount(2, $messages[0]->message->VEVENT);
$this->assertEquals('20240715T080000', $messages[0]->message->VEVENT[1]->{'RECURRENCE-ID'}->getValue());
$this->assertEquals('REQUEST', $messages[1]->method);
$this->assertEquals(1, $messages[1]->sequence);
$this->assertEquals($mutatedCalendar->VEVENT[1]->ORGANIZER->getValue(), $messages[1]->sender);
$this->assertEquals($mutatedCalendar->VEVENT[1]->ATTENDEE[1]->getValue(), $messages[1]->recipient);
$this->assertCount(1, $messages[1]->message->VEVENT);
$this->assertEquals('20240715T080000', $messages[1]->message->VEVENT->{'RECURRENCE-ID'}->getValue());
}
/**
* Tests user modifying recurring (instance) event by removing attendee
*/
public function testParseEventForOrganizerModifyInstanceRemoveAttendee(): void {
// construct calendar and generate event info for modified event with two attendees
$originalCalendar = clone $this->vCalendar2a;
$originalInstance = clone $originalCalendar->VEVENT;
$originalInstance->add('RECURRENCE-ID', '20240715T080000', ['TZID' => 'America/Toronto']);
$originalInstance->SEQUENCE->setValue(1);
$originalInstance->DTSTART->setValue('20240717T080000');
$originalInstance->DTEND->setValue('20240717T090000');
$originalInstance->add('ATTENDEE', 'mailto:attendee2@testing.com', [
'CN' => 'Attendee Two',
'CUTYPE' => 'INDIVIDUAL',
'PARTSTAT' => 'NEEDS-ACTION',
'ROLE' => 'REQ-PARTICIPANT',
'RSVP' => 'TRUE'
]);
$originalCalendar->add($originalInstance);
$originalEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$originalCalendar]);
$mutatedInstance = clone $originalInstance;
$mutatedInstance->SEQUENCE->setValue(2);
$mutatedInstance->remove('ATTENDEE');
$mutatedInstance->add('ATTENDEE', 'mailto:attendee1@testing.com', [
'CN' => 'Attendee One',
'CUTYPE' => 'INDIVIDUAL',
'PARTSTAT' => 'NEEDS-ACTION',
'ROLE' => 'REQ-PARTICIPANT',
'RSVP' => 'TRUE'
]);
$mutatedCalendar = clone $this->vCalendar2a;
$mutatedCalendar->add($mutatedInstance);
$mutatedEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$mutatedCalendar]);
// test iTip generation
$messages = $this->invokePrivate($this->broker, 'parseEventForOrganizer', [$mutatedCalendar, $mutatedEventInfo, $originalEventInfo]);
// attendee modifications get generated in order of Added, Removed, Existing
$this->assertCount(2, $messages);
$this->assertEquals('REQUEST', $messages[0]->method);
$this->assertEquals(1, $messages[0]->sequence);
$this->assertEquals($originalCalendar->VEVENT[1]->ORGANIZER->getValue(), $messages[0]->sender);
$this->assertEquals($originalCalendar->VEVENT[1]->ATTENDEE[0]->getValue(), $messages[0]->recipient);
$this->assertCount(2, $messages[0]->message->VEVENT);
$this->assertEquals('20240715T080000', $messages[0]->message->VEVENT[1]->{'RECURRENCE-ID'}->getValue());
$this->assertEquals('CANCEL', $messages[1]->method);
$this->assertEquals(1, $messages[1]->sequence);
$this->assertEquals($originalCalendar->VEVENT[1]->ORGANIZER->getValue(), $messages[1]->sender);
$this->assertEquals($originalCalendar->VEVENT[1]->ATTENDEE[1]->getValue(), $messages[1]->recipient);
$this->assertCount(1, $messages[1]->message->VEVENT);
$this->assertEquals('20240715T080000', $messages[1]->message->VEVENT->{'RECURRENCE-ID'}->getValue());
}
/**
* Tests user deleting master instance of recurring event
*/
public function testParseEventForOrganizerDeleteMasterInstance(): void {
// construct calendar with recurring event
$originalCalendar = clone $this->vCalendar2a;
$originalEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$originalCalendar]);
// delete the master instance (convert to non-scheduling)
$mutatedCalendar = clone $this->vCalendar2a;
$mutatedCalendar->VEVENT->{'LAST-MODIFIED'}->setValue('20240701T020000Z');
$mutatedCalendar->VEVENT->SEQUENCE->setValue(2);
$mutatedCalendar->VEVENT->remove('ORGANIZER');
$mutatedCalendar->VEVENT->remove('ATTENDEE');
$mutatedEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$mutatedCalendar]);
// test iTip generation
$messages = $this->invokePrivate($this->broker, 'parseEventForOrganizer', [$mutatedCalendar, $mutatedEventInfo, $originalEventInfo]);
$this->assertCount(1, $messages);
$this->assertEquals('CANCEL', $messages[0]->method);
$this->assertEquals(2, $messages[0]->sequence);
$this->assertEquals($originalCalendar->VEVENT->ORGANIZER->getValue(), $messages[0]->sender);
$this->assertEquals($originalCalendar->VEVENT->ATTENDEE[0]->getValue(), $messages[0]->recipient);
}
/**
* Tests user adding EXDATE to master instance
*/
public function testParseEventForOrganizerAddExdate(): void {
// construct calendar with recurring event
$originalCalendar = clone $this->vCalendar2a;
$originalEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$originalCalendar]);
// add EXDATE to exclude specific occurrences
$mutatedCalendar = clone $this->vCalendar2a;
$mutatedCalendar->VEVENT->{'LAST-MODIFIED'}->setValue('20240701T020000Z');
$mutatedCalendar->VEVENT->SEQUENCE->setValue(2);
$mutatedCalendar->VEVENT->add('EXDATE', ['20240715T080000', '20240722T080000'], ['TZID' => 'America/Toronto']);
$mutatedEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$mutatedCalendar]);
// test iTip generation
$messages = $this->invokePrivate($this->broker, 'parseEventForOrganizer', [$mutatedCalendar, $mutatedEventInfo, $originalEventInfo]);
$this->assertCount(1, $messages);
$this->assertEquals('REQUEST', $messages[0]->method);
$this->assertEquals(2, $messages[0]->sequence);
$this->assertEquals($mutatedCalendar->VEVENT->ORGANIZER->getValue(), $messages[0]->sender);
$this->assertEquals($mutatedCalendar->VEVENT->ATTENDEE[0]->getValue(), $messages[0]->recipient);
// verify EXDATE is present in the message
$this->assertTrue(isset($messages[0]->message->VEVENT->EXDATE));
$exdates = $messages[0]->message->VEVENT->EXDATE->getParts();
$this->assertContains('20240715T080000', $exdates);
$this->assertContains('20240722T080000', $exdates);
}
/**
* Tests user removing EXDATE from master instance
*/
public function testParseEventForOrganizerRemoveExdate(): void {
// construct calendar with recurring event that has EXDATE
$originalCalendar = clone $this->vCalendar2a;
$originalCalendar->VEVENT->add('EXDATE', ['20240715T080000', '20240722T080000'], ['TZID' => 'America/Toronto']);
$originalEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$originalCalendar]);
// remove EXDATE to restore excluded occurrences
$mutatedCalendar = clone $this->vCalendar2a;
$mutatedCalendar->VEVENT->{'LAST-MODIFIED'}->setValue('20240701T020000Z');
$mutatedCalendar->VEVENT->SEQUENCE->setValue(2);
$mutatedEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$mutatedCalendar]);
// test iTip generation
$messages = $this->invokePrivate($this->broker, 'parseEventForOrganizer', [$mutatedCalendar, $mutatedEventInfo, $originalEventInfo]);
$this->assertCount(1, $messages);
$this->assertEquals('REQUEST', $messages[0]->method);
$this->assertEquals(2, $messages[0]->sequence);
$this->assertEquals($mutatedCalendar->VEVENT->ORGANIZER->getValue(), $messages[0]->sender);
$this->assertEquals($mutatedCalendar->VEVENT->ATTENDEE[0]->getValue(), $messages[0]->recipient);
// verify EXDATE is not present in the message
$this->assertFalse(isset($messages[0]->message->VEVENT->EXDATE));
}
/**
* Tests user converting recurring event to non-scheduling
*/
public function testParseEventForOrganizerConvertRecurringToNonScheduling(): void {
// construct calendar with recurring event
$originalCalendar = clone $this->vCalendar2a;
$originalEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$originalCalendar]);
// remove ORGANIZER and ATTENDEE properties to convert to non-scheduling
$mutatedCalendar = clone $this->vCalendar2a;
$mutatedCalendar->VEVENT->{'LAST-MODIFIED'}->setValue('20240701T020000Z');
$mutatedCalendar->VEVENT->SEQUENCE->setValue(2);
$mutatedCalendar->VEVENT->remove('ORGANIZER');
$mutatedCalendar->VEVENT->remove('ATTENDEE');
$mutatedEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$mutatedCalendar]);
// test iTip generation
$messages = $this->invokePrivate($this->broker, 'parseEventForOrganizer', [$mutatedCalendar, $mutatedEventInfo, $originalEventInfo]);
$this->assertCount(1, $messages);
$this->assertEquals('CANCEL', $messages[0]->method);
$this->assertEquals(2, $messages[0]->sequence);
$this->assertEquals($originalCalendar->VEVENT->ORGANIZER->getValue(), $messages[0]->sender);
$this->assertEquals($originalCalendar->VEVENT->ATTENDEE[0]->getValue(), $messages[0]->recipient);
}
/**
* Tests SCHEDULE-FORCE-SEND parameter handling
*/
public function testParseEventForOrganizerScheduleForceSend(): void {
// construct calendar with event
$originalCalendar = clone $this->vCalendar1a;
$originalEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$originalCalendar]);
// add SCHEDULE-FORCE-SEND parameter to ATTENDEE
$mutatedCalendar = clone $this->vCalendar1a;
$mutatedCalendar->VEVENT->{'LAST-MODIFIED'}->setValue('20240701T020000Z');
$mutatedCalendar->VEVENT->SEQUENCE->setValue(2);
$mutatedCalendar->VEVENT->ATTENDEE->add('SCHEDULE-FORCE-SEND', 'REQUEST');
$mutatedEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$mutatedCalendar]);
// test iTip generation
$messages = $this->invokePrivate($this->broker, 'parseEventForOrganizer', [$mutatedCalendar, $mutatedEventInfo, $originalEventInfo]);
$this->assertCount(1, $messages);
$this->assertEquals('REQUEST', $messages[0]->method);
$this->assertEquals(2, $messages[0]->sequence);
$this->assertEquals($mutatedCalendar->VEVENT->ORGANIZER->getValue(), $messages[0]->sender);
$this->assertEquals($mutatedCalendar->VEVENT->ATTENDEE->getValue(), $messages[0]->recipient);
// verify SCHEDULE-FORCE-SEND is removed from the message (sanitized)
$this->assertFalse(isset($messages[0]->message->VEVENT->ATTENDEE['SCHEDULE-FORCE-SEND']));
}
}
@@ -15,16 +15,12 @@ use OCP\Security\Bruteforce\IThrottler;
use OCP\Share\Exceptions\ShareNotFound;
use OCP\Share\IManager;
use OCP\Share\IShare;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\MockObject\MockObject;
use Test\TestCase;
/**
* Class LegacyPublicAuthTest
*
*
* @package OCA\DAV\Tests\unit\Connector
*/
#[\PHPUnit\Framework\Attributes\Group(name: 'DB')]
class LegacyPublicAuthTest extends \Test\TestCase {
#[Group(name: 'DB')]
class LegacyPublicAuthTest extends TestCase {
private ISession&MockObject $session;
private IRequest&MockObject $request;
private IManager&MockObject $shareManager;
@@ -55,7 +51,7 @@ class LegacyPublicAuthTest extends \Test\TestCase {
\OC_User::setIncognitoMode(false);
// Set old user
\OC_User::setUserId($this->oldUser);
\OC_User::setUserId($this->oldUser ?: null);
if ($this->oldUser !== false) {
\OC_Util::setupFS($this->oldUser);
}
@@ -61,14 +61,14 @@ class CommentsPropertiesPluginTest extends \Test\TestCase {
public static function baseUriProvider(): array {
return [
['owncloud/remote.php/webdav/', '4567', 'owncloud/remote.php/dav/comments/files/4567'],
['owncloud/remote.php/files/', '4567', 'owncloud/remote.php/dav/comments/files/4567'],
['owncloud/wicked.php/files/', '4567', null]
['owncloud/remote.php/webdav/', 4567, 'owncloud/remote.php/dav/comments/files/4567'],
['owncloud/remote.php/files/', 4567, 'owncloud/remote.php/dav/comments/files/4567'],
['owncloud/wicked.php/files/', 4567, null]
];
}
#[\PHPUnit\Framework\Attributes\DataProvider(methodName: 'baseUriProvider')]
public function testGetCommentsLink(string $baseUri, string $fid, ?string $expectedHref): void {
public function testGetCommentsLink(string $baseUri, int $fid, ?string $expectedHref): void {
$node = $this->createMock(File::class);
$node->expects($this->any())
->method('getId')
@@ -94,7 +94,7 @@ class CommentsPropertiesPluginTest extends \Test\TestCase {
$node = $this->createMock(File::class);
$node->expects($this->any())
->method('getId')
->willReturn('4567');
->willReturn(4567);
if ($user !== null) {
$user = $this->createMock($user);
@@ -230,6 +230,10 @@ class DirectoryTest extends \Test\TestCase {
$info->expects($this->any())
->method('isReadable')
->willReturn(false);
$this->view
->method('getRelativePath')
->with(null)
->willReturn('');
$dir = new Directory($this->view, $info);
$dir->getChildren();
@@ -242,6 +246,10 @@ class DirectoryTest extends \Test\TestCase {
$this->info->expects($this->any())
->method('isReadable')
->willReturn(false);
$this->view
->method('getRelativePath')
->with('/admin/files/folder')
->willReturn('');
$dir = new Directory($this->view, $this->info);
$dir->getChild('test');
@@ -254,6 +262,10 @@ class DirectoryTest extends \Test\TestCase {
$this->view->expects($this->once())
->method('getFileInfo')
->willThrowException(new StorageNotAvailableException());
$this->view
->method('getRelativePath')
->with('/admin/files/folder')
->willReturn('');
$dir = new Directory($this->view, $this->info);
$dir->getChild('.');
@@ -268,6 +280,10 @@ class DirectoryTest extends \Test\TestCase {
->willThrowException(new InvalidPathException());
$this->view->expects($this->never())
->method('getFileInfo');
$this->view
->method('getRelativePath')
->with('/admin/files/folder')
->willReturn('');
$dir = new Directory($this->view, $this->info);
$dir->getChild('.');
@@ -376,6 +392,11 @@ class DirectoryTest extends \Test\TestCase {
}
public function testGetNodeForPathFailsWithNoReadPermissionsForPath(): void {
$this->view
->method('getRelativePath')
->with('/admin/files/')
->willReturn('');
$directoryNode = $this->createMock(Folder::class);
$pathNode = $this->createMock(Folder::class);
$storage = $this->createMock(IStorage::class);
@@ -396,7 +417,7 @@ class DirectoryTest extends \Test\TestCase {
2 => false,
};
});
$directoryNode->expects($this->once())
$directoryNode
->method('getPath')
->willReturn('/admin/files/');
$directoryNode->expects($this->once())
@@ -673,6 +673,10 @@ class FileTest extends TestCase {
/** @var View&MockObject */
$view = $this->getMockBuilder(View::class)
->getMock();
$view
->method('getRelativePath')
->with('/test.txt')
->willReturn('');
$view->expects($this->once())
->method('unlink')
@@ -697,6 +701,10 @@ class FileTest extends TestCase {
/** @var View&MockObject */
$view = $this->getMockBuilder(View::class)
->getMock();
$view
->method('getRelativePath')
->with('/test.txt')
->willReturn('');
$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [
'permissions' => 0,
@@ -717,6 +725,10 @@ class FileTest extends TestCase {
/** @var View&MockObject */
$view = $this->getMockBuilder(View::class)
->getMock();
$view
->method('getRelativePath')
->with('/test.txt')
->willReturn('');
// but fails
$view->expects($this->once())
@@ -742,6 +754,10 @@ class FileTest extends TestCase {
/** @var View&MockObject */
$view = $this->getMockBuilder(View::class)
->getMock();
$view
->method('getRelativePath')
->with('/test.txt')
->willReturn('');
// but fails
$view->expects($this->once())
@@ -101,7 +101,7 @@ class FilesPluginTest extends TestCase {
->willReturn('00000123instanceid');
$node->expects($this->any())
->method('getInternalFileId')
->willReturn('123');
->willReturn(123);
$node->expects($this->any())
->method('getEtag')
->willReturn('"abc"');
@@ -455,7 +455,7 @@ class FilesPluginTest extends TestCase {
$node->expects($this->once())
->method('setEtag')
->with('newetag')
->willReturn(true);
->willReturn(123);
$node->expects($this->once())
->method('setCreationTime')
@@ -562,35 +562,11 @@ class FilesPluginTest extends TestCase {
$this->plugin->checkMove('FolderA/test.txt', 'test.txt');
}
public function testMoveSrcNotExist(): void {
$this->expectException(\Sabre\DAV\Exception\NotFound::class);
$this->expectExceptionMessage('FolderA/test.txt does not exist');
$node = $this->createMock(Node::class);
$node->expects($this->atLeastOnce())
->method('getFileInfo')
->willReturn(null);
$this->tree->expects($this->atLeastOnce())
->method('getNodeForPath')
->willReturn($node);
$this->plugin->checkMove('FolderA/test.txt', 'test.txt');
}
public function testMoveDestinationInvalid(): void {
$this->expectException(InvalidPath::class);
$this->expectExceptionMessage('Mocked exception');
$fileInfoFolderATestTXT = $this->createMock(FileInfo::class);
$fileInfoFolderATestTXT->expects(self::any())
->method('isDeletable')
->willReturn(true);
$node = $this->createMock(Node::class);
$node->expects($this->atLeastOnce())
->method('getFileInfo')
->willReturn($fileInfoFolderATestTXT);
$this->tree->expects($this->atLeastOnce())
->method('getNodeForPath')
@@ -604,31 +580,11 @@ class FilesPluginTest extends TestCase {
$this->plugin->checkMove('FolderA/test.txt', 'invalid\\path.txt');
}
public function testCopySrcNotExist(): void {
$this->expectException(\Sabre\DAV\Exception\NotFound::class);
$this->expectExceptionMessage('FolderA/test.txt does not exist');
$node = $this->createMock(Node::class);
$node->expects($this->atLeastOnce())
->method('getFileInfo')
->willReturn(null);
$this->tree->expects($this->atLeastOnce())
->method('getNodeForPath')
->willReturn($node);
$this->plugin->checkCopy('FolderA/test.txt', 'test.txt');
}
public function testCopyDestinationInvalid(): void {
$this->expectException(InvalidPath::class);
$this->expectExceptionMessage('Mocked exception');
$fileInfoFolderATestTXT = $this->createMock(FileInfo::class);
$node = $this->createMock(Node::class);
$node->expects($this->atLeastOnce())
->method('getFileInfo')
->willReturn($fileInfoFolderATestTXT);
$this->tree->expects($this->atLeastOnce())
->method('getNodeForPath')
@@ -57,6 +57,10 @@ class FilesReportPluginTest extends \Test\TestCase {
$this->tree = $this->createMock(Tree::class);
$this->view = $this->createMock(View::class);
$this->view
->method('getRelativePath')
->with(null)
->willReturn('');
$this->server = $this->getMockBuilder(Server::class)
->setConstructorArgs([$this->tree])
@@ -315,14 +319,14 @@ class FilesReportPluginTest extends \Test\TestCase {
$node1->expects($this->once())
->method('getInternalFileId')
->willReturn('111');
->willReturn(111);
$node1->expects($this->any())
->method('getPath')
->willReturn('/node1');
$node1->method('getFileInfo')->willReturn($fileInfo);
$node2->expects($this->once())
->method('getInternalFileId')
->willReturn('222');
->willReturn(222);
$node2->expects($this->once())
->method('getSize')
->willReturn(1024);
@@ -94,6 +94,10 @@ class NodeTest extends \Test\TestCase {
$info->method('getStorage')
->willReturn($storage);
$view = $this->createMock(View::class);
$view
->method('getRelativePath')
->with(null)
->willReturn('');
$node = new File($view, $info);
$this->assertEquals($expected, $node->getDavPermissions());
@@ -169,6 +173,10 @@ class NodeTest extends \Test\TestCase {
$info->method('getPermissions')->willReturn($permissions);
$view = $this->createMock(View::class);
$view
->method('getRelativePath')
->with(null)
->willReturn('');
$node = new File($view, $info);
$this->invokePrivate($node, 'shareManager', [$shareManager]);
@@ -204,6 +212,10 @@ class NodeTest extends \Test\TestCase {
/** @var View&MockObject $view */
$view = $this->createMock(View::class);
$view
->method('getRelativePath')
->with(null)
->willReturn('');
$node = new File($view, $info);
$this->invokePrivate($node, 'shareManager', [$shareManager]);
@@ -225,6 +237,10 @@ class NodeTest extends \Test\TestCase {
/** @var View&MockObject */
$view = $this->createMock(View::class);
$view
->method('getRelativePath')
->with(null)
->willReturn('');
$node = new File($view, $info);
$this->invokePrivate($node, 'shareManager', [$shareManager]);
@@ -243,6 +259,10 @@ class NodeTest extends \Test\TestCase {
$view = $this->getMockBuilder(View::class)
->disableOriginalConstructor()
->getMock();
$view
->method('getRelativePath')
->with(null)
->willReturn('');
$info = $this->getMockBuilder(FileInfo::class)
->disableOriginalConstructor()
->getMock();
@@ -263,6 +283,11 @@ class NodeTest extends \Test\TestCase {
$this->expectException(\InvalidArgumentException::class);
$view = $this->createMock(View::class);
$view
->method('getRelativePath')
->with(null)
->willReturn('');
$info = $this->createMock(FileInfo::class);
$node = new File($view, $info);
@@ -63,6 +63,10 @@ class ObjectTreeTest extends \Test\TestCase {
->method('getFileInfo')
->with($targetParent === '' ? '.' : $targetParent)
->willReturn($info);
$view
->method('getRelativePath')
->with(null)
->willReturn('');
$rootDir = new Directory($view, $info);
$objectTree = $this->getMockBuilder(ObjectTree::class)
@@ -104,6 +108,10 @@ class ObjectTreeTest extends \Test\TestCase {
->method('getFileInfo')
->with($targetParent === '' ? '.' : $targetParent)
->willReturn($info);
$view
->method('getRelativePath')
->with(null)
->willReturn('');
$rootDir = new Directory($view, $info);
$objectTree = $this->getMockBuilder(ObjectTree::class)
@@ -141,6 +149,10 @@ class ObjectTreeTest extends \Test\TestCase {
$view->method('getFileInfo')
->with($fileInfoQueryPath)
->willReturn($fileInfo);
$view
->method('getRelativePath')
->with(null)
->willReturn('');
$tree = new ObjectTree();
$tree->init($rootNode, $view, $mountManager);
@@ -78,7 +78,7 @@ class PrincipalTest extends TestCase {
}
public function testGetPrincipalsByPrefixWithUsers(): void {
$fooUser = $this->createMock(User::class);
$fooUser = $this->createMock(IUser::class);
$fooUser
->expects($this->once())
->method('getUID')
@@ -91,7 +91,7 @@ class PrincipalTest extends TestCase {
->expects($this->once())
->method('getSystemEMailAddress')
->willReturn('');
$barUser = $this->createMock(User::class);
$barUser = $this->createMock(IUser::class);
$barUser
->expects($this->once())
->method('getUID')
@@ -183,7 +183,7 @@ class PrincipalTest extends TestCase {
}
public function testGetPrincipalsByPathWithoutMail(): void {
$fooUser = $this->createMock(User::class);
$fooUser = $this->createMock(IUser::class);
$fooUser
->expects($this->once())
->method('getUID')
@@ -211,7 +211,7 @@ class PrincipalTest extends TestCase {
}
public function testGetPrincipalsByPathWithMail(): void {
$fooUser = $this->createMock(User::class);
$fooUser = $this->createMock(IUser::class);
$fooUser
->expects($this->once())
->method('getSystemEMailAddress')
@@ -182,4 +182,15 @@ class UserEventsListenerTest extends TestCase {
$this->userEventsListener->preDeleteUser($user);
$this->userEventsListener->postDeleteUser('newUser');
}
public function testDeleteGroup(): void {
$this->calDavBackend->expects($this->once())
->method('deleteAllSharesByUser')
->with('principals/groups/testGroup');
$this->cardDavBackend->expects($this->once())
->method('deleteAllSharesByUser')
->with('principals/groups/testGroup');
$this->userEventsListener->postDeleteGroup('testGroup');
}
}
+8 -7
View File
@@ -22,19 +22,20 @@ OC.L10N.register(
"Private key password successfully updated." : "Kişisel anahtar parolası güncellendi.",
"Invalid private key for encryption app. Please update your private key password in your personal settings to recover access to your encrypted files." : "Şifreleme uygulaması kişisel anahtarı geçersiz. Şifrelenmiş dosyalarınıza erişebilmek için kişisel ayarlarınızdaki kişisel anahtar parolanızı güncelleyin.",
"Encryption App is enabled, but your keys are not initialized. Please log-out and log-in again." : "Şifreleme uygulaması kullanıma alınmış ancak anahtarlarınız hazırlanmamış. Lütfen oturumunuzu kapatıp yeniden açın.",
"Please enable server side encryption in the admin settings in order to use the encryption module." : "Şifreleme modülünü kullanabilmek için yönetici ayarlarından sunucu tarafında şifreleme seçeneğini açın.",
"Please enable server side encryption in the admin settings in order to use the encryption module." : "Şifreleme modülünü kullanabilmek için yönetici ayarlarından sunucuda şifreleme seçeneğini açın.",
"Encryption app is enabled and ready" : "Şifreleme uygulaması kullanıma alındı ve hazır",
"Bad Signature" : "İmza bozuk",
"Missing Signature" : "İmza eksik",
"one-time password for server-side-encryption" : "sunucu tarafında şifreleme için tek kullanımlık parola",
"one-time password for server-side-encryption" : "sunucuda şifreleme için tek kullanımlık parola",
"Encryption password" : "Şifreleme parolası",
"The administration enabled server-side-encryption. Your files were encrypted using the password <strong>%s</strong>." : "Yönetici, sunucu tarafında şifreleme özelliğini açmış. Dosyalarınız <strong>%s</strong> parolası ile şifrelendi.",
"The administration enabled server-side-encryption. Your files were encrypted using the password \"%s\"." : "Yönetici, sunucu tarafında şifreleme özelliğini açmış. Dosyalarınız \"%s\" parolası ile şifrelendi.",
"The administration enabled server-side-encryption. Your files were encrypted using the password <strong>%s</strong>." : "Yönetici, sunucuda şifreleme özelliğini açmış. Dosyalarınız <strong>%s</strong> parolası ile şifrelendi.",
"The administration enabled server-side-encryption. Your files were encrypted using the password \"%s\"." : "Yönetici, sunucuda şifreleme özelliğini açmış. Dosyalarınız \"%s\" parolası ile şifrelendi.",
"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." : "Lütfen yönetim bölümünden oturum açarak kişisel ayarlarınızdaki \"Güvenlik\" bölümüne gidin ve \"Eski oturum açma parolası\" alanına bu parolayı ve geçerli oturum açma parolanızı yazarak şifreleme parolanızı güncelleyin.",
"Cannot decrypt this file, probably this is a shared file. Please ask the file owner to reshare the file with you." : "Bu dosyanın şifresi çözülemedi ve büyük olasılıkla paylaşılan bir dosya. Lütfen dosya sahibi ile görüşerek sizinle yeniden paylaşmasını isteyin.",
"Cannot read this file, probably this is a shared file. Please ask the file owner to reshare the file with you." : "Bu dosya okunamadı ve büyük olasılıkla paylaşılan bir dosya. Lütfen dosya sahibi ile görüşerek sizinle yeniden paylaşmasını isteyin.",
"Default Encryption Module" : "Varsayılan şifreleme modülü",
"Default encryption module for Nextcloud Server-side Encryption (SSE)" : "Nextcloud sunucu tarafında şifreleme (SSE) için varsayılan şifreleme modülü",
"Default encryption module for Nextcloud Server-side Encryption (SSE)" : "Nextcloud sunucuda şifreleme (SSE) için varsayılan şifreleme modülü",
"This app provides the (default) cryptography implementation for Nextcloud's Server-side Encryption (SSE) feature.\n\n\t\t\t**Encryption Details**\n\t\t\t* **Cipher Mode:** AES-256-CTR (default)\n\t\t\t* **Authentication:** HMAC-SHA256\n\n\t\t\t**Important Warnings**\n\t\t\t* **DANGER:** Do not disable this application until all files have been decrypted (`occ encryption:decrypt-all`).\n\t\t\t* **WARNING**: Reverting to non-encrypted file storage after activation requires command-line access. The action is permanent via the Web UI.\"\n\n\t\t\t**Notes for Existing Files**\n\t\t\t* By default, enabling SSE does not encrypt existing files; only new files will be encrypted.\n\t\t\t* To encrypt all existing files, use the command `occ encryption:encrypt-all`.\n\n\t\t\t**Before You Begin**\n\t\t\t* **Read the Documentation:** Before you enable SSE, encrypt existing files, or disable SSE, it is critical to\n\t\t\t\tread the documentation to understand implications and the appropriate procedures to avoid data loss." : "Bu uygulama, Nextcloud sunucuda şifreleme (SSE) özelliği için (varsayılan) şifreleme uygulamasını sağlar.\n\n\t\t\t**Şifreleme ayrıntıları**\n\t\t\t* **Şifreleme kipi:** AES-256-CTR (varsayılan)\n\t\t\t* **Kimlik doğrulaması:** HMAC-SHA256\n\n\t\t\t**Önemli uyarılar**\n\t\t\t* **DİKKAT:** Tüm dosyaların şifresi çözülene kadar bu uygulamayı kapatmayın (`occ encryption:decrypt-all`).\n\t\t\t* **UYARI**: Açtıktan sonra şifrelenmemiş dosya depolama alanına geri dönmek için komut satırına erişim gerekir. İşlem, internet kullanıcı arayüzünden kalıcıdır.\"\n\n\t\t\t**Var olan dosyalar için notlar**\n\t\t\t* Varsayılan olarak, sunucuda şifrelemeyi açmak var olan dosyaları şifrelemez. Yalnızca yeni dosyalar şifrelenir.\n\t\t\t* Var olan tüm dosyaları şifrelemek için şu komutu kullanın: `occ encryption:encrypt-all`.\n\n\t\t\t**Başlamadan önce**\n\t\t\t* **Belgeleri okuyun:** Açmadan önce var olan dosyaları şifreleyin ya da sunucuda şifrelemeyi kapatın. \n\t\t\t\tVeri kaybını önlemek için, sonuçları ve uygun prosedürleri anlamak amacıyla belgeleri okumak çok önemlidir.",
"Enabling this option encrypts all files stored on the main storage, otherwise only files on external storage will be encrypted" : "Bu seçenek açıldığında, ana depolama alanındaki tüm dosyalar şifrelenir. Kapatıldığında, yalnızca dış depolama alanındaki dosyalar şifrelenir",
"Encrypt the home storage" : "Ana depolama şifrelensin",
"Disable recovery key" : "Kurtarma anahtarını kullanımdan kaldır",
@@ -63,7 +64,7 @@ OC.L10N.register(
"Basic encryption module" : "Temel şifreleme modülü",
"Missing parameters" : "Parametreler eksik",
"Default encryption module for server-side encryption" : "Sunucu tarafında şifreleme için varsayılan şifreleme modülü",
"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." : "Bu şifreleme modülünün kullanılması için sunucu tarafında yönetim bölümünden şifreleme seçeneği açılmalıdır. Bu modül kullanıma alındıktan sonra tüm dosyalarınızı size farkettirmeden şifreler. Şifreleme AES 256 anahtarları ile yapılır. \nModül var olan dosyaları değiştirmez, yalnızca sunucu tarafında şifreleme açıldıktan sonra eklenen yeni dosyalar şifrelenir. Şifreleme açıldıktan sonra kapatılamaz ve şifreleme olmayan sisteme geri dönülemez.\nLütfen sunucu tarafı şifrelemeyi açmadan önce belgeleri okuyun ve uygulamadan doğacak tüm sonuçlarını öğrenin.",
"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." : "Bu şifreleme modülünün kullanılması için sunucuda yönetim bölümünden şifreleme seçeneği açılmalıdır. Bu modül kullanıma alındıktan sonra tüm dosyalarınızı size farkettirmeden şifreler. Şifreleme AES 256 anahtarları ile yapılır. \nModül var olan dosyaları değiştirmez, yalnızca sunucuda şifreleme açıldıktan sonra eklenen yeni dosyalar şifrelenir. Şifreleme açıldıktan sonra kapatılamaz ve şifreleme olmayan sisteme geri dönülemez.\nLütfen sunucuda şifrelemeyi açmadan önce belgeleri okuyun ve uygulamadan doğacak tüm sonuçlarını öğrenin.",
"Change recovery key password:" : "Kurtarma anahtarı parolasını değiştir:",
"Change Password" : "Parolayı değiştir",
"Your private key password no longer matches your log-in password." : "Kişisel anahtar parolanız artık oturum açma parolanız ile eşleşmiyor.",
@@ -72,6 +73,6 @@ OC.L10N.register(
"Enable password recovery:" : "Parola kurtarma özelliğini aç:",
"Enabled" : "Açık",
"Disabled" : "Kapalı",
"This app provides the (default) cryptography implementation for Nextcloud's Server-side Encryption (SSE) feature.\n\n\t\t\t**Encryption Details**\n\t\t\t* **Cipher Mode:** AES-256-CTR (default)\n\t\t\t* **Authentication:** HMAC-SHA256\n\n\t\t\t**Important Warnings**\n\t\t\t* **DANGER:** Do not disable this application until all files have been decrypted (`occ encryption:decrypt-all`).\n\t\t\t* **WARNING**: Reverting to non-encrypted file storage after activation requires command-line access. The action is permanent via the Web UI.\"\n\n\t\t\t**Notes for Existing Files**\n\t\t\t* By default, enabling SSE does not encrypt existing files; only new files will be encrypted.\n\t\t\t* To encrypt all existing files, use the command `occ encryption:encrypt-all`.\n\n\t\t\t**Before You Begin**\n\t\t\t* **Read the Documentation:** Before you enable SSE, encrypt existing files, or disable SSE, it is critical to \n\t\t\t\tread the documentation to understand implications and the appropriate procedures to avoid data loss." : "Bu uygulama, Nextcloud sunucu tarafı şifreleme (SSE) özelliği için (varsayılan) şifreleme uygulamasını sağlar.\n\n\t\t\t**Şifreleme ayrıntıları**\n\t\t\t* **Şifreleme kipi:** AES-256-CTR (varsayılan)\n\t\t\t* **Kimlik doğrulaması:** HMAC-SHA256\n\n\t\t\t**Önemli uyarılar**\n\t\t\t* **DİKKAT:** Tüm dosyaların şifresi çözülene kadar bu uygulamayı etkisizleştirmeyin (`occ encryption:decrypt-all`).\n\t\t\t* **UYARI**: Etkinleştirildikten sonra şifrelenmemiş dosya depolama alanına geri dönmek için komut satırına erişim gerekir. İşlem, internet kullanıcı arayüzünden kalıcıdır.\"\n\n\t\t\t**Var olan dosyalar için notlar**\n\t\t\t* Varsayılan olarak, sunucu tarafı şifrelemeyi etkinleştirmek var olan dosyaları şifrelemez. Yalnızca yeni dosyalar şifrelenir.\n\t\t\t* Var olan tüm dosyaları şifrelemek için şu komutu kullanın: `occ encryption:encrypt-all`.\n\n\t\t\t**Başlamadan önce**\n\t\t\t* **Belgeleri okuyun:** Sunucu tarafı şifrelemeyi etkinleştirmeden önce var olan dosyaları şifreleyin ya da sunucu tarafı şifrelemeyi etkisizleştirin. \n\t\t\t\tVeri kaybını önlemek için sonuçları ve uygun prosedürleri anlamak üzere belgeleri okumak çok önemlidir."
"This app provides the (default) cryptography implementation for Nextcloud's Server-side Encryption (SSE) feature.\n\n\t\t\t**Encryption Details**\n\t\t\t* **Cipher Mode:** AES-256-CTR (default)\n\t\t\t* **Authentication:** HMAC-SHA256\n\n\t\t\t**Important Warnings**\n\t\t\t* **DANGER:** Do not disable this application until all files have been decrypted (`occ encryption:decrypt-all`).\n\t\t\t* **WARNING**: Reverting to non-encrypted file storage after activation requires command-line access. The action is permanent via the Web UI.\"\n\n\t\t\t**Notes for Existing Files**\n\t\t\t* By default, enabling SSE does not encrypt existing files; only new files will be encrypted.\n\t\t\t* To encrypt all existing files, use the command `occ encryption:encrypt-all`.\n\n\t\t\t**Before You Begin**\n\t\t\t* **Read the Documentation:** Before you enable SSE, encrypt existing files, or disable SSE, it is critical to \n\t\t\t\tread the documentation to understand implications and the appropriate procedures to avoid data loss." : "Bu uygulama, Nextcloud sunucuda şifreleme (SSE) özelliği için (varsayılan) şifreleme uygulamasını sağlar.\n\n\t\t\t**Şifreleme ayrıntıları**\n\t\t\t* **Şifreleme kipi:** AES-256-CTR (varsayılan)\n\t\t\t* **Kimlik doğrulaması:** HMAC-SHA256\n\n\t\t\t**Önemli uyarılar**\n\t\t\t* **DİKKAT:** Tüm dosyaların şifresi çözülene kadar bu uygulamayı kapatmayın (`occ encryption:decrypt-all`).\n\t\t\t* **UYARI**: Açıldıktan sonra şifrelenmemiş dosya depolama alanına geri dönmek için komut satırına erişim gerekir. İşlem, internet kullanıcı arayüzünden kalıcıdır.\"\n\n\t\t\t**Var olan dosyalar için notlar**\n\t\t\t* Varsayılan olarak, sunucuda şifrelemeyi açmak var olan dosyaları şifrelemez. Yalnızca yeni dosyalar şifrelenir.\n\t\t\t* Var olan tüm dosyaları şifrelemek için şu komutu kullanın: `occ encryption:encrypt-all`.\n\n\t\t\t**Başlamadan önce**\n\t\t\t* **Belgeleri okuyun:** Açmadan önce var olan dosyaları şifreleyin ya da sunucuda şifrelemeyi kapatın. \n\t\t\t\tVeri kaybını önlemek için sonuçları ve uygun prosedürleri anlamak amacıyla belgeleri okumak çok önemlidir."
},
"nplurals=2; plural=(n > 1);");

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