/// Subset of Nextcloud's `files_sharing` capabilities block that the mobile /// sharing UI gates on. Nextcloud reports these per authenticated user, so a /// group that an admin excluded from creating public links sees /// `public.enabled == false` here — exactly how the web UI hides those buttons. /// /// The block is deeply nested and varies between server versions, so this is /// parsed by hand from the raw OCS map with safe fallbacks rather than via /// code generation. Missing fields default to the most restrictive value so a /// newer/older server never accidentally unlocks a capability. class NextcloudSharingCapabilities { /// `files_sharing.api_enabled` — master switch. When false the user may not /// create any share (user, group or link). final bool apiEnabled; /// `files_sharing.public.enabled` — public link shares allowed. final bool publicEnabled; /// `files_sharing.public.multiple_links` — more than one link per file. final bool publicMultipleLinks; /// `files_sharing.public.upload` — public upload / file-drop folders. final bool publicUploadEnabled; /// `files_sharing.public.password.enforced` — a password is mandatory on /// public links, so the create flow must collect one upfront. final bool publicPasswordEnforced; /// `files_sharing.public.expire_date.enabled`. final bool publicExpireEnabled; /// `files_sharing.public.expire_date.days` — default/maximum lifetime. final int? publicExpireDays; /// `files_sharing.public.expire_date.enforced` — expiry cannot be removed. final bool publicExpireEnforced; /// `files_sharing.group.enabled` (falls back to the older `group_sharing`). final bool groupEnabled; /// `files_sharing.resharing` — recipients may reshare. final bool resharing; // --- password_policy (a sibling capability of files_sharing) --- // These let the link-password UI state the rules up front instead of only // surfacing them after the server rejects a weak password. The // "non-common password" (breach) check can only be enforced server-side. /// `password_policy.minLength`. final int? passwordMinLength; /// `password_policy.enforceUpperLowerCase`. final bool passwordEnforceUpperLower; /// `password_policy.enforceNumericCharacters`. final bool passwordEnforceNumeric; /// `password_policy.enforceSpecialCharacters`. final bool passwordEnforceSpecial; const NextcloudSharingCapabilities({ this.apiEnabled = false, this.publicEnabled = false, this.publicMultipleLinks = false, this.publicUploadEnabled = false, this.publicPasswordEnforced = false, this.publicExpireEnabled = false, this.publicExpireDays, this.publicExpireEnforced = false, this.groupEnabled = false, this.resharing = false, this.passwordMinLength, this.passwordEnforceUpperLower = false, this.passwordEnforceNumeric = false, this.passwordEnforceSpecial = false, }); /// Parses the `files_sharing` sub-map of an OCS `cloud/capabilities` /// response, plus the optional sibling `password_policy` map. Tolerates /// missing intermediate maps and type drift. factory NextcloudSharingCapabilities.fromFilesSharing( Map filesSharing, { Map? passwordPolicy, }) { Map? sub(Map? m, String key) { final value = m?[key]; return value is Map ? value : null; } bool boolAt(Map? m, String key) => m?[key] == true; int? intAt(Map? m, String key) { final v = m?[key]; if (v is int) return v; if (v is String) return int.tryParse(v); return null; } final public = sub(filesSharing, 'public'); final password = sub(public, 'password'); final expire = sub(public, 'expire_date'); final group = sub(filesSharing, 'group'); return NextcloudSharingCapabilities( apiEnabled: boolAt(filesSharing, 'api_enabled'), publicEnabled: boolAt(public, 'enabled'), publicMultipleLinks: boolAt(public, 'multiple_links'), publicUploadEnabled: boolAt(public, 'upload'), publicPasswordEnforced: boolAt(password, 'enforced'), publicExpireEnabled: boolAt(expire, 'enabled'), publicExpireDays: intAt(expire, 'days'), publicExpireEnforced: boolAt(expire, 'enforced'), // Newer servers nest it under `group.enabled`; older ones expose a flat // `group_sharing` boolean. groupEnabled: boolAt(group, 'enabled') || boolAt(filesSharing, 'group_sharing'), resharing: boolAt(filesSharing, 'resharing'), passwordMinLength: intAt(passwordPolicy, 'minLength'), passwordEnforceUpperLower: boolAt( passwordPolicy, 'enforceUpperLowerCase', ), passwordEnforceNumeric: boolAt( passwordPolicy, 'enforceNumericCharacters', ), passwordEnforceSpecial: boolAt( passwordPolicy, 'enforceSpecialCharacters', ), ); } }