diff --git a/Sources/NextcloudCapabilitiesKit/Capabilities.swift b/Sources/NextcloudCapabilitiesKit/Capabilities.swift index 7250649..e411087 100644 --- a/Sources/NextcloudCapabilitiesKit/Capabilities.swift +++ b/Sources/NextcloudCapabilitiesKit/Capabilities.swift @@ -20,6 +20,10 @@ public struct Capabilities: Equatable, Sendable { public let userStatus: UserStatus? public let filesSharing: FilesSharing? public let notifyPush: NotifyPush? + public let sharing: Sharing? + public let systemTags: SystemTags? + public let weatherStatus: WeatherStatus? + public let provisioningAPI: ProvisioningAPI? public init?(data: Data) { guard let anyJson = try? JSONSerialization.jsonObject(with: data, options: []) else { @@ -65,5 +69,9 @@ public struct Capabilities: Equatable, Sendable { userStatus = UserStatus(capabilities: capabilities) filesSharing = FilesSharing(capabilities: capabilities) notifyPush = NotifyPush(capabilities: capabilities) + sharing = Sharing(capabilities: capabilities) + systemTags = SystemTags(capabilities: capabilities) + weatherStatus = WeatherStatus(capabilities: capabilities) + provisioningAPI = ProvisioningAPI(capabilities: capabilities) } } diff --git a/Sources/NextcloudCapabilitiesKit/Core.swift b/Sources/NextcloudCapabilitiesKit/Core.swift index e64d946..f912bc5 100644 --- a/Sources/NextcloudCapabilitiesKit/Core.swift +++ b/Sources/NextcloudCapabilitiesKit/Core.swift @@ -4,10 +4,13 @@ import Foundation public struct Core: Equatable, Sendable { - let pollInterval: Int - let webdavRoot: String - let referenceApi: Bool - let referenceRegex: String + public let pollInterval: Int + public let webdavRoot: String + public let referenceApi: Bool + public let referenceRegex: String + public let modRewriteWorking: Bool + public let user: CoreUser? + public let canCreateAppToken: Bool? init?(capabilities: [String: Any]) { guard let capabilities = capabilities["core"] as? [String: Any] else { @@ -19,5 +22,8 @@ public struct Core: Equatable, Sendable { webdavRoot = capabilities["webdav-root"] as? String ?? "remote.php/webdav" referenceApi = capabilities["reference-api"] as? Bool ?? false referenceRegex = capabilities["reference-regex"] as? String ?? "" + modRewriteWorking = capabilities["mod-rewrite-working"] as? Bool ?? false + user = CoreUser(capabilities: capabilities["user"] as? [String: Any] ?? [:]) + canCreateAppToken = capabilities["can-create-app-token"] as? Bool } } diff --git a/Sources/NextcloudCapabilitiesKit/CoreUser.swift b/Sources/NextcloudCapabilitiesKit/CoreUser.swift new file mode 100644 index 0000000..c8ad6c0 --- /dev/null +++ b/Sources/NextcloudCapabilitiesKit/CoreUser.swift @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors +// SPDX-License-Identifier: LGPL-3.0-or-later + +import Foundation + +public struct CoreUser: Equatable, Sendable { + public let language: String + public let locale: String + public let timeZone: String + + init?(capabilities: [String: Any]) { + guard let language = capabilities["language"] as? String, + let locale = capabilities["locale"] as? String, + let timeZone = capabilities["timezone"] as? String + else { + return nil + } + + self.language = language + self.locale = locale + self.timeZone = timeZone + } +} diff --git a/Sources/NextcloudCapabilitiesKit/Dav.swift b/Sources/NextcloudCapabilitiesKit/Dav.swift index 6607543..a7ec506 100644 --- a/Sources/NextcloudCapabilitiesKit/Dav.swift +++ b/Sources/NextcloudCapabilitiesKit/Dav.swift @@ -4,8 +4,14 @@ import Foundation public struct Dav: Equatable, Sendable { - let chunking: String - let bulkUpload: String + public let chunking: String + public let publicSharesChunking: Bool + public let searchSupportsCreationTime: Bool + public let searchSupportsUploadTime: Bool + public let searchSupportsLastActivity: Bool + public let bulkUpload: String? + public let absenceSupported: Bool + public let absenceReplacement: Bool init?(capabilities: [String: Any]) { guard let capabilities = capabilities["dav"] as? [String: Any] else { @@ -14,6 +20,12 @@ public struct Dav: Equatable, Sendable { } chunking = capabilities["chunking"] as? String ?? "" - bulkUpload = capabilities["bulkupload"] as? String ?? "" + publicSharesChunking = capabilities["public_shares_chunking"] as? Bool ?? false + searchSupportsCreationTime = capabilities["search_supports_creation_time"] as? Bool ?? false + searchSupportsUploadTime = capabilities["search_supports_upload_time"] as? Bool ?? false + searchSupportsLastActivity = capabilities["search_supports_last_activity"] as? Bool ?? false + bulkUpload = capabilities["bulkupload"] as? String + absenceSupported = capabilities["absence-supported"] as? Bool ?? false + absenceReplacement = capabilities["absence-replacement"] as? Bool ?? false } } diff --git a/Sources/NextcloudCapabilitiesKit/Files/FileConversion.swift b/Sources/NextcloudCapabilitiesKit/Files/FileConversion.swift new file mode 100644 index 0000000..9efc906 --- /dev/null +++ b/Sources/NextcloudCapabilitiesKit/Files/FileConversion.swift @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors +// SPDX-License-Identifier: LGPL-3.0-or-later + +import Foundation + +public struct FileConversion: Equatable, Sendable { + public let from: String + public let to: String + public let `extension`: String + public let displayName: String + + init?(capabilities: [String: Any]) { + guard let from = capabilities["from"] as? String, + let to = capabilities["to"] as? String, + let fileExtension = capabilities["extension"] as? String, + let displayName = capabilities["displayName"] as? String + else { + return nil + } + + self.from = from + self.to = to + `extension` = fileExtension + self.displayName = displayName + } +} diff --git a/Sources/NextcloudCapabilitiesKit/Files/Files.swift b/Sources/NextcloudCapabilitiesKit/Files/Files.swift index cf54324..83aa6bd 100644 --- a/Sources/NextcloudCapabilitiesKit/Files/Files.swift +++ b/Sources/NextcloudCapabilitiesKit/Files/Files.swift @@ -6,14 +6,21 @@ import Foundation public struct Files: Equatable, Sendable { public let bigFileChunking: Bool public let blackListedFiles: [String] + public let forbiddenFilenames: [String] + public let forbiddenFilenameBasenames: [String] + public let forbiddenFilenameCharacters: [String] + public let forbiddenFilenameExtensions: [String] + public let fileConversions: [FileConversion] public let directEditing: DirectEditing? public let chunkedUpload: ChunkedUpload? public let comments: Bool public let undelete: Bool + public let deleteFromTrash: Bool public let versioning: Bool public let versionLabeling: Bool public let versionDeletion: Bool public let locking: String? + public let windowsCompatibleFilenames: Bool init?(capabilities: [String: Any]) { guard let capabilities = capabilities["files"] as? [String: Any] else { @@ -23,13 +30,20 @@ public struct Files: Equatable, Sendable { bigFileChunking = capabilities["bigfilechunking"] as? Bool ?? false blackListedFiles = capabilities["blacklisted_files"] as? [String] ?? [] + forbiddenFilenames = capabilities["forbidden_filenames"] as? [String] ?? [] + forbiddenFilenameBasenames = capabilities["forbidden_filename_basenames"] as? [String] ?? [] + forbiddenFilenameCharacters = capabilities["forbidden_filename_characters"] as? [String] ?? [] + forbiddenFilenameExtensions = capabilities["forbidden_filename_extensions"] as? [String] ?? [] + fileConversions = (capabilities["file_conversions"] as? [[String: Any]] ?? []).compactMap(FileConversion.init) directEditing = DirectEditing(filesCapabilities: capabilities) chunkedUpload = ChunkedUpload(filesCapabilities: capabilities) comments = capabilities["comments"] as? Bool ?? false undelete = capabilities["undelete"] as? Bool ?? false + deleteFromTrash = capabilities["delete_from_trash"] as? Bool ?? false versioning = capabilities["versioning"] as? Bool ?? false versionLabeling = capabilities["version_labeling"] as? Bool ?? false versionDeletion = capabilities["version_deletion"] as? Bool ?? false locking = capabilities["locking"] as? String ?? nil + windowsCompatibleFilenames = capabilities["windows_compatible_filenames"] as? Bool ?? false } } diff --git a/Sources/NextcloudCapabilitiesKit/ProvisioningAPI.swift b/Sources/NextcloudCapabilitiesKit/ProvisioningAPI.swift new file mode 100644 index 0000000..e38baac --- /dev/null +++ b/Sources/NextcloudCapabilitiesKit/ProvisioningAPI.swift @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors +// SPDX-License-Identifier: LGPL-3.0-or-later + +import Foundation + +public struct ProvisioningAPI: Equatable, Sendable { + public let version: String + public let accountPropertyScopesVersion: Int + public let accountPropertyScopesFederatedEnabled: Bool + public let accountPropertyScopesPublishedEnabled: Bool + + init?(capabilities: [String: Any]) { + guard let capabilities = capabilities["provisioning_api"] as? [String: Any] else { + debugPrint("No provisioning API data in received capabilities.") + return nil + } + + version = capabilities["version"] as? String ?? "" + accountPropertyScopesVersion = capabilities["AccountPropertyScopesVersion"] as? Int ?? 0 + accountPropertyScopesFederatedEnabled = capabilities["AccountPropertyScopesFederatedEnabled"] as? Bool ?? false + accountPropertyScopesPublishedEnabled = capabilities["AccountPropertyScopesPublishedEnabled"] as? Bool ?? false + } +} diff --git a/Sources/NextcloudCapabilitiesKit/Shares/FilesSharing.swift b/Sources/NextcloudCapabilitiesKit/Shares/FilesSharing.swift index 56d452d..bb7d792 100644 --- a/Sources/NextcloudCapabilitiesKit/Shares/FilesSharing.swift +++ b/Sources/NextcloudCapabilitiesKit/Shares/FilesSharing.swift @@ -12,7 +12,9 @@ public struct FilesSharing: Equatable, Sendable { public let sharee: Sharee? public let apiEnabled: Bool public let resharing: Bool + public let groupSharing: Bool public let defaultPermissions: Int + public let excludeReshareFromEdit: Bool init?(capabilities: [String: Any]) { guard let capabilities = capabilities["files_sharing"] as? [String: Any] else { @@ -28,7 +30,9 @@ public struct FilesSharing: Equatable, Sendable { sharee = Sharee(filesSharingCapabilities: capabilities) apiEnabled = capabilities["api_enabled"] as? Bool ?? false resharing = capabilities["resharing"] as? Bool ?? false + groupSharing = capabilities["group_sharing"] as? Bool ?? false defaultPermissions = capabilities["default_permissions"] as? Int ?? 0 + excludeReshareFromEdit = capabilities["exclude_reshare_from_edit"] as? Bool ?? false debugPrint("Parsed share capabilities.") } } diff --git a/Sources/NextcloudCapabilitiesKit/Shares/Share Type/Email.swift b/Sources/NextcloudCapabilitiesKit/Shares/Share Type/Email.swift index d69ac28..57f605e 100644 --- a/Sources/NextcloudCapabilitiesKit/Shares/Share Type/Email.swift +++ b/Sources/NextcloudCapabilitiesKit/Shares/Share Type/Email.swift @@ -4,8 +4,13 @@ import Foundation public struct Email: Equatable, Sendable { + public let enabled: Bool + public let sendsPasswordByEmail: Bool + public let uploadFilesDropEnabled: Bool public let passwordEnabled: Bool public let passwordEnforced: Bool + public let expireDateEnabled: Bool + public let expireDateEnforced: Bool init?(filesSharingCapabilities: [String: Any]) { guard let emailCaps = filesSharingCapabilities["sharebymail"] as? [String: Any] else { @@ -13,6 +18,10 @@ public struct Email: Equatable, Sendable { return nil } + enabled = emailCaps["enabled"] as? Bool ?? false + sendsPasswordByEmail = emailCaps["send_password_by_mail"] as? Bool ?? false + uploadFilesDropEnabled = (emailCaps["upload_files_drop"] as? [String: Any])?["enabled"] as? Bool ?? false + if let passwordCapabilities = emailCaps["password"] as? [String: Any] { passwordEnabled = passwordCapabilities["enabled"] as? Bool ?? false passwordEnforced = passwordCapabilities["enforced"] as? Bool ?? false @@ -20,5 +29,13 @@ public struct Email: Equatable, Sendable { passwordEnabled = false passwordEnforced = false } + + if let expireDateCapabilities = emailCaps["expire_date"] as? [String: Any] { + expireDateEnabled = expireDateCapabilities["enabled"] as? Bool ?? false + expireDateEnforced = expireDateCapabilities["enforced"] as? Bool ?? false + } else { + expireDateEnabled = false + expireDateEnforced = false + } } } diff --git a/Sources/NextcloudCapabilitiesKit/Shares/Share Type/Federation.swift b/Sources/NextcloudCapabilitiesKit/Shares/Share Type/Federation.swift index 5644931..b22f868 100644 --- a/Sources/NextcloudCapabilitiesKit/Shares/Share Type/Federation.swift +++ b/Sources/NextcloudCapabilitiesKit/Shares/Share Type/Federation.swift @@ -4,10 +4,10 @@ import Foundation public struct Federation: Equatable, Sendable { - let outgoing: Bool - let incoming: Bool - let expireDateEnabled: Bool - let expireDateSupported: Bool + public let outgoing: Bool + public let incoming: Bool + public let expireDateEnabled: Bool + public let expireDateSupported: Bool init?(filesSharingCapabilities: [String: Any]) { guard let federationCaps = filesSharingCapabilities["federation"] as? [String: Any] else { diff --git a/Sources/NextcloudCapabilitiesKit/Shares/Share Type/Group.swift b/Sources/NextcloudCapabilitiesKit/Shares/Share Type/Group.swift index fec1dd4..6fd9a9f 100644 --- a/Sources/NextcloudCapabilitiesKit/Shares/Share Type/Group.swift +++ b/Sources/NextcloudCapabilitiesKit/Shares/Share Type/Group.swift @@ -4,8 +4,8 @@ import Foundation public struct Group: Equatable, Sendable { - let enabled: Bool - let expireDateEnabled: Bool + public let enabled: Bool + public let expireDateEnabled: Bool init?(filesSharingCapabilities: [String: Any]) { guard let groupCaps = filesSharingCapabilities["group"] as? [String: Any] else { diff --git a/Sources/NextcloudCapabilitiesKit/Shares/Share Type/PublicLink.swift b/Sources/NextcloudCapabilitiesKit/Shares/Share Type/PublicLink.swift index 6132946..c045cfc 100644 --- a/Sources/NextcloudCapabilitiesKit/Shares/Share Type/PublicLink.swift +++ b/Sources/NextcloudCapabilitiesKit/Shares/Share Type/PublicLink.swift @@ -7,12 +7,17 @@ public struct PublicLink: Equatable, Sendable { public let enabled: Bool public let allowUpload: Bool public let supportsUploadOnly: Bool + public let sendMail: Bool + public let customTokens: Bool public let askOptionalPassword: Bool public let passwordEnforced: Bool + public let expireDateEnabled: Bool public let expireDateEnforced: Bool public let expireDateDays: Int + public let internalExpireDateEnabled: Bool public let internalExpireDateEnforced: Bool public let internalExpireDateDays: Int + public let remoteExpireDateEnabled: Bool public let remoteExpireDateEnforced: Bool public let remoteExpireDateDays: Int public let multipleAllowed: Bool @@ -25,8 +30,14 @@ public struct PublicLink: Equatable, Sendable { enabled = publicLinkCaps["enabled"] as? Bool ?? false allowUpload = publicLinkCaps["upload"] as? Bool ?? false - supportsUploadOnly = publicLinkCaps["supports_upload_only"] as? Bool ?? false - multipleAllowed = publicLinkCaps["multiple"] as? Bool ?? false + supportsUploadOnly = publicLinkCaps["upload_files_drop"] as? Bool + ?? publicLinkCaps["supports_upload_only"] as? Bool + ?? false + sendMail = publicLinkCaps["send_mail"] as? Bool ?? false + customTokens = publicLinkCaps["custom_tokens"] as? Bool ?? false + multipleAllowed = publicLinkCaps["multiple_links"] as? Bool + ?? publicLinkCaps["multiple"] as? Bool + ?? false if let passwordCaps = publicLinkCaps["password"] as? [String: Any] { askOptionalPassword = passwordCaps["askForOptionalPassword"] as? Bool ?? false @@ -37,25 +48,31 @@ public struct PublicLink: Equatable, Sendable { } if let expireDateCapabilities = publicLinkCaps["expire_date"] as? [String: Any] { + expireDateEnabled = expireDateCapabilities["enabled"] as? Bool ?? false expireDateDays = expireDateCapabilities["days"] as? Int ?? 1 expireDateEnforced = expireDateCapabilities["enforced"] as? Bool ?? false } else { + expireDateEnabled = false expireDateDays = 1 expireDateEnforced = false } if let internalExpDateCaps = publicLinkCaps["expire_date_internal"] as? [String: Any] { + internalExpireDateEnabled = internalExpDateCaps["enabled"] as? Bool ?? false internalExpireDateDays = internalExpDateCaps["days"] as? Int ?? 1 internalExpireDateEnforced = internalExpDateCaps["enforced"] as? Bool ?? false } else { + internalExpireDateEnabled = false internalExpireDateDays = 1 internalExpireDateEnforced = false } if let remoteExpDateCaps = publicLinkCaps["expire_date_remote"] as? [String: Any] { + remoteExpireDateEnabled = remoteExpDateCaps["enabled"] as? Bool ?? false remoteExpireDateDays = remoteExpDateCaps["days"] as? Int ?? 1 remoteExpireDateEnforced = remoteExpDateCaps["enforced"] as? Bool ?? false } else { + remoteExpireDateEnabled = false remoteExpireDateDays = 1 remoteExpireDateEnforced = false } diff --git a/Sources/NextcloudCapabilitiesKit/Shares/Share Type/User.swift b/Sources/NextcloudCapabilitiesKit/Shares/Share Type/User.swift index f1969f9..84ab4dc 100644 --- a/Sources/NextcloudCapabilitiesKit/Shares/Share Type/User.swift +++ b/Sources/NextcloudCapabilitiesKit/Shares/Share Type/User.swift @@ -4,8 +4,8 @@ import Foundation public struct User: Equatable, Sendable { - let sendMail: Bool - let expireDateEnabled: Bool + public let sendMail: Bool + public let expireDateEnabled: Bool init?(filesSharingCapabilities: [String: Any]) { guard let userCaps = filesSharingCapabilities["user"] as? [String: Any] else { diff --git a/Sources/NextcloudCapabilitiesKit/Shares/Sharee.swift b/Sources/NextcloudCapabilitiesKit/Shares/Sharee.swift index 601e72e..1e5c6f8 100644 --- a/Sources/NextcloudCapabilitiesKit/Shares/Sharee.swift +++ b/Sources/NextcloudCapabilitiesKit/Shares/Sharee.swift @@ -4,8 +4,9 @@ import Foundation public struct Sharee: Equatable, Sendable { - let queryLookupDefault: Bool - let alwaysShowUnique: Bool + public let minSearchStringLength: Int + public let queryLookupDefault: Bool + public let alwaysShowUnique: Bool init?(filesSharingCapabilities: [String: Any]) { guard let shareeCaps = filesSharingCapabilities["sharee"] as? [String: Any] else { @@ -13,6 +14,7 @@ public struct Sharee: Equatable, Sendable { return nil } + minSearchStringLength = shareeCaps["minSearchStringLength"] as? Int ?? 0 queryLookupDefault = shareeCaps["query_lookup_default"] as? Bool ?? false alwaysShowUnique = shareeCaps["always_show_unique"] as? Bool ?? false } diff --git a/Sources/NextcloudCapabilitiesKit/Sharing.swift b/Sources/NextcloudCapabilitiesKit/Sharing.swift new file mode 100644 index 0000000..53eecd5 --- /dev/null +++ b/Sources/NextcloudCapabilitiesKit/Sharing.swift @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors +// SPDX-License-Identifier: LGPL-3.0-or-later + +import Foundation + +public struct Sharing: Equatable, Sendable { + public let apiVersions: [String] + public let sourceTypes: [SharingSourceType] + public let permissionPresets: [SharingPermissionPreset] + + init?(capabilities: [String: Any]) { + guard let capabilities = capabilities["sharing"] as? [String: Any] else { + debugPrint("No sharing data in received capabilities.") + return nil + } + + apiVersions = capabilities["api_versions"] as? [String] ?? [] + sourceTypes = (capabilities["source_types"] as? [[String: Any]] ?? []).compactMap(SharingSourceType.init) + permissionPresets = (capabilities["permission_presets"] as? [[String: Any]] ?? []).compactMap(SharingPermissionPreset.init) + } +} diff --git a/Sources/NextcloudCapabilitiesKit/Sharing/SharingPermissionPreset.swift b/Sources/NextcloudCapabilitiesKit/Sharing/SharingPermissionPreset.swift new file mode 100644 index 0000000..90e4d2d --- /dev/null +++ b/Sources/NextcloudCapabilitiesKit/Sharing/SharingPermissionPreset.swift @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors +// SPDX-License-Identifier: LGPL-3.0-or-later + +import Foundation + +public struct SharingPermissionPreset: Equatable, Sendable { + public let className: String + public let displayName: String + public let hint: String + + init?(capabilities: [String: Any]) { + guard let className = capabilities["class"] as? String, + let displayName = capabilities["display_name"] as? String, + let hint = capabilities["hint"] as? String + else { + return nil + } + + self.className = className + self.displayName = displayName + self.hint = hint + } +} diff --git a/Sources/NextcloudCapabilitiesKit/Sharing/SharingSourceType.swift b/Sources/NextcloudCapabilitiesKit/Sharing/SharingSourceType.swift new file mode 100644 index 0000000..1e4d002 --- /dev/null +++ b/Sources/NextcloudCapabilitiesKit/Sharing/SharingSourceType.swift @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors +// SPDX-License-Identifier: LGPL-3.0-or-later + +import Foundation + +public struct SharingSourceType: Equatable, Sendable { + public let className: String + + init?(capabilities: [String: Any]) { + guard let className = capabilities["class"] as? String else { + return nil + } + + self.className = className + } +} diff --git a/Sources/NextcloudCapabilitiesKit/SystemTags.swift b/Sources/NextcloudCapabilitiesKit/SystemTags.swift new file mode 100644 index 0000000..dbb8f5c --- /dev/null +++ b/Sources/NextcloudCapabilitiesKit/SystemTags.swift @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors +// SPDX-License-Identifier: LGPL-3.0-or-later + +import Foundation + +public struct SystemTags: Equatable, Sendable { + public let enabled: Bool + + init?(capabilities: [String: Any]) { + guard let capabilities = capabilities["systemtags"] as? [String: Any] else { + debugPrint("No system tags data in received capabilities.") + return nil + } + + enabled = capabilities["enabled"] as? Bool ?? false + } +} diff --git a/Sources/NextcloudCapabilitiesKit/Theming.swift b/Sources/NextcloudCapabilitiesKit/Theming.swift index 08d2542..fdffe63 100644 --- a/Sources/NextcloudCapabilitiesKit/Theming.swift +++ b/Sources/NextcloudCapabilitiesKit/Theming.swift @@ -4,20 +4,46 @@ import Foundation public struct Theming: Equatable, Sendable { - let name: String - let url: URL? - let slogan: String - let color: String - let colorText: String - let colorElement: String - let colorElementBright: String - let colorElementDark: String - let logoUrl: URL? - let backgroundColor: String - let backgroundPlain: Bool - let backgroundDefault: Bool - let logoHeaderUrl: URL? - let faviconUrl: URL? + public let name: String + public let productName: String + public let url: URL? + public let imprintURL: URL? + public let privacyURL: URL? + public let slogan: String + public let color: String + public let colorText: String + public let colorElement: String + public let colorElementBright: String + public let colorElementDark: String + public let logoURL: URL? + public let background: String + public let backgroundText: String + public let backgroundPlain: Bool + public let backgroundDefault: Bool + public let logoHeaderURL: URL? + public let faviconURL: URL? + public let primaryColor: String + public let backgroundColor: String + public let defaultPrimaryColor: String + public let defaultBackgroundColor: String + public let inverted: Bool + public let cacheBuster: String + public let enabledThemes: [String] + + @available(*, deprecated, renamed: "logoURL") + public var logoUrl: URL? { + logoURL + } + + @available(*, deprecated, renamed: "logoHeaderURL") + public var logoHeaderUrl: URL? { + logoHeaderURL + } + + @available(*, deprecated, renamed: "faviconURL") + public var faviconUrl: URL? { + faviconURL + } init?(capabilities: [String: Any]) { guard let capabilities = capabilities["theming"] as? [String: Any] else { @@ -26,18 +52,29 @@ public struct Theming: Equatable, Sendable { } name = capabilities["name"] as? String ?? "" + productName = capabilities["productName"] as? String ?? "" url = URL(string: capabilities["url"] as? String ?? "") + imprintURL = URL(string: capabilities["imprintUrl"] as? String ?? "") + privacyURL = URL(string: capabilities["privacyUrl"] as? String ?? "") slogan = capabilities["slogan"] as? String ?? "" color = capabilities["color"] as? String ?? "" colorText = capabilities["color-text"] as? String ?? "" colorElement = capabilities["color-element"] as? String ?? "" colorElementBright = capabilities["color-element-bright"] as? String ?? "" colorElementDark = capabilities["color-element-dark"] as? String ?? "" - logoUrl = URL(string: capabilities["logo"] as? String ?? "") - backgroundColor = capabilities["background"] as? String ?? "" + logoURL = URL(string: capabilities["logo"] as? String ?? "") + background = capabilities["background"] as? String ?? "" + backgroundText = capabilities["background-text"] as? String ?? "" backgroundPlain = capabilities["background-plain"] as? Bool ?? false backgroundDefault = capabilities["background-default"] as? Bool ?? false - logoHeaderUrl = URL(string: capabilities["logoheader"] as? String ?? "") - faviconUrl = URL(string: capabilities["favicon"] as? String ?? "") + logoHeaderURL = URL(string: capabilities["logoheader"] as? String ?? "") + faviconURL = URL(string: capabilities["favicon"] as? String ?? "") + primaryColor = capabilities["primaryColor"] as? String ?? "" + backgroundColor = capabilities["backgroundColor"] as? String ?? background + defaultPrimaryColor = capabilities["defaultPrimaryColor"] as? String ?? "" + defaultBackgroundColor = capabilities["defaultBackgroundColor"] as? String ?? "" + inverted = capabilities["inverted"] as? Bool ?? false + cacheBuster = capabilities["cacheBuster"] as? String ?? "" + enabledThemes = capabilities["enabledThemes"] as? [String] ?? [] } } diff --git a/Sources/NextcloudCapabilitiesKit/UserStatus.swift b/Sources/NextcloudCapabilitiesKit/UserStatus.swift index 582b6b8..d37cdd2 100644 --- a/Sources/NextcloudCapabilitiesKit/UserStatus.swift +++ b/Sources/NextcloudCapabilitiesKit/UserStatus.swift @@ -4,9 +4,10 @@ import Foundation public struct UserStatus: Equatable, Sendable { - let enabled: Bool - let restore: Bool - let supportsEmoji: Bool + public let enabled: Bool + public let restore: Bool + public let supportsEmoji: Bool + public let supportsBusy: Bool init?(capabilities: [String: Any]) { guard let capabilities = capabilities["user_status"] as? [String: Any] else { @@ -17,5 +18,6 @@ public struct UserStatus: Equatable, Sendable { enabled = capabilities["enabled"] as? Bool ?? false restore = capabilities["restore"] as? Bool ?? false supportsEmoji = capabilities["supports_emoji"] as? Bool ?? false + supportsBusy = capabilities["supports_busy"] as? Bool ?? false } } diff --git a/Sources/NextcloudCapabilitiesKit/WeatherStatus.swift b/Sources/NextcloudCapabilitiesKit/WeatherStatus.swift new file mode 100644 index 0000000..677eeb6 --- /dev/null +++ b/Sources/NextcloudCapabilitiesKit/WeatherStatus.swift @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors +// SPDX-License-Identifier: LGPL-3.0-or-later + +import Foundation + +public struct WeatherStatus: Equatable, Sendable { + public let enabled: Bool + + init?(capabilities: [String: Any]) { + guard let capabilities = capabilities["weather_status"] as? [String: Any] else { + debugPrint("No weather status data in received capabilities.") + return nil + } + + enabled = capabilities["enabled"] as? Bool ?? false + } +} diff --git a/Tests/NextcloudCapabilitiesKitTests/CoreTests.swift b/Tests/NextcloudCapabilitiesKitTests/CoreTests.swift index c35cfb9..54f43cc 100644 --- a/Tests/NextcloudCapabilitiesKitTests/CoreTests.swift +++ b/Tests/NextcloudCapabilitiesKitTests/CoreTests.swift @@ -12,7 +12,14 @@ class CoreTests: XCTestCase { "pollinterval": 30, "webdav-root": "custom/webdav", "reference-api": true, - "reference-regex": "^[a-zA-Z0-9]*$" + "reference-regex": "^[a-zA-Z0-9]*$", + "mod-rewrite-working": true, + "user": [ + "language": "en", + "locale": "en_US", + "timezone": "Europe/Berlin" + ], + "can-create-app-token": true ] ] @@ -22,6 +29,11 @@ class CoreTests: XCTestCase { XCTAssertEqual(core?.webdavRoot, "custom/webdav", "Core webdavRoot should match the provided value") XCTAssertTrue(core?.referenceApi ?? false, "Core referenceApi should match the provided value") XCTAssertEqual(core?.referenceRegex, "^[a-zA-Z0-9]*$", "Core referenceRegex should match the provided value") + XCTAssertTrue(core?.modRewriteWorking ?? false, "Core modRewriteWorking should match the provided value") + XCTAssertEqual(core?.user?.language, "en", "Core user language should match the provided value") + XCTAssertEqual(core?.user?.locale, "en_US", "Core user locale should match the provided value") + XCTAssertEqual(core?.user?.timeZone, "Europe/Berlin", "Core user timezone should match the provided value") + XCTAssertEqual(core?.canCreateAppToken, true, "Core app token capability should match the provided value") } func testCoreInitializationWithInvalidCapabilities() { @@ -46,5 +58,8 @@ class CoreTests: XCTestCase { XCTAssertEqual(core?.webdavRoot, "remote.php/webdav", "Core webdavRoot should default to 'remote.php/webdav' with partial input") XCTAssertFalse(core?.referenceApi ?? true, "Core referenceApi should default to false with partial input") XCTAssertEqual(core?.referenceRegex, "", "Core referenceRegex should default to an empty string with partial input") + XCTAssertFalse(core?.modRewriteWorking ?? true, "Core modRewriteWorking should default to false with partial input") + XCTAssertNil(core?.user, "Core user should be nil when it is not provided") + XCTAssertNil(core?.canCreateAppToken, "Core app token capability should be nil when it is not provided") } } diff --git a/Tests/NextcloudCapabilitiesKitTests/CurrentServerCapabilitiesTests.swift b/Tests/NextcloudCapabilitiesKitTests/CurrentServerCapabilitiesTests.swift new file mode 100644 index 0000000..bdfe54d --- /dev/null +++ b/Tests/NextcloudCapabilitiesKitTests/CurrentServerCapabilitiesTests.swift @@ -0,0 +1,168 @@ +// SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors +// SPDX-License-Identifier: LGPL-3.0-or-later + +@testable import NextcloudCapabilitiesKit +import XCTest + +final class CurrentServerCapabilitiesTests: XCTestCase { + func testCurrentServerCapabilities() { + let data = """ + { + "ocs": { + "data": { + "version": { + "major": 33, + "minor": 0, + "micro": 0 + }, + "capabilities": { + "core": { + "pollinterval": 60, + "webdav-root": "remote.php/webdav", + "reference-api": true, + "reference-regex": "regex", + "mod-rewrite-working": true, + "user": { + "language": "en", + "locale": "en_US", + "timezone": "Europe/Berlin" + }, + "can-create-app-token": true + }, + "dav": { + "chunking": "1.0", + "public_shares_chunking": true, + "search_supports_creation_time": true, + "search_supports_upload_time": true, + "search_supports_last_activity": true, + "bulkupload": "1.0", + "absence-supported": true, + "absence-replacement": true + }, + "files": { + "bigfilechunking": true, + "blacklisted_files": ["legacy"], + "forbidden_filenames": ["forbidden"], + "forbidden_filename_basenames": [".DS_Store"], + "forbidden_filename_characters": ["/"], + "forbidden_filename_extensions": [".tmp"], + "chunked_upload": { + "max_size": 1000, + "max_parallel_count": 3 + }, + "file_conversions": [ + { + "from": "application/vnd.oasis.opendocument.text", + "to": "application/pdf", + "extension": "pdf", + "displayName": "PDF" + } + ], + "delete_from_trash": true, + "windows_compatible_filenames": true + }, + "files_sharing": { + "api_enabled": true, + "public": { + "enabled": true, + "multiple_links": true, + "upload_files_drop": true, + "custom_tokens": true + }, + "user": { + "send_mail": false + }, + "resharing": true, + "group_sharing": true, + "group": { + "enabled": true + }, + "default_permissions": 31, + "exclude_reshare_from_edit": true, + "federation": { + "outgoing": true, + "incoming": true, + "expire_date": { + "enabled": true + }, + "expire_date_supported": { + "enabled": true + } + }, + "sharee": { + "minSearchStringLength": 3, + "query_lookup_default": true, + "always_show_unique": true + }, + "sharebymail": { + "enabled": true, + "send_password_by_mail": true, + "upload_files_drop": { + "enabled": true + }, + "password": { + "enabled": true, + "enforced": false + }, + "expire_date": { + "enabled": true, + "enforced": true + } + } + }, + "sharing": { + "api_versions": ["v1"], + "source_types": [ + { + "class": "OCA\\\\Files\\\\Sharing\\\\FileSource" + } + ], + "permission_presets": [ + { + "class": "OCA\\\\Files\\\\Sharing\\\\ReadOnlyPreset", + "display_name": "Read only", + "hint": "Can view but not edit" + } + ] + }, + "systemtags": { + "enabled": true + }, + "weather_status": { + "enabled": true + }, + "provisioning_api": { + "version": "1.0.0", + "AccountPropertyScopesVersion": 2, + "AccountPropertyScopesFederatedEnabled": true, + "AccountPropertyScopesPublishedEnabled": true + }, + "user_status": { + "enabled": true, + "restore": true, + "supports_emoji": true, + "supports_busy": true + } + } + } + } + } + """.data(using: .utf8)! + + let capabilities = Capabilities(data: data) + + XCTAssertEqual(capabilities?.core?.canCreateAppToken, true) + XCTAssertEqual(capabilities?.dav?.absenceReplacement, true) + XCTAssertEqual(capabilities?.files?.forbiddenFilenameExtensions, [".tmp"]) + XCTAssertEqual(capabilities?.files?.fileConversions.first?.displayName, "PDF") + XCTAssertEqual(capabilities?.filesSharing?.publicLink?.multipleAllowed, true) + XCTAssertEqual(capabilities?.filesSharing?.email?.expireDateEnforced, true) + XCTAssertEqual(capabilities?.sharing?.apiVersions, ["v1"]) + XCTAssertEqual(capabilities?.sharing?.sourceTypes.first?.className, "OCA\\Files\\Sharing\\FileSource") + XCTAssertEqual(capabilities?.sharing?.permissionPresets.first?.displayName, "Read only") + XCTAssertEqual(capabilities?.systemTags?.enabled, true) + XCTAssertEqual(capabilities?.weatherStatus?.enabled, true) + XCTAssertEqual(capabilities?.provisioningAPI?.accountPropertyScopesVersion, 2) + XCTAssertEqual(capabilities?.userStatus?.supportsBusy, true) + } +} diff --git a/Tests/NextcloudCapabilitiesKitTests/DavTests.swift b/Tests/NextcloudCapabilitiesKitTests/DavTests.swift index 556e736..e7bd466 100644 --- a/Tests/NextcloudCapabilitiesKitTests/DavTests.swift +++ b/Tests/NextcloudCapabilitiesKitTests/DavTests.swift @@ -10,14 +10,26 @@ class DavTests: XCTestCase { let capabilities: [String: Any] = [ "dav": [ "chunking": "1.0", - "bulkupload": "1.0" + "public_shares_chunking": true, + "search_supports_creation_time": true, + "search_supports_upload_time": true, + "search_supports_last_activity": true, + "bulkupload": "1.0", + "absence-supported": true, + "absence-replacement": true ] ] let dav = Dav(capabilities: capabilities) XCTAssertNotNil(dav, "Dav instance should be created with valid input") XCTAssertEqual(dav?.chunking, "1.0", "Dav chunking should match the provided value") + XCTAssertEqual(dav?.publicSharesChunking, true, "Dav public shares chunking should match the provided value") + XCTAssertEqual(dav?.searchSupportsCreationTime, true, "Dav creation time search should match the provided value") + XCTAssertEqual(dav?.searchSupportsUploadTime, true, "Dav upload time search should match the provided value") + XCTAssertEqual(dav?.searchSupportsLastActivity, true, "Dav activity search should match the provided value") XCTAssertEqual(dav?.bulkUpload, "1.0", "Dav bulkUpload should match the provided value") + XCTAssertEqual(dav?.absenceSupported, true, "Dav absence support should match the provided value") + XCTAssertEqual(dav?.absenceReplacement, true, "Dav absence replacement should match the provided value") } func testDavInitializationWithInvalidCapabilities() { @@ -39,6 +51,6 @@ class DavTests: XCTestCase { let dav = Dav(capabilities: capabilities) XCTAssertNotNil(dav, "Dav instance should be created with partial input") XCTAssertEqual(dav?.chunking, "1.0", "Dav chunking should match the provided value with partial input") - XCTAssertEqual(dav?.bulkUpload, "", "Dav bulkUpload should default to an empty string with partial input") + XCTAssertNil(dav?.bulkUpload, "Dav bulkUpload should be nil when it is not provided") } } diff --git a/Tests/NextcloudCapabilitiesKitTests/Files/FilesTests.swift b/Tests/NextcloudCapabilitiesKitTests/Files/FilesTests.swift index 819ab6e..97259f5 100644 --- a/Tests/NextcloudCapabilitiesKitTests/Files/FilesTests.swift +++ b/Tests/NextcloudCapabilitiesKitTests/Files/FilesTests.swift @@ -11,8 +11,21 @@ class FilesTests: XCTestCase { "files": [ "bigfilechunking": true, "blacklisted_files": ["file1", "file2"], + "forbidden_filenames": ["forbidden"], + "forbidden_filename_basenames": [".DS_Store"], + "forbidden_filename_characters": ["/"], + "forbidden_filename_extensions": [".tmp"], + "file_conversions": [ + [ + "from": "application/vnd.oasis.opendocument.text", + "to": "application/pdf", + "extension": "pdf", + "displayName": "PDF" + ] + ], "comments": false, "undelete": true, + "delete_from_trash": true, "versioning": false, "version_labeling": true, "version_deletion": false, @@ -25,7 +38,8 @@ class FilesTests: XCTestCase { "max_size": 100_000_000, "max_parallel_count": 5 ], - "locking": "1.0" + "locking": "1.0", + "windows_compatible_filenames": true ] ] @@ -34,9 +48,15 @@ class FilesTests: XCTestCase { XCTAssertNotNil(files, "Files instance should be created with valid input") XCTAssertEqual(files?.bigFileChunking, true, "Big File Chunking should be true") XCTAssertEqual(files?.blackListedFiles, ["file1", "file2"], "Blacklisted Files should match the provided values") + XCTAssertEqual(files?.forbiddenFilenames, ["forbidden"], "Forbidden filenames should match the provided values") + XCTAssertEqual(files?.forbiddenFilenameBasenames, [".DS_Store"], "Forbidden basenames should match the provided values") + XCTAssertEqual(files?.forbiddenFilenameCharacters, ["/"], "Forbidden characters should match the provided values") + XCTAssertEqual(files?.forbiddenFilenameExtensions, [".tmp"], "Forbidden extensions should match the provided values") + XCTAssertEqual(files?.fileConversions.first?.extension, "pdf", "File conversion extension should match the provided value") XCTAssertNotNil(files?.directEditing, "Direct Editing should be initialized") XCTAssertEqual(files?.comments, false, "Comments should be false") XCTAssertEqual(files?.undelete, true, "Undelete should be true") + XCTAssertEqual(files?.deleteFromTrash, true, "Delete from trash should be true") XCTAssertEqual(files?.versioning, false, "Versioning should be false") XCTAssertEqual(files?.versionLabeling, true, "Version Labeling should be true") XCTAssertEqual(files?.versionDeletion, false, "Version Deletion should be false") @@ -44,6 +64,7 @@ class FilesTests: XCTestCase { XCTAssertEqual(files?.chunkedUpload?.maxChunkSize, 100_000_000) XCTAssertEqual(files?.chunkedUpload?.maxParallelCount, 5) XCTAssertEqual(files?.locking, "1.0", "Locking should match the provided value") + XCTAssertEqual(files?.windowsCompatibleFilenames, true, "Windows compatible filenames should be true") } func testInvalidFilesInitialization() { @@ -71,6 +92,8 @@ class FilesTests: XCTestCase { XCTAssertNotNil(files, "Files instance should be created even with partially valid input") XCTAssertEqual(files?.bigFileChunking, true, "Big File Chunking should be true") XCTAssertEqual(files?.blackListedFiles, [], "Blacklisted Files should default to an empty array") + XCTAssertEqual(files?.forbiddenFilenames, [], "Forbidden filenames should default to an empty array") + XCTAssertEqual(files?.fileConversions, [], "File conversions should default to an empty array") XCTAssertNil(files?.directEditing, "Direct Editing should be nil when not provided") XCTAssertEqual(files?.comments, false, "Comments should be false") XCTAssertEqual(files?.undelete, true, "Undelete should be true") @@ -79,5 +102,7 @@ class FilesTests: XCTestCase { XCTAssertEqual(files?.versionDeletion, false, "Version Deletion should be false") XCTAssertNil(files?.chunkedUpload, "Chunked Upload should be nil when not provided") XCTAssertNil(files?.locking, "Locking should be nil when not provided") + XCTAssertFalse(files?.deleteFromTrash ?? true, "Delete from trash should default to false when not provided") + XCTAssertFalse(files?.windowsCompatibleFilenames ?? true, "Windows filename support should default to false when not provided") } } diff --git a/Tests/NextcloudCapabilitiesKitTests/Shares/FilesSharingTests.swift b/Tests/NextcloudCapabilitiesKitTests/Shares/FilesSharingTests.swift index c3bdf24..09789e5 100644 --- a/Tests/NextcloudCapabilitiesKitTests/Shares/FilesSharingTests.swift +++ b/Tests/NextcloudCapabilitiesKitTests/Shares/FilesSharingTests.swift @@ -10,29 +10,43 @@ class FilesSharingTests: XCTestCase { let validCapabilities: [String: Any] = [ "files_sharing": [ "sharebymail": [ + "enabled": true, + "send_password_by_mail": true, + "upload_files_drop": [ + "enabled": true + ], "password": [ "enabled": true, "enforced": false + ], + "expire_date": [ + "enabled": true, + "enforced": true ] ], "public": [ "enabled": true, "upload": false, - "supports_upload_only": true, - "multiple": false, + "upload_files_drop": true, + "send_mail": true, + "custom_tokens": true, + "multiple_links": false, "password": [ "askForOptionalPassword": true, "enforced": false ], "expire_date": [ + "enabled": true, "days": 10, "enforced": true ], "expire_date_internal": [ + "enabled": true, "days": 5, "enforced": true ], "expire_date_remote": [ + "enabled": true, "days": 7, "enforced": false ] @@ -60,12 +74,15 @@ class FilesSharingTests: XCTestCase { ] ], "sharee": [ + "minSearchStringLength": 3, "query_lookup_default": true, "always_show_unique": false ], "api_enabled": true, "resharing": false, - "default_permissions": 31 + "group_sharing": true, + "default_permissions": 31, + "exclude_reshare_from_edit": true ] ] @@ -79,7 +96,12 @@ class FilesSharingTests: XCTestCase { XCTAssertNotNil(filesSharing?.sharee, "Sharee subobject should be initialized") XCTAssertEqual(filesSharing?.apiEnabled, true, "APIEnabled should be true") XCTAssertEqual(filesSharing?.resharing, false, "Resharing should be false") - XCTAssertEqual(filesSharing?.defaultPermissions, 31, "DefaultPermissions should be 755") + XCTAssertEqual(filesSharing?.groupSharing, true, "Group sharing should be true") + XCTAssertEqual(filesSharing?.defaultPermissions, 31, "DefaultPermissions should be 31") + XCTAssertEqual(filesSharing?.excludeReshareFromEdit, true, "Exclude reshare from edit should be true") + XCTAssertEqual(filesSharing?.email?.sendsPasswordByEmail, true, "Share by mail should send passwords") + XCTAssertEqual(filesSharing?.publicLink?.customTokens, true, "Public links should support custom tokens") + XCTAssertEqual(filesSharing?.sharee?.minSearchStringLength, 3, "Sharee search length should match the provided value") } func testInvalidFilesSharingInitialization() { diff --git a/Tests/NextcloudCapabilitiesKitTests/Shares/Share Type/EmailTests.swift b/Tests/NextcloudCapabilitiesKitTests/Shares/Share Type/EmailTests.swift index 1495c71..4353452 100644 --- a/Tests/NextcloudCapabilitiesKitTests/Shares/Share Type/EmailTests.swift +++ b/Tests/NextcloudCapabilitiesKitTests/Shares/Share Type/EmailTests.swift @@ -9,17 +9,31 @@ final class EmailTests: XCTestCase { // Prepare test data let filesSharingCapabilities: [String: Any] = [ "sharebymail": [ + "enabled": true, + "send_password_by_mail": true, + "upload_files_drop": [ + "enabled": true + ], "password": [ "enabled": true, "enforced": false + ], + "expire_date": [ + "enabled": true, + "enforced": true ] ] ] let email = Email(filesSharingCapabilities: filesSharingCapabilities) XCTAssertNotNil(email) + XCTAssertEqual(email?.enabled, true) + XCTAssertEqual(email?.sendsPasswordByEmail, true) + XCTAssertEqual(email?.uploadFilesDropEnabled, true) XCTAssertEqual(email?.passwordEnabled, true) XCTAssertEqual(email?.passwordEnforced, false) + XCTAssertEqual(email?.expireDateEnabled, true) + XCTAssertEqual(email?.expireDateEnforced, true) } func testEmailInitializationWithMissingData() { diff --git a/Tests/NextcloudCapabilitiesKitTests/Shares/Share Type/PublicLinkTests.swift b/Tests/NextcloudCapabilitiesKitTests/Shares/Share Type/PublicLinkTests.swift index 54912f0..25c001c 100644 --- a/Tests/NextcloudCapabilitiesKitTests/Shares/Share Type/PublicLinkTests.swift +++ b/Tests/NextcloudCapabilitiesKitTests/Shares/Share Type/PublicLinkTests.swift @@ -10,21 +10,26 @@ final class PublicLinkTests: XCTestCase { "public": [ "enabled": true, "upload": false, - "supports_upload_only": true, - "multiple": false, + "upload_files_drop": true, + "send_mail": true, + "custom_tokens": true, + "multiple_links": false, "password": [ "askForOptionalPassword": true, "enforced": false ], "expire_date": [ + "enabled": true, "days": 10, "enforced": true ], "expire_date_internal": [ + "enabled": true, "days": 5, "enforced": true ], "expire_date_remote": [ + "enabled": true, "days": 7, "enforced": false ] @@ -37,13 +42,18 @@ final class PublicLinkTests: XCTestCase { XCTAssertEqual(publicLink?.enabled, true, "Enabled should be true") XCTAssertEqual(publicLink?.allowUpload, false, "AllowUpload should be false") XCTAssertEqual(publicLink?.supportsUploadOnly, true, "SupportsUploadOnly should be true") + XCTAssertEqual(publicLink?.sendMail, true, "SendMail should be true") + XCTAssertEqual(publicLink?.customTokens, true, "CustomTokens should be true") XCTAssertEqual(publicLink?.askOptionalPassword, true, "AskOptionalPassword should be true") XCTAssertEqual(publicLink?.passwordEnforced, false, "PasswordEnforced should be false") XCTAssertEqual(publicLink?.expireDateEnforced, true, "ExpireDateEnforced should be true") + XCTAssertEqual(publicLink?.expireDateEnabled, true, "ExpireDateEnabled should be true") XCTAssertEqual(publicLink?.expireDateDays, 10, "ExpireDateDays should be 10") XCTAssertEqual(publicLink?.internalExpireDateEnforced, true, "InternalExpireDateEnforced should be true") + XCTAssertEqual(publicLink?.internalExpireDateEnabled, true, "InternalExpireDateEnabled should be true") XCTAssertEqual(publicLink?.internalExpireDateDays, 5, "InternalExpireDateDays should be 5") XCTAssertEqual(publicLink?.remoteExpireDateEnforced, false, "RemoteExpireDateEnforced should be false") + XCTAssertEqual(publicLink?.remoteExpireDateEnabled, true, "RemoteExpireDateEnabled should be true") XCTAssertEqual(publicLink?.remoteExpireDateDays, 7, "RemoteExpireDateDays should be 7") XCTAssertEqual(publicLink?.multipleAllowed, false, "MultipleAllowed should be false") } diff --git a/Tests/NextcloudCapabilitiesKitTests/UserStatusTests.swift b/Tests/NextcloudCapabilitiesKitTests/UserStatusTests.swift index 6b7d818..526a8fb 100644 --- a/Tests/NextcloudCapabilitiesKitTests/UserStatusTests.swift +++ b/Tests/NextcloudCapabilitiesKitTests/UserStatusTests.swift @@ -11,7 +11,8 @@ class UserStatusTests: XCTestCase { "user_status": [ "enabled": true, "restore": false, - "supports_emoji": true + "supports_emoji": true, + "supports_busy": true ] ] @@ -20,6 +21,7 @@ class UserStatusTests: XCTestCase { XCTAssertTrue(userStatus?.enabled ?? false, "UserStatus enabled should be true") XCTAssertFalse(userStatus?.restore ?? true, "UserStatus restore should be false") XCTAssertTrue(userStatus?.supportsEmoji ?? false, "UserStatus supportsEmoji should be true") + XCTAssertTrue(userStatus?.supportsBusy ?? false, "UserStatus supportsBusy should be true") } func testUserStatusInitializationWithMissingCapabilities() { @@ -43,5 +45,6 @@ class UserStatusTests: XCTestCase { XCTAssertFalse(userStatus?.enabled ?? true, "UserStatus enabled should be false with partial input") XCTAssertFalse(userStatus?.restore ?? true, "UserStatus restore should default to false with partial input") XCTAssertFalse(userStatus?.supportsEmoji ?? true, "UserStatus supportsEmoji should default to false with partial input") + XCTAssertFalse(userStatus?.supportsBusy ?? true, "UserStatus supportsBusy should default to false with partial input") } }