diff --git a/.github/normalize-strings.yml b/.github/normalize-strings.yml new file mode 100644 index 000000000..9ae42f15c --- /dev/null +++ b/.github/normalize-strings.yml @@ -0,0 +1,28 @@ +name: Normalize .strings file encoding +# This workflow is triggered on pushes to the repository. +on: + push: + branches: + - fix/* + - feature/* + - milestone/* + - translation-sync + +jobs: + build: + runs-on: macos-latest + name: Normalize .strings file encoding + steps: + - uses: actions/checkout@v2 + with: + submodules: true + - name: Xcode version + run: /usr/bin/xcodebuild -version + - name: Normalize .strings files to UTF-8 + run: ./normalizestrings.sh + working-directory: ./tools/normalizestrings/ + - name: Commit files + uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: Normalized Localizable.strings encoding (UTF-8) + file_pattern: '*.strings' diff --git a/CHANGELOG.md b/CHANGELOG.md index 03058aba8..d69a8e65e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,73 @@ +Changelog for ownCloud iOS Client [12.0.3] (2023-08-31) +======================================= +The following sections list the changes in ownCloud iOS Client 12.0.3 relevant to +ownCloud admins and users. + +[12.0.3]: https://github.com/owncloud/ios-app/compare/milestone/12.0.2...milestone/12.0.3 + +Summary +------- + +* Bugfix - Upload-Metadata: [#1227](https://github.com/owncloud/ios-app/issues/1227) +* Bugfix - Open Folder in Files.app: [#1240](https://github.com/owncloud/ios-app/issues/1240) +* Bugfix - Connection name: [#1254](https://github.com/owncloud/ios-app/issues/1254) +* Bugfix - Unable to access files from Files.app: [#1262](https://github.com/owncloud/ios-app/issues/1262) +* Bugfix - File Provider Crash: [#1266](https://github.com/owncloud/ios-app/issues/1266) +* Bugfix - Translation: [#1269](https://github.com/owncloud/ios-app/pull/1269) +* Bugfix - Open in Web for ownCloud 10: [#5747](https://github.com/owncloud/enterprise/issues/5747) +* Bugfix - Copy Item not working: [#5889](https://github.com/owncloud/enterprise/issues/5889) + +Details +------- + +* Bugfix - Upload-Metadata: [#1227](https://github.com/owncloud/ios-app/issues/1227) + + TUS -H Upload-Metadata: mtime missing + + https://github.com/owncloud/ios-app/issues/1227 + +* Bugfix - Open Folder in Files.app: [#1240](https://github.com/owncloud/ios-app/issues/1240) + + In some cases it was not possible to open Folder using iOS Files.app. + + https://github.com/owncloud/ios-app/issues/1240 + +* Bugfix - Connection name: [#1254](https://github.com/owncloud/ios-app/issues/1254) + + Connection name doesn't change immediately + + https://github.com/owncloud/ios-app/issues/1254 + +* Bugfix - Unable to access files from Files.app: [#1262](https://github.com/owncloud/ios-app/issues/1262) + + In some cases it was not possible to access files from the Files.app. + + https://github.com/owncloud/ios-app/issues/1262 + +* Bugfix - File Provider Crash: [#1266](https://github.com/owncloud/ios-app/issues/1266) + + IOS invokes a FileProvider method with a nil value, which causes a crash. + + https://github.com/owncloud/ios-app/issues/1266 + +* Bugfix - Translation: [#1269](https://github.com/owncloud/ios-app/pull/1269) + + Updated translations from Transifex. + + https://github.com/owncloud/ios-app/pull/1269 + +* Bugfix - Open in Web for ownCloud 10: [#5747](https://github.com/owncloud/enterprise/issues/5747) + + Open in Web feature was not available for ownCloud 10 instances in the app. + + https://github.com/owncloud/enterprise/issues/5747 + +* Bugfix - Copy Item not working: [#5889](https://github.com/owncloud/enterprise/issues/5889) + + Copy and paste to a Space was not working + + https://github.com/owncloud/enterprise/issues/5889 + Changelog for ownCloud iOS Client [12.0.2] (2023-06-23) ======================================= The following sections list the changes in ownCloud iOS Client 12.0.2 relevant to diff --git a/changelog/12.0.3_2023-08-31/1227 b/changelog/12.0.3_2023-08-31/1227 new file mode 100644 index 000000000..b17c660f8 --- /dev/null +++ b/changelog/12.0.3_2023-08-31/1227 @@ -0,0 +1,5 @@ +Bugfix: Upload-Metadata + +TUS -H Upload-Metadata: mtime missing + +https://github.com/owncloud/ios-app/issues/1227 diff --git a/changelog/12.0.3_2023-08-31/1240 b/changelog/12.0.3_2023-08-31/1240 new file mode 100644 index 000000000..6bdce670c --- /dev/null +++ b/changelog/12.0.3_2023-08-31/1240 @@ -0,0 +1,5 @@ +Bugfix: Open Folder in Files.app + +In some cases it was not possible to open Folder using iOS Files.app. + +https://github.com/owncloud/ios-app/issues/1240 diff --git a/changelog/12.0.3_2023-08-31/1254 b/changelog/12.0.3_2023-08-31/1254 new file mode 100644 index 000000000..6cec6fc63 --- /dev/null +++ b/changelog/12.0.3_2023-08-31/1254 @@ -0,0 +1,5 @@ +Bugfix: Connection name + +Connection name doesn't change immediately + +https://github.com/owncloud/ios-app/issues/1254 diff --git a/changelog/12.0.3_2023-08-31/1262 b/changelog/12.0.3_2023-08-31/1262 new file mode 100644 index 000000000..c8f199d69 --- /dev/null +++ b/changelog/12.0.3_2023-08-31/1262 @@ -0,0 +1,5 @@ +Bugfix: Unable to access files from Files.app + +In some cases it was not possible to access files from the Files.app. + +https://github.com/owncloud/ios-app/issues/1262 diff --git a/changelog/12.0.3_2023-08-31/1266 b/changelog/12.0.3_2023-08-31/1266 new file mode 100644 index 000000000..73e0ef041 --- /dev/null +++ b/changelog/12.0.3_2023-08-31/1266 @@ -0,0 +1,5 @@ +Bugfix: File Provider Crash + +iOS invokes a FileProvider method with a nil value, which causes a crash. + +https://github.com/owncloud/ios-app/issues/1266 diff --git a/changelog/12.0.3_2023-08-31/1269 b/changelog/12.0.3_2023-08-31/1269 new file mode 100644 index 000000000..6a73e73b6 --- /dev/null +++ b/changelog/12.0.3_2023-08-31/1269 @@ -0,0 +1,5 @@ +Bugfix: Translation + +Updated translations from Transifex. + +https://github.com/owncloud/ios-app/pull/1269 diff --git a/changelog/12.0.3_2023-08-31/5747 b/changelog/12.0.3_2023-08-31/5747 new file mode 100644 index 000000000..a5c7d6843 --- /dev/null +++ b/changelog/12.0.3_2023-08-31/5747 @@ -0,0 +1,5 @@ +Bugfix: Open in Web for ownCloud 10 + +Open in Web feature was not available for ownCloud 10 instances in the app. + +https://github.com/owncloud/enterprise/issues/5747 diff --git a/changelog/12.0.3_2023-08-31/5889 b/changelog/12.0.3_2023-08-31/5889 new file mode 100644 index 000000000..1573f1e9c --- /dev/null +++ b/changelog/12.0.3_2023-08-31/5889 @@ -0,0 +1,5 @@ +Bugfix: Copy Item not working + +Copy and paste to a Space was not working + +https://github.com/owncloud/enterprise/issues/5889 diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 53eeba7fb..90cd7e1f1 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -185,9 +185,8 @@ platform :ios do ) puts("Build EMM iOS App…") - # Set EMM App-Icon for App Build - sh "mv ../ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/ ../ownCloud/Resources/Assets.xcassets/AppIcon-regular.appiconset/" - sh "mv ../ownCloud/Resources/Assets.xcassets/AppIcon-emm.appiconset/ ../ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/" + # Moving all EMM resources to the correct place + sh "cp -R ../ownCloud/Resources/Theming/com.owncloud.ios-app.emm/* ../ownCloud/Resources/Theming/" # Build App owncloud_emm_build() @@ -222,30 +221,9 @@ platform :ios do puts("Build ownCloud Online iOS App…") - # Moving the ownCloud.online Branding.plist to the correct place - sh "mv ../ownCloud/Resources/Theming/Branding.plist ../ownCloud/Resources/Theming/Branding-regular.plist" - sh "mv ../ownCloud/Resources/Theming/Branding-owncloud-online.plist ../ownCloud/Resources/Theming/Branding.plist" - - # Set ownCloud.online Splashscreen Background for App Build - sh "mv ../ownCloud/Resources/Theming/branding-splashscreen-background.png ../ownCloud/Resources/Theming/branding-splashscreen-background-regular.png" - sh "mv ../ownCloud/Resources/Theming/branding-splashscreen-background-owncloud-online.png ../ownCloud/Resources/Theming/branding-splashscreen-background.png" - - # Set ownCloud.online Splashscreen Logo for App Build - sh "mv ../ownCloud/Resources/Theming/branding-splashscreen.png ../ownCloud/Resources/Theming/branding-splashscreen-regular.png" - sh "mv ../ownCloud/Resources/Theming/branding-splashscreen-owncloud-online.png ../ownCloud/Resources/Theming/branding-splashscreen.png" - - # Set ownCloud.online Branded Login Logo for App Build - sh "mv ../ownCloud/Resources/Theming/branding-login-logo.png ../ownCloud/Resources/Theming/branding-login-logo-regular.png" - sh "mv ../ownCloud/Resources/Theming/branding-login-logo-owncloud-online.png ../ownCloud/Resources/Theming/branding-login-logo.png" - - # Set ownCloud.online Branded Login Background for App Build - sh "mv ../ownCloud/Resources/Theming/branding-login-background.png ../ownCloud/Resources/Theming/branding-login-background-regular.png" - sh "mv ../ownCloud/Resources/Theming/branding-login-background-owncloud-online.png ../ownCloud/Resources/Theming/branding-login-background.png" - - # Set ownCloud.online App-Icon for App Build - sh "mv ../ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/ ../ownCloud/Resources/Assets.xcassets/AppIcon-regular.appiconset/" - sh "mv ../ownCloud/Resources/Assets.xcassets/AppIcon-ownCloud-online.appiconset/ ../ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/" - + # Moving all ownCloud.online resources to the correct place + sh "cp -R ../ownCloud/Resources/Theming/online.owncloud.ios-app/* ../ownCloud/Resources/Theming/" + # Build App owncloud_online_build() @@ -424,7 +402,7 @@ end end lane :generate_appicon do - iconPath = "ownCloud/Resources/Theming/icon-1024.png" + iconPath = "ownCloud/Resources/Theming/branding-icon.png" if File.exist?("../" + iconPath) appicon( appicon_image_file: iconPath, diff --git a/fastlane/metadata-emm/en-US/release_notes.txt b/fastlane/metadata-emm/en-US/release_notes.txt index 9633be580..e9ca129c0 100644 --- a/fastlane/metadata-emm/en-US/release_notes.txt +++ b/fastlane/metadata-emm/en-US/release_notes.txt @@ -1,7 +1,3 @@ -Bug Fixes - -• Added missing Recents Group -• Unable to authenticate using OpenID Connect -• Attach files from third-party apps -• Solves "Content unavailable" in Files.app +• Bug Fixes +Fixed File Provider, App Provider and layout issues. diff --git a/fastlane/metadata-owncloud-online/en-US/release_notes.txt b/fastlane/metadata-owncloud-online/en-US/release_notes.txt index 9633be580..e9ca129c0 100644 --- a/fastlane/metadata-owncloud-online/en-US/release_notes.txt +++ b/fastlane/metadata-owncloud-online/en-US/release_notes.txt @@ -1,7 +1,3 @@ -Bug Fixes - -• Added missing Recents Group -• Unable to authenticate using OpenID Connect -• Attach files from third-party apps -• Solves "Content unavailable" in Files.app +• Bug Fixes +Fixed File Provider, App Provider and layout issues. diff --git a/fastlane/metadata/en-US/release_notes.txt b/fastlane/metadata/en-US/release_notes.txt index 9633be580..e9ca129c0 100644 --- a/fastlane/metadata/en-US/release_notes.txt +++ b/fastlane/metadata/en-US/release_notes.txt @@ -1,7 +1,3 @@ -Bug Fixes - -• Added missing Recents Group -• Unable to authenticate using OpenID Connect -• Attach files from third-party apps -• Solves "Content unavailable" in Files.app +• Bug Fixes +Fixed File Provider, App Provider and layout issues. diff --git a/fastlane/screenshots/bg_BG/keyword.strings b/fastlane/screenshots/bg_BG/keyword.strings new file mode 100644 index 000000000..775d68e0e Binary files /dev/null and b/fastlane/screenshots/bg_BG/keyword.strings differ diff --git a/fastlane/screenshots/bg_BG/title.strings b/fastlane/screenshots/bg_BG/title.strings new file mode 100644 index 000000000..4b51fcb3a Binary files /dev/null and b/fastlane/screenshots/bg_BG/title.strings differ diff --git a/fastlane/screenshots/de-DE/keyword.strings b/fastlane/screenshots/de-DE/keyword.strings index fe237c7fe..7f55500ee 100644 Binary files a/fastlane/screenshots/de-DE/keyword.strings and b/fastlane/screenshots/de-DE/keyword.strings differ diff --git a/fastlane/screenshots/de-DE/title.strings b/fastlane/screenshots/de-DE/title.strings index 0a9465ab8..73399af8f 100644 Binary files a/fastlane/screenshots/de-DE/title.strings and b/fastlane/screenshots/de-DE/title.strings differ diff --git a/ios-sdk b/ios-sdk index e301f3dff..740b63e7e 160000 --- a/ios-sdk +++ b/ios-sdk @@ -1 +1 @@ -Subproject commit e301f3dff57ea85e9119c809bb165f7a68c4e09b +Subproject commit 740b63e7eb05a906558cc17cb03415aa8257f327 diff --git a/ownCloud File Provider/FileProviderContentEnumerator.h b/ownCloud File Provider/FileProviderContentEnumerator.h index 08f0e8ede..635caad0e 100644 --- a/ownCloud File Provider/FileProviderContentEnumerator.h +++ b/ownCloud File Provider/FileProviderContentEnumerator.h @@ -26,6 +26,8 @@ NS_ASSUME_NONNULL_BEGIN { NSMutableArray *_enumerationObservers; NSMutableArray *_changeObservers; + + NSHashTable *_didReturnAnyContentQueries; } @property(nonatomic,readonly,class) OCAsyncSequentialQueue *queue; diff --git a/ownCloud File Provider/FileProviderContentEnumerator.m b/ownCloud File Provider/FileProviderContentEnumerator.m index 7bc81146d..9578b211b 100644 --- a/ownCloud File Provider/FileProviderContentEnumerator.m +++ b/ownCloud File Provider/FileProviderContentEnumerator.m @@ -72,6 +72,8 @@ - (instancetype)initWithVFSCore:(OCVFSCore *)vfsCore containerItemIdentifier:(OC _enumerationObservers = [NSMutableArray new]; _changeObservers = [NSMutableArray new]; + _didReturnAnyContentQueries = [NSHashTable weakObjectsHashTable]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_displaySettingsChanged:) name:DisplaySettingsChanged object:nil]; } @@ -217,59 +219,85 @@ - (void)invalidate - (void)requestContentWithErrorHandler:(void(^)(NSError *))errorHandler contentConsumer:(void(^)(OCVFSContent *))contentConsumer observerQueuer:(BOOL(^)(dispatch_block_t completionHandler))observerQueuer { __weak FileProviderContentEnumerator *weakSelf = self; + OCVFSItemID containerItemIdentifier = _containerItemIdentifier; + NSString *contentRequestUUID = [containerItemIdentifier stringByAppendingFormat:@"#%@", NSUUID.UUID.UUIDString]; + + OCLogDebug(@"[QUEUE] Queuing content request %@", contentRequestUUID); [FileProviderContentEnumerator.queue async:^(dispatch_block_t _Nonnull completionHandler) { FileProviderContentEnumerator *strongSelf = weakSelf; + OCWLogDebug(@"[START] Starting content request %@", contentRequestUUID); + + // Add completion handler call debugging + completionHandler = ^{ + OCWLogDebug(@"[CMPHL] Completion handler called for request %@, stack trace: %@", contentRequestUUID, NSThread.callStackSymbols); + completionHandler(); + }; + if (strongSelf == nil) { + OCWLogDebug(@"[CMPLT] Content Enumerator deallocated before content could be returned for %@", contentRequestUUID); + errorHandler(OCErrorWithDescription(OCErrorInternal, @"Content Enumerator deallocated before content could be returned")); + completionHandler(); return; } - [strongSelf.vfsCore provideContentForContainerItemID:strongSelf->_containerItemIdentifier changesFromSyncAnchor:nil completionHandler:^(NSError * _Nullable error, OCVFSContent * _Nullable content) { + [strongSelf.vfsCore provideContentForContainerItemID:containerItemIdentifier changesFromSyncAnchor:nil completionHandler:^(NSError * _Nullable error, OCVFSContent * _Nullable content) { + OCWLogDebug(@"[HAND1] Handling response for content request %@", contentRequestUUID); + dispatch_async(FileProviderContentEnumerator.dispatchQueue, ^{ FileProviderContentEnumerator *strongSelf = weakSelf; + OCWLogDebug(@"[HAND2] Handling response for content request %@", contentRequestUUID); + if (strongSelf == nil) { + OCWLogDebug(@"[ERROR] Content Enumerator deallocated before content could be returned for %@", contentRequestUUID); + errorHandler(OCErrorWithDescription(OCErrorInternal, @"Content Enumerator deallocated before content could be returned")); + completionHandler(); return; } if (error != nil) { + OCWLogDebug(@"[ERROR] Content Enumerator VFS response error %@ for %@", error, contentRequestUUID); errorHandler(error); - completionHandler(); } else { if (content.isSnapshot) { // Content is a snapshot, so there's no need to keep the content around - it can be sent now + OCWLogDebug(@"[CMPLT] Content Enumerator VFS snapshot response for %@", contentRequestUUID); contentConsumer(content); - completionHandler(); } else { // Content is self-updating, so we can send it if (strongSelf.content != nil) { + // Content already available + OCWLogDebug(@"[CMPLT] Content Enumerator VFS immediately available content response for %@", contentRequestUUID); contentConsumer(strongSelf.content); - completionHandler(); } else { - if (observerQueuer(completionHandler)) - { - strongSelf.content = content; - } - else + // Content to be provided by observer + OCWLogDebug(@"[REQST] Content Enumerator VFS provided asynchronously for %@", contentRequestUUID); + strongSelf.content = content; // effectively sets it to nil, since this can only be reached following (strongSelf.content == nil) + + if (!observerQueuer(nil)) { - strongSelf.content = content; - completionHandler(); + // No observer available - unexpected, so complete with an error + OCWLogDebug(@"[ERROR] No content observer available for %@", contentRequestUUID); + errorHandler(OCErrorWithDescription(OCErrorInternal, @"No content observer available")); } } } } + + completionHandler(); }); }]; }]; @@ -542,8 +570,11 @@ - (void)setContent:(OCVFSContent *)content #pragma mark - Content distribution - (BOOL)provideItemsToEnumerationObserver:(id)enumerationObserver fromContent:(OCVFSContent *)content { - if (((content.query.state == OCQueryStateContentsFromCache) || ((content.query.state == OCQueryStateWaitingForServerReply) && (content.query.queryResults.count > 0)) || (content.query.state == OCQueryStateIdle)) - || ((content.query == nil) && (content != nil))) + if (( (content.query.state == OCQueryStateContentsFromCache) || + ((content.query.state == OCQueryStateWaitingForServerReply) && (content.query.queryResults.count > 0)) || + (content.query.state == OCQueryStateIdle) + ) || + ((content.query == nil) && (content != nil))) { NSArray *queryResults = content.query.queryResults; OCBookmarkUUIDString bookmarkUUIDString = content.core.bookmark.uuid.UUIDString; @@ -553,6 +584,11 @@ - (BOOL)provideItemsToEnumerationObserver:(id item.bookmarkUUID = bookmarkUUIDString; } + if (content.query != nil) + { + [_didReturnAnyContentQueries addObject:content.query]; + } + OCLogDebug(@"##### PROVIDE ITEMS TO %ld --ENUMERATION-- OBSERVER %@ FOR %@: %@", _enumerationObservers.count, enumerationObserver, content.query.queryLocation.path, queryResults); dispatch_async(dispatch_get_main_queue(), ^{ @@ -587,6 +623,11 @@ - (BOOL)provideItemsForChangeObserver:(id)changeOb item.bookmarkUUID = bookmarkUUIDString; } + if (content.query != nil) + { + [_didReturnAnyContentQueries addObject:content.query]; + } + NSFileProviderSyncAnchor syncAnchor = [content.core.latestSyncAnchor syncAnchorData]; dispatch_async(dispatch_get_main_queue(), ^{ @@ -598,14 +639,14 @@ - (BOOL)provideItemsForChangeObserver:(id)changeOb } #pragma mark - OCQuery delegate -- (void)queryHasChangesAvailable:(OCQuery *)query +- (void)_updateFromQuery:(OCQuery *)query { - OCLogDebug(@"##### Query for %@ has changes. Query state: %lu, SinceSyncAnchor: %@, Changes available: %d", query.queryLocation.path, (unsigned long)query.state, query.querySinceSyncAnchor, query.hasChangesAvailable); - if ( (query.state == OCQueryStateContentsFromCache) || ((query.state == OCQueryStateWaitingForServerReply) && (query.queryResults.count > 0)) || (query.state == OCQueryStateIdle)) { + OCLogDebug(@"##### Serving query %@ for %@ content. Query state: %lu, SinceSyncAnchor: %@, Changes available: %d", query, query.queryLocation.path, (unsigned long)query.state, query.querySinceSyncAnchor, query.hasChangesAvailable); + dispatch_async(FileProviderContentEnumerator.dispatchQueue, ^{ // Send content to enumeration observers NSArray *enumerationObservers = [self->_enumerationObservers copy]; @@ -619,7 +660,6 @@ - (void)queryHasChangesAvailable:(OCQuery *)query } } - // Send content to change observers NSArray *changeObservers = [self->_changeObservers copy]; @@ -635,6 +675,32 @@ - (void)queryHasChangesAvailable:(OCQuery *)query } } +- (void)queryHasChangesAvailable:(OCQuery *)query +{ + OCLogDebug(@"##### Query %@ for %@ has changes. Query state: %lu, SinceSyncAnchor: %@, Changes available: %d", query, query.queryLocation.path, (unsigned long)query.state, query.querySinceSyncAnchor, query.hasChangesAvailable); + + [self _updateFromQuery:query]; +} + +- (void)queryHasChangedState:(OCQuery *)query +{ + OCLogDebug(@"##### Query %@ for %@ has changed state. Query state: %lu, SinceSyncAnchor: %@, Changes available: %d", query, query.queryLocation.path, (unsigned long)query.state, query.querySinceSyncAnchor, query.hasChangesAvailable); + + BOOL didReturnContentForQueryBefore = [_didReturnAnyContentQueries containsObject:query]; + BOOL isEmptyQueryResult = (query.queryResults.count == 0); + + // This catches the following special case: + // - query has no results (f.ex. empty folder) + // -> query had no results when returning from the database and waiting for a reply (=> does not provide items to FP) + // -> query returns from PROPFIND and changes state to idle, but still has no results (=> chance that only state changes, no additional call to -queryHasChangesAvailable:, because it has not) + // -> query does not return any results -> Files.app shows spinner indefinitely (=> now, the state change to idle with an empty result set triggers a return of items + if ((query.state == OCQueryStateIdle) && !didReturnContentForQueryBefore && isEmptyQueryResult) + { + OCLogDebug(@"Would have updated %@", query); + // [self _updateFromQuery:query]; + } +} + - (void)query:(OCQuery *)query failedWithError:(NSError *)error { OCLogDebug(@"### Query failed with error: %@", error); diff --git a/ownCloud File Provider/FileProviderEnumerator.h b/ownCloud File Provider/FileProviderEnumerator.h deleted file mode 100644 index 71e3e85c8..000000000 --- a/ownCloud File Provider/FileProviderEnumerator.h +++ /dev/null @@ -1,50 +0,0 @@ -// -// FileProviderEnumerator.h -// ownCloud File Provider -// -// Created by Felix Schwarz on 07.06.18. -// Copyright © 2018 ownCloud GmbH. All rights reserved. -// - -/* - * Copyright (C) 2018, ownCloud GmbH. - * - * This code is covered by the GNU Public License Version 3. - * - * For distribution utilizing Apple mechanisms please see https://owncloud.org/contribute/iOS-license-exception/ - * You should have received a copy of this license along with this program. If not, see . - * - */ - -#import -#import -#import "FileProviderEnumeratorObserver.h" - -@class FileProviderExtension; - -@interface FileProviderEnumerator : NSObject -{ - __weak FileProviderExtension *_fileProviderExtension; - - __weak OCCore *_core; - OCBookmark *_bookmark; - NSFileProviderItemIdentifier _enumeratedItemIdentifier; - - OCQuery *_query; - - NSMutableArray *_enumerationObservers; - NSMutableArray *_changeObservers; - - BOOL _invalidated; - - OCMeasurement *_measurement; -} - -@property(weak) FileProviderExtension *fileProviderExtension; - -- (instancetype)init NS_UNAVAILABLE; -- (instancetype)initWithBookmark:(OCBookmark *)bookmark enumeratedItemIdentifier:(NSFileProviderItemIdentifier)enumeratedItemIdentifier; - -@property (nonatomic, readonly, strong) NSFileProviderItemIdentifier enumeratedItemIdentifier; - -@end diff --git a/ownCloud File Provider/FileProviderEnumerator.m b/ownCloud File Provider/FileProviderEnumerator.m deleted file mode 100644 index d2cfebfb3..000000000 --- a/ownCloud File Provider/FileProviderEnumerator.m +++ /dev/null @@ -1,464 +0,0 @@ -// -// FileProviderEnumerator.m -// ownCloud File Provider -// -// Created by Felix Schwarz on 07.06.18. -// Copyright © 2018 ownCloud GmbH. All rights reserved. -// - -/* - * Copyright (C) 2018, ownCloud GmbH. - * - * This code is covered by the GNU Public License Version 3. - * - * For distribution utilizing Apple mechanisms please see https://owncloud.org/contribute/iOS-license-exception/ - * You should have received a copy of this license along with this program. If not, see . - * - */ - -#import - -#import "FileProviderEnumerator.h" -#import "FileProviderExtension.h" -#import "OCCore+FileProviderTools.h" -#import "OCItem+FileProviderItem.h" -#import "NSNumber+OCSyncAnchorData.h" - -@interface OCVault (InternalSignal) -- (void)signalEnumeratorForContainerItemIdentifier:(NSFileProviderItemIdentifier)changedDirectoryLocalID; -@end - - -@implementation FileProviderEnumerator - -@synthesize fileProviderExtension = _fileProviderExtension; - -- (instancetype)initWithBookmark:(OCBookmark *)bookmark enumeratedItemIdentifier:(NSFileProviderItemIdentifier)enumeratedItemIdentifier -{ - if ((self = [super init]) != nil) - { - _bookmark = bookmark; - _enumeratedItemIdentifier = enumeratedItemIdentifier; - - _enumerationObservers = [NSMutableArray new]; - _changeObservers = [NSMutableArray new]; - - _measurement = [OCMeasurement measurementWithTitle:[NSString stringWithFormat:@"Enumerator for item %@", enumeratedItemIdentifier]]; - - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_displaySettingsChanged:) name:DisplaySettingsChanged object:nil]; - } - - return (self); -} - -- (void)invalidate -{ - OCLogDebug(@"##### INVALIDATE %@", _query.queryLocation); - - [[NSNotificationCenter defaultCenter] removeObserver:self name:DisplaySettingsChanged object:nil]; - - if (_core != nil) - { - [_core stopQuery:_query]; - - [[OCCoreManager sharedCoreManager] returnCoreForBookmark:_bookmark completionHandler:nil]; - - _core = nil; - } - - _query.delegate = nil; - _query = nil; -} - -- (void)_displaySettingsChanged:(NSNotification *)notification -{ - OCLogDebug(@"Received display settings update notification (enumerator for %@)", _enumeratedItemIdentifier); - - [DisplaySettings.sharedDisplaySettings updateQueryWithDisplaySettings:_query]; - - if (_enumeratedItemIdentifier != nil) - { - [_core.vault signalEnumeratorForContainerItemIdentifier:_enumeratedItemIdentifier]; - } -} - -- (void)enumerateItemsForObserver:(id)observer startingAtPage:(NSFileProviderPage)page -{ - FileProviderEnumeratorObserver *enumerationObserver = [FileProviderEnumeratorObserver new]; - - OCLogDebug(@"##### Enumerate ITEMS for observer: %@ fromPage: %@", observer, page); - - enumerationObserver.enumerationObserver = observer; - enumerationObserver.enumerationStartPage = page; - enumerationObserver.didProvideInitialItems = NO; - - @synchronized(self) - { - [_enumerationObservers addObject:enumerationObserver]; - } - - [self _startQuery]; -} - -- (void)_finishAllEnumeratorsWithError:(NSError *)error -{ - @synchronized(self) - { - for (FileProviderEnumeratorObserver *observer in _enumerationObservers) - { - dispatch_async(dispatch_get_main_queue(), ^{ - [observer.enumerationObserver finishEnumeratingWithError:error]; - }); - } - [_enumerationObservers removeAllObjects]; - - for (FileProviderEnumeratorObserver *observer in _changeObservers) - { - dispatch_async(dispatch_get_main_queue(), ^{ - [observer.changeObserver finishEnumeratingWithError:error]; - }); - } - [_changeObservers removeAllObjects]; - } -} - -- (void)_startQuery -{ - OCLogDebug(@"##### Starting query.."); - - OCMeasureEvent(self, @"fp.enumerator", @"Starting enumerator…"); - - if ((_core == nil) && (_query == nil)) - { - OCMeasureEventBegin(self, @"fp.enumerator", coreRequestRef, @"Requesting core…"); - - [[OCCoreManager sharedCoreManager] requestCoreForBookmark:_bookmark setup:nil completionHandler:^(OCCore *core, NSError *error) { - OCMeasureEventEnd(self, @"fp.enumerator", coreRequestRef, @"Received core…"); - - if (self->_core != nil) - { - // Already has a core - balance duplicate requested core - [[OCCoreManager sharedCoreManager] returnCoreForBookmark:core.bookmark completionHandler:nil]; - } - else - { - self->_core = core; - } - - if (error != nil) - { - // TODO: Report error as NSFileProviderErrorServerUnreachable or NSFileProviderErrorNotAuthenticated, depending on what the underlying error is - [self _finishAllEnumeratorsWithError:error]; - } - else - { - // Create and add query - __block OCPath queryPath = nil; - - OCMeasureEventBegin(self, @"db.resolve-item", resolveEventRef, @"Resolve item identifier"); - - if ([self->_enumeratedItemIdentifier isEqualToString:NSFileProviderRootContainerItemIdentifier]) - { - queryPath = @"/"; - } - else - { - NSError *error = nil; - OCItem *item; - - if ((item = [core synchronousRetrieveItemFromDatabaseForLocalID:self->_enumeratedItemIdentifier syncAnchor:NULL error:&error]) != nil) - { - if (item.type == OCItemTypeCollection) - { - queryPath = item.path; - } -// -// if (item.type == OCItemTypeFile) -// { -// OCLogDebug(@"Observe item: %@", item); -// -// [observer didEnumerateItems:@[ item ]]; -// [observer finishEnumeratingUpToPage:nil]; -// return; -// } - } - } - - OCMeasureEventEnd(self, @"db.resolve-item", resolveEventRef, @"Resolve item identifier"); - - if (queryPath == nil) - { - // Item not found or not a directory - NSError *enumerationError = [NSError errorWithDomain:NSFileProviderErrorDomain code:NSFileProviderErrorNoSuchItem userInfo:nil]; - - [self _finishAllEnumeratorsWithError:enumerationError]; - return; - } - else - { - // Start query - self->_query = [OCQuery queryForLocation:[OCLocation legacyRootPath:queryPath]]; - self->_query.includeRootItem = queryPath.isRootPath; // Include the root item only for the root folder. If it's not included, no folder can be created in the root directory. If a non-root folder is included in a query result for its content, the Files Duplicate action will loop infinitely. - self->_query.delegate = self; - - [DisplaySettings.sharedDisplaySettings updateQueryWithDisplaySettings:self->_query]; - - @synchronized(self) - { - if ([self->_enumerationObservers.lastObject.enumerationStartPage isEqual:NSFileProviderInitialPageSortedByDate]) - { - self->_query.sortComparator = ^NSComparisonResult(OCItem *item1, OCItem *item2) { - return ([item1.lastModified compare:item2.lastModified]); - }; - } - - if ([self->_enumerationObservers.lastObject.enumerationStartPage isEqual:NSFileProviderInitialPageSortedByName]) - { - self->_query.sortComparator = ^NSComparisonResult(OCItem *item1, OCItem *item2) { - return ([item1.name compare:item2.name]); - }; - } - } - - OCLogDebug(@"##### START QUERY FOR %@", self->_query.queryLocation); - - [self->_query attachMeasurement:self->_measurement]; - - [core startQuery:self->_query]; - } - } - }]; - } - else - { - OCLogDebug(@"Query already running.."); - - if (_query != nil) - { - @synchronized(self) - { - if (_enumerationObservers.count!=0) - { - dispatch_async(dispatch_get_main_queue(), ^{ - [self provideItemsForEnumerationObserverFromQuery:self->_query]; - }); - } - - if (_changeObservers.count!=0) - { - dispatch_async(dispatch_get_main_queue(), ^{ - [self provideItemsForChangeObserverFromQuery:self->_query]; - }); - } - } - } - } - - /* TODO: - - inspect the page to determine whether this is an initial or a follow-up request - - If this is an enumerator for a directory, the root container or all directories: - - perform a server request to fetch directory contents - If this is an enumerator for the active set: - - perform a server request to update your local database - - fetch the active set from your local database - - - inform the observer about the items returned by the server (possibly multiple times) - - inform the observer that you are finished with this page - */ -} - -- (void)provideItemsForEnumerationObserverFromQuery:(OCQuery *)query -{ - if ((query.state == OCQueryStateContentsFromCache) || ((query.state == OCQueryStateWaitingForServerReply) && (query.queryResults.count > 0)) || (query.state == OCQueryStateIdle)) - { - @synchronized(self) - { - NSMutableArray *removeObservers = [NSMutableArray new]; - - for (FileProviderEnumeratorObserver *observer in _enumerationObservers) - { - if (observer.enumerationObserver != nil) - { - if (!observer.didProvideInitialItems) - { - NSArray *queryResults = query.queryResults; - - OCLogDebug(@"##### PROVIDE ITEMS TO %ld --ENUMERATION-- OBSERVER %@ FOR %@: %@", _enumerationObservers.count, observer.enumerationObserver, query.queryLocation.path, queryResults); - - observer.didProvideInitialItems = YES; - - if (queryResults != nil) - { -// NSUInteger offset = 0, count = queryResults.count; -// -// while (offset < count) -// { -// NSUInteger sliceCount = 100; -// -// if (offset + sliceCount > count) -// { -// sliceCount = count - offset; -// } -// -// NSArray *partialResults = [queryResults subarrayWithRange:NSMakeRange(offset, sliceCount)]; -// -// [observer.enumerationObserver didEnumerateItems:partialResults]; -// -// offset += sliceCount; -// }; - - [observer.enumerationObserver didEnumerateItems:queryResults]; - } - - [observer.enumerationObserver finishEnumeratingUpToPage:nil]; - - [removeObservers addObject:observer]; - } - } - } - - [_enumerationObservers removeObjectsInArray:removeObservers]; - } - } -} - -- (void)provideItemsForChangeObserverFromQuery:(OCQuery *)query -{ - @synchronized(self) - { - if (_changeObservers.count > 0) - { - OCLogDebug(@"##### PROVIDE ITEMS TO %lu --CHANGE-- OBSERVER FOR %@: %@", _changeObservers.count, query.queryLocation.path, query.queryResults); - - for (FileProviderEnumeratorObserver *observer in _changeObservers) - { - [observer.changeObserver didUpdateItems:query.queryResults]; - [observer.changeObserver finishEnumeratingChangesUpToSyncAnchor:[_core.latestSyncAnchor syncAnchorData] moreComing:NO]; - } - - [_changeObservers removeAllObjects]; - } - } -} - -- (void)queryHasChangesAvailable:(OCQuery *)query -{ - OCLogDebug(@"##### Query for %@ has changes. Query state: %lu, SinceSyncAnchor: %@, Changes available: %d", query.queryLocation.path, (unsigned long)query.state, query.querySinceSyncAnchor, query.hasChangesAvailable); - - if ( (query.state == OCQueryStateContentsFromCache) || - ((query.state == OCQueryStateWaitingForServerReply) && (query.queryResults.count > 0)) || - (query.state == OCQueryStateIdle)) - { - dispatch_async(dispatch_get_main_queue(), ^{ - @synchronized(self) - { - if (self->_enumerationObservers.count > 0) - { - [self provideItemsForEnumerationObserverFromQuery:query]; - } - - if (self->_changeObservers.count > 0) - { - [self provideItemsForChangeObserverFromQuery:query]; - } - } - }); - } -} - -- (void)query:(OCQuery *)query failedWithError:(NSError *)error -{ - OCLogDebug(@"### Query failed with error: %@", error); -} - -- (void)enumerateChangesForObserver:(id)observer fromSyncAnchor:(NSFileProviderSyncAnchor)syncAnchor -{ - OCLogDebug(@"##### Enumerate CHANGES for observer: %@ fromSyncAnchor: %@", observer, syncAnchor); - - if (syncAnchor != nil) - { - /** Apple: - If the enumeration fails with NSFileProviderErrorSyncAnchorExpired, we will - drop all cached data and start the enumeration over starting with sync anchor - nil. - */ - dispatch_async(dispatch_get_main_queue(), ^{ - if ([syncAnchor isEqual:[self->_core.latestSyncAnchor syncAnchorData]]) - { - OCLogDebug(@"##### END(LATEST) Enumerate CHANGES for observer: %@ fromSyncAnchor: %@", observer, syncAnchor); - [observer finishEnumeratingChangesUpToSyncAnchor:syncAnchor moreComing:NO]; - } - else - { - OCLogDebug(@"##### END(EXPIRED) Enumerate CHANGES for observer: %@ fromSyncAnchor: %@", observer, syncAnchor); - [observer finishEnumeratingWithError:[NSError errorWithDomain:NSFileProviderErrorDomain code:NSFileProviderErrorSyncAnchorExpired userInfo:nil]]; - } - }); - } - else - { - /** Apple: - "If anchor is nil, then the system is enumerating from scratch: the system wants - to receives changes to reconstruct the list of items in this enumeration as if - starting from an empty list." - */ - - FileProviderEnumeratorObserver *enumerationObserver = [FileProviderEnumeratorObserver new]; - - enumerationObserver.changeObserver = observer; - enumerationObserver.changesFromSyncAnchor = syncAnchor; - - @synchronized(self) - { - [_enumerationObservers addObject:enumerationObserver]; - } - - [self _startQuery]; - } -} - -- (void)currentSyncAnchorWithCompletionHandler:(void (^)(NSFileProviderSyncAnchor _Nullable))completionHandler -{ - OCLogDebug(@"#### Request current sync anchor"); - - dispatch_async(dispatch_get_main_queue(), ^{ - completionHandler([self->_core.latestSyncAnchor syncAnchorData]); - }); -} - -// - (void)enumerateChangesForObserver:(id)observer fromSyncAnchor:(NSFileProviderSyncAnchor)anchor -// { - /* TODO: - - query the server for updates since the passed-in sync anchor - - If this is an enumerator for the active set: - - note the changes in your local database - - - inform the observer about item deletions and updates (modifications + insertions) - - inform the observer when you have finished enumerating up to a subsequent sync anchor - */ - /** - If the enumeration fails with NSFileProviderErrorSyncAnchorExpired, we will - drop all cached data and start the enumeration over starting with sync anchor - nil. - */ - // - (void)finishEnumeratingWithError:(NSError *)error; -// } - -- (OCMeasurement *)hostedMeasurement -{ - return (_measurement); -} - -+ (NSArray *)logTags -{ - return (@[ @"FPEnum" ]); -} - -- (NSArray *)logTags -{ - return (@[ @"FPEnum", OCLogTagInstance(self)]); -} - -@end diff --git a/ownCloud File Provider/FileProviderEnumeratorObserver.h b/ownCloud File Provider/FileProviderEnumeratorObserver.h index bdcaab76e..c37484e9b 100644 --- a/ownCloud File Provider/FileProviderEnumeratorObserver.h +++ b/ownCloud File Provider/FileProviderEnumeratorObserver.h @@ -28,7 +28,6 @@ @property(strong) id changeObserver; @property(strong) NSFileProviderSyncAnchor changesFromSyncAnchor; -@property(strong) OCQuery *changeQuery; @property(copy) dispatch_block_t enumerationCompletionHandler; diff --git a/ownCloud File Provider/FileProviderEnumeratorObserver.m b/ownCloud File Provider/FileProviderEnumeratorObserver.m index 427bdbff4..d534c1fb6 100644 --- a/ownCloud File Provider/FileProviderEnumeratorObserver.m +++ b/ownCloud File Provider/FileProviderEnumeratorObserver.m @@ -20,6 +20,15 @@ @implementation FileProviderEnumeratorObserver +- (void)dealloc +{ + if (_enumerationCompletionHandler != nil) + { + OCLogWarning(@"Enumeration completion handler not called for FileProviderEnumeratorObserver at the time of deallocation - this should not happen"); + } + [self completeEnumeration]; +} + - (void)completeEnumeration { dispatch_block_t enumerationCompletionHandler; diff --git a/ownCloud File Provider/FileProviderExtension.m b/ownCloud File Provider/FileProviderExtension.m index 9a67d8236..985a9ed62 100644 --- a/ownCloud File Provider/FileProviderExtension.m +++ b/ownCloud File Provider/FileProviderExtension.m @@ -20,7 +20,6 @@ #import #import "FileProviderExtension.h" -#import "FileProviderEnumerator.h" #import "OCItem+FileProviderItem.h" #import "FileProviderExtensionThumbnailRequest.h" #import "NSError+MessageResolution.h" @@ -183,21 +182,29 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N #pragma mark - ItemIdentifier & URL lookup - (NSFileProviderItem)itemForIdentifier:(NSFileProviderItemIdentifier)identifier error:(NSError *__autoreleasing _Nullable *)outError { - __block NSFileProviderItem item = nil; - __block NSError *returnError = nil; + NSFileProviderItem item = nil; + NSError *returnError = nil; - if ([identifier isEqual:NSFileProviderRootContainerItemIdentifier] || [identifier isEqual:OCVFSItemIDRoot]) + if (identifier == nil) { - item = (NSFileProviderItem)self.vfsCore.rootNode; + returnError = OCError(OCErrorInvalidParameter); } - else + + if (returnError == nil) { - item = (NSFileProviderItem)[self.vfsCore itemForIdentifier:(OCVFSItemID)identifier error:&returnError]; + if ([identifier isEqual:NSFileProviderRootContainerItemIdentifier] || [identifier isEqual:OCVFSItemIDRoot]) + { + item = (NSFileProviderItem)self.vfsCore.rootNode; + } + else + { + item = (NSFileProviderItem)[self.vfsCore itemForIdentifier:(OCVFSItemID)identifier error:&returnError]; + } } OCLogDebug(@"-itemForIdentifier:error: %@ resolved into %@ / %@", identifier, item, returnError); - if ((item == nil) && (returnError == nil)) + if ((item == nil) && (returnError == nil) && (identifier != nil)) { returnError = [NSError fileProviderErrorForNonExistentItemWithIdentifier:identifier]; } @@ -209,14 +216,15 @@ - (NSFileProviderItem)itemForIdentifier:(NSFileProviderItemIdentifier)identifier *outError = [returnError translatedError]; } - return item; + return (item); } - (OCItem *)ocItemForIdentifier:(NSFileProviderItemIdentifier)identifier vfsNode:(OCVFSNode **)outNode error:(NSError *__autoreleasing _Nullable *)outError { id item; + NSError *resolutionError = nil; - if ((item = [self itemForIdentifier:identifier error:outError]) != nil) + if ((item = [self itemForIdentifier:identifier error:&resolutionError]) != nil) { if ([item isKindOfClass:OCItem.class]) { @@ -239,11 +247,22 @@ - (OCItem *)ocItemForIdentifier:(NSFileProviderItemIdentifier)identifier vfsNode } } - OCLogDebug(@"-ocItemForIdentifier:%@ could not find/return OCItem", identifier); + OCLogDebug(@"-ocItemForIdentifier:%@ could not find/return OCItem (resolutionError=%@)", identifier, resolutionError); if (outError != NULL) { - *outError = [[NSError fileProviderErrorForNonExistentItemWithIdentifier:identifier] translatedError]; + NSError *error; + + if (identifier != nil) + { + error = [NSError fileProviderErrorForNonExistentItemWithIdentifier:identifier]; + } + else + { + error = (resolutionError != nil) ? resolutionError : OCError(OCErrorInvalidParameter); + } + + *outError = [error translatedError]; } return (nil); @@ -933,6 +952,8 @@ - (void)setLastUsedDate:(NSDate *)lastUsedDate forItemIdentifier:(NSFileProvider #pragma mark - Enumeration - (nullable id)enumeratorForContainerItemIdentifier:(NSFileProviderItemIdentifier)containerItemIdentifier error:(NSError **)error { + OCLogDebug(@"##### Enumerator request for %@", containerItemIdentifier); + if (!OCFileProviderSettings.browseable) { if (error != NULL) @@ -940,6 +961,8 @@ - (void)setLastUsedDate:(NSDate *)lastUsedDate forItemIdentifier:(NSFileProvider *error = [NSError errorWithDomain:NSFileProviderErrorDomain code:NSFileProviderErrorNotAuthenticated userInfo:nil]; } + OCLogDebug(@"##### Enumerator request for %@: FileProvider disabled: %@", containerItemIdentifier, ((error != NULL) ? *error : nil)); + return (nil); } @@ -961,6 +984,8 @@ - (void)setLastUsedDate:(NSDate *)lastUsedDate forItemIdentifier:(NSFileProvider *error = [NSError errorWithDomain:NSFileProviderErrorDomain code:NSFileProviderErrorNotAuthenticated userInfo:nil]; } + OCLogDebug(@"##### Enumerator request for %@: unauthenticated return(1): %@", containerItemIdentifier, ((error != NULL) ? *error : nil)); + return (nil); } } else if ((unlockData != nil) && ![[NSKeyedUnarchiver unarchivedObjectOfClass:NSNumber.class fromData:unlockData error:NULL] boolValue]) { @@ -969,6 +994,8 @@ - (void)setLastUsedDate:(NSDate *)lastUsedDate forItemIdentifier:(NSFileProvider *error = [NSError errorWithDomain:NSFileProviderErrorDomain code:NSFileProviderErrorNotAuthenticated userInfo:nil]; } + OCLogDebug(@"##### Enumerator request for %@: unauthenticated return(2): %@", containerItemIdentifier, ((error != NULL) ? *error : nil)); + return (nil); } } @@ -980,9 +1007,13 @@ - (void)setLastUsedDate:(NSDate *)lastUsedDate forItemIdentifier:(NSFileProvider *error = [NSError errorWithDomain:NSFileProviderErrorDomain code:NSFileProviderErrorNotAuthenticated userInfo:nil]; } + OCLogDebug(@"##### Enumerator request for %@: missing domain ID: %@", containerItemIdentifier, ((error != NULL) ? *error : nil)); + return (nil); } + id enumerator = nil; + if (![containerItemIdentifier isEqualToString:NSFileProviderWorkingSetContainerItemIdentifier]) { if ([containerItemIdentifier isEqual:NSFileProviderRootContainerItemIdentifier]) @@ -1000,6 +1031,8 @@ - (void)setLastUsedDate:(NSDate *)lastUsedDate forItemIdentifier:(NSFileProvider *error = coreError; } + OCLogDebug(@"##### Enumerator request for %@: missing core: %@", containerItemIdentifier, ((error != NULL) ? *error : nil)); + return (nil); } @@ -1007,10 +1040,12 @@ - (void)setLastUsedDate:(NSDate *)lastUsedDate forItemIdentifier:(NSFileProvider containerItemIdentifier = OCVFSItemIDRoot; } - return ([[FileProviderContentEnumerator alloc] initWithVFSCore:self.vfsCore containerItemIdentifier:containerItemIdentifier]); + enumerator = [[FileProviderContentEnumerator alloc] initWithVFSCore:self.vfsCore containerItemIdentifier:containerItemIdentifier]; } - return (nil); + OCLogDebug(@"##### Enumerator request for %@: returned %@/%@", containerItemIdentifier, enumerator, ((error != NULL) ? *error : nil)); + + return (enumerator); // ### Apple template comments: ### diff --git a/ownCloud.xcodeproj/project.pbxproj b/ownCloud.xcodeproj/project.pbxproj index 9eed3b59b..38c7ad13a 100644 --- a/ownCloud.xcodeproj/project.pbxproj +++ b/ownCloud.xcodeproj/project.pbxproj @@ -306,6 +306,7 @@ DC51FD922475715F0069AB79 /* CellularSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC51FD912475715F0069AB79 /* CellularSettingsViewController.swift */; }; DC576EC022647A070087316D /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = DC576EC222647A070087316D /* Localizable.strings */; }; DC5C48A32918FB7400EBC053 /* CollectionSidebarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC5C48A22918FB7400EBC053 /* CollectionSidebarViewController.swift */; }; + DC5D58FF2A7166A300BFF393 /* ThemeCSS+SystemColors.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC5D58FE2A7166A300BFF393 /* ThemeCSS+SystemColors.swift */; }; DC60F2A629802ABE00905EC8 /* UINavigationItem+NavigationContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC60F2A529802ABE00905EC8 /* UINavigationItem+NavigationContent.swift */; }; DC60F2A829802B0900905EC8 /* NavigationContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC60F2A729802B0900905EC8 /* NavigationContent.swift */; }; DC60F2AA29802D5800905EC8 /* NavigationContentItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC60F2A929802D5800905EC8 /* NavigationContentItem.swift */; }; @@ -499,6 +500,7 @@ DCD864122811FC5700CA6631 /* GradientView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCD864112811FC5700CA6631 /* GradientView.swift */; }; DCD954DF247D62FA00E184E6 /* MessageTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCD954DE247D62FA00E184E6 /* MessageTableViewController.swift */; }; DCD9B87B2379612B00691929 /* OCLicenseManager+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = DCD9B873237960E600691929 /* OCLicenseManager+Internal.h */; }; + DCDA83852A9CE6C300BFF393 /* InitialSetupViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCDA83842A9CE6C300BFF393 /* InitialSetupViewController.swift */; }; DCDBB60A2525305600FAD707 /* NotificationAuthErrorForwarder.h in Headers */ = {isa = PBXBuildFile; fileRef = DCDBB60225252FDA00FAD707 /* NotificationAuthErrorForwarder.h */; settings = {ATTRIBUTES = (Public, ); }; }; DCDBB60B2525306000FAD707 /* NotificationAuthErrorForwarder.m in Sources */ = {isa = PBXBuildFile; fileRef = DCDBB60325252FDA00FAD707 /* NotificationAuthErrorForwarder.m */; }; DCDC0ACF23CD186400DFE36D /* ownCloudApp.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCC0855C2293F1FD008CC05C /* ownCloudApp.framework */; }; @@ -1022,10 +1024,10 @@ 398393BD246D63B0001A212B /* branding-login-logo.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "branding-login-logo.png"; path = "ownCloud/Resources/Theming/branding-login-logo.png"; sourceTree = SOURCE_ROOT; }; 3984F56B2319202200DC2639 /* DeletePathItemIntentHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeletePathItemIntentHandler.swift; sourceTree = ""; }; 39878B7321FB1DE800DBF693 /* UINavigationController+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UINavigationController+Extension.swift"; sourceTree = ""; }; - 39880BAA233B5236006EA539 /* eu */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; lineEnding = 0; name = eu; path = eu.lproj/Localizable.strings; sourceTree = ""; }; + 39880BAA233B5236006EA539 /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/Localizable.strings; sourceTree = ""; }; 39880BB0233B524B006EA539 /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/InfoPlist.strings; sourceTree = ""; }; 398FD4502334CF66004B68A1 /* UIView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Extension.swift"; sourceTree = ""; }; - 39954DDF23A39549006B6DC0 /* ar */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; lineEnding = 0; name = ar; path = ar.lproj/Localizable.strings; sourceTree = ""; }; + 39954DDF23A39549006B6DC0 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Localizable.strings; sourceTree = ""; }; 39954DE023A39625006B6DC0 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/InfoPlist.strings; sourceTree = ""; }; 399697F1260255B100E5AEBA /* PDFGotoPageAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PDFGotoPageAction.swift; sourceTree = ""; }; 399725E0233DF39300FC3B94 /* Calendar+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Calendar+Extension.swift"; sourceTree = ""; }; @@ -1139,38 +1141,38 @@ 4CC4A21122FA20AD00AE7E2C /* URL+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+Extensions.swift"; sourceTree = ""; }; 4CC4A21822FB4F4C00AE7E2C /* MediaUploadQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaUploadQueue.swift; sourceTree = ""; }; 54199937F74A129BC74DEB0A /* Pods_ownCloudScreenshotsTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ownCloudScreenshotsTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 593A821220C7D4C5000E2A90 /* en */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; lineEnding = 0; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; - 593A821820C7D4DC000E2A90 /* es */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; lineEnding = 0; name = es; path = es.lproj/Localizable.strings; sourceTree = ""; }; + 593A821220C7D4C5000E2A90 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; + 593A821820C7D4DC000E2A90 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = ""; }; 593BAB44209AE1BC00023634 /* PasscodeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasscodeViewController.swift; sourceTree = ""; }; 593BAB45209AE1BC00023634 /* PasscodeViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = PasscodeViewController.xib; sourceTree = ""; }; 593BAB96209F8A0500023634 /* AppLockManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockManager.swift; sourceTree = ""; }; 597A404820AD59EF00B028B2 /* AppLockWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockWindow.swift; sourceTree = ""; }; 59AAD95921F76B6800D15F07 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/InfoPlist.strings; sourceTree = ""; }; - 59AAD95A21F76B6800D15F07 /* cs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; lineEnding = 0; name = cs; path = cs.lproj/Localizable.strings; sourceTree = ""; }; + 59AAD95A21F76B6800D15F07 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Localizable.strings; sourceTree = ""; }; 59AAD97621F784CA00D15F07 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InfoPlist.strings; sourceTree = ""; }; - 59AAD97721F784CB00D15F07 /* de */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; lineEnding = 0; name = de; path = de.lproj/Localizable.strings; sourceTree = ""; }; + 59AAD97721F784CB00D15F07 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = ""; }; 59AAD97A21F7854C00D15F07 /* en-GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-GB"; path = "en-GB.lproj/InfoPlist.strings"; sourceTree = ""; }; - 59AAD97B21F7854C00D15F07 /* en-GB */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; lineEnding = 0; name = "en-GB"; path = "en-GB.lproj/Localizable.strings"; sourceTree = ""; }; + 59AAD97B21F7854C00D15F07 /* en-GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-GB"; path = "en-GB.lproj/Localizable.strings"; sourceTree = ""; }; 59AAD97C21F7858300D15F07 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/InfoPlist.strings; sourceTree = ""; }; - 59AAD97D21F7858300D15F07 /* ko */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; lineEnding = 0; name = ko; path = ko.lproj/Localizable.strings; sourceTree = ""; }; + 59AAD97D21F7858300D15F07 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Localizable.strings; sourceTree = ""; }; 59AAD97E21F785F800D15F07 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/InfoPlist.strings; sourceTree = ""; }; - 59AAD97F21F785F900D15F07 /* ru */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; lineEnding = 0; name = ru; path = ru.lproj/Localizable.strings; sourceTree = ""; }; + 59AAD97F21F785F900D15F07 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = ""; }; 59AAD98021F7860D00D15F07 /* sq */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sq; path = sq.lproj/InfoPlist.strings; sourceTree = ""; }; - 59AAD98121F7860E00D15F07 /* sq */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; lineEnding = 0; name = sq; path = sq.lproj/Localizable.strings; sourceTree = ""; }; + 59AAD98121F7860E00D15F07 /* sq */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sq; path = sq.lproj/Localizable.strings; sourceTree = ""; }; 59AAD98221F7861600D15F07 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/InfoPlist.strings"; sourceTree = ""; }; - 59AAD98321F7861600D15F07 /* zh-Hans */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; lineEnding = 0; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; + 59AAD98321F7861600D15F07 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; 59AAD98421F7862D00D15F07 /* mk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = mk; path = mk.lproj/InfoPlist.strings; sourceTree = ""; }; - 59AAD98521F7862D00D15F07 /* mk */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; lineEnding = 0; name = mk; path = mk.lproj/Localizable.strings; sourceTree = ""; }; + 59AAD98521F7862D00D15F07 /* mk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = mk; path = mk.lproj/Localizable.strings; sourceTree = ""; }; 59AAD98621F7868000D15F07 /* nb-NO */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "nb-NO"; path = "nb-NO.lproj/InfoPlist.strings"; sourceTree = ""; }; - 59AAD98721F7868000D15F07 /* nb-NO */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; lineEnding = 0; name = "nb-NO"; path = "nb-NO.lproj/Localizable.strings"; sourceTree = ""; }; + 59AAD98721F7868000D15F07 /* nb-NO */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "nb-NO"; path = "nb-NO.lproj/Localizable.strings"; sourceTree = ""; }; 59AAD98821F7869700D15F07 /* nn-NO */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "nn-NO"; path = "nn-NO.lproj/InfoPlist.strings"; sourceTree = ""; }; - 59AAD98921F7869800D15F07 /* nn-NO */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; lineEnding = 0; name = "nn-NO"; path = "nn-NO.lproj/Localizable.strings"; sourceTree = ""; }; + 59AAD98921F7869800D15F07 /* nn-NO */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "nn-NO"; path = "nn-NO.lproj/Localizable.strings"; sourceTree = ""; }; 59AAD98A21F786BC00D15F07 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/InfoPlist.strings"; sourceTree = ""; }; - 59AAD98B21F786BD00D15F07 /* pt-BR */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; lineEnding = 0; name = "pt-BR"; path = "pt-BR.lproj/Localizable.strings"; sourceTree = ""; }; + 59AAD98B21F786BD00D15F07 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Localizable.strings"; sourceTree = ""; }; 59AAD98C21F786CB00D15F07 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/InfoPlist.strings"; sourceTree = ""; }; - 59AAD98D21F786CC00D15F07 /* pt-PT */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; lineEnding = 0; name = "pt-PT"; path = "pt-PT.lproj/Localizable.strings"; sourceTree = ""; }; + 59AAD98D21F786CC00D15F07 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/Localizable.strings"; sourceTree = ""; }; 59AAD98E21F7870B00D15F07 /* th-TH */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "th-TH"; path = "th-TH.lproj/InfoPlist.strings"; sourceTree = ""; }; - 59AAD98F21F7870B00D15F07 /* th-TH */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; lineEnding = 0; name = "th-TH"; path = "th-TH.lproj/Localizable.strings"; sourceTree = ""; }; + 59AAD98F21F7870B00D15F07 /* th-TH */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "th-TH"; path = "th-TH.lproj/Localizable.strings"; sourceTree = ""; }; 59D4895320C83F2E00369C2E /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 59EACA8020CAA37F00F082EE /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = ""; }; 6E37F48A2188B27D00CF16CA /* Action.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Action.swift; sourceTree = ""; }; @@ -1309,6 +1311,7 @@ DC51FD912475715F0069AB79 /* CellularSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CellularSettingsViewController.swift; sourceTree = ""; }; DC576EC122647A070087316D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; DC5C48A22918FB7400EBC053 /* CollectionSidebarViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionSidebarViewController.swift; sourceTree = ""; }; + DC5D58FE2A7166A300BFF393 /* ThemeCSS+SystemColors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ThemeCSS+SystemColors.swift"; sourceTree = ""; }; DC5D9E742496512400BFFE8E /* MessageQueueExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageQueueExample.swift; sourceTree = ""; }; DC60F2A529802ABE00905EC8 /* UINavigationItem+NavigationContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UINavigationItem+NavigationContent.swift"; sourceTree = ""; }; DC60F2A729802B0900905EC8 /* NavigationContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationContent.swift; sourceTree = ""; }; @@ -1494,8 +1497,6 @@ DCC6564620C9B7E300110A97 /* ownCloud File Provider.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "ownCloud File Provider.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; DCC6564820C9B7E400110A97 /* FileProviderExtension.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FileProviderExtension.h; sourceTree = ""; }; DCC6564920C9B7E400110A97 /* FileProviderExtension.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FileProviderExtension.m; sourceTree = ""; }; - DCC6564E20C9B7E400110A97 /* FileProviderEnumerator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FileProviderEnumerator.h; sourceTree = ""; }; - DCC6564F20C9B7E400110A97 /* FileProviderEnumerator.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FileProviderEnumerator.m; sourceTree = ""; }; DCC6565120C9B7E400110A97 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; DCC6565220C9B7E400110A97 /* ownCloud_File_Provider.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ownCloud_File_Provider.entitlements; sourceTree = ""; }; DCC832DD242C0C3700153F8C /* DisplaySleepPreventer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplaySleepPreventer.swift; sourceTree = ""; }; @@ -1525,6 +1526,7 @@ DCD864112811FC5700CA6631 /* GradientView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GradientView.swift; sourceTree = ""; }; DCD954DE247D62FA00E184E6 /* MessageTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageTableViewController.swift; sourceTree = ""; }; DCD9B873237960E600691929 /* OCLicenseManager+Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "OCLicenseManager+Internal.h"; sourceTree = ""; }; + DCDA83842A9CE6C300BFF393 /* InitialSetupViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InitialSetupViewController.swift; sourceTree = ""; }; DCDBB60225252FDA00FAD707 /* NotificationAuthErrorForwarder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NotificationAuthErrorForwarder.h; sourceTree = ""; }; DCDBB60325252FDA00FAD707 /* NotificationAuthErrorForwarder.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NotificationAuthErrorForwarder.m; sourceTree = ""; }; DCDC0AD023CD18D200DFE36D /* OCLicenseManager+Setup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OCLicenseManager+Setup.swift"; sourceTree = ""; }; @@ -2836,6 +2838,7 @@ DCB1B89E29C7378200BFF393 /* ThemeCSS.swift */, DCB1B8A329C73DB800BFF393 /* ThemeCSSRecord.swift */, DCB1B8A629C75EBD00BFF393 /* ThemeCSS+AutoSelectors.swift */, + DC5D58FE2A7166A300BFF393 /* ThemeCSS+SystemColors.swift */, DCB1B8A129C7379C00BFF393 /* NSObject+ThemeCSS.swift */, DCB1B8C329C8A84000BFF393 /* UIView+ThemeCSS.swift */, DCB1B8AC29C89E0F00BFF393 /* Views */, @@ -2945,6 +2948,7 @@ DCB6B1EA292B7C2400D27573 /* AppRootViewController+ItemActions.swift */, DCB6B1F4292CC46B00D27573 /* AccountController+ItemActions.swift */, DCB6B20B292E428000D27573 /* AccountController+ExtraItems.swift */, + DCDA83842A9CE6C300BFF393 /* InitialSetupViewController.swift */, ); path = "App Controllers"; sourceTree = ""; @@ -3085,8 +3089,6 @@ DC27A1A320CBEF85008ACB6C /* OCBookmark+FileProvider.h */, DC27A1A720CC095C008ACB6C /* OCCore+FileProviderTools.m */, DC27A1A620CC095C008ACB6C /* OCCore+FileProviderTools.h */, - DCC6564F20C9B7E400110A97 /* FileProviderEnumerator.m */, - DCC6564E20C9B7E400110A97 /* FileProviderEnumerator.h */, DC98BBD320FF824600F4ED3E /* FileProviderEnumeratorObserver.m */, DC98BBD220FF824600F4ED3E /* FileProviderEnumeratorObserver.h */, DC98BBCA20FF815C00F4ED3E /* NSNumber+OCSyncAnchorData.m */, @@ -4420,6 +4422,7 @@ DCD1301123A23F4E00255779 /* OCLicenseManager+AppStore.swift in Sources */, DC62514C225D254500736874 /* UploadBaseAction.swift in Sources */, 4C6B78102226B83300C5F3DB /* PhotoAlbumTableViewController.swift in Sources */, + DCDA83852A9CE6C300BFF393 /* InitialSetupViewController.swift in Sources */, 23EC77592137F3DD0032D4E6 /* DisplayExtension.swift in Sources */, DCDF58B323CE82E100080BEB /* LicenseInAppPurchaseFeatureView.swift in Sources */, DC33939622E0747400DD3DA4 /* MakeAvailableOfflineAction.swift in Sources */, @@ -4520,6 +4523,7 @@ DC298C9F2934D6D9009FA87F /* AccountAuthenticationUpdaterPasswordPromptViewController.swift in Sources */, DCB1B8A229C7379C00BFF393 /* NSObject+ThemeCSS.swift in Sources */, DCB1B8C429C8A84000BFF393 /* UIView+ThemeCSS.swift in Sources */, + DC5D58FF2A7166A300BFF393 /* ThemeCSS+SystemColors.swift in Sources */, DCE4E4C724C255E00051722F /* AppExtensionNavigationController.swift in Sources */, DC3F0C2529828AE300C832DB /* OCLocation+Breadcrumbs.swift in Sources */, DC0A358024C0E43C00FB58FC /* NSObject+ThemeApplication.swift in Sources */, @@ -5137,8 +5141,8 @@ APP_BUILD_FLAGS = "$(inherited)"; APP_BUILD_FLAGS_SWIFT = "$(APP_BUILD_FLAGS)"; APP_PRODUCT_NAME = ownCloud; - APP_SHORT_VERSION = 12.0.2; - APP_VERSION = 270; + APP_SHORT_VERSION = 12.0.3; + APP_VERSION = 271; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -5207,8 +5211,8 @@ APP_BUILD_FLAGS = "$(inherited)"; APP_BUILD_FLAGS_SWIFT = "$(APP_BUILD_FLAGS)"; APP_PRODUCT_NAME = ownCloud; - APP_SHORT_VERSION = 12.0.2; - APP_VERSION = 270; + APP_SHORT_VERSION = 12.0.3; + APP_VERSION = 271; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; diff --git a/ownCloud.xcodeproj/xcshareddata/xcschemes/ownCloud.xcscheme b/ownCloud.xcodeproj/xcshareddata/xcschemes/ownCloud.xcscheme index b6976a578..42ceaa148 100644 --- a/ownCloud.xcodeproj/xcshareddata/xcschemes/ownCloud.xcscheme +++ b/ownCloud.xcodeproj/xcshareddata/xcschemes/ownCloud.xcscheme @@ -54,6 +54,11 @@ value = "${PROJECT_DIR}/doc/CONFIGURATION.json" isEnabled = "YES"> + + . + * + */ + +import UIKit +import ownCloudApp +import ownCloudAppShared + +class InitialSetupViewController: UIViewController { + override var preferredStatusBarStyle : UIStatusBarStyle { + return Theme.shared.activeCollection.css.getStatusBarStyle(for: self) ?? .default + } + + override func loadView() { + cssSelectors = [.modal, .welcome] + + var addAccountTitle = "Add account".localized + if !VendorServices.shared.canAddAccount { + addAccountTitle = "Login".localized + } + + let messageView = ComposedMessageView.infoBox(additionalElements: [ + .image(AccountSettingsProvider.shared.logo, size: CGSize(width: 128, height: 128), cssSelectors: [.icon]), + .title(String(format: "Welcome to %@".localized, VendorServices.shared.appName), alignment: .centered, cssSelectors: [.title], insets: NSDirectionalEdgeInsets(top: 25, leading: 0, bottom: 25, trailing: 0)), + .button(addAccountTitle, action: UIAction(handler: { [weak self] action in + if let self = self { + BookmarkViewController.showBookmarkUI(on: self, attemptLoginOnSuccess: true) + } + }), image: UIImage(systemName: "plus.circle"), cssSelectors: [.welcome]), + .button("Settings".localized ,action: UIAction(handler: { [weak self] action in + if let self = self { + self.present(ThemeNavigationController(rootViewController: SettingsViewController()), animated: true) + } + }), image: UIImage(systemName: "gearshape"), cssSelectors: [.welcome]) + ]) + messageView.elementInsets = NSDirectionalEdgeInsets(top: 25, leading: 50, bottom: 50, trailing: 50) + + let rootView = ThemeCSSView(withSelectors: []) + + if let image = Branding.shared.brandedImageNamed(.loginBackground) { + messageView.isOpaque = false + let backgroundImageView = UIImageView(image: image) + backgroundImageView.contentMode = .scaleAspectFill + rootView.embed(toFillWith: backgroundImageView) + } + + rootView.embed(centered: messageView, minimumInsets: NSDirectionalEdgeInsets(top: 20, leading: 20, bottom: 20, trailing: 20)) + + view = rootView + } +} diff --git a/ownCloud/Bookmarks/BookmarkViewController.swift b/ownCloud/Bookmarks/BookmarkViewController.swift index ca5d2d580..263406e61 100644 --- a/ownCloud/Bookmarks/BookmarkViewController.swift +++ b/ownCloud/Bookmarks/BookmarkViewController.swift @@ -44,9 +44,12 @@ class BookmarkViewController: StaticTableViewController { var passwordRow : StaticTableViewRow? var tokenInfoRow : StaticTableViewRow? var deleteAuthDataButtonRow : StaticTableViewRow? + var activeTextField: UITextField? + var showOAuthInfoHeader = false var showedOAuthInfoHeader : Bool = false - var activeTextField: UITextField? + var tokenHelpSection : StaticTableViewSection? + var tokenHelpRow: StaticTableViewRow? var userActionCompletionHandler : BookmarkViewControllerUserActionCompletionHandler? @@ -129,6 +132,8 @@ class BookmarkViewController: StaticTableViewController { // Super init super.init(style: .grouped) + self.cssSelector = .bookmarkEditor + // Accessibility Identifiers continueBarButtonItem.accessibilityIdentifier = "continue-bar-button" saveBarButtonItem.accessibilityIdentifier = "save-bar-button" @@ -224,6 +229,10 @@ class BookmarkViewController: StaticTableViewController { tokenInfoRow = StaticTableViewRow(label: "", identifier: "row-credentials-token-info") + // Token help + tokenHelpRow = StaticTableViewRow(label: "", identifier: "row-token-help") + tokenHelpSection = StaticTableViewSection(headerTitle: "", footerTitle: nil, identifier: "section-token-help", rows: [ tokenHelpRow! ]) + deleteAuthDataButtonRow = StaticTableViewRow(buttonWithAction: { [weak self] (_, _) in if self?.bookmark?.authenticationData != nil { @@ -329,20 +338,18 @@ class BookmarkViewController: StaticTableViewController { } let logoAndAppNameView = ComposedMessageView.infoBox(additionalElements: [ - .image(AccountSettingsProvider.shared.logo, size: CGSize(width: 64, height: 64)), - .title(VendorServices.shared.appName, alignment: .centered) + .image(AccountSettingsProvider.shared.logo, size: CGSize(width: 64, height: 64), cssSelectors: [.icon]), + .title(VendorServices.shared.appName, alignment: .centered, cssSelectors: [.title]) ]) - logoAndAppNameView.cssSelector = .info - logoAndAppNameView.backgroundView?.cssSelector = .info + + logoAndAppNameView.cssSelectors = [.welcome, .message] logoAndAppNameView.backgroundInsets = NSDirectionalEdgeInsets(top: 20, leading: 20, bottom: 0, trailing: 20) logoAndAppNameView.elementInsets = NSDirectionalEdgeInsets(top: 30, leading: 20, bottom: 10, trailing: 20) + (logoAndAppNameView.backgroundView as? RoundCornerBackgroundView)?.fillImage = Branding.shared.brandedImageNamed(.loginBackground) + self.tableView.tableHeaderView = logoAndAppNameView self.tableView.layoutTableHeaderView() - - if Branding.shared.isBranded, let image = Branding.shared.brandedImageNamed(.loginBackground) { - self.tableView.backgroundView = UIImageView(image: image) - } } required init?(coder aDecoder: NSCoder) { @@ -944,18 +951,19 @@ class BookmarkViewController: StaticTableViewController { authMethodName = localizedAuthMethodName } - let tokenMessageView = ComposedMessageView.infoBox(additionalElements: [ - .text("If you 'Continue', you will be prompted to allow the '{{app.name}}' app to open the {{authmethodName}} login page where you can enter your credentials.".localized(["authmethodName" : authMethodName]), style: .system(textStyle: .body, weight: .semibold), alignment: .centered, cssSelectors: [.info]) - ]) - tokenMessageView.cssSelector = .info - tokenMessageView.backgroundView?.cssSelector = .info - tokenMessageView.backgroundInsets = NSDirectionalEdgeInsets(top: 20, leading: 20, bottom: 0, trailing: 20) - tokenMessageView.elementInsets = NSDirectionalEdgeInsets(top: 30, leading: 20, bottom: 10, trailing: 20) + let messageText = "If you 'Continue', you will be prompted to allow the '{{app.name}}' app to open the {{authmethodName}} login page where you can enter your credentials.".localized(["authmethodName" : authMethodName]) + + tokenHelpRow?.value = messageText - self.tableView.tableHeaderView = tokenMessageView - self.tableView.layoutTableHeaderView() + OnMainThread { + if self.tokenHelpSection?.attached == false { + self.insertSection(self.tokenHelpSection!, at: 0, animated: animated) + } + } } else { - self.tableView.tableHeaderView = nil + if tokenHelpSection?.attached == true { + removeSection(tokenHelpSection!, animated: animated) + } } // Continue button: show always @@ -1273,3 +1281,7 @@ public extension OCAuthenticationMethod { } } + +extension ThemeCSSSelector { + static let bookmarkEditor = ThemeCSSSelector(rawValue: "bookmarkEditor") +} diff --git a/ownCloud/Client/Actions/Actions+Extensions/CreateDocumentAction.swift b/ownCloud/Client/Actions/Actions+Extensions/CreateDocumentAction.swift index ad6f1eefb..a7c832f63 100644 --- a/ownCloud/Client/Actions/Actions+Extensions/CreateDocumentAction.swift +++ b/ownCloud/Client/Actions/Actions+Extensions/CreateDocumentAction.swift @@ -74,7 +74,7 @@ class CreateDocumentAction: Action { return .none } - if forContext.items.first?.type != OCItemType.collection { + if forContext.items.first?.type != .collection { return .none } @@ -82,10 +82,12 @@ class CreateDocumentAction: Action { return .none } - if forContext.core?.appProvider?.types?.contains(where: { fileType in - return fileType.allowCreation - }) == true { - return .first + if let appProvider = forContext.core?.appProvider, appProvider.supportsCreateDocument { + if appProvider.types?.contains(where: { fileType in + return fileType.allowCreation + }) == true { + return .first + } } return .none diff --git a/ownCloud/Client/Viewer/DisplayViewController.swift b/ownCloud/Client/Viewer/DisplayViewController.swift index d8509c0f7..86008909f 100644 --- a/ownCloud/Client/Viewer/DisplayViewController.swift +++ b/ownCloud/Client/Viewer/DisplayViewController.swift @@ -749,11 +749,11 @@ class DisplayViewController: UIViewController, Themeable, OCQueryDelegate { func queryHasChangesAvailable(_ query: OCQuery) { query.requestChangeSet(withFlags: .onlyResults) { [weak self] (query, changeSet) in OnMainThread { - Log.log("DisplayViewController.queryHasChangesAvailable: \(changeSet?.queryResult.description ?? "nil") - state: \(String(describing: query.state.rawValue))") + Log.log("DisplayViewController.queryHasChangesAvailable: \(changeSet?.queryResult?.description ?? "nil") - state: \(String(describing: query.state.rawValue))") switch query.state { case .idle, .contentsFromCache, .waitingForServerReply: - if let firstItem = changeSet?.queryResult.first { + if let firstItem = changeSet?.queryResult?.first { self?.item = firstItem } else { // No item available diff --git a/ownCloud/Release Notes/ReleaseNotes.plist b/ownCloud/Release Notes/ReleaseNotes.plist index c0647fcdf..717842625 100644 --- a/ownCloud/Release Notes/ReleaseNotes.plist +++ b/ownCloud/Release Notes/ReleaseNotes.plist @@ -1771,6 +1771,23 @@ Added an optional "Wait for completion" option to the "Save File& + + Version + 12.0.3 + ReleaseNotes + + + Title + Bug Fixes + Subtitle + Fixed File Provider, App Provider and layout issues. + Type + Fix + ImageName + wrench + + + diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json index b75f2d40f..6df8b31de 100644 --- a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,116 +1,116 @@ { - "images" : [ - { - "size" : "20x20", - "idiom": "iphone", - "filename" : "icon-20@2x.png", - "scale": "2x" + "images": [ + { + "idiom": "ios-marketing", + "filename": "branding-icon-1024x1024.png", + "scale": "1x", + "size": "1024x1024" }, - { - "size" : "20x20", + { "idiom": "iphone", - "filename" : "icon-20@3x.png", - "scale": "3x" + "filename": "branding-icon-180x180.png", + "scale": "3x", + "size": "60x60" }, - { - "size" : "20x20", + { "idiom": "ipad", - "filename" : "icon-20.png", - "scale": "1x" + "filename": "branding-icon-167x167.png", + "scale": "2x", + "size": "83.5x83.5" }, { - "size" : "20x20", "idiom": "ipad", - "filename" : "icon-20@2x.png", - "scale": "2x" + "filename": "branding-icon-152x152.png", + "scale": "2x", + "size": "76x76" }, { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "icon-29@2x.png", - "scale" : "2x" + "idiom": "iphone", + "filename": "branding-icon-120x120.png", + "scale": "3x", + "size": "40x40" }, { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "icon-29@3x.png", - "scale" : "3x" + "idiom": "iphone", + "filename": "branding-icon-120x120.png", + "scale": "2x", + "size": "60x60" }, { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "icon-40@2x.png", - "scale" : "2x" + "idiom": "iphone", + "filename": "branding-icon-87x87.png", + "scale": "3x", + "size": "29x29" }, { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "icon-40@3x.png", - "scale" : "3x" + "idiom": "ipad", + "filename": "branding-icon-80x80.png", + "scale": "2x", + "size": "40x40" }, { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "icon-60@2x.png", - "scale" : "2x" + "idiom": "iphone", + "filename": "branding-icon-80x80.png", + "scale": "2x", + "size": "40x40" }, { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "icon-60@3x.png", - "scale" : "3x" + "idiom": "ipad", + "filename": "branding-icon-76x76.png", + "scale": "1x", + "size": "76x76" }, { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "icon-29.png", - "scale" : "1x" + "idiom": "iphone", + "filename": "branding-icon-60x60.png", + "scale": "3x", + "size": "20x20" }, { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "icon-29@2x.png", - "scale" : "2x" + "idiom": "ipad", + "filename": "branding-icon-58x58.png", + "scale": "2x", + "size": "29x29" }, { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "icon-40.png", - "scale" : "1x" + "idiom": "iphone", + "filename": "branding-icon-58x58.png", + "scale": "2x", + "size": "29x29" }, { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "icon-40@2x.png", - "scale" : "2x" + "idiom": "ipad", + "filename": "branding-icon-40x40.png", + "scale": "1x", + "size": "40x40" }, { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "icon-76.png", - "scale" : "1x" + "idiom": "iphone", + "filename": "branding-icon-40x40.png", + "scale": "2x", + "size": "20x20" }, { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "icon-76@2x.png", - "scale" : "2x" + "idiom": "ipad", + "filename": "branding-icon-40x40.png", + "scale": "2x", + "size": "20x20" }, { - "size" : "83.5x83.5", - "idiom" : "ipad", - "filename" : "icon-83.5@2x.png", - "scale" : "2x" + "idiom": "ipad", + "filename": "branding-icon-29x29.png", + "scale": "1x", + "size": "29x29" }, { - "size" : "1024x1024", - "idiom" : "ios-marketing", - "filename" : "icon-1024.png", - "scale" : "1x" + "idiom": "ipad", + "filename": "branding-icon-20x20.png", + "scale": "1x", + "size": "20x20" } ], - "info" : { - "version" : 1, - "author" : "xcode" + "info": { + "version": 1, + "author": "fastlane" } -} +} \ No newline at end of file diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/branding-icon-1024x1024.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/branding-icon-1024x1024.png new file mode 100644 index 000000000..2bddda788 Binary files /dev/null and b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/branding-icon-1024x1024.png differ diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/branding-icon-120x120.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/branding-icon-120x120.png new file mode 100644 index 000000000..15322073b Binary files /dev/null and b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/branding-icon-120x120.png differ diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/branding-icon-152x152.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/branding-icon-152x152.png new file mode 100644 index 000000000..dbc6b8aab Binary files /dev/null and b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/branding-icon-152x152.png differ diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/branding-icon-167x167.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/branding-icon-167x167.png new file mode 100644 index 000000000..a026e7e48 Binary files /dev/null and b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/branding-icon-167x167.png differ diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/branding-icon-180x180.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/branding-icon-180x180.png new file mode 100644 index 000000000..bb66bbb03 Binary files /dev/null and b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/branding-icon-180x180.png differ diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/branding-icon-20x20.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/branding-icon-20x20.png new file mode 100644 index 000000000..6bdb42926 Binary files /dev/null and b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/branding-icon-20x20.png differ diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/branding-icon-29x29.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/branding-icon-29x29.png new file mode 100644 index 000000000..edabadab0 Binary files /dev/null and b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/branding-icon-29x29.png differ diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/branding-icon-40x40.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/branding-icon-40x40.png new file mode 100644 index 000000000..4f1613797 Binary files /dev/null and b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/branding-icon-40x40.png differ diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/branding-icon-58x58.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/branding-icon-58x58.png new file mode 100644 index 000000000..8e2e27749 Binary files /dev/null and b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/branding-icon-58x58.png differ diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/branding-icon-60x60.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/branding-icon-60x60.png new file mode 100644 index 000000000..475cc6d88 Binary files /dev/null and b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/branding-icon-60x60.png differ diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/branding-icon-76x76.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/branding-icon-76x76.png new file mode 100644 index 000000000..0b8a3ec7a Binary files /dev/null and b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/branding-icon-76x76.png differ diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/branding-icon-80x80.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/branding-icon-80x80.png new file mode 100644 index 000000000..0c29dcc18 Binary files /dev/null and b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/branding-icon-80x80.png differ diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/branding-icon-87x87.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/branding-icon-87x87.png new file mode 100644 index 000000000..eca51a9c0 Binary files /dev/null and b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/branding-icon-87x87.png differ diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024-1024x1024.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024-1024x1024.png new file mode 100644 index 000000000..2bddda788 Binary files /dev/null and b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024-1024x1024.png differ diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024-120x120.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024-120x120.png new file mode 100644 index 000000000..15322073b Binary files /dev/null and b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024-120x120.png differ diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024-152x152.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024-152x152.png new file mode 100644 index 000000000..dbc6b8aab Binary files /dev/null and b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024-152x152.png differ diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024-167x167.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024-167x167.png new file mode 100644 index 000000000..a026e7e48 Binary files /dev/null and b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024-167x167.png differ diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024-180x180.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024-180x180.png new file mode 100644 index 000000000..bb66bbb03 Binary files /dev/null and b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024-180x180.png differ diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024-20x20.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024-20x20.png new file mode 100644 index 000000000..6bdb42926 Binary files /dev/null and b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024-20x20.png differ diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024-29x29.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024-29x29.png new file mode 100644 index 000000000..edabadab0 Binary files /dev/null and b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024-29x29.png differ diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024-40x40.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024-40x40.png new file mode 100644 index 000000000..4f1613797 Binary files /dev/null and b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024-40x40.png differ diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024-58x58.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024-58x58.png new file mode 100644 index 000000000..8e2e27749 Binary files /dev/null and b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024-58x58.png differ diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024-60x60.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024-60x60.png new file mode 100644 index 000000000..475cc6d88 Binary files /dev/null and b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024-60x60.png differ diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024-76x76.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024-76x76.png new file mode 100644 index 000000000..0b8a3ec7a Binary files /dev/null and b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024-76x76.png differ diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024-80x80.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024-80x80.png new file mode 100644 index 000000000..0c29dcc18 Binary files /dev/null and b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024-80x80.png differ diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024-87x87.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024-87x87.png new file mode 100644 index 000000000..eca51a9c0 Binary files /dev/null and b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024-87x87.png differ diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-20.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-20.png deleted file mode 100644 index 7b8c551c4..000000000 Binary files a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-20.png and /dev/null differ diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png deleted file mode 100644 index 5265cd644..000000000 Binary files a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png and /dev/null differ diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png deleted file mode 100644 index 397221439..000000000 Binary files a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png and /dev/null differ diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-29.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-29.png deleted file mode 100644 index 1449e2ce7..000000000 Binary files a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-29.png and /dev/null differ diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png deleted file mode 100644 index 1675c21cf..000000000 Binary files a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png and /dev/null differ diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png deleted file mode 100644 index a14729826..000000000 Binary files a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png and /dev/null differ diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-40.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-40.png deleted file mode 100644 index 5265cd644..000000000 Binary files a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-40.png and /dev/null differ diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png deleted file mode 100644 index c68484026..000000000 Binary files a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png and /dev/null differ diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png deleted file mode 100644 index 7c0aef50c..000000000 Binary files a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png and /dev/null differ diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png deleted file mode 100644 index 7c0aef50c..000000000 Binary files a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png and /dev/null differ diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png deleted file mode 100644 index 61d545717..000000000 Binary files a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png and /dev/null differ diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-76.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-76.png deleted file mode 100644 index 006d2505c..000000000 Binary files a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-76.png and /dev/null differ diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png deleted file mode 100644 index bfbe42d61..000000000 Binary files a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png and /dev/null differ diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png b/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png deleted file mode 100644 index 08dc61093..000000000 Binary files a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png and /dev/null differ diff --git a/ownCloud/Resources/Theming/Branding-default.plist b/ownCloud/Resources/Theming/Branding-default.plist deleted file mode 100644 index 0c67376eb..000000000 --- a/ownCloud/Resources/Theming/Branding-default.plist +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/ownCloud/Resources/Theming/Branding-flat-identifiers-example.plist b/ownCloud/Resources/Theming/Branding-flat-identifiers-example.plist deleted file mode 100644 index 74b7a114c..000000000 --- a/ownCloud/Resources/Theming/Branding-flat-identifiers-example.plist +++ /dev/null @@ -1,28 +0,0 @@ - - - - - branding.can-add-account - - branding.can-edit-account - - branding.organization-name - demo.owncloud.com - branding.profile-definitions$[0].profile-identifier - demo.owncloud.com - branding.profile-definitions$[0].profile-welcome-message - Welcome to demo.owncloud.com - branding.profile-definitions$[0].profile-password-auth-prompt - Enter your username and password - branding.profile-definitions$[0].profile-token-auth-prompt - Please log in to authorize the app. - branding.profile-definitions$[0].profile-url-prompt - Please enter your demo.owncloud.com URL - branding.profile-definitions$[0].profile-bookmark-name - demo.owncloud.com - branding.profile-definitions$[0].profile-url - https://demo.owncloud.com - branding.profile-definitions$[0].profile-allow-url-configuration - - - diff --git a/ownCloud/Resources/Theming/Branding-mibrowser-example.plist b/ownCloud/Resources/Theming/Branding-mibrowser-example.plist deleted file mode 100644 index 8ce8a4a3d..000000000 --- a/ownCloud/Resources/Theming/Branding-mibrowser-example.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - authentication.browser-session-class - MIBrowser - - diff --git a/ownCloud/Resources/Theming/Branding-owncloud-online-legacy.plist b/ownCloud/Resources/Theming/Branding-owncloud-online-legacy.plist deleted file mode 100644 index 169c2584d..000000000 --- a/ownCloud/Resources/Theming/Branding-owncloud-online-legacy.plist +++ /dev/null @@ -1,612 +0,0 @@ - - - - - canAddAccount - - canEditAccount - - enableReviewPrompt - - organizationName - ownCloud Online - feedbackMail - - Configuration - - Profiles - - - identifier - ownCloud Online - name - ownCloud Online - promptForURL - Please enter your ownCloud Online URL - promptForHelpURL - If you do not have an ownCloud Online account yet, you can request a free test account. - helpURLButtonString - TRY IT FOR FREE - bookmarkName - ownCloud Online - url - - canConfigureURL - - helpURL - https://owncloud.online/try/ - allowedHosts - - owncloud.online - owncloud.com - owncloud.team - - allowedAuthenticationMethods - - com.owncloud.basicauth - com.owncloud.oauth2 - - - - URLs - - Help - https://owncloud.online/faq/ - Privacy - https://owncloud.online/privacy-policy/ - - Generic - - Corporate - - Color - #52DDA9 - - Action - - Primary - #1279C5 - Inverse - #FFFFFF - Secondary - #777777 - - Text - - Primary - #333333 - Inverse - #000000 - Secondary - #666666 - Inverse-Light - #000000 - Disabled - #BFC9D9 - - Login - - Primary - #333333 - Inverse - #FFFFFF - Secondary - #666666 - Inverse-Light - #F2F4F7 - Disabled - #BFC9D9 - - Background - - Standard - #FFFFFF - Light - #EEEEEE - Input-Form - #FFFFFF - Selected - #E5EFFF - Tooltip - #1F2939 - - System - - success - #4CD964 - success-background - #DBF7E0 - warning - #FF8800 - warning-background - #FFE7CC - danger - #E00000 - danger-background - #F9CCCC - - Status - - Progress-Indicator - #7357C7 - Progress-Background - #D5D3DC - - Icon - - System - #667FA3 - System-Light - #CCD4E0 - Filetype-Normal - #00af90 - Disabled - #C4C4C4 - - Tab - - Active - #00af90 - Inactive - #777777 - - - Themes - - - Name - ownCloud-online - Identifier - com.owncloud.branding - ThemeStyle - contrast - darkBrandColor - #52DDA9 - lightBrandColor - #628a99 - Colors - - tintColor - #000000 - Label - - informativeColor - - successColor - System.Success - warningColor - System.Warning - errorColor - System.Danger - - Fill - - approvalColors - - normal - - background - #52DDA9 - foreground - - - highlighted - - background - #52DDA9 - foreground - - - disabled - - background - #37C390 - foreground - - - - neutralColors - - normal - - background - - foreground - - - highlighted - - background - - foreground - - - disabled - - background - - foreground - - - - destructiveColors - - normal - - background - - foreground - - - highlighted - - background - - foreground - - - disabled - - background - - foreground - - - - - Favorite - - enabledColor - - disabledColor - - - Table - - tableBackgroundColor - Background.Standard - tableGroupBackgroundColor - - tableSeparatorColor - - tableRowBorderColor - - tableRowColors - - backgroundColor - Background.Standard - labelColor - Text.Primary - secondaryLabelColor - Text.Secondary - symbolColor - Action.Primary - tintColor - - filledColorPairCollection - - normal - - background - - foreground - - - highlighted - - background - - foreground - - - disabled - - background - - foreground - - - - - tableRowHighlightColors - - backgroundColor - #DCEBF6 - labelColor - - secondaryLabelColor - - symbolColor - - tintColor - - filledColorPairCollection - - normal - - background - - foreground - - - highlighted - - background - - foreground - - - disabled - - background - - foreground - - - - - - Icon - - folderFillColor - Icon.Filetype-Normal - fileFillColor - Icon.Filetype-Normal - logoFillColor - Icon.Filetype-Normal - iconFillColor - Icon.Filetype-Normal - symbolFillColor - Icon.Filetype-Normal - - Progress - - foreground - Progress.Indicator - background - Progress.Background - - Toolbar - - backgroundColor - Background.Standard - labelColor - Text.Inverse - secondaryLabelColor - Tab.Inactive - symbolColor - - tintColor - Tab.Active - filledColorPairCollection - - normal - - background - - foreground - Tab.Inactive - - highlighted - - background - - foreground - - - disabled - - background - - foreground - - - - - Searchbar - - backgroundColor - Background.Input-Form - labelColor - Text.Inverse - secondaryLabelColor - Text.Secondary - symbolColor - - tintColor - Text.Inverse - filledColorPairCollection - - normal - - background - - foreground - - - highlighted - - background - - foreground - - - disabled - - background - - foreground - - - - - Login - - backgroundColor - Background.Standard - labelColor - Login.Primary - secondaryLabelColor - Login.Secondary - symbolColor - - tintColor - Login.Inverse - filledColorPairCollection - - normal - - background - - foreground - #000000 - - highlighted - - background - - foreground - - - disabled - - background - - foreground - - - - - NavigationBar - - backgroundColor - #FFFFFF - labelColor - Text.Inverse - secondaryLabelColor - - symbolColor - - tintColor - Text.Inverse-Light - filledColorPairCollection - - normal - - background - - foreground - - - highlighted - - background - - foreground - - - disabled - - background - - foreground - - - - - darkBrandColors - - backgroundColor - - labelColor - - secondaryLabelColor - - symbolColor - - tintColor - - filledColorPairCollection - - normal - - background - - foreground - - - highlighted - - background - - foreground - - - disabled - - background - - foreground - - - - - lightBrandColors - - backgroundColor - - labelColor - - secondaryLabelColor - - symbolColor - - tintColor - - filledColorPairCollection - - normal - - background - - foreground - - - highlighted - - background - - foreground - - - disabled - - background - - foreground - - - - - - Styles - - statusBarStyle - darkContent - barStyle - black - activityIndicatorViewStyle - gray - searchBarActivityIndicatorViewStyle - white - interfaceStyle - light - keyboardAppearance - light - backgroundBlurEffectStyle - light - - - - - diff --git a/ownCloud/Resources/Theming/Branding.json b/ownCloud/Resources/Theming/Branding.json new file mode 100644 index 000000000..33f2e67a8 --- /dev/null +++ b/ownCloud/Resources/Theming/Branding.json @@ -0,0 +1,13 @@ +{ + "ios_branding.organization-name_text": "Example Cloud", + "ios_branding.profile-bookmark-name_text": "Example Cloud", + "ios_branding.theme-definitions$[0].darkBrandColor_color": "#5BB75B", + "ios_branding.theme-definitions$[0].lightBrandColor_color": "#000000", + "ios_branding.send-feedback-address_text": "mail@example.com", + "ios_branding.profile-url_text": "https://demo.owncloud.com", + "ios_branding.navigation.style_select": "colored", + "ios_branding.profile-help-url_text": "", + "ios_branding.profile-help-button-label_text": "", + "ios_branding.profile-open-help-message_text": "", + "ios_branding.style_select": "light", +} \ No newline at end of file diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024.png b/ownCloud/Resources/Theming/branding-icon.png similarity index 100% rename from ownCloud/Resources/Assets.xcassets/AppIcon.appiconset/icon-1024.png rename to ownCloud/Resources/Theming/branding-icon.png diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon-emm.appiconset/Contents.json b/ownCloud/Resources/Theming/com.owncloud.ios-app.emm/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from ownCloud/Resources/Assets.xcassets/AppIcon-emm.appiconset/Contents.json rename to ownCloud/Resources/Theming/com.owncloud.ios-app.emm/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon-emm.appiconset/icon-1024.png b/ownCloud/Resources/Theming/com.owncloud.ios-app.emm/Assets.xcassets/AppIcon.appiconset/icon-1024.png similarity index 100% rename from ownCloud/Resources/Assets.xcassets/AppIcon-emm.appiconset/icon-1024.png rename to ownCloud/Resources/Theming/com.owncloud.ios-app.emm/Assets.xcassets/AppIcon.appiconset/icon-1024.png diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon-emm.appiconset/icon-20.png b/ownCloud/Resources/Theming/com.owncloud.ios-app.emm/Assets.xcassets/AppIcon.appiconset/icon-20.png similarity index 100% rename from ownCloud/Resources/Assets.xcassets/AppIcon-emm.appiconset/icon-20.png rename to ownCloud/Resources/Theming/com.owncloud.ios-app.emm/Assets.xcassets/AppIcon.appiconset/icon-20.png diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon-emm.appiconset/icon-20@2x.png b/ownCloud/Resources/Theming/com.owncloud.ios-app.emm/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png similarity index 100% rename from ownCloud/Resources/Assets.xcassets/AppIcon-emm.appiconset/icon-20@2x.png rename to ownCloud/Resources/Theming/com.owncloud.ios-app.emm/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon-emm.appiconset/icon-20@3x.png b/ownCloud/Resources/Theming/com.owncloud.ios-app.emm/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png similarity index 100% rename from ownCloud/Resources/Assets.xcassets/AppIcon-emm.appiconset/icon-20@3x.png rename to ownCloud/Resources/Theming/com.owncloud.ios-app.emm/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon-emm.appiconset/icon-29.png b/ownCloud/Resources/Theming/com.owncloud.ios-app.emm/Assets.xcassets/AppIcon.appiconset/icon-29.png similarity index 100% rename from ownCloud/Resources/Assets.xcassets/AppIcon-emm.appiconset/icon-29.png rename to ownCloud/Resources/Theming/com.owncloud.ios-app.emm/Assets.xcassets/AppIcon.appiconset/icon-29.png diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon-emm.appiconset/icon-29@2x.png b/ownCloud/Resources/Theming/com.owncloud.ios-app.emm/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png similarity index 100% rename from ownCloud/Resources/Assets.xcassets/AppIcon-emm.appiconset/icon-29@2x.png rename to ownCloud/Resources/Theming/com.owncloud.ios-app.emm/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon-emm.appiconset/icon-29@3x.png b/ownCloud/Resources/Theming/com.owncloud.ios-app.emm/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png similarity index 100% rename from ownCloud/Resources/Assets.xcassets/AppIcon-emm.appiconset/icon-29@3x.png rename to ownCloud/Resources/Theming/com.owncloud.ios-app.emm/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon-emm.appiconset/icon-40.png b/ownCloud/Resources/Theming/com.owncloud.ios-app.emm/Assets.xcassets/AppIcon.appiconset/icon-40.png similarity index 100% rename from ownCloud/Resources/Assets.xcassets/AppIcon-emm.appiconset/icon-40.png rename to ownCloud/Resources/Theming/com.owncloud.ios-app.emm/Assets.xcassets/AppIcon.appiconset/icon-40.png diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon-emm.appiconset/icon-40@2x.png b/ownCloud/Resources/Theming/com.owncloud.ios-app.emm/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png similarity index 100% rename from ownCloud/Resources/Assets.xcassets/AppIcon-emm.appiconset/icon-40@2x.png rename to ownCloud/Resources/Theming/com.owncloud.ios-app.emm/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon-emm.appiconset/icon-40@3x.png b/ownCloud/Resources/Theming/com.owncloud.ios-app.emm/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png similarity index 100% rename from ownCloud/Resources/Assets.xcassets/AppIcon-emm.appiconset/icon-40@3x.png rename to ownCloud/Resources/Theming/com.owncloud.ios-app.emm/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon-emm.appiconset/icon-60@2x.png b/ownCloud/Resources/Theming/com.owncloud.ios-app.emm/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png similarity index 100% rename from ownCloud/Resources/Assets.xcassets/AppIcon-emm.appiconset/icon-60@2x.png rename to ownCloud/Resources/Theming/com.owncloud.ios-app.emm/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon-emm.appiconset/icon-60@3x.png b/ownCloud/Resources/Theming/com.owncloud.ios-app.emm/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png similarity index 100% rename from ownCloud/Resources/Assets.xcassets/AppIcon-emm.appiconset/icon-60@3x.png rename to ownCloud/Resources/Theming/com.owncloud.ios-app.emm/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon-emm.appiconset/icon-76.png b/ownCloud/Resources/Theming/com.owncloud.ios-app.emm/Assets.xcassets/AppIcon.appiconset/icon-76.png similarity index 100% rename from ownCloud/Resources/Assets.xcassets/AppIcon-emm.appiconset/icon-76.png rename to ownCloud/Resources/Theming/com.owncloud.ios-app.emm/Assets.xcassets/AppIcon.appiconset/icon-76.png diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon-emm.appiconset/icon-76@2x.png b/ownCloud/Resources/Theming/com.owncloud.ios-app.emm/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png similarity index 100% rename from ownCloud/Resources/Assets.xcassets/AppIcon-emm.appiconset/icon-76@2x.png rename to ownCloud/Resources/Theming/com.owncloud.ios-app.emm/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon-emm.appiconset/icon-83.5@2x.png b/ownCloud/Resources/Theming/com.owncloud.ios-app.emm/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png similarity index 100% rename from ownCloud/Resources/Assets.xcassets/AppIcon-emm.appiconset/icon-83.5@2x.png rename to ownCloud/Resources/Theming/com.owncloud.ios-app.emm/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon-ownCloud-online.appiconset/Contents.json b/ownCloud/Resources/Theming/online.owncloud.ios-app/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from ownCloud/Resources/Assets.xcassets/AppIcon-ownCloud-online.appiconset/Contents.json rename to ownCloud/Resources/Theming/online.owncloud.ios-app/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon-ownCloud-online.appiconset/icon-1024.png b/ownCloud/Resources/Theming/online.owncloud.ios-app/Assets.xcassets/AppIcon.appiconset/icon-1024.png similarity index 100% rename from ownCloud/Resources/Assets.xcassets/AppIcon-ownCloud-online.appiconset/icon-1024.png rename to ownCloud/Resources/Theming/online.owncloud.ios-app/Assets.xcassets/AppIcon.appiconset/icon-1024.png diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon-ownCloud-online.appiconset/icon-20.png b/ownCloud/Resources/Theming/online.owncloud.ios-app/Assets.xcassets/AppIcon.appiconset/icon-20.png similarity index 100% rename from ownCloud/Resources/Assets.xcassets/AppIcon-ownCloud-online.appiconset/icon-20.png rename to ownCloud/Resources/Theming/online.owncloud.ios-app/Assets.xcassets/AppIcon.appiconset/icon-20.png diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon-ownCloud-online.appiconset/icon-20@2x.png b/ownCloud/Resources/Theming/online.owncloud.ios-app/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png similarity index 100% rename from ownCloud/Resources/Assets.xcassets/AppIcon-ownCloud-online.appiconset/icon-20@2x.png rename to ownCloud/Resources/Theming/online.owncloud.ios-app/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon-ownCloud-online.appiconset/icon-20@3x.png b/ownCloud/Resources/Theming/online.owncloud.ios-app/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png similarity index 100% rename from ownCloud/Resources/Assets.xcassets/AppIcon-ownCloud-online.appiconset/icon-20@3x.png rename to ownCloud/Resources/Theming/online.owncloud.ios-app/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon-ownCloud-online.appiconset/icon-29.png b/ownCloud/Resources/Theming/online.owncloud.ios-app/Assets.xcassets/AppIcon.appiconset/icon-29.png similarity index 100% rename from ownCloud/Resources/Assets.xcassets/AppIcon-ownCloud-online.appiconset/icon-29.png rename to ownCloud/Resources/Theming/online.owncloud.ios-app/Assets.xcassets/AppIcon.appiconset/icon-29.png diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon-ownCloud-online.appiconset/icon-29@2x.png b/ownCloud/Resources/Theming/online.owncloud.ios-app/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png similarity index 100% rename from ownCloud/Resources/Assets.xcassets/AppIcon-ownCloud-online.appiconset/icon-29@2x.png rename to ownCloud/Resources/Theming/online.owncloud.ios-app/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon-ownCloud-online.appiconset/icon-29@3x.png b/ownCloud/Resources/Theming/online.owncloud.ios-app/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png similarity index 100% rename from ownCloud/Resources/Assets.xcassets/AppIcon-ownCloud-online.appiconset/icon-29@3x.png rename to ownCloud/Resources/Theming/online.owncloud.ios-app/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon-ownCloud-online.appiconset/icon-40.png b/ownCloud/Resources/Theming/online.owncloud.ios-app/Assets.xcassets/AppIcon.appiconset/icon-40.png similarity index 100% rename from ownCloud/Resources/Assets.xcassets/AppIcon-ownCloud-online.appiconset/icon-40.png rename to ownCloud/Resources/Theming/online.owncloud.ios-app/Assets.xcassets/AppIcon.appiconset/icon-40.png diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon-ownCloud-online.appiconset/icon-40@2x.png b/ownCloud/Resources/Theming/online.owncloud.ios-app/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png similarity index 100% rename from ownCloud/Resources/Assets.xcassets/AppIcon-ownCloud-online.appiconset/icon-40@2x.png rename to ownCloud/Resources/Theming/online.owncloud.ios-app/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon-ownCloud-online.appiconset/icon-40@3x.png b/ownCloud/Resources/Theming/online.owncloud.ios-app/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png similarity index 100% rename from ownCloud/Resources/Assets.xcassets/AppIcon-ownCloud-online.appiconset/icon-40@3x.png rename to ownCloud/Resources/Theming/online.owncloud.ios-app/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon-ownCloud-online.appiconset/icon-60@2x.png b/ownCloud/Resources/Theming/online.owncloud.ios-app/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png similarity index 100% rename from ownCloud/Resources/Assets.xcassets/AppIcon-ownCloud-online.appiconset/icon-60@2x.png rename to ownCloud/Resources/Theming/online.owncloud.ios-app/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon-ownCloud-online.appiconset/icon-60@3x.png b/ownCloud/Resources/Theming/online.owncloud.ios-app/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png similarity index 100% rename from ownCloud/Resources/Assets.xcassets/AppIcon-ownCloud-online.appiconset/icon-60@3x.png rename to ownCloud/Resources/Theming/online.owncloud.ios-app/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon-ownCloud-online.appiconset/icon-76.png b/ownCloud/Resources/Theming/online.owncloud.ios-app/Assets.xcassets/AppIcon.appiconset/icon-76.png similarity index 100% rename from ownCloud/Resources/Assets.xcassets/AppIcon-ownCloud-online.appiconset/icon-76.png rename to ownCloud/Resources/Theming/online.owncloud.ios-app/Assets.xcassets/AppIcon.appiconset/icon-76.png diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon-ownCloud-online.appiconset/icon-76@2x.png b/ownCloud/Resources/Theming/online.owncloud.ios-app/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png similarity index 100% rename from ownCloud/Resources/Assets.xcassets/AppIcon-ownCloud-online.appiconset/icon-76@2x.png rename to ownCloud/Resources/Theming/online.owncloud.ios-app/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png diff --git a/ownCloud/Resources/Assets.xcassets/AppIcon-ownCloud-online.appiconset/icon-83.5@2x.png b/ownCloud/Resources/Theming/online.owncloud.ios-app/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png similarity index 100% rename from ownCloud/Resources/Assets.xcassets/AppIcon-ownCloud-online.appiconset/icon-83.5@2x.png rename to ownCloud/Resources/Theming/online.owncloud.ios-app/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png diff --git a/ownCloud/Resources/Theming/Branding-owncloud-online.plist b/ownCloud/Resources/Theming/online.owncloud.ios-app/Branding.plist similarity index 100% rename from ownCloud/Resources/Theming/Branding-owncloud-online.plist rename to ownCloud/Resources/Theming/online.owncloud.ios-app/Branding.plist diff --git a/ownCloud/Resources/Theming/branding-login-background-owncloud-online.png b/ownCloud/Resources/Theming/online.owncloud.ios-app/branding-login-background.png similarity index 100% rename from ownCloud/Resources/Theming/branding-login-background-owncloud-online.png rename to ownCloud/Resources/Theming/online.owncloud.ios-app/branding-login-background.png diff --git a/ownCloud/Resources/Theming/branding-login-logo-owncloud-online.png b/ownCloud/Resources/Theming/online.owncloud.ios-app/branding-login-logo.png similarity index 100% rename from ownCloud/Resources/Theming/branding-login-logo-owncloud-online.png rename to ownCloud/Resources/Theming/online.owncloud.ios-app/branding-login-logo.png diff --git a/ownCloud/Resources/Theming/branding-splashscreen-background-owncloud-online.png b/ownCloud/Resources/Theming/online.owncloud.ios-app/branding-splashscreen-background.png similarity index 100% rename from ownCloud/Resources/Theming/branding-splashscreen-background-owncloud-online.png rename to ownCloud/Resources/Theming/online.owncloud.ios-app/branding-splashscreen-background.png diff --git a/ownCloud/Resources/Theming/branding-splashscreen-owncloud-online.png b/ownCloud/Resources/Theming/online.owncloud.ios-app/branding-splashscreen.png similarity index 100% rename from ownCloud/Resources/Theming/branding-splashscreen-owncloud-online.png rename to ownCloud/Resources/Theming/online.owncloud.ios-app/branding-splashscreen.png diff --git a/ownCloud/Resources/ar.lproj/InfoPlist.strings b/ownCloud/Resources/ar.lproj/InfoPlist.strings index 2dda65d31..afbc56cdc 100644 Binary files a/ownCloud/Resources/ar.lproj/InfoPlist.strings and b/ownCloud/Resources/ar.lproj/InfoPlist.strings differ diff --git a/ownCloud/Resources/ar.lproj/Localizable.strings b/ownCloud/Resources/ar.lproj/Localizable.strings index b2b20ffa3..ec56f0246 100644 Binary files a/ownCloud/Resources/ar.lproj/Localizable.strings and b/ownCloud/Resources/ar.lproj/Localizable.strings differ diff --git a/ownCloud/Resources/bg_BG.lproj/InfoPlist.strings b/ownCloud/Resources/bg_BG.lproj/InfoPlist.strings new file mode 100644 index 000000000..5ee67e4b6 --- /dev/null +++ b/ownCloud/Resources/bg_BG.lproj/InfoPlist.strings @@ -0,0 +1,26 @@ +/* + InfoPlist.strings + ownCloud + + Created by Javier Gonzalez on 06/06/2018. + Copyright © 2018 ownCloud GmbH. All rights reserved. +*/ + +/* + * Copyright (C) 2018, ownCloud GmbH. + * + * This code is covered by the GNU Public License Version 3. + * + * For distribution utilizing Apple mechanisms please see https://owncloud.org/contribute/iOS-license-exception/ + * You should have received a copy of this license along with this program. If not, see . + * + */ + +"NSFaceIDUsageDescription" = "Отключване на приложението без въвеждане на парола."; +"NSAppleMusicUsageDescription" = "Тези права са необходими, за да качвате мултимедийни файлове на вашия сървър."; +"NSPhotoLibraryAddUsageDescription" = "Тези права са необходими, за да качвате снимки и видеоклипове от вашата библиотека със снимки."; +"NSPhotoLibraryUsageDescription" = "Тези права са необходими, за да качвате снимки и видеоклипове от вашата библиотека със снимки."; +"NSLocationAlwaysAndWhenInUseUsageDescription" = "Това разрешение е необходимо, за да се активира автоматичното качване на снимки и видеоклипове във фонов режим."; +"NSLocationAlwaysUsageDescription" = "Това разрешение е необходимо, за да се активира автоматичното качване на снимки и видеоклипове във фонов режим"; +"NSLocationWhenInUseUsageDescription" = "Това разрешение е необходимо, за да се активира автоматичното качване на снимки и видеоклипове във фонов режим"; +"NSMicrophoneUsageDescription" = "Това разрешение е необходимо за запис на видеоклипове"; diff --git a/ownCloud/Resources/bg_BG.lproj/Localizable.strings b/ownCloud/Resources/bg_BG.lproj/Localizable.strings new file mode 100644 index 000000000..f6286b178 --- /dev/null +++ b/ownCloud/Resources/bg_BG.lproj/Localizable.strings @@ -0,0 +1,1013 @@ +/* + Localizable.strings + ownCloud + + Created by Pablo Carrascal on 13/03/2018. + Copyright © 2018 ownCloud GmbH. All rights reserved. +*/ + +/* + * Copyright (C) 2018, ownCloud GmbH. + * + * This code is covered by the GNU Public License Version 3. + * + * For distribution utilizing Apple mechanisms please see https://owncloud.org/contribute/iOS-license-exception/ + * You should have received a copy of this license along with this program. If not, see . + * + */ + +/* Add / Edit Bookmark */ +"Edit account" = "Редактиране на профил"; +"Add account" = "Добавяне на профил"; +"Server URL" = "URL адрес на сървъра"; +"https://example.com" = "https://example.com"; +"Continue" = "Продължаване"; +"Name" = "Име"; +"Example Server" = "Примерен сървър"; +"Show Certificate Details" = "Показване на подробности за сертификата"; +"Connect" = "Свързване"; +"Delete Authentication Data" = "Изтриване на данните за удостоверяване"; +"Authentication" = "Удостоверяване"; +"Username" = "Потребителско име"; +"Password" = "Парола"; +"Certificate Details" = "Подробности за сертификата"; +"Cancel" = "Отказ"; +"Approve" = "Одобряване"; +"Error" = "Грешка"; +"Issues" = "Въпроси"; +"Review Connection" = "Преглед на връзката"; +"Authenticated via" = "Удостоверяване чрез"; +"Authenticated as %@ via %@" = "Удостоверяване %@ като чрез %@"; +"Edit" = "Редактиране"; +"Credentials" = "Идентификационни данни"; +"Rejected" = "Отхвърлено"; +"Passed" = "Преминато"; +"Accepted" = "Прието"; +"Validation Error" = "Грешка при потвърждаване"; +"Certificate was rejected by user." = "Сертификатът е отхвърлен от потребителя."; +"Certificate has issues.\nOpen 'Certificate Details' for more informations." = "Сертификатът има проблеми.\nЗа повече информация отворете \"Подробности за сертификата\"."; +"No issues found. Certificate passed validation." = "Няма открити проблеми. Сертификатът е преминал успешно проверката."; +"Certificate may have issues, but was accepted by user.\nOpen 'Certificate Details' for more informations." = "Сертификатът може да има проблеми, но е приет от потребителя.\nЗа повече информация отворете \"Подробности за сертификата\"."; +"If you 'Continue', you will be prompted to allow the '{{app.name}}' app to open the {{authmethodName}} login page where you can enter your credentials." = "If you 'Continue', you will be prompted to allow the '{{app.name}}' app to open the {{authmethodName}} login page where you can enter your credentials."; + +"Error opening %@" = "Грешка при отваряне на %@"; + +"Contacting server…" = "Свързване със сървъра..."; +"Authenticating…" = "Удостоверяване..."; + +"Fetching user information…" = "Извличане на информация за потребителя..."; +"Updating connection…" = "Актуализиране на връзката..."; + +"Preparing account" = "Подготовка на профил"; +"Please wait…" = "Моля, изчакайте..."; +"Skip" = "Пропусни"; + +"Missing hostname" = "Липсващо име на хост"; +"The entered URL does not include a hostname." = "Въведеният URL адрес не съдържа име на хост."; +"Add account" = "Добавяне на профил"; +"Server name" = "Име на сървъра"; +"Server Password" = "Парола за сървъра"; +"Server Username" = "Потребителско име за сървъра"; +"Show accounts" = "Показване на профилите"; +"Setup complete" = "Настройката е завършена"; +"Edit Login" = "Редактиране на потребителския вход"; +"Manage Storage" = "Управление на хранилището"; +"Access Files" = "Достъп до файловете"; +"Log out" = "Изход"; +"Retry" = "Повторение"; + +/* Bookmark Info */ +"Bookmark Metadata" = "Метаданни на отметките"; + +/* Client */ +"Browse" = "Преглед"; +"Files" = "Файлове"; +"Disconnect" = "Прекъсване"; +"Connecting…" = "Свързване..."; +"Connected." = "Свързано."; +"Select" = "Избиране"; +"Done" = "Завършено"; + +"Folder" = "Папка"; + +"Stopped" = "Спряно"; +"Started…" = "Започва..."; +"Contents from cache." = "Съдържание от кеша."; +"Waiting for server response…" = "Изчаква се отговор от сървъра..."; +"This folder no longer exists." = "Тази папка вече не съществува."; +"Everything up-to-date." = "Всичко е актуално."; +"Please wait…" = "Моля, изчакайте..."; +"Sort by %@" = "Подреждане по %@"; +"Sort by" = "Подреждане по"; + +"name" = "име"; +"kind" = "вид"; +"size" = "размер"; +"date" = "дата"; + +"Tree" = "Tree"; +"Search folder" = "Търсене на папка"; +"Search tree" = "Search tree"; +"Search from {{folder.name}}" = "Search from {{folder.name}}"; +"Search space" = "Search space"; +"Search {{space.name}}" = "Search {{space.name}}"; +"Search account" = "Търсене в профила"; +"Enter a search term" = "Enter a search term"; +"Search for users or groups" = "Search for users or groups"; +"Enter the user or group you want to invite." = "Enter the user or group you want to invite."; + +"Pending" = "Чакащо"; +"Show parent paths" = "Показване на родителските пътища"; +"Reveal in folder" = "Откриване в папка"; + +"%@ of %@ used" = "%@ от %@ използвани"; +"Total: %@" = "Общо: %@"; +"%@ item | " = "%@ елемент |"; +"%@ items | " = "%@ елемента |"; + +"{{itemCount}} items with {{totalSize}} total ({{fileCount}} files, {{folderCount}} folders)" = "{{itemCount}} items with {{totalSize}} total ({{fileCount}} files, {{folderCount}} folders)"; +"{{remaining}} available" = "{{remaining}} available"; + +"Show more results" = "Показване на повече резултати"; + +"An error occurred" = "Възникна грешка"; + +/* Item Layouts */ +"List" = "Списък"; +"Grid" = "Grid"; +"Item grid" = "Item grid"; +"Image grid" = "Image grid"; + +/* Saved searches */ +"Saved searches" = "Saved searches"; +"Saved search templates" = "Saved search templates"; +"Save search" = "Save search"; +"Save as search template" = "Save as search template"; + +"Name of saved search" = "Name of saved search"; +"Saved search" = "Saved search"; +"Saved searches" = "Saved searches"; +"Name of template" = "Name of template"; +"Search template" = "Search template"; +"Search templates" = "Search templates"; + +/* Search scope */ +"Search scope" = "Обхват на търсенето"; +"Toggle Search Scope" = "Превключване на обхвата на търсенето"; + +/* Static Login Setup */ +"Server error" = "Сървърна грешка"; +"The server doesn't support any allowed authentication method." = "Сървърът не поддържа нито един разрешен метод за удостоверяване."; +"The server doesn't support any known and allowed authentication method found." = "Сървърът не поддържа нито един открит познат и разрешен метод за удостоверяване."; +"Retry detection" = "Повторен опит за откриване"; +"Enter your username and password" = "Въведете вашето потребителско име и парола"; +"Please log in to authorize the app." = "Моля, влезте, за да упълномощите приложението."; +"Welcome to %@" = "Добре дошли в %@"; +"Please enter a server URL" = "Моля, въведете URL адрес на сървъра"; +"Please pick a profile to begin setup" = "Моля, изберете профил за да започнете с настройката"; + +/* Single Account */ +"You are connected as\n%@" = "Свързани сте като\n%@"; +"Login" = "Вход"; +"Wrong URL" = "Грешен URL адрес"; +"Please enter a valid URL" = "Моля, въведете валиден URL адрес"; +"Missing Profile URL" = "Липсващ URL адрес за профила"; +"The Profile '%@' does not have a URL configured.\nPlease provide a URL via configuration or MDM." = "Профилът '%@' няма конфигуриран URL адрес.\nМоля, задайте URL адрес чрез конфигурацията или MDM."; + +/* Client Messages */ +"No contents" = "No contents"; +"This folder contains no files or folders." = "Тази папка не съдържа файлове и/или папки."; +"This folder is empty." = "This folder is empty."; +"This folder is empty. Fill it with content:" = "This folder is empty. Fill it with content:"; + +"Folder removed" = "Папката е премахната"; +"This folder no longer exists on the server." = "Тази папка вече не съществува на сървъра."; +"Are you sure you want to delete this item from the server?" = "Сигурни ли сте, че искате да изтриете този елемент от сървъра?"; +"Are you sure you want to delete these items from the server?" = "Сигурни ли сте, че искате да изтриете тези елементи от сървъра?"; +"Multiple items" = "Множество елементи"; +"Deleting '%@'" = "Изтриване на '%@'"; +"Renaming to %@" = "Преименуване в %@"; +"Upload from your photo library" = "Качване от вашата библиотека със снимки"; +"Missing permissions" = "Липсват необходимите права"; +"This permission is needed to upload photos and videos from your photo library." = "Тези права са необходими, за да качвате снимки и видеоклипове от вашата библиотека със снимки."; +"Not now" = "Не сега"; +"Upload file" = "Качване на файл"; +"Upload files" = "Качване на файлове"; + +"No matches" = "Няма съвпадения"; +"The search term you entered did not match any item in the selected scope." = "The search term you entered did not match any item in the selected scope."; +"There are no results for this search term" = "Няма резултати от търсенето за тази дума"; +"No items found matching the search criteria." = "No items found matching the search criteria."; + +"Status" = "Статус"; + +"Authorization failed" = "Упълномощаването е неуспешно"; +"The account has been disabled." = "Този профил е деактивиран."; +"The server declined access with the credentials stored for this connection." = "Сървърът отказа достъп с идентификационните данни, съхранени за тази връзка."; +"Access denied" = "Достъпът е отказан"; +"The connection's access token has expired or become invalid. Sign in again to re-gain access." = "Токенът за достъп на връзката е изтекъл или е станал невалиден. Влезте отново, за да възстановите достъпа."; +"No authentication data has been found for this connection." = "За тази връзка не са намерени данни за удостоверяване."; +"Authentication with %@ is no longer allowed. Re-authentication needed." = "Удостоверяването с %@ вече не е възможно. Необходимо е повторно удостоверяване."; +"Sign in" = "Влезте"; +"Continue offline" = "Продължете офлайн"; +"Media upload in the previous session was incomplete since the application was terminated" = "Качването на мултимедия в предишната сесия не е било завършено, тъй като приложението е било спряно"; + +"Waiting for response from login session in external browser…" = "Изчакване на отговор от сесията за вход във външния браузър..."; + +"All done" = "Всичко е готово"; +"No pending messages or ongoing actions." = "Няма чакащи съобщения или текущи действия."; + +"Show all" = "Показване на всички"; +"+ %ld more" = "+ още %ld"; +"Apply choice to all similar issues" = "Прилагане на избора към всички подобни въпроси"; +"Apply to all" = "Приложи към всички"; + +/* Server List*/ +"Cancel" = "Отказ"; +"OK" = "ОК"; + +"'%@' is currently locked" = "'%@' в момента е заключен"; +"An operation is currently performed that prevents connecting to '%@'. Please try again later." = "В момента се извършва операция, която не позволява свързването с '%@'. Моля, опитайте отново по-късно."; +"Do you really want to disconnect from your '%@' account?" = "Наистина ли искате да прекъснете връзката с профила си в '%@'?"; +"This will remove all locally stored file copies from your device." = "Това ще премахне всички локално съхранени копия на файлове от вашето устройство."; +"Really delete '%@'?" = "Наистина ли ще изтриете '%@'?"; +"Do you want to log out from '%@'?" = "Искате ли да излезете от '%@'?"; +"This will also delete all locally stored file copies." = "Това ще доведе и до изтриване на всички локално съхранени копия на файлове."; +"Delete" = "Изтриване"; +"Remove" = "Премахване"; +"Removing of '%@' failed" = "Премахването на '%@' е неуспешно"; +"Accounts" = "Профили"; + +"Help" = "Помощ"; +"Feedback" = "Обратна връзка"; +"Welcome" = "Добре дошли"; +"Thanks for choosing %@! \n Start by adding your account." = "Благодарим ви за избора %@! \nЗапочнете с добавяне на вашия профил."; + +/* Settings Messages */ + +"Security" = "Сигурност"; +"Passcode Lock" = "Заключване с парола"; +"Face ID" = "Лицево разпознаване Face ID"; +"Touch ID" = "Пръстов отпечатък Touch ID"; +"Unlock using %@?" = "Отключване чрез %@?"; +"Videos" = "Видеоклипове"; +"Photos" = "Снимки"; +"Wifi only" = "Само през Wi-Fi"; +"Settings" = "Настройки"; +"Lock application" = "Заключване на приложението"; +"Upload videos via WiFi only" = "Качване на видео само през WiFi"; +"Upload pictures via WiFi only" = "Качване на снимки само през WiFi"; +"More" = "Още"; +"Documentation" = "Документация"; +"Send feedback" = "Изпращане на обратна връзка"; +"Recommend to a friend" = "Препоръчай на приятел"; +"Privacy Policy" = "Политика за поверителност"; +"Terms Of Use" = "Условия за ползване"; +"Acknowledgements" = "Благодарности"; +"license" = "Лиценз"; +"Portions of this app may utilize the following copyrighted material, the use of which is hereby acknowledged." = "Части от това приложение може да използват следния защитен с авторски права материал, чието използване се потвърждава с настоящото."; +"Video upload path" = "Път за качване на видео"; +"Photo upload path" = "Път за качване на снимки"; +"Immediately" = "Веднага"; +"After 1 minute" = "След 1 минута"; +"After 5 minutes" = "След 5 минути"; +"After 30 minutes" = "След 30 минути"; +"After %ld seconds" = "След %ld секунди"; +"If you choose \"Immediately\" the App will be locked, when it is no longer in foreground." = "Ако изберете \"Веднага\", приложението ще бъде заключено, когато вече не е на преден план."; +"Please configure an email account" = "Моля, конфигурирайте имейл профил"; +"You need to configure an email account first to be able to send emails." = "За да можете да изпращате имейли, първо трябва да конфигурирате имейл профил."; +"Do you want to open the following URL?" = "Искате ли да отворите този URL адрес?"; +"Passcode option" = "Опция за парола"; +"Please choose how many digits you want to use for the passcode lock?" = "Моля, изберете колко цифри искате да използвате за заключване с парола?"; +"%ld digit code" = "%ld цифрен код"; +"Enter a new code with %ld digits" = "Въведете нов код с %ld цифри"; +"To use the app, you need to create a passcode." = "To use the app, you need to create a passcode."; +"Passcode setup" = "Passcode setup"; + +"%@%@ %@ version %@ build %@\n(app: %@, sdk: %@)" = "%@%@%@версия %@ компилация%@ \n(app: %@, sdk: %@)"; +"beta" = "бета"; +"release" = "издание"; +"App Version" = "Версия на приложението"; +"Version information were copied to the clipboard" = "Информацията за версията беше копирана в клипборда"; + + +/* User Interface Settings */ +"Theme" = "Тема"; +"User Interface" = "Потребителски интерфейс"; +"Dark" = "Тъмна"; +"Dark Blue" = "Тъмно синьо"; +"Dark Web" = "Тъмна мрежа"; +"Light" = "Светла"; +"Classic" = "Класическа"; +"System" = "Система"; +"System Appeareance" = "Външен вид на системата"; + +/* Log settings */ +"Log Files" = "Регистрационни файлове"; +"Browse log files" = "Преглед на регистрационните файлове"; +"Share" = "Споделяне"; +"Delete all log files?" = "Изтриване на всички регистрационни файлове?"; +"Delete all" = "Изтриване на всичко"; + +"Log Level" = "Нива на дневника"; +"Log Destinations" = "Дестинации за регистрационните файлове"; +"Privacy" = "Поверителност"; + +"Logging" = "Регистрация"; +"Enable logging" = "Активиране на регистрацията"; +"Options" = "Опции"; + +"Off" = "Изключено"; +"Info" = "Информация"; +"Default" = "По подразбиране"; +"Warning" = "Предупреждение"; +"Error" = "Грешка"; + +"Share log file" = "Споделяне на регистрационния файл"; +"Reset log file" = "Възстановяване на регистрационния файл"; + +"When activated, logs may impact performance and include sensitive information. However the logs are not subject to automatic submission to %@ servers. Sharing logs with others is sole user responsibility." = "Когато са активирани, дневниците могат да повлияят на производителността и да съдържат чувствителна информация. Въпреки това дневниците не подлежат на автоматично подаване към %@ сървърите. Споделянето на дневници с други лица е отговорност единствено на потребителя."; + +"The last 10 archived logs are kept on the device - with each log covering up to 24 hours of usage. When sharing please bear in mind that logs may contain sensitive information such as server URLs and user-specific information." = "Последните 10 архивирани дневника се съхраняват в устройството, като всеки дневник обхваща до 24 часа използване. Когато споделяте, моля, имайте предвид, че дневниците могат да съдържат чувствителна информация, като например URL адреси на сървъри и специфична за потребителя информация."; + +"Mask private data" = "Маскиране на лични данни"; +"Enabling this option will attempt to mask private data, so it does not become part of any log. Since logging is a development and debugging feature, though, we can't guarantee that the log file will be free of any private data even with this option enabled. Therefore, please look through any log file and verify its free of any data you're not comfortable sharing before sharing it with anybody." = "Активирането на тази опция ще направи опит за маскиране на личните данни, така че те да не станат част от дневника. Тъй като воденето на дневник е функция за разработка и отстраняване на грешки не можем да гарантираме, че регистрационният файл няма да съдържа лични данни, дори ако тази опция е активирана. Затова, моля прегледайте всеки регистрационен файл и се уверете, че няма данни, които не желаете да споделяте преди да го споделите с някого."; + +"No log file found" = "Не е намерен регистрационен файл"; +"The log file can't be shared because no log file could be found or the log file is empty." = "Регистрационният файл не може да бъде споделен, защото не може да бъде намерен, или е празен."; +"Enable log file" = "Активиране на регистрационния файл"; + +"Really reset log file?" = "Наистина ли искате да нулирате регистрационния файл?"; +"This action can't be undone." = "Това действие не може да бъде отменено."; + +/* Data usage settings */ +"Data usage" = "Използване на данни"; +"Delete unused local copies" = "Изтриване на неизползваните локални копия"; +"Time measured since uploading, editing, downloading or viewing the respective file through this device. Does not apply to files downloaded via the Available Offline feature. Local copies may be deleted before the given period of time has passed, f.ex. because there's a newer version of a file on the server - or through the manual deletion of offline copies. Also, local copies may not be deleted after the given period of time has passed, f.ex. if an action is performed on it, the file is still in use - or the account holding the file hasn't been used in the app." = "Време, измерено от качването, редактирането, изтеглянето или преглеждането на съответния файл чрез това устройство. Не се отнася за файлове, изтеглени чрез функцията Налично офлайн. Локалните копия могат да бъдат изтрити преди изтичането на дадения период от време, напр. поради наличието на по-нова версия на файла на сървъра - или чрез ръчното изтриване на офлайн копията. Също така локалните копия не могат да бъдат изтривани след изтичането на дадения период от време, напр. ако върху тях е извършено някакво действие, файлът все още се използва - или профилът, в който се намира файлът не е бил използван в приложението."; +"never" = "никога"; +"after %@" = "след %@"; +"Decrease Slider Value" = "Намаляване на стойността с плъзгача"; +"Increase Slider Value" = "Увеличаване на стойността с плъзгача"; + +"Cellular Data Usage" = "Използване на мобилни данни"; +"Some cellular data may still be used. To completely avoid the usage of cellular data, please turn off access to cellular for the entire app in the Settings app." = "Възможно е все още да се използват някои мобилни данни. За да избегнете напълно използването на мобилни данни, изключете достъпа до мобилни мрежи за цялото приложение в Настройки."; +"Cellular Data Usage have been disabled via MDM configuration. Please contact your administrator for more information." = "Използването на мобилни данни е деактивирано чрез конфигурацията MDM. Моля, свържете се с администратор за повече информация."; +"General" = "Общи"; +"By feature" = "По функция"; + +"off" = "изключено"; +"enabled" = "активирано"; + +/* Display settings */ +"Advanced settings" = "Разширени настройки"; +"Show hidden files and folders" = "Показване на скритите файлове и папки"; +"Show folders on top" = "Показване на папките в горната част"; +"Disable gestures" = "Деактивиране на жестове"; +"Prevent dragging of files and folders and multiselection using system defined gestures" = "Предотвратяване на плъзгането на файлове и папки и множественото избиране с помощта на определени от системата жестове"; + +/* Advanced settings */ +"Enable diagnostics" = "Активиране на диагностика"; + +/* Passcode Messages */ + +"Enter code" = "Въведете код"; +"Repeat code" = "Повторете кода"; +"Delete code" = "Изтриване на код"; +"The entered codes are different" = "Въведените кодове са различни"; +"Incorrect code" = "Грешен код"; +"Please try again in %@" = "Моля, опитайте отново в %@"; +"Unlock %@" = "Отключване на %@"; +"Biometric authentication failed" = "Биометричното удостоверяване е неуспешно"; +"You are required to set the passcode" = "Необходимо е да зададете парола"; + +/* Certificate management */ + +"Certificates" = "Сертификати"; +"User-approved certificates" = "Сертификати, одобрени от потребителя"; + +"Approved" = "Одобрено"; +"Auto-approved" = "Автоматично одобрени"; +"Revoke approval" = "Отмяна на одобрението"; + +/* Actions */ + +"Forbidden Characters" = "Забранени символи"; +"File name cannot contain / or \\" = "Името на файла не може да съдържа / или \\"; +"Item with same name already exists" = "Вече съществува елемент със същото име"; +"An item with the same name already exists in this location." = "На това място вече съществува елемент със същото име."; +"New Folder" = "Нова папка"; +"Folder name" = "Име на папката"; +"Rename" ="Преименуване"; +"Create folder" ="Създаване на папка"; +"Duplicate" = "Копие"; +"Move" = "Преместване"; +"Move {{itemCount}} items" = "Move {{itemCount}} items"; +"Move \"{{itemName}}\"" = "Move \"{{itemName}}\""; +"Move here" = "Преместване тук"; +"Select target." = "Select target."; +"Open in" = "Отваряне в"; +"Copy" = "Копиране"; +"Copy {{itemCount}} items" = "Copy {{itemCount}} items"; +"Copy \"{{itemName}}\"" = "Copy \"{{itemName}}\""; +"Copy here" = "Копиране тук"; + +"Cannot connect to " = "Не можете да се свържете с "; +" couldn't download file(s)" = "неуспех при изтеглянето на файл( ове)"; +"Actions" = "Действия"; +"copy" = "копиране"; +"Close Window" = "Затваряне на прозореца"; +"Open in a new Window" = "Отваряне в нов прозорец"; +"Open in Window" = "Отваряне в прозорец"; +"Take photo or video" = "Направете снимка или видео"; +"%ld Item was copied to the clipboard" = "%ld Елемент беше копиран в клипборда"; +"%ld Items were copied to the clipboard" = "%ld Елемента бяха копирани в клипборда"; +"Please note: Folders can only be pasted into the %@ app and the same account." = "Моля, обърнете внимание: Папките могат да се поставят само в %@ приложението и в същия профил."; + +"Processing on server" = "Processing on server"; + +"Preparing…" = "Подготовка..."; + +"No actions available" = "Няма налични действия"; +"No actions are available for this folder, possibly because of missing permissions." = "Няма налични действия за тази папка, вероятно поради липсващи разрешения."; + +"Close actions menu" = "Затваряне на менюто за действия"; +"Favorited" = "Любими"; +"Favorite item" = "Любим елемент"; +"Unfavorite item" = "Нелюбим елемент"; +"Presentation Mode" = "Режим на презентация"; +"Enabling presentation mode will prevent the display from sleep mode until the view is closed." = "Активирането на режим на презентация ще предотврати преминаването на дисплея в режим на заспиване, докато изгледът не бъде затворен."; +"Enable" = "Активиране"; +"Exit Full Screen" = "Излизане от цял екран"; + +"Select one or more items." = "Select one or more items."; + +"Add item" = "Добавяне на елемент"; + +/* App Provider */ +"New document" = "Нов документ"; +"Pick a name" = "Pick a name"; +"Pick a document type to create:" = "Pick a document type to create:"; +"Error creating {{itemName}}" = "Error creating {{itemName}}"; + +"Opening…" = "Opening…"; + +"Open in {{appName}} (web)" = "Open in {{appName}} (web)"; +"Error opening {{itemName}} in {{appName}}" = "Error opening {{itemName}} in {{appName}}"; +"Opening documents is not supported by the app provider on this instance." = "Opening documents is not supported by the app provider on this instance."; + +/* Preview */ +"Open file" = "Отваряне на файл"; +"Network unavailable" = "Няма достъп до мрежата"; +"Error" = "Грешка"; +"Could not get the picture" = "Получаването на снимката е неуспешно"; +"Downloading" = "Изтегляне"; +"File couldn't be opened" = "Файлът не може да бъде отворен"; +"Connecting..." = "Свързване..."; + +"File" = "Файл"; +"%@ was updated" = "%@ беше актуализирана"; +"Would you like to view the updated version?" = "Искате ли да видите актуализираната версия?"; +"Show new version" = "Показване на новата версия"; +"Refresh without asking" = "Опресняване без запитване"; +"Ignore updates" = "Игнориране на актуализациите"; + +/* PDF Viewer */ +"Resume" = "Продължаване"; +"Go to page" = "Към страницата"; +"Page" = "Страница"; +"This document has %@ pages" = "Този документ съдържа %@ страници"; +"%@ of %@" = "%@ от %@"; +"Invalid Page" = "Невалидна страница"; +"The entered page number doesn't exist" = "Въведеният номер на страница не съществува"; +"Search PDF" = "Търсене на PDF"; +"Outline" = "Контур"; +"Find Next" = "Намиране на следващ"; +"Find Previous" = "Намиране на предишен"; +"Close Search" = "Затваряне на търсенето"; + +/* Photo Upload */ +"Upload" = "Качване"; +"Select All" = "Избиране на всичко"; +"Deselect All" = "Премахване на избора от всички"; +"All Photos" = "Всички снимки"; +"Albums" = "Албуми"; +"Importing from photo library" = "Импортиране от библиотека със снимки"; +"Media import" = "Импортиране на медия"; +"%@ of %@" = "%@ от %@"; +"Selected folder lacks file creation permission" = "Избраната папка няма разрешение за създаване на файлове"; +"%d Item" = "%d елемент"; +"%d Items" = "%d елемента"; +"Select Items" = "Избор на елементи"; + +/* Scan */ +"Scan" = "Сканиране"; +"Scans" = "Сканирания"; +"Scan document" = "Сканиране на документ"; +"Saving" = "Запазване"; +"File format" = "Файлов формат"; +"Name" = "Име"; +"Save as" = "Запазване като"; +"Options" = "Опции"; +"Scan additional" = "Допълнително сканиране"; +"Create one file per page" = "Създаване на отделен файл на страница"; + +/* Sharing */ +"Sharing requires an active connection." = "Sharing requires an active connection."; +"No items shared with you" = "No items shared with you"; +"No items shared by you" = "No items shared by you"; +"No items shared by link" = "No items shared by link"; + +"Searching Shares…" = "Търсене на споделяния..."; +"Recipient" = "Получател"; +"Recipients" = "Получатели"; +"Public Link" = "Публична връзка"; +"Public Links" = "Публични връзки"; +"Shared by {{owner}}" = "Shared by {{owner}}"; +"Shared with {{recipient}}" = "Shared with {{recipient}}"; +"Shared with {{recipients}}" = "Shared with {{recipients}}"; +"Expires {{expirationDate}}" = "Expires {{expirationDate}}"; +"Share {{itemName}}" = "Share {{itemName}}"; +"Create link" = "Create link"; +"Invite" = "Invite"; +"Invite Recipient" = "Поканете получател"; +"Recipients" = "Получатели"; +"Add email or name" = "Добавете имейл или име"; +"Users" = "Потребители"; +"Groups" = "Групи"; +"Start typing to search users, groups and remote users." = "Започнете да пишете, за да търсите потребители, групи и отдалечени потребители."; +"(Group)" = "(Групово)"; +"Adding User to Share failed" = "Неуспешно добавяне на потребител за споделяне"; +"Permissions" = "Разрешения"; +"Invited: %@" = "Поканени: %@"; +"Created: %@" = "Създадено: %@"; +"Allows the users you share with to re-share" = "Позволява на потребителите, с които споделяте да споделят отново"; +"Allows the users you share with to edit your shared files, and to collaborate" = "Позволява на потребителите, с които споделяте да редактират вашите споделени файлове и да си сътрудничат"; +"Allows the users you share with to create new files and add them to the share" = "Позволява на потребителите, с които споделяте да създават нови файлове и да ги добавят към споделянето"; +"Allows uploading a new version of a shared file and replacing it" = "Позволява качване на нова версия на споделен файл и заместването му"; +"Allows the users you share with to delete shared files" = "Позволява на потребителите, с които споделяте да изтриват споделените файлове"; +"Setting permission failed" = "Неуспешно задаване на разрешение"; +"Shared with" = "Споделено с"; +"Remove Recipient failed" = "Премахването на получателя е неуспешно"; +"Remove Recipient" = "Премахване на получателя"; +"Create" = "Създаване"; +"Change" = "Промяна"; +"Recipients can view or download contents." = "Получателите могат да преглеждат или изтеглят съдържанието."; +"Recipients can view, download, edit, delete and upload contents." = "Получателите могат да преглеждат, изтеглят, редактират, изтриват и качват съдържание."; +"Receive files from multiple recipients without revealing the contents of the folder." = "Получаване на файлове от множество получатели без да се разкрива съдържанието на папката."; +"Download / View" = "Изтегляне / Преглед"; +"Download / View / Upload" = "Изтегляне / Преглед / Качване"; +"Upload only (File Drop)" = "Само качване (пускане на файл)"; +"Creating public link failed" = "Създаването на публична връзка е неуспешно"; +"Create Public Link" = "Създаване на публична връзка"; +"Links" = "Връзки"; +"Link" = "Връзка"; +"Setting expiration date failed" = "Не бе успешно задаването на дата за изтичане на валидност"; +"Expiration date" = "Дата за изтичане на валидността"; +"Copy Public Link" = "Копиране на публичната връзка"; +"Delete Public Link" = "Изтриване на публична връзка"; +"Deleting Public Link failed" = "Изтриването на публичната връзка е неуспешно"; +"Deleting password failed" = "Изтриването на паролата е неуспешно"; +"Setting password failed" = "Задаването на парола е неуспешно"; +"Type to update password" = "Въведете, за да актуализирате паролата"; +"Cannot change permission" = "Не може да се промени разрешението"; +"Before you can set the permission\n%@,\n you must enter a password." = "Преди да зададете разрешението\n%@,\nтрябва да въведете парола."; +"Password Protected" = "Защитено с парола"; +"Pending Federated Invites" = "Чакащи федеративни покани"; +"Pending Invites" = "Чакащи покани"; +"Shared with you" = "Споделено с вас"; +"Shared with others" = "Споделено с други"; +"Shares" = "Споделяния"; +"Copy Private Link" = "Копиране на поверителната връзка"; +"Only people who have access to the file/folder can use it. Use it as a permanent link for yourself or to point others to files within shares." = "Само хора, които имат достъп до файла/папката могат да го използват. Използвайте го като постоянна връзка за себе си или за да насочвате други към споделените файлове."; +"Accept Share failed" = "Приемането на споделянето е неуспешно"; +"Decline Share failed" = "Отказът на споделянето е неуспешен"; +"Accept" = "Приемане"; +"Decline" = "Отказване"; +"Declined" = "Отхвърляне"; +"Decline Share" = "Отказ от споделяне"; +"Unshare" = "Прекратяване на споделянето"; +"Unshare failed" = "Прекратяването на споделянето е неуспешно"; +"Are you sure you want to unshare these items?" = "Сигурни ли сте, че искате да прекратите споделянето на тези елементи?"; +"Are you sure you want to unshare this item?" = "Сигурни ли сте, че искате да прекратите споделянето на този елемент?"; +"Share" = "Споделяне"; +"Read" = "Прочетете"; +"Can Share" = "Може да споделя"; +"Can Edit" = "Може да редактира"; +"Can Edit and Change" = "Може да редактира и променя"; +"Can Create" = "Може да създава"; +"Can Change" = "Може да променя"; +"Can Delete" = "Може да изтрива"; +"Accept Invite %@" = "Приемане на покана %@"; +"Decline Invite %@" = "Отхвърляне на покана %@"; +"Decline cannot be undone." = "Отказът не може да бъде отменен."; +"Sharing" = "Споделяне"; +"You" = "Вие"; +"Share this file" = "Споделяне на този файл"; +"Share this folder" = "Споделяне на тази папка"; +"shared" = "споделено"; +"Owner" = "Собственик"; +"Private Link" = "Поверителна връзка"; +"Created Public Link" = "Създаване на публична връзка"; +"URL was copied to the clipboard" = "URL адреса е копиран в клипборда"; +"%@ is not available, when this account is offline. Please open the app and log into your account before you can do this action." = "%@ не е наличен, когато този профил е офлайн. Моля, отворете приложението и влезте в профила си, за да извършите това действие."; +"%@ is not available for this item." = "%@ не е налично за този елемент."; +"Share with user/group" = "Споделяне с потребител/група"; +"Share link" = "Връзка за споделяне"; + +"Shared with me" = "Shared with me"; +"Shared by me" = "Shared by me"; +"Shared by link" = "Споделено чрез връзка"; + +"Share with" = "Споделяне с"; +"Add" = "Добавяне"; +"Save changes" = "Запазване на промените"; + +/* Quick Access view */ +"Quick Access" = "Бърз достъп"; +"Collection" = "Колекция"; +"Recents" = "Последни"; +"Favorites"= "Любими"; +"Images" = "Изображения"; +"PDF Documents" = "PDF документи"; +"Text" = "Текст"; +"Documents" = "Документи"; +"Audio" = "Аудио"; + +/* Favorites view */ +"No favorites found" = "No favorites found"; +"If you make an item a favorite, it will turn up here." = "If you make an item a favorite, it will turn up here."; + +/* Media files settings */ +"Media Files" = "Медийни файлове"; +"Download instead of streaming" = "Изтегляне вместо поточно предаване"; + +/* Photo upload settings */ +"Media Upload" = "Качване на медия"; +"Media Export" = "Експортиране на медии"; +"Auto Upload" = "Автоматично качване"; +"Convert HEIC to JPEG" = "Конвертиране на HEIC в JPEG"; +"Convert videos to MP4" = "Конвертиране на видеоклипове в MP4"; +"Preserve original media file names" = "Запазване на оригиналните имена на мултимедийните файлове"; +"Auto Upload Photos" = "Автоматично качване на снимки"; +"Auto Upload Videos" = "Автоматично качване на видеоклипове"; +"Account" = "Профил"; +"Accounts" = "Профили"; +"Select account" = "Избор на профил"; +"Photo upload path" = "Път за качване на снимки"; +"Video upload path" = "Път за качване на видео"; +"Select Upload Path" = "Избор на път за качване"; +"Auto upload of media was disabled since configured account / folder was not found" = "Автоматичното качване на медия е деактивирано, тъй като не е намерен конфигурираният профил/папка"; +"Importing %@ media files for upload" = "Импортиране на %@ медийни файла за качване"; +"Photo upload" = "Качване на снимка"; +"Video upload" = "Качване на видео"; + +/* Background media uploads */ +"Background uploads (Lab Version)" = "Качвания във фонов режим (лабораторна версия)"; +"Background uploads" = "Качвания във фонов режим"; +"Use background refresh" = "Използване на опресняване във фонов режим "; +"Allow this app to refresh the content when on Wi-Fi or mobile network in background." = "Разрешаване на приложението да опреснява съдържанието във фонов режим, когато е свързано с Wi-Fi или мобилна мрежа."; +"Enable background uploads" = "Активиране на качвания във фонов режим"; +"Use background location updates" = "Използване на актуализации на местоположението във фонов режим"; +"Background upload notifications" = "Известия за качване във фонов режим"; +"If you would like background media uploads to be more reliable, you should enable background location updates." = "Ако искате качването на медийни файлове във фонов режим да бъде по-надеждно, трябва да разрешите актуализациите на местоположението на заден план."; +"Background media uploads rely on location updates and will stop working if location acquisition permissions are revoked." = "Качването на медийни файлове във фонов режим зависи от актуализациите на местоположението и ще спре да работи, ако разрешението за достъп до местоположението бъде спряно."; +"Otherwise background media uploads using background refresh technology would depend on how frequently you use the app." = "В противен случай качването на медийни файлове във фонов режим с помощта на технологията за опресняване на заден план ще зависи от това колко често използвате приложението."; +"Location permission denied" = "Разрешението за местоположение е отказано"; +"Please re-enable location acquisition in system settings" = "Моля, разрешете отново достъп до местоположението в системните настройки"; +"Scheduled upload of %ld media assets" = "Планирано е качване на %ld медийни активи"; + +/* Progress summarizer */ +"Creating %ld folders…" = "Създаване на %ld папки..."; +"Moving %ld items…" = "Преместване на %ld елемента..."; +"Copying %ld items…" = "Копиране на %ld елемента..."; +"Deleting %ld items…" = "Изтриване на %ld елемента..."; +"Uploading %ld files…" = "Качване на %ld файла..."; +"Downloading %ld files…" = "Изтегляне на %ld файла..."; +"Updating %ld items…" = "Актуализиране на %ld файла..."; + +/* Offline storage management */ +"Free on %@" = "Безплатно на %@"; +"unknown" = "неизвестен"; +"Offline files use" = "Използване на файловете офлайн"; +"Compacting of '%@' failed" = "Уплътняването на '%@' е неуспешно"; +"Include available offline files" = "Включване на файловете налични офлайн"; +"Delete all Offline Files" = "Изтриване на всички офлайн файлове"; +"Manage" = "Управление"; +"Storage" = "Хранилище"; +"Compacting" = "Уплътняване"; + +"Really include available offline files?" = "Наистина ли ще включите наличните офлайн файлове?"; +"Files and folders marked as Available Offline will become unavailable. They will be re-downloaded next time you log into your account (connectivity required)." = "Файловете и папките, маркирани като Налични офлайн, ще станат недостъпни. Те ще бъдат изтеглени повторно следващия път, когато влезете в профила си (изисква се свързаност)."; +"Removes downloaded files and local copies of items marked as Available Offline. The latter will be re-downloaded next time you log into your account (connectivity required)." = "Премахва изтеглени файлове и локални копия на елементи, маркирани като Налични офлайн. Последните ще бъдат изтеглени отново при следващото влизане в профила ви (изисква се свързаност)."; + +/* Diagnostic */ +"Diagnostic Overview" = "Преглед на диагностиката"; +"Diagnostics" = "Диагностика"; +"Share Diagnostics" = "Споделяне на диагностиката"; +"Action executed" = "Действието е изпълнено"; + +"Update Status" = "Актуализиране на статуса"; +"Completed update scans" = "Завършено е сканирането за актуализация"; +"Total update scans" = "Общо сканирания за актуализации"; + +/* Available offline */ +"Root folder" = "Основна папка"; +"at" = "в"; +"(no match)" = "(без съвпадение)"; + +"Make available offline" = "Предоставяне на достъп офлайн"; +"Make unavailable offline" = "Да се направи недостъпен офлайн"; + +"Available Offline" = "Налично офлайн"; + +"No files available offline" = "No files available offline"; +"Files selected and downloaded for offline availability will show up here." = "Files selected and downloaded for offline availability will show up here."; + +"Locations" = "Locations"; +"Downloaded Files" = "Downloaded Files"; + +/* Single Account */ +"You are connected as\n%@" = "Свързани сте като\n%@"; + +/* Release Notes */ +"Proceed" = "Продължете"; +"New in %@" = "Нови в %@"; +"Thank you for using %@.\nIf you like our App, please leave an AppStore review.\n❤️" = "Благодарим ви, че използвате %@.\nАко нашето приложение ви харесва, моля оставете отзив в AppStore.\n❤️"; +"Thank you for using %@.\n" = "Благодарим ви, че използвате %@.\n"; + +/* Key Commands */ +"Select Next" = "Избор и напред"; +"Select Previous" = "Избор на предишното"; +"Open Selected" = "Отваряне на избраното"; +"Change Sort Order" = "Промяна на реда на подреждане"; +"Search" = "Търсене"; +"Back" = "Назад"; +"Save" = "Запазване"; +"Sort by %@" = "Подреждане по %@"; +"Tab %@" = "Раздел %@"; +"Select Last Item on Page" = "Избор на последния елемент от страницата"; +"Scroll to Top" = "Превъртане нагоре"; +"Scroll to Bottom" = "Превъртане надолу"; +"Copy to Clipboard" = "Копиране в клипборда"; +"Import from Clipboard" = "Импортиране от клипборда"; +"Choose destination directory…" = "Изберете директория за дестинация..."; +"Next" = "Напред"; +"Previous" = "Предишна"; +"Favorite" = "Любими"; +"Cut" = "Изрязване"; +"Paste" = "Поставяне"; +"Play/Pause" = "Възпроизвеждане/пауза"; +"Skip Back" = "Прескачане назад"; +"Skip Ahead" = "Прескачане напред"; +"Go to Beginning" = "Към началото"; +"Mute/Unmute" = "Заглушаване/Включване на звука"; +"Full Screen" = "Цял екран"; +"Switch Theme Style" = "Превключване на стила на темата"; + +/* Markup */ +"How should this file be saved?" = "Как трябва да бъде запазен този файл?"; +"Overwrite original" = "Презаписване на оригинала"; +"Save as copy" = "Запазване като копие"; +"Discard changes" = "Отхвърляне на промените"; +"Markup" = "Отбелязване"; +"Crop or Rotate" = "Изрязване или завъртане"; +"Saving edited file failed" = "Запазването на редактирания файл е неуспешно"; +"File no longer exists" = "Файлът вече не съществува"; + +/* Licensing */ +"Unlocked" = "Отключен"; +"Unlock" = "Отключване"; + +"Subscribe Now" = "Абонирайте се сега"; +"Free" = "Безплатно"; + +"%@ / %@" = "%@ / %@"; +"after free %@ trial" = "след безплатен %@ пробен период"; + +/* Example usage: "Try [14 days] for free." */ +"Try %@ for free." = "Изпробвайте %@ безплатно."; + +/* Example usage: "Then [1,99 €] / [year]." */ +"Then %@ / %@." = "След това %@ / %@."; + +/* Example usage: "[1,99 €] / [year] - starting immediately." */ +"%@ / %@ – starting immediately" = "%@ / %@ - започване веднага"; + +/* Licensing: Pro Features */ +"Pro Features" = "Професионални функции"; + +/* Licensing: App Store */ +"Restore purchases" = "Възстановяване на покупки"; +"Restoring purchases…" = "Възстановяване на покупките..."; +"Error restoring purchases" = "Грешка при възстановяване на покупките"; +"Error loading product info from App Store"= "Грешка при зареждане от App Store на информацията за продукта"; +"Purchase failed" = "Покупката е неуспешна"; +"More information" = "Повече информация"; + +/* Licensing: Enterprise */ +"Enterprise" = "Корпоративно"; + +/* Licensing: Settings */ +"In-App Purchases" = "Покупки в приложението"; +"Purchases & Subscriptions" = "Purchases & Subscriptions"; +"Error fetching transactions" = "Грешка при извличането на транзакции"; +"Document Scanner" = "Скенер за документи"; +"Scan documents and photos with your camera." = "Сканирайте документи и снимки с камерата си."; +"Shortcuts Actions" = "Преки пътища и действия"; +"Use ownCloud actions in Shortcuts." = "Използвайте Преки пътища за действия в ownCloud."; +"Markup Documents" = "Отбелязване на документи"; +"Markup photos and PDF files." = "Отбелязване на снимки и PDF файлове"; +"Unlock all Pro Features." = "Отключете всички професионални функции."; + +"Purchased App Version" = "Закупена версия на приложението"; +"Receipt Date" = "Дата на квитанцията"; +"Manage subscription" = "Управление на абонамента"; +"Purchases are not allowed on this device." = "Покупките не са разрешени на това устройство."; +"Type" = "Тип"; +"Quantity" = "Количество"; +"Product" = "Продукт"; +"Date" = "Дата"; +"Cancelled" = "Отменено"; +"Ends" = "Край"; +"none" = "няма"; +"trial" = "пробен период"; +"purchase" = "закупуване"; +"subscription" = "абонамент"; +"Photo Pro Features" = "Функции на Photo Pro"; +"Image metadata, extended upload options" = "Метаданни за изображения, разширени възможности за качване"; +"Purchase" = "Покупка"; +"Subscribe" = "Абонамент"; + +"No accounts found" = "Няма намерени профили"; +"In order to accurately determine your current licensing status, please add one or more accounts first." = "За да определите точно текущия си лицензионен статус, първо трябва да добавите един или повече профили."; + +/* Share Sheet */ +"Save File" = "Запазване на файл"; +"Choose an account and folder to import into." = "Изберете профил и папка, в които да импортирате."; +"No account configured.\nSetup an new account in the app to save to." = "Няма конфигуриран профил.\nНастройте нов профил в приложението, в който да запазвате."; +"Importing item %ld of %ld" = "Импортиране на елемент %ld от %ld"; +"Error importing %@" = "Грешка при импортиране %@"; +"Error loading item" = "Грешка при зареждането на елемента"; +"Preparing…" = "Подготовка..."; +"Import \"{{itemName}}\"" = "Import \"{{itemName}}\""; +"Import {{itemCount}} files" = "Import {{itemCount}} files"; +"Save here" = "Запазване тук"; + +/* FileProvider */ +/* - Disabled */ +"File Provider access has been disabled by the administrator.\n\nPlease use the app to access your files." = "File Provider access has been disabled by the administrator.\n\nPlease use the app to access your files."; +"File Provider access has been disabled by the administrator. Please use the app to create new folders." = "File Provider access has been disabled by the administrator. Please use the app to create new folders."; +"File Provider access has been disabled by the administrator. Please use the share extension to import files." = "File Provider access has been disabled by the administrator. Please use the share extension to import files."; + +/* Disallowed Import Methods */ +/* - Share Extension */ +"Share Extension disabled" = "Деактивиране на разширението за споделяне"; +"Importing files through the Share Extension is not allowed on this device." = "Импортирането на файлове чрез разширението Споделяне не е разрешено на това устройство."; + +/* - Open with */ +"Opening not allowed" = "Отварянето не е разрешено"; +"Importing files through opening is not allowed on this device." = "Импортирането на файлове чрез отваряне не е разрешено на това устройство."; + +/* - Files.app */ +"Import not allowed" = "Импортирането не е разрешено"; +"Importing files through the File Provider is not allowed on this device." = "Импортирането на файлове чрез услугата Доставчик на файлове не е разрешено на това устройство."; + +/* Misc. accessibility */ +"Enter multiple selection" = "Въвеждане на множествена селекция"; + +/* Universal Links */ +"Resolving link…" = "Разрешаване на връзката..."; +"Link resolution failed" = "Неуспешно разрешаване на връзката"; +"Couldn't resolve a private link since you are offline and corresponding item is not cached locally." = "Неуспешно е разрешаването на поверителната връзка, тъй като сте офлайн и съответният елемент не е кеширан локално."; +"Couldn't resolve a private link since the item is not known to the server." = "Неуспешно е разрешаването на поверителната връзка, тъй като елементът е неизвестен за сървъра."; +"Link points to an account bookmark which is not configured in the app." = "Връзката сочи към отметка на профил, което не е конфигурирано в приложението."; + +/* Photo meta-data */ +"Image metadata" = "Метаданни на изображението"; +"Image details" = "Подробности за изображението"; +"Profile" = "Профил"; +"Size" = "Размер"; +"Density" = "Плътност"; +"Color model" = "Цветови модел"; +"%@ (%d bits/channel)" = "%@ (%d бита/канал)"; +"Histogram" = "Хистограма"; + +"Camera details" = "Подробности за камерата"; +"Capture settings" = "Настройки за заснемане"; +"Time" = "Време"; +"EXIF aux info" = "EXIF aux информация"; +"Authoring" = "Авторство"; +"TIFF" = "TIFF"; + +"Lens make" = "Производител на обектива"; +"Lens model" = "Модел на обектива"; +"Lens info" = "Информация за обектива"; +"Focal length" = "Фокусно разстояние"; +"Focal length @ 35 mm" = "Фокусно разстояние @ 35 mm"; + +"Shutter speed" = "Скорост на затвора"; +"Aperture" = "Бленда"; +"ISO" = "ISO"; + +/* EXIF: exposure program */ +"Program" = "Програма"; +"Not defined" = "Неопределен"; +"Manual" = "Ръчен"; +"Normal" = "Нормален"; +"Aperture priority" = "Режим на блендата"; +"Shutter priority" = "Режим на затвора"; +"Creative" = "Творчески"; +"Action" = "Действие"; +"Portrait" = "Портрет"; +"Landscape" = "Пейзаж"; + +/* EXIF: exposure metering mode */ +"Metering" = "Измерване"; +"unknown" = "неизвестен"; +"Average" = "Средна стойност"; +"CenterWeightedAverage" = "Централно претеглена средна стойност"; +"Spot" = "Точка"; +"MultiSpot" = "Множество точки"; +"Pattern" = "Модел"; +"Partial" = "Частично"; + +/* EXIF: flash mode */ +"Flash" = "Светкавица"; +"Fired" = "Изгаряне"; +"Didn't fire" = "Без изгаряне"; +"No strobe return detection" = "Няма открит върнат отблясък"; +"Strobe return light not detected" = "Не е открит светлинен отблясък"; +"Strobe return light detected" = "Открит е върнат светлинен отблясък"; +"Compulsory flash firing" = "Задължителна светкавица"; +"Compulsory flash supression" = "Задължително потискане на светкавицата"; +"Auto mode" = "Автоматичен режим"; +"Red eye detection supported" = "Поддържа се разпознаване на червените очи"; +"not present" = "не присъства"; + +/* EXIF: white balance */ +"White balance" = "Баланс на бялото"; +"Auto" = "Автоматично"; +"Manual" = "Ръчен"; + +"Exposure bias" = "Изкривяване на експозицията"; +"Uncalibrated" = "Некалибрирано"; +"Original date" = "Оригинална дата"; +"Digitized date" = "Дигитализирана дата"; + +"Color space" = "Цветово пространство"; + +/* EXIF: aux info */ +"Lens ID" = "ID на обектива"; +"Lens serial" = "Серийна версия на обектива"; +"Serial number" = "Сериен номер"; +"Flash compensation" = "Компенсация на светкавицата"; +"Owner" = "Собственик"; +"Firmware" = "Фърмуер"; +"Keywords" = "Ключови думи"; +"Copyright" = "Авторско право"; + +/* IPTC meta data */ +"Contact info" = "Информация за контакт"; +"Usage terms" = "Условия за ползване"; +"Scene" = "Сцена"; +"Description" = "Описание"; + +/* IPTC photometric interpretation */ +"Photometric interpretation" = "Фотометрична интерпретация"; +"none" = "няма"; +"RGB" = "RGB"; +"YCbCr" = "YCbCr"; + +/* EXIF: gps position data */ +"GPS Location" = "GPS местоположение"; +"Coordinates" = "Координати"; +"Altitude" = "Надморска височина"; +"Place" = "Място"; +"E" = "Е"; +"W" = "W"; +"N" = "N"; +"S" = "S"; + +/* Pro photo upload settings */ +"Extended upload settings" = "Разширени настройки за качване"; +"Prefer unedited photos" = "Предпочитам нередактирани снимки"; +"Prefer RAW photos" = "Предпочитам снимки в RAW формат"; +"Prefer original videos" = "Предпочитам оригинални видеоклипове"; + +/* Limited photo library access */ +"Limited Photo Access" = "Ограничен достъп до снимки"; +"Access for the media selected for upload is limited" = "Достъпът е ограничен до медията избрана за качване"; +"No Access to the media selected for upload" = "Няма достъп до избраната за качване медия"; + +/* macOS support */ +"Passcode protection is not supported on this device.\nPlease disable passcode lock in the app settings." = "Защитата с парола не се поддържа на това устройство.\nМоля, деактивирайте заключването с парола от настройките на приложението."; + +/* MDM settings push */ +"New settings received from MDM" = "Получени са нови настройки от MDM"; +"Tap to quit the app." = "Докоснете, за да спрете приложението."; +"Tap to launch the app." = "Докоснете за да стартирате приложението."; + +/* Beta warning */ +"Beta Warning" = "Beta Warning"; +"\nThis is a BETA release that may - and likely will - still contain bugs.\n\nYOU SHOULD NOT USE THIS BETA VERSION WITH PRODUCTION SYSTEMS, PRODUCTION DATA OR DATA OF VALUE. YOU'RE USING THIS BETA AT YOUR OWN RISK.\n\nPlease let us know about any issues that come up via the \"Send Feedback\" option in the settings." = "\nThis is a BETA release that may - and likely will - still contain bugs.\n\nYOU SHOULD NOT USE THIS BETA VERSION WITH PRODUCTION SYSTEMS, PRODUCTION DATA OR DATA OF VALUE. YOU'RE USING THIS BETA AT YOUR OWN RISK.\n\nPlease let us know about any issues that come up via the \"Send Feedback\" option in the settings."; +"Agree" = "Agree"; diff --git a/ownCloud/Resources/cs.lproj/InfoPlist.strings b/ownCloud/Resources/cs.lproj/InfoPlist.strings index 4e2788526..e97ffac19 100644 Binary files a/ownCloud/Resources/cs.lproj/InfoPlist.strings and b/ownCloud/Resources/cs.lproj/InfoPlist.strings differ diff --git a/ownCloud/Resources/de-DE.lproj/InfoPlist.strings b/ownCloud/Resources/de-DE.lproj/InfoPlist.strings index 1ace92534..ca2233854 100644 Binary files a/ownCloud/Resources/de-DE.lproj/InfoPlist.strings and b/ownCloud/Resources/de-DE.lproj/InfoPlist.strings differ diff --git a/ownCloud/Resources/de-DE.lproj/Localizable.strings b/ownCloud/Resources/de-DE.lproj/Localizable.strings index 2c9b259d2..2930d8c17 100644 Binary files a/ownCloud/Resources/de-DE.lproj/Localizable.strings and b/ownCloud/Resources/de-DE.lproj/Localizable.strings differ diff --git a/ownCloud/Resources/de.lproj/InfoPlist.strings b/ownCloud/Resources/de.lproj/InfoPlist.strings index 31069d00f..ca2233854 100644 Binary files a/ownCloud/Resources/de.lproj/InfoPlist.strings and b/ownCloud/Resources/de.lproj/InfoPlist.strings differ diff --git a/ownCloud/Resources/de.lproj/Localizable.strings b/ownCloud/Resources/de.lproj/Localizable.strings index 0016f6f03..df7c44913 100644 Binary files a/ownCloud/Resources/de.lproj/Localizable.strings and b/ownCloud/Resources/de.lproj/Localizable.strings differ diff --git a/ownCloud/Resources/de.lproj/Localizable.strings.current b/ownCloud/Resources/de.lproj/Localizable.strings.current new file mode 100644 index 000000000..a8e0dbf3b Binary files /dev/null and b/ownCloud/Resources/de.lproj/Localizable.strings.current differ diff --git a/ownCloud/Resources/de_CH.lproj/InfoPlist.strings b/ownCloud/Resources/de_CH.lproj/InfoPlist.strings index 31069d00f..69d989e7c 100644 Binary files a/ownCloud/Resources/de_CH.lproj/InfoPlist.strings and b/ownCloud/Resources/de_CH.lproj/InfoPlist.strings differ diff --git a/ownCloud/Resources/de_CH.lproj/Localizable.strings b/ownCloud/Resources/de_CH.lproj/Localizable.strings index 2ff6db788..5bfd6723b 100644 Binary files a/ownCloud/Resources/de_CH.lproj/Localizable.strings and b/ownCloud/Resources/de_CH.lproj/Localizable.strings differ diff --git a/ownCloud/Resources/el.lproj/InfoPlist.strings b/ownCloud/Resources/el.lproj/InfoPlist.strings index 453295626..91a17eac5 100644 Binary files a/ownCloud/Resources/el.lproj/InfoPlist.strings and b/ownCloud/Resources/el.lproj/InfoPlist.strings differ diff --git a/ownCloud/Resources/el.lproj/Localizable.strings b/ownCloud/Resources/el.lproj/Localizable.strings index bf00d4605..5a4da9536 100644 Binary files a/ownCloud/Resources/el.lproj/Localizable.strings and b/ownCloud/Resources/el.lproj/Localizable.strings differ diff --git a/ownCloud/Resources/en-GB.lproj/InfoPlist.strings b/ownCloud/Resources/en-GB.lproj/InfoPlist.strings index 604423c3b..e97ffac19 100644 Binary files a/ownCloud/Resources/en-GB.lproj/InfoPlist.strings and b/ownCloud/Resources/en-GB.lproj/InfoPlist.strings differ diff --git a/ownCloud/Resources/en-GB.lproj/Localizable.strings b/ownCloud/Resources/en-GB.lproj/Localizable.strings index 259c4468a..f0789809d 100644 --- a/ownCloud/Resources/en-GB.lproj/Localizable.strings +++ b/ownCloud/Resources/en-GB.lproj/Localizable.strings @@ -1,4 +1,4 @@ -/* +/* Localizable.strings ownCloud @@ -34,6 +34,7 @@ "Cancel" = "Cancel"; "Approve" = "Approve"; "Error" = "Error"; +"Issues" = "Issues"; "Review Connection" = "Review Connection"; "Authenticated via" = "Authenticated via"; "Authenticated as %@ via %@" = "Authenticated as %@ via %@"; @@ -46,8 +47,10 @@ "Certificate was rejected by user." = "Certificate was rejected by user."; "Certificate has issues.\nOpen 'Certificate Details' for more informations." = "Certificate has issues.\nOpen 'Certificate Details' for more informations."; "No issues found. Certificate passed validation." = "No issues found. Certificate passed validation."; -"Certificate may have issues, but was accepted by user.\nOpen 'Certificate Details' for more informations." = "Certificate may have issues, but was accepted by user.\nOpen 'Certificate Details' for more informations."; -"If you 'Continue', you will be prompted to allow the '%@' App to open OAuth2 login where you can enter your credentials." = "If you 'Continue', you will be prompted to allow the '%@' App to open OAuth2 login where you can enter your credentials."; +"Certificate may have issues, but was accepted by user.\nOpen 'Certificate Details' for more informations." = "Certificate may have issues but was accepted by the user.\nOpen 'Certificate Details' for more information."; +"If you 'Continue', you will be prompted to allow the '{{app.name}}' app to open the {{authmethodName}} login page where you can enter your credentials." = "If you 'Continue', you will be prompted to allow the '{{app.name}}' app to open the {{authmethodName}} login page where you can enter your credentials."; + +"Error opening %@" = "Error opening %@"; "Contacting server…" = "Contacting server…"; "Authenticating…" = "Authenticating…"; @@ -55,19 +58,30 @@ "Fetching user information…" = "Fetching user information…"; "Updating connection…" = "Updating connection…"; +"Preparing account" = "Preparing account"; +"Please wait…" = "Please wait…"; +"Skip" = "Skip"; + "Missing hostname" = "Missing hostname"; "The entered URL does not include a hostname." = "The entered URL does not include a hostname."; "Add account" = "Add account"; "Server name" = "Server name"; "Server Password" = "Server Password"; "Server Username" = "Server Username"; +"Show accounts" = "Show accounts"; +"Setup complete" = "Setup complete"; "Edit Login" = "Edit Login"; "Manage Storage" = "Manage Storage"; "Access Files" = "Access Files"; "Log out" = "Log out"; +"Retry" = "Retry"; + +/* Bookmark Info */ +"Bookmark Metadata" = "Bookmark Metadata"; /* Client */ "Browse" = "Browse"; +"Files" = "Files"; "Disconnect" = "Disconnect"; "Connecting…" = "Connecting…"; "Connected." = "Connected."; @@ -86,21 +100,85 @@ "Sort by %@" = "Sort by %@"; "Sort by" = "Sort by"; -"name (A-Z)" = "name (A-Z)"; -"name (Z-A)" = "name (Z-A)"; -"type" = "type"; +"name" = "name"; +"kind" = "kind"; "size" = "size"; "date" = "date"; -"Search this folder" = "Search this folder"; + +"Tree" = "Tree"; +"Search folder" = "Search folder"; +"Search tree" = "Search tree"; +"Search from {{folder.name}}" = "Search from {{folder.name}}"; +"Search space" = "Search space"; +"Search {{space.name}}" = "Search {{space.name}}"; +"Search account" = "Search account"; +"Enter a search term" = "Enter a search term"; +"Search for users or groups" = "Search for users or groups"; +"Enter the user or group you want to invite." = "Enter the user or group you want to invite."; + "Pending" = "Pending"; "Show parent paths" = "Show parent paths"; +"Reveal in folder" = "Reveal in folder"; "%@ of %@ used" = "%@ of %@ used"; "Total: %@" = "Total: %@"; +"%@ item | " = "%@ item | "; +"%@ items | " = "%@ items | "; + +"{{itemCount}} items with {{totalSize}} total ({{fileCount}} files, {{folderCount}} folders)" = "{{itemCount}} items with {{totalSize}} total ({{fileCount}} files, {{folderCount}} folders)"; +"{{remaining}} available" = "{{remaining}} available"; + +"Show more results" = "Show more results"; + +"An error occurred" = "An error occurred"; + +/* Item Layouts */ +"List" = "List"; +"Grid" = "Grid"; +"Item grid" = "Item grid"; +"Image grid" = "Image grid"; + +/* Saved searches */ +"Saved searches" = "Saved searches"; +"Saved search templates" = "Saved search templates"; +"Save search" = "Save search"; +"Save as search template" = "Save as search template"; + +"Name of saved search" = "Name of saved search"; +"Saved search" = "Saved search"; +"Saved searches" = "Saved searches"; +"Name of template" = "Name of template"; +"Search template" = "Search template"; +"Search templates" = "Search templates"; + +/* Search scope */ +"Search scope" = "Search scope"; +"Toggle Search Scope" = "Toggle Search Scope"; + +/* Static Login Setup */ +"Server error" = "Server error"; +"The server doesn't support any allowed authentication method." = "The server doesn't support any allowed authentication method."; +"The server doesn't support any known and allowed authentication method found." = "The server doesn't support any known and allowed authentication method found."; +"Retry detection" = "Retry detection"; +"Enter your username and password" = "Enter your username and password"; +"Please log in to authorize the app." = "Please log in to authorise the app."; +"Welcome to %@" = "Welcome to %@"; +"Please enter a server URL" = "Please enter a server URL"; +"Please pick a profile to begin setup" = "Please pick a profile to begin setup"; + +/* Single Account */ +"You are connected as\n%@" = "You are connected as\n%@"; +"Login" = "Login"; +"Wrong URL" = "Wrong URL"; +"Please enter a valid URL" = "Please enter a valid URL"; +"Missing Profile URL" = "Missing Profile URL"; +"The Profile '%@' does not have a URL configured.\nPlease provide a URL via configuration or MDM." = "The Profile '%@' does not have a URL configured.\nPlease provide a URL via configuration or MDM."; /* Client Messages */ -"Empty folder" = "Empty folder"; +"No contents" = "No contents"; "This folder contains no files or folders." = "This folder contains no files or folders."; +"This folder is empty." = "This folder is empty."; +"This folder is empty. Fill it with content:" = "This folder is empty. Fill it with content:"; "Folder removed" = "Folder removed"; "This folder no longer exists on the server." = "This folder no longer exists on the server."; @@ -115,24 +193,49 @@ "Not now" = "Not now"; "Upload file" = "Upload file"; "Upload files" = "Upload files"; -"Authorization failed" = "Authorization failed"; -"The server declined access with the credentials stored for this connection." = "The server declined access with the credentials stored for this connection."; + "No matches" = "No matches"; -"There is no results for this search" = "There is no results for this search"; +"The search term you entered did not match any item in the selected scope." = "The search term you entered did not match any item in the selected scope."; +"There are no results for this search term" = "There are no results for this search term"; +"No items found matching the search criteria." = "No items found matching the search criteria."; + "Status" = "Status"; +"Authorization failed" = "Authorisation failed"; +"The account has been disabled." = "The account has been disabled."; +"The server declined access with the credentials stored for this connection." = "The server declined access with the credentials stored for this connection."; +"Access denied" = "Access denied"; +"The connection's access token has expired or become invalid. Sign in again to re-gain access." = "The connection's access token has expired or become invalid. Sign in again to re-gain access."; +"No authentication data has been found for this connection." = "No authentication data has been found for this connection."; +"Authentication with %@ is no longer allowed. Re-authentication needed." = "Authentication with %@ is no longer allowed. Re-authentication needed."; +"Sign in" = "Sign in"; +"Continue offline" = "Continue offline"; +"Media upload in the previous session was incomplete since the application was terminated" = "Media upload in the previous session was incomplete since the application was terminated"; + +"Waiting for response from login session in external browser…" = "Waiting for response from login session in external browser…"; + +"All done" = "All done"; +"No pending messages or ongoing actions." = "No pending messages or ongoing actions."; + +"Show all" = "Show all"; +"+ %ld more" = "+ %ld more"; +"Apply choice to all similar issues" = "Apply choice to all similar issues"; +"Apply to all" = "Apply to all"; + /* Server List*/ "Cancel" = "Cancel"; "OK" = "OK"; "'%@' is currently locked" = "'%@' is currently locked"; "An operation is currently performed that prevents connecting to '%@'. Please try again later." = "An operation is currently performed that prevents connecting to '%@'. Please try again later."; +"Do you really want to disconnect from your '%@' account?" = "Do you really want to disconnect from your '%@' account?"; +"This will remove all locally stored file copies from your device." = "This will remove all locally stored file copies from your device."; "Really delete '%@'?" = "Really delete '%@'?"; "Do you want to log out from '%@'?" = "Do you want to log out from '%@'?"; "This will also delete all locally stored file copies." = "This will also delete all locally stored file copies."; "Delete" = "Delete"; -"Deletion of '%@' failed" = "Deletion of '%@' failed"; -"Log out of '%@' failed" = "Log out of '%@' failed"; +"Remove" = "Remove"; +"Removing of '%@' failed" = "Removing of '%@' failed"; "Accounts" = "Accounts"; "Help" = "Help"; @@ -146,40 +249,58 @@ "Passcode Lock" = "Passcode Lock"; "Face ID" = "Face ID"; "Touch ID" = "Touch ID"; +"Unlock using %@?" = "Unlock using %@?"; "Videos" = "Videos"; "Photos" = "Photos"; -"Background uploads" = "Background uploads"; "Wifi only" = "WiFi only"; "Settings" = "Settings"; "Lock application" = "Lock application"; "Upload videos via WiFi only" = "Upload videos via WiFi only"; "Upload pictures via WiFi only" = "Upload pictures via WiFi only"; "More" = "More"; +"Documentation" = "Documentation"; "Send feedback" = "Send feedback"; "Recommend to a friend" = "Recommend to a friend"; "Privacy Policy" = "Privacy Policy"; +"Terms Of Use" = "Terms Of Use"; "Acknowledgements" = "Acknowledgements"; +"license" = "licence"; +"Portions of this app may utilize the following copyrighted material, the use of which is hereby acknowledged." = "Portions of this app may utilise the following copyrighted material, the use of which is hereby acknowledged."; "Video upload path" = "Video upload path"; "Photo upload path" = "Photo upload path"; -"Always" = "Always"; +"Immediately" = "Immediately"; "After 1 minute" = "After 1 minute"; "After 5 minutes" = "After 5 minutes"; "After 30 minutes" = "After 30 minutes"; +"After %ld seconds" = "After %ld seconds"; +"If you choose \"Immediately\" the App will be locked, when it is no longer in foreground." = "If you choose \"Immediately\" the App will be locked, when it is no longer in foreground."; "Please configure an email account" = "Please configure an email account"; "You need to configure an email account first to be able to send emails." = "You need to configure an email account first to be able to send emails."; "Do you want to open the following URL?" = "Do you want to open the following URL?"; - -"%@ %@ version %@ build %@ (%@)" = "%@ %@ version %@ build %@ (%@)"; +"Passcode option" = "Passcode option"; +"Please choose how many digits you want to use for the passcode lock?" = "Please choose how many digits you want to use for the passcode lock?"; +"%ld digit code" = "%ld digit code"; +"Enter a new code with %ld digits" = "Enter a new code with %ld digits"; +"To use the app, you need to create a passcode." = "To use the app, you need to create a passcode."; +"Passcode setup" = "Passcode setup"; + +"%@%@ %@ version %@ build %@\n(app: %@, sdk: %@)" = "%@%@ %@ version %@ build %@\n(app: %@, sdk: %@)"; "beta" = "beta"; "release" = "release"; +"App Version" = "App Version"; +"Version information were copied to the clipboard" = "Version information were copied to the clipboard"; /* User Interface Settings */ "Theme" = "Theme"; "User Interface" = "User Interface"; "Dark" = "Dark"; +"Dark Blue" = "Dark Blue"; +"Dark Web" = "Dark Web"; "Light" = "Light"; "Classic" = "Classic"; +"System" = "System"; +"System Appeareance" = "System Appeareance"; /* Log settings */ "Log Files" = "Log Files"; @@ -205,7 +326,9 @@ "Share log file" = "Share log file"; "Reset log file" = "Reset log file"; -"Up to 10 recent logs are kept on the device and each log can accumulate messages for the last 24 hours of the app use" = "Up to 10 recent logs are kept on the device and each log can accumulate messages for the last 24 hours of the app use"; +"When activated, logs may impact performance and include sensitive information. However the logs are not subject to automatic submission to %@ servers. Sharing logs with others is sole user responsibility." = "When activated, logs may impact performance and include sensitive information. However, the logs are not subject to automatic submission to %@ servers. Sharing logs with others is sole user responsibility."; + +"The last 10 archived logs are kept on the device - with each log covering up to 24 hours of usage. When sharing please bear in mind that logs may contain sensitive information such as server URLs and user-specific information." = "The last 10 archived logs are kept on the device - with each log covering up to 24 hours of usage. When sharing please bear in mind that logs may contain sensitive information such as server URLs and user-specific information."; "Mask private data" = "Mask private data"; "Enabling this option will attempt to mask private data, so it does not become part of any log. Since logging is a development and debugging feature, though, we can't guarantee that the log file will be free of any private data even with this option enabled. Therefore, please look through any log file and verify its free of any data you're not comfortable sharing before sharing it with anybody." = "Enabling this option will attempt to mask private data, so it does not become part of any log. Since logging is a development and debugging feature, though, we can't guarantee that the log file will be free of any private data even with this option enabled. Therefore, please look through any log file and verify its free of any data you're not comfortable sharing before sharing it with anybody."; @@ -217,6 +340,34 @@ "Really reset log file?" = "Really reset log file?"; "This action can't be undone." = "This action can't be undone."; +/* Data usage settings */ +"Data usage" = "Data usage"; +"Delete unused local copies" = "Delete unused local copies"; +"Time measured since uploading, editing, downloading or viewing the respective file through this device. Does not apply to files downloaded via the Available Offline feature. Local copies may be deleted before the given period of time has passed, f.ex. because there's a newer version of a file on the server - or through the manual deletion of offline copies. Also, local copies may not be deleted after the given period of time has passed, f.ex. if an action is performed on it, the file is still in use - or the account holding the file hasn't been used in the app." = "Time measured since uploading, editing, downloading or viewing the respective file through this device. Does not apply to files downloaded via the Available Offline feature. Local copies may be deleted before the given period of time has passed, for example because there's a newer version of a file on the server - or through the manual deletion of offline copies. Also, local copies may not be deleted after the given period of time has passed, for example if an action is performed on it, the file is still in use - or the account holding the file hasn't been used in the app."; +"never" = "never"; +"after %@" = "after %@"; +"Decrease Slider Value" = "Decrease Slider Value"; +"Increase Slider Value" = "Increase Slider Value"; + +"Cellular Data Usage" = "Cellular Data Usage"; +"Some cellular data may still be used. To completely avoid the usage of cellular data, please turn off access to cellular for the entire app in the Settings app." = "Some cellular data may still be used. To completely avoid the usage of cellular data, please turn off access to cellular for the entire app in the Settings app."; +"Cellular Data Usage have been disabled via MDM configuration. Please contact your administrator for more information." = "Cellular Data Usage have been disabled via MDM configuration. Please contact your administrator for more information."; +"General" = "General"; +"By feature" = "By feature"; + +"off" = "off"; +"enabled" = "enabled"; + +/* Display settings */ +"Advanced settings" = "Advanced settings"; +"Show hidden files and folders" = "Show hidden files and folders"; +"Show folders on top" = "Show folders on top"; +"Disable gestures" = "Disable gestures"; +"Prevent dragging of files and folders and multiselection using system defined gestures" = "Prevent dragging of files and folders and multiselection using system defined gestures"; + +/* Advanced settings */ +"Enable diagnostics" = "Enable diagnostics"; + /* Passcode Messages */ "Enter code" = "Enter code"; @@ -227,6 +378,7 @@ "Please try again in %@" = "Please try again in %@"; "Unlock %@" = "Unlock %@"; "Biometric authentication failed" = "Biometric authentication failed"; +"You are required to set the passcode" = "You are required to set the passcode"; /* Certificate management */ @@ -241,22 +393,67 @@ "Forbidden Characters" = "Forbidden Characters"; "File name cannot contain / or \\" = "File name cannot contain / or \\"; +"Item with same name already exists" = "Item with same name already exists"; +"An item with the same name already exists in this location." = "An item with the same name already exists in this location."; "New Folder" = "New Folder"; "Folder name" = "Folder name"; -"Rename" = "Rename"; -"Create folder" = "Create folder"; +"Rename" ="Rename"; +"Create folder" ="Create folder"; "Duplicate" = "Duplicate"; "Move" = "Move"; +"Move {{itemCount}} items" = "Move {{itemCount}} items"; +"Move \"{{itemName}}\"" = "Move \"{{itemName}}\""; +"Move here" = "Move here"; +"Select target." = "Select target."; "Open in" = "Open in"; "Copy" = "Copy"; +"Copy {{itemCount}} items" = "Copy {{itemCount}} items"; +"Copy \"{{itemName}}\"" = "Copy \"{{itemName}}\""; "Copy here" = "Copy here"; + "Cannot connect to " = "Cannot connect to "; " couldn't download file(s)" = " couldn't download file(s)"; "Actions" = "Actions"; "copy" = "copy"; +"Close Window" = "Close Window"; +"Open in a new Window" = "Open in a new Window"; +"Open in Window" = "Open in Window"; +"Take photo or video" = "Take photo or video"; +"%ld Item was copied to the clipboard" = "%ld Item was copied to the clipboard"; +"%ld Items were copied to the clipboard" = "%ld Items were copied to the clipboard"; +"Please note: Folders can only be pasted into the %@ app and the same account." = "Please note: Folders can only be pasted into the %@ app and the same account."; -/* Directory Picker Messages */ -"Move here" = "Move here"; +"Processing on server" = "Processing on server"; + +"Preparing…" = "Preparing…"; + +"No actions available" = "No actions available"; +"No actions are available for this folder, possibly because of missing permissions." = "No actions are available for this folder, possibly because of missing permissions."; + +"Close actions menu" = "Close actions menu"; +"Favorited" = "Favourited"; +"Favorite item" = "Favourite item"; +"Unfavorite item" = "Unfavourite item"; +"Presentation Mode" = "Presentation Mode"; +"Enabling presentation mode will prevent the display from sleep mode until the view is closed." = "Enabling presentation mode will prevent the display from sleep mode until the view is closed."; +"Enable" = "Enable"; +"Exit Full Screen" = "Exit Full Screen"; + +"Select one or more items." = "Select one or more items."; + +"Add item" = "Add item"; + +/* App Provider */ +"New document" = "New document"; +"Pick a name" = "Pick a name"; +"Pick a document type to create:" = "Pick a document type to create:"; +"Error creating {{itemName}}" = "Error creating {{itemName}}"; + +"Opening…" = "Opening…"; + +"Open in {{appName}} (web)" = "Open in {{appName}} (web)"; +"Error opening {{itemName}} in {{appName}}" = "Error opening {{itemName}} in {{appName}}"; +"Opening documents is not supported by the app provider on this instance." = "Opening documents is not supported by the app provider on this instance."; /* Preview */ "Open file" = "Open file"; @@ -264,6 +461,15 @@ "Error" = "Error"; "Could not get the picture" = "Could not get the picture"; "Downloading" = "Downloading"; +"File couldn't be opened" = "File couldn't be opened"; +"Connecting..." = "Connecting..."; + +"File" = "File"; +"%@ was updated" = "%@ was updated"; +"Would you like to view the updated version?" = "Would you like to view the updated version?"; +"Show new version" = "Show new version"; +"Refresh without asking" = "Refresh without asking"; +"Ignore updates" = "Ignore updates"; /* PDF Viewer */ "Resume" = "Resume"; @@ -273,6 +479,11 @@ "%@ of %@" = "%@ of %@"; "Invalid Page" = "Invalid Page"; "The entered page number doesn't exist" = "The entered page number doesn't exist"; +"Search PDF" = "Search PDF"; +"Outline" = "Outline"; +"Find Next" = "Find Next"; +"Find Previous" = "Find Previous"; +"Close Search" = "Close Search"; /* Photo Upload */ "Upload" = "Upload"; @@ -281,20 +492,198 @@ "All Photos" = "All Photos"; "Albums" = "Albums"; "Importing from photo library" = "Importing from photo library"; +"Media import" = "Media import"; +"%@ of %@" = "%@ of %@"; +"Selected folder lacks file creation permission" = "Selected folder lacks file creation permission"; +"%d Item" = "%d Item"; +"%d Items" = "%d Items"; +"Select Items" = "Select Items"; + +/* Scan */ +"Scan" = "Scan"; +"Scans" = "Scans"; +"Scan document" = "Scan document"; +"Saving" = "Saving"; +"File format" = "File format"; +"Name" = "Name"; +"Save as" = "Save as"; +"Options" = "Options"; +"Scan additional" = "Scan additional"; +"Create one file per page" = "Create one file per page"; + +/* Sharing */ +"Sharing requires an active connection." = "Sharing requires an active connection."; +"No items shared with you" = "No items shared with you"; +"No items shared by you" = "No items shared by you"; +"No items shared by link" = "No items shared by link"; + +"Searching Shares…" = "Searching Shares…"; +"Recipient" = "Recipient"; +"Recipients" = "Recipients"; +"Public Link" = "Public Link"; +"Public Links" = "Public Links"; +"Shared by {{owner}}" = "Shared by {{owner}}"; +"Shared with {{recipient}}" = "Shared with {{recipient}}"; +"Shared with {{recipients}}" = "Shared with {{recipients}}"; +"Expires {{expirationDate}}" = "Expires {{expirationDate}}"; +"Share {{itemName}}" = "Share {{itemName}}"; +"Create link" = "Create link"; +"Invite" = "Invite"; +"Invite Recipient" = "Invite Recipient"; +"Recipients" = "Recipients"; +"Add email or name" = "Add email or name"; +"Users" = "Users"; +"Groups" = "Groups"; +"Start typing to search users, groups and remote users." = "Start typing to search users, groups and remote users."; +"(Group)" = "(Group)"; +"Adding User to Share failed" = "Adding User to Share failed"; +"Permissions" = "Permissions"; +"Invited: %@" = "Invited: %@"; +"Created: %@" = "Created: %@"; +"Allows the users you share with to re-share" = "Allows the users you share with to re-share"; +"Allows the users you share with to edit your shared files, and to collaborate" = "Allows the users you share with to edit your shared files, and to collaborate"; +"Allows the users you share with to create new files and add them to the share" = "Allows the users you share with to create new files and add them to the share"; +"Allows uploading a new version of a shared file and replacing it" = "Allows uploading a new version of a shared file and replacing it"; +"Allows the users you share with to delete shared files" = "Allows the users you share with to delete shared files"; +"Setting permission failed" = "Setting permission failed"; +"Shared with" = "Shared with"; +"Remove Recipient failed" = "Remove Recipient failed"; +"Remove Recipient" = "Remove Recipient"; +"Create" = "Create"; +"Change" = "Change"; +"Recipients can view or download contents." = "Recipients can view or download contents."; +"Recipients can view, download, edit, delete and upload contents." = "Recipients can view, download, edit, delete and upload contents."; +"Receive files from multiple recipients without revealing the contents of the folder." = "Receive files from multiple recipients without revealing the contents of the folder."; +"Download / View" = "Download / View"; +"Download / View / Upload" = "Download / View / Upload"; +"Upload only (File Drop)" = "Upload only (File Drop)"; +"Creating public link failed" = "Creating public link failed"; +"Create Public Link" = "Create Public Link"; +"Links" = "Links"; +"Link" = "Link"; +"Setting expiration date failed" = "Setting expiration date failed"; +"Expiration date" = "Expiration date"; +"Copy Public Link" = "Copy Public Link"; +"Delete Public Link" = "Delete Public Link"; +"Deleting Public Link failed" = "Deleting Public Link failed"; +"Deleting password failed" = "Deleting password failed"; +"Setting password failed" = "Setting password failed"; +"Type to update password" = "Type to update password"; +"Cannot change permission" = "Cannot change permission"; +"Before you can set the permission\n%@,\n you must enter a password." = "Before you can set the permission\n%@,\n you must enter a password."; +"Password Protected" = "Password Protected"; +"Pending Federated Invites" = "Pending Federated Invites"; +"Pending Invites" = "Pending Invites"; +"Shared with you" = "Shared with you"; +"Shared with others" = "Shared with others"; +"Shares" = "Shares"; +"Copy Private Link" = "Copy Private Link"; +"Only people who have access to the file/folder can use it. Use it as a permanent link for yourself or to point others to files within shares." = "Only people who have access to the file/folder can use it. Use it as a permanent link for yourself or to point others to files within shares."; +"Accept Share failed" = "Accept Share failed"; +"Decline Share failed" = "Decline Share failed"; +"Accept" = "Accept"; +"Decline" = "Decline"; +"Declined" = "Declined"; +"Decline Share" = "Decline Share"; +"Unshare" = "Unshare"; +"Unshare failed" = "Unshare failed"; +"Are you sure you want to unshare these items?" = "Are you sure you want to unshare these items?"; +"Are you sure you want to unshare this item?" = "Are you sure you want to unshare this item?"; +"Share" = "Share"; +"Read" = "Read"; +"Can Share" = "Can Share"; +"Can Edit" = "Can Edit"; +"Can Edit and Change" = "Can Edit and Change"; +"Can Create" = "Can Create"; +"Can Change" = "Can Change"; +"Can Delete" = "Can Delete"; +"Accept Invite %@" = "Accept Invite %@"; +"Decline Invite %@" = "Decline Invite %@"; +"Decline cannot be undone." = "Decline cannot be undone."; +"Sharing" = "Sharing"; +"You" = "You"; +"Share this file" = "Share this file"; +"Share this folder" = "Share this folder"; +"shared" = "shared"; +"Owner" = "Owner"; +"Private Link" = "Private Link"; +"Created Public Link" = "Created Public Link"; +"URL was copied to the clipboard" = "URL was copied to the clipboard"; +"%@ is not available, when this account is offline. Please open the app and log into your account before you can do this action." = "%@ is not available, when this account is offline. Please open the app and log into your account before you can do this action."; +"%@ is not available for this item." = "%@ is not available for this item."; +"Share with user/group" = "Share with user/group"; +"Share link" = "Share link"; + +"Shared with me" = "Shared with me"; +"Shared by me" = "Shared by me"; +"Shared by link" = "Shared by link"; + +"Share with" = "Share with"; +"Add" = "Add"; +"Save changes" = "Save changes"; + +/* Quick Access view */ +"Quick Access" = "Quick Access"; +"Collection" = "Collection"; +"Recents" = "Recents"; +"Favorites"= "Favourites"; +"Images" = "Images"; +"PDF Documents" = "PDF Documents"; +"Text" = "Text"; +"Documents" = "Documents"; +"Audio" = "Audio"; + +/* Favorites view */ +"No favorites found" = "No favourites found"; +"If you make an item a favorite, it will turn up here." = "If you make an item a favourite, it will turn up here."; + +/* Media files settings */ +"Media Files" = "Media Files"; +"Download instead of streaming" = "Download instead of streaming"; /* Photo upload settings */ "Media Upload" = "Media Upload"; +"Media Export" = "Media Export"; +"Auto Upload" = "Auto Upload"; "Convert HEIC to JPEG" = "Convert HEIC to JPEG"; "Convert videos to MP4" = "Convert videos to MP4"; +"Preserve original media file names" = "Preserve original media file names"; +"Auto Upload Photos" = "Auto Upload Photos"; +"Auto Upload Videos" = "Auto Upload Videos"; +"Account" = "Account"; +"Accounts" = "Accounts"; +"Select account" = "Select account"; +"Photo upload path" = "Photo upload path"; +"Video upload path" = "Video upload path"; +"Select Upload Path" = "Select Upload Path"; +"Auto upload of media was disabled since configured account / folder was not found" = "Auto upload of media was disabled since configured account / folder was not found"; +"Importing %@ media files for upload" = "Importing %@ media files for upload"; +"Photo upload" = "Photo upload"; +"Video upload" = "Video upload"; + +/* Background media uploads */ +"Background uploads (Lab Version)" = "Background uploads (Lab Version)"; +"Background uploads" = "Background uploads"; +"Use background refresh" = "Use background refresh"; +"Allow this app to refresh the content when on Wi-Fi or mobile network in background." = "Allow this app to refresh the content when on Wi-Fi or mobile network in background."; +"Enable background uploads" = "Enable background uploads"; +"Use background location updates" = "Use background location updates"; +"Background upload notifications" = "Background upload notifications"; +"If you would like background media uploads to be more reliable, you should enable background location updates." = "If you would like background media uploads to be more reliable, you should enable background location updates."; +"Background media uploads rely on location updates and will stop working if location acquisition permissions are revoked." = "Background media uploads rely on location updates and will stop working if location acquisition permissions are revoked."; +"Otherwise background media uploads using background refresh technology would depend on how frequently you use the app." = "Otherwise background media uploads using background refresh technology would depend on how frequently you use the app."; +"Location permission denied" = "Location permission denied"; +"Please re-enable location acquisition in system settings" = "Please re-enable location acquisition in system settings"; +"Scheduled upload of %ld media assets" = "Scheduled upload of %ld media assets"; /* Progress summarizer */ -"Creating %ld of %ld folders…" = "Creating %ld of %ld folders…"; -"Moving %ld of %ld items…" = "Moving %ld of %ld items…"; -"Copying %ld of %ld items…" = "Copying %ld of %ld items…"; -"Deleting %ld of %ld items…" = "Deleting %ld of %ld items…"; -"Uploading %ld of %ld files…" = "Uploading %ld of %ld files…"; -"Downloading %ld of %ld files…" = "Downloading %ld of %ld files…"; -"Updating %ld of %ld items…" = "Updating %ld of %ld items…"; +"Creating %ld folders…" = "Creating %ld folders…"; +"Moving %ld items…" = "Moving %ld items…"; +"Copying %ld items…" = "Copying %ld items…"; +"Deleting %ld items…" = "Deleting %ld items…"; +"Uploading %ld files…" = "Uploading %ld files…"; +"Downloading %ld files…" = "Downloading %ld files…"; +"Updating %ld items…" = "Updating %ld items…"; /* Offline storage management */ "Free on %@" = "Free on %@"; @@ -305,11 +694,21 @@ "Delete all Offline Files" = "Delete all Offline Files"; "Manage" = "Manage"; "Storage" = "Storage"; -"Proceed" = "Proceed"; "Compacting" = "Compacting"; "Really include available offline files?" = "Really include available offline files?"; "Files and folders marked as Available Offline will become unavailable. They will be re-downloaded next time you log into your account (connectivity required)." = "Files and folders marked as Available Offline will become unavailable. They will be re-downloaded next time you log into your account (connectivity required)."; +"Removes downloaded files and local copies of items marked as Available Offline. The latter will be re-downloaded next time you log into your account (connectivity required)." = "Removes downloaded files and local copies of items marked as Available Offline. The latter will be re-downloaded next time you log into your account (connectivity required)."; + +/* Diagnostic */ +"Diagnostic Overview" = "Diagnostic Overview"; +"Diagnostics" = "Diagnostics"; +"Share Diagnostics" = "Share Diagnostics"; +"Action executed" = "Action executed"; + +"Update Status" = "Update Status"; +"Completed update scans" = "Completed update scans"; +"Total update scans" = "Total update scans"; /* Available offline */ "Root folder" = "Root folder"; @@ -320,12 +719,295 @@ "Make unavailable offline" = "Make unavailable offline"; "Available Offline" = "Available Offline"; -"No items have been selected for offline availability." = "No items have been selected for offline availability."; -"Overview" = "Overview"; -"All Files" = "All Files"; +"No files available offline" = "No files available offline"; +"Files selected and downloaded for offline availability will show up here." = "Files selected and downloaded for offline availability will show up here."; + "Locations" = "Locations"; +"Downloaded Files" = "Downloaded Files"; + +/* Single Account */ +"You are connected as\n%@" = "You are connected as\n%@"; -/* Import File */ +/* Release Notes */ +"Proceed" = "Proceed"; +"New in %@" = "New in %@"; +"Thank you for using %@.\nIf you like our App, please leave an AppStore review.\n❤️" = "Thank you for using %@.\nIf you like our App, please leave an AppStore review.\n❤️"; +"Thank you for using %@.\n" = "Thank you for using %@.\n"; + +/* Key Commands */ +"Select Next" = "Select Next"; +"Select Previous" = "Select Previous"; +"Open Selected" = "Open Selected"; +"Change Sort Order" = "Change Sort Order"; +"Search" = "Search"; +"Back" = "Back"; +"Save" = "Save"; +"Sort by %@" = "Sort by %@"; +"Tab %@" = "Tab %@"; +"Select Last Item on Page" = "Select Last Item on Page"; +"Scroll to Top" = "Scroll to Top"; +"Scroll to Bottom" = "Scroll to Bottom"; +"Copy to Clipboard" = "Copy to Clipboard"; +"Import from Clipboard" = "Import from Clipboard"; +"Choose destination directory…" = "Choose destination directory…"; +"Next" = "Next"; +"Previous" = "Previous"; +"Favorite" = "Favourite"; +"Cut" = "Cut"; +"Paste" = "Paste"; +"Play/Pause" = "Play/Pause"; +"Skip Back" = "Skip Back"; +"Skip Ahead" = "Skip Ahead"; +"Go to Beginning" = "Go to Beginning"; +"Mute/Unmute" = "Mute/Unmute"; +"Full Screen" = "Full Screen"; +"Switch Theme Style" = "Switch Theme Style"; + +/* Markup */ +"How should this file be saved?" = "How should this file be saved?"; +"Overwrite original" = "Overwrite original"; +"Save as copy" = "Save as copy"; +"Discard changes" = "Discard changes"; +"Markup" = "Markup"; +"Crop or Rotate" = "Crop or Rotate"; +"Saving edited file failed" = "Saving edited file failed"; +"File no longer exists" = "File no longer exists"; + +/* Licensing */ +"Unlocked" = "Unlocked"; +"Unlock" = "Unlock"; + +"Subscribe Now" = "Subscribe Now"; +"Free" = "Free"; + +"%@ / %@" = "%@ / %@"; +"after free %@ trial" = "after free %@ trial"; + +/* Example usage: "Try [14 days] for free." */ +"Try %@ for free." = "Try %@ for free."; + +/* Example usage: "Then [1,99 €] / [year]." */ +"Then %@ / %@." = "Then %@ / %@."; + +/* Example usage: "[1,99 €] / [year] - starting immediately." */ +"%@ / %@ – starting immediately" = "%@ / %@ – starting immediately"; + +/* Licensing: Pro Features */ +"Pro Features" = "Pro Features"; + +/* Licensing: App Store */ +"Restore purchases" = "Restore purchases"; +"Restoring purchases…" = "Restoring purchases…"; +"Error restoring purchases" = "Error restoring purchases"; +"Error loading product info from App Store"= "Error loading product info from App Store"; +"Purchase failed" = "Purchase failed"; +"More information" = "More information"; + +/* Licensing: Enterprise */ +"Enterprise" = "Enterprise"; + +/* Licensing: Settings */ +"In-App Purchases" = "In-App Purchases"; +"Purchases & Subscriptions" = "Purchases & Subscriptions"; +"Error fetching transactions" = "Error fetching transactions"; +"Document Scanner" = "Document Scanner"; +"Scan documents and photos with your camera." = "Scan documents and photos with your camera."; +"Shortcuts Actions" = "Shortcuts Actions"; +"Use ownCloud actions in Shortcuts." = "Use ownCloud actions in Shortcuts."; +"Markup Documents" = "Markup Documents"; +"Markup photos and PDF files." = "Markup photos and PDF files."; +"Unlock all Pro Features." = "Unlock all Pro Features."; + +"Purchased App Version" = "Purchased App Version"; +"Receipt Date" = "Receipt Date"; +"Manage subscription" = "Manage subscription"; +"Purchases are not allowed on this device." = "Purchases are not allowed on this device."; +"Type" = "Type"; +"Quantity" = "Quantity"; +"Product" = "Product"; +"Date" = "Date"; +"Cancelled" = "Cancelled"; +"Ends" = "Ends"; +"none" = "none"; +"trial" = "trial"; +"purchase" = "purchase"; +"subscription" = "subscription"; +"Photo Pro Features" = "Photo Pro Features"; +"Image metadata, extended upload options" = "Image metadata, extended upload options"; +"Purchase" = "Purchase"; +"Subscribe" = "Subscribe"; + +"No accounts found" = "No accounts found"; +"In order to accurately determine your current licensing status, please add one or more accounts first." = "In order to accurately determine your current licensing status, please add one or more accounts first."; + +/* Share Sheet */ "Save File" = "Save File"; -"Choose an account and folder to import the file into.\n\nOnly one file can be imported at once." = "Choose an account and folder to import the file into.\n\nOnly one file can be imported at once."; +"Choose an account and folder to import into." = "Choose an account and folder to import into."; +"No account configured.\nSetup an new account in the app to save to." = "No account configured.\nSetup an new account in the app to save to."; +"Importing item %ld of %ld" = "Importing item %ld of %ld"; +"Error importing %@" = "Error importing %@"; +"Error loading item" = "Error loading item"; +"Preparing…" = "Preparing…"; +"Import \"{{itemName}}\"" = "Import \"{{itemName}}\""; +"Import {{itemCount}} files" = "Import {{itemCount}} files"; +"Save here" = "Save here"; + +/* FileProvider */ +/* - Disabled */ +"File Provider access has been disabled by the administrator.\n\nPlease use the app to access your files." = "File Provider access has been disabled by the administrator.\n\nPlease use the app to access your files."; +"File Provider access has been disabled by the administrator. Please use the app to create new folders." = "File Provider access has been disabled by the administrator. Please use the app to create new folders."; +"File Provider access has been disabled by the administrator. Please use the share extension to import files." = "File Provider access has been disabled by the administrator. Please use the share extension to import files."; + +/* Disallowed Import Methods */ +/* - Share Extension */ +"Share Extension disabled" = "Share Extension disabled"; +"Importing files through the Share Extension is not allowed on this device." = "Importing files through the Share Extension is not allowed on this device."; + +/* - Open with */ +"Opening not allowed" = "Opening not allowed"; +"Importing files through opening is not allowed on this device." = "Importing files through opening is not allowed on this device."; + +/* - Files.app */ +"Import not allowed" = "Import not allowed"; +"Importing files through the File Provider is not allowed on this device." = "Importing files through the File Provider is not allowed on this device."; + +/* Misc. accessibility */ +"Enter multiple selection" = "Enter multiple selection"; + +/* Universal Links */ +"Resolving link…" = "Resolving link…"; +"Link resolution failed" = "Link resolution failed"; +"Couldn't resolve a private link since you are offline and corresponding item is not cached locally." = "Couldn't resolve a private link since you are offline and corresponding item is not cached locally."; +"Couldn't resolve a private link since the item is not known to the server." = "Couldn't resolve a private link since the item is not known to the server."; +"Link points to an account bookmark which is not configured in the app." = "Link points to an account bookmark which is not configured in the app."; + +/* Photo meta-data */ +"Image metadata" = "Image metadata"; +"Image details" = "Image details"; +"Profile" = "Profile"; +"Size" = "Size"; +"Density" = "Density"; +"Color model" = "Colour model"; +"%@ (%d bits/channel)" = "%@ (%d bits/channel)"; +"Histogram" = "Histogram"; + +"Camera details" = "Camera details"; +"Capture settings" = "Capture settings"; +"Time" = "Time"; +"EXIF aux info" = "EXIF aux info"; +"Authoring" = "Authoring"; +"TIFF" = "TIFF"; + +"Lens make" = "Lens make"; +"Lens model" = "Lens model"; +"Lens info" = "Lens info"; +"Focal length" = "Focal length"; +"Focal length @ 35 mm" = "Focal length @ 35 mm"; + +"Shutter speed" = "Shutter speed"; +"Aperture" = "Aperture"; +"ISO" = "ISO"; + +/* EXIF: exposure program */ +"Program" = "Program"; +"Not defined" = "Not defined"; +"Manual" = "Manual"; +"Normal" = "Normal"; +"Aperture priority" = "Aperture priority"; +"Shutter priority" = "Shutter priority"; +"Creative" = "Creative"; +"Action" = "Action"; +"Portrait" = "Portrait"; +"Landscape" = "Landscape"; + +/* EXIF: exposure metering mode */ +"Metering" = "Metering"; +"unknown" = "unknown"; +"Average" = "Average"; +"CenterWeightedAverage" = "CentreWeightedAverage"; +"Spot" = "Spot"; +"MultiSpot" = "MultiSpot"; +"Pattern" = "Pattern"; +"Partial" = "Partial"; + +/* EXIF: flash mode */ +"Flash" = "Flash"; +"Fired" = "Fired"; +"Didn't fire" = "Didn't fire"; +"No strobe return detection" = "No strobe return detection"; +"Strobe return light not detected" = "Strobe return light not detected"; +"Strobe return light detected" = "Strobe return light detected"; +"Compulsory flash firing" = "Compulsory flash firing"; +"Compulsory flash supression" = "Compulsory flash supression"; +"Auto mode" = "Auto mode"; +"Red eye detection supported" = "Red eye detection supported"; +"not present" = "not present"; + +/* EXIF: white balance */ +"White balance" = "White balance"; +"Auto" = "Auto"; +"Manual" = "Manual"; + +"Exposure bias" = "Exposure bias"; +"Uncalibrated" = "Uncalibrated"; +"Original date" = "Original date"; +"Digitized date" = "Digitised date"; + +"Color space" = "Colour space"; + +/* EXIF: aux info */ +"Lens ID" = "Lens ID"; +"Lens serial" = "Lens serial"; +"Serial number" = "Serial number"; +"Flash compensation" = "Flash compensation"; +"Owner" = "Owner"; +"Firmware" = "Firmware"; +"Keywords" = "Keywords"; +"Copyright" = "Copyright"; + +/* IPTC meta data */ +"Contact info" = "Contact info"; +"Usage terms" = "Usage terms"; +"Scene" = "Scene"; +"Description" = "Description"; + +/* IPTC photometric interpretation */ +"Photometric interpretation" = "Photometric interpretation"; +"none" = "none"; +"RGB" = "RGB"; +"YCbCr" = "YCbCr"; + +/* EXIF: gps position data */ +"GPS Location" = "GPS Location"; +"Coordinates" = "Coordinates"; +"Altitude" = "Altitude"; +"Place" = "Place"; +"E" = "E"; +"W" = "W"; +"N" = "N"; +"S" = "S"; + +/* Pro photo upload settings */ +"Extended upload settings" = "Extended upload settings"; +"Prefer unedited photos" = "Prefer unedited photos"; +"Prefer RAW photos" = "Prefer RAW photos"; +"Prefer original videos" = "Prefer original videos"; + +/* Limited photo library access */ +"Limited Photo Access" = "Limited Photo Access"; +"Access for the media selected for upload is limited" = "Access for the media selected for upload is limited"; +"No Access to the media selected for upload" = "No Access to the media selected for upload"; + +/* macOS support */ +"Passcode protection is not supported on this device.\nPlease disable passcode lock in the app settings." = "Passcode protection is not supported on this device.\nPlease disable passcode lock in the app settings."; + +/* MDM settings push */ +"New settings received from MDM" = "New settings received from MDM"; +"Tap to quit the app." = "Tap to quit the app."; +"Tap to launch the app." = "Tap to launch the app."; + +/* Beta warning */ +"Beta Warning" = "Beta Warning"; +"\nThis is a BETA release that may - and likely will - still contain bugs.\n\nYOU SHOULD NOT USE THIS BETA VERSION WITH PRODUCTION SYSTEMS, PRODUCTION DATA OR DATA OF VALUE. YOU'RE USING THIS BETA AT YOUR OWN RISK.\n\nPlease let us know about any issues that come up via the \"Send Feedback\" option in the settings." = "\nThis is a BETA release that may - and likely will - still contain bugs.\n\nYOU SHOULD NOT USE THIS BETA VERSION WITH PRODUCTION SYSTEMS, PRODUCTION DATA OR DATA OF VALUE. YOU'RE USING THIS BETA AT YOUR OWN RISK.\n\nPlease let us know about any issues that come up via the \"Send Feedback\" option in the settings."; +"Agree" = "Agree"; diff --git a/ownCloud/Resources/en.lproj/Localizable.strings b/ownCloud/Resources/en.lproj/Localizable.strings index 4d1025bb2..ee8167459 100644 --- a/ownCloud/Resources/en.lproj/Localizable.strings +++ b/ownCloud/Resources/en.lproj/Localizable.strings @@ -113,6 +113,8 @@ "Search {{space.name}}" = "Search {{space.name}}"; "Search account" = "Search account"; "Enter a search term" = "Enter a search term"; +"Search for users or groups" = "Search for users or groups"; +"Enter the user or group you want to invite." = "Enter the user or group you want to invite."; "Pending" = "Pending"; "Show parent paths" = "Show parent paths"; @@ -128,6 +130,8 @@ "Show more results" = "Show more results"; +"An error occurred" = "An error occurred"; + /* Item Layouts */ "List" = "List"; "Grid" = "Grid"; @@ -277,6 +281,8 @@ "Please choose how many digits you want to use for the passcode lock?" = "Please choose how many digits you want to use for the passcode lock?"; "%ld digit code" = "%ld digit code"; "Enter a new code with %ld digits" = "Enter a new code with %ld digits"; +"To use the app, you need to create a passcode." = "To use the app, you need to create a passcode."; +"Passcode setup" = "Passcode setup"; "%@%@ %@ version %@ build %@\n(app: %@, sdk: %@)" = "%@%@ %@ version %@ build %@\n(app: %@, sdk: %@)"; "beta" = "beta"; @@ -447,6 +453,7 @@ "Open in {{appName}} (web)" = "Open in {{appName}} (web)"; "Error opening {{itemName}} in {{appName}}" = "Error opening {{itemName}} in {{appName}}"; +"Opening documents is not supported by the app provider on this instance." = "Opening documents is not supported by the app provider on this instance."; /* Preview */ "Open file" = "Open file"; @@ -520,6 +527,8 @@ "Shared with {{recipients}}" = "Shared with {{recipients}}"; "Expires {{expirationDate}}" = "Expires {{expirationDate}}"; "Share {{itemName}}" = "Share {{itemName}}"; +"Create link" = "Create link"; +"Invite" = "Invite"; "Invite Recipient" = "Invite Recipient"; "Recipients" = "Recipients"; "Add email or name" = "Add email or name"; diff --git a/ownCloud/Resources/es.lproj/InfoPlist.strings b/ownCloud/Resources/es.lproj/InfoPlist.strings index 7644890d6..117bed35b 100644 Binary files a/ownCloud/Resources/es.lproj/InfoPlist.strings and b/ownCloud/Resources/es.lproj/InfoPlist.strings differ diff --git a/ownCloud/Resources/es.lproj/Localizable.strings b/ownCloud/Resources/es.lproj/Localizable.strings index 14c6fab5f..bb225df4c 100644 Binary files a/ownCloud/Resources/es.lproj/Localizable.strings and b/ownCloud/Resources/es.lproj/Localizable.strings differ diff --git a/ownCloud/Resources/et_EE.lproj/Localizable.strings b/ownCloud/Resources/et_EE.lproj/Localizable.strings new file mode 100644 index 000000000..59572d788 --- /dev/null +++ b/ownCloud/Resources/et_EE.lproj/Localizable.strings @@ -0,0 +1,1013 @@ +/* + Localizable.strings + ownCloud + + Created by Pablo Carrascal on 13/03/2018. + Copyright © 2018 ownCloud GmbH. All rights reserved. +*/ + +/* + * Copyright (C) 2018, ownCloud GmbH. + * + * This code is covered by the GNU Public License Version 3. + * + * For distribution utilizing Apple mechanisms please see https://owncloud.org/contribute/iOS-license-exception/ + * You should have received a copy of this license along with this program. If not, see . + * + */ + +/* Add / Edit Bookmark */ +"Edit account" = "Muuda kontot"; +"Add account" = "Lisa konto"; +"Server URL" = "Serveri URL"; +"https://example.com" = "https://example.com"; +"Continue" = "Jätka"; +"Name" = "Nimi"; +"Example Server" = "Näidisserver"; +"Show Certificate Details" = "Näita sertifikaadi üksikasju"; +"Connect" = "Ühenda"; +"Delete Authentication Data" = "Kustuta autentimise andmed"; +"Authentication" = "Autentimine"; +"Username" = "Kasutajanimi"; +"Password" = "Parool"; +"Certificate Details" = "Sertifikaadi üksikasjad"; +"Cancel" = "Loobu"; +"Approve" = "Kiida heaks"; +"Error" = "Viga"; +"Issues" = "Vead"; +"Review Connection" = "Ühenduse ülevaatamine"; +"Authenticated via" = "Autenditud läbi"; +"Authenticated as %@ via %@" = "Autenditud kui %@ %@ kaudu"; +"Edit" = "Muuda"; +"Credentials" = "Kasutajatunnused"; +"Rejected" = "Tagasi lükatud"; +"Passed" = "Läbitud"; +"Accepted" = "Vastu võetud"; +"Validation Error" = "Valideerimise viga"; +"Certificate was rejected by user." = "Kasutaja lükkas sertifikaadi tagasi."; +"Certificate has issues.\nOpen 'Certificate Details' for more informations." = "Sertifikaadiga on probleeme.\nAva 'Sertifikaadi üksikasjad' lisainfo saamiseks."; +"No issues found. Certificate passed validation." = "Probleeme ei leitud. Sertifikaat läbis valideerimise."; +"Certificate may have issues, but was accepted by user.\nOpen 'Certificate Details' for more informations." = "Sertifikaadiga esineb probleeme, kuid kasutaja on selle aktsepteerinud.\nAva 'Sertifikaadi üksikasjad' lisainfo saamiseks."; +"If you 'Continue', you will be prompted to allow the '{{app.name}}' app to open the {{authmethodName}} login page where you can enter your credentials." = "If you 'Continue', you will be prompted to allow the '{{app.name}}' app to open the {{authmethodName}} login page where you can enter your credentials."; + +"Error opening %@" = "Viga %@ avamisel"; + +"Contacting server…" = "Serveriga ühendumine..."; +"Authenticating…" = "Autentimine..."; + +"Fetching user information…" = "Kasutaja info tõmbamine..."; +"Updating connection…" = "Ühenduse uuendamine..."; + +"Preparing account" = "Konto ettevalmistamine"; +"Please wait…" = "Palun oota..."; +"Skip" = "Jäta vahele"; + +"Missing hostname" = "Hostinimi puudub"; +"The entered URL does not include a hostname." = "Sisestatud URL ei sisalda hostinime."; +"Add account" = "Lisa konto"; +"Server name" = "Serveri nimi"; +"Server Password" = "Serveri parool"; +"Server Username" = "Serveri kasutajanimi"; +"Show accounts" = "Kuva kontosid"; +"Setup complete" = "Seadistamine lõpetatud"; +"Edit Login" = "Muuda sisselogimist"; +"Manage Storage" = "Mahu haldamine"; +"Access Files" = "Juurdepääs failidele"; +"Log out" = "Logi välja"; +"Retry" = "Proovi uuesti"; + +/* Bookmark Info */ +"Bookmark Metadata" = "Järjehoidja Metaandmed"; + +/* Client */ +"Browse" = "Sirvi"; +"Files" = "Failid"; +"Disconnect" = "Ühenda lahti"; +"Connecting…" = "Ühendamine..."; +"Connected." = "Ühendatud."; +"Select" = "Vali"; +"Done" = "Valmis"; + +"Folder" = "Kaust"; + +"Stopped" = "Peatatud"; +"Started…" = "Alustatud..."; +"Contents from cache." = "Sisu vahemälust."; +"Waiting for server response…" = "Serveri vastuse ootamine..."; +"This folder no longer exists." = "Seda kausta pole enam olemas."; +"Everything up-to-date." = "Kõik on ajakohane."; +"Please wait…" = "Palun oota..."; +"Sort by %@" = "Sorteerida %@ järgi"; +"Sort by" = "Sorteeri"; + +"name" = "nimi"; +"kind" = "liik"; +"size" = "suurus"; +"date" = "kuupäev"; + +"Tree" = "Tree"; +"Search folder" = "Otsi kaustast"; +"Search tree" = "Search tree"; +"Search from {{folder.name}}" = "Search from {{folder.name}}"; +"Search space" = "Search space"; +"Search {{space.name}}" = "Search {{space.name}}"; +"Search account" = "Otsi kontot"; +"Enter a search term" = "Enter a search term"; +"Search for users or groups" = "Search for users or groups"; +"Enter the user or group you want to invite." = "Enter the user or group you want to invite."; + +"Pending" = "Ootel"; +"Show parent paths" = "Näita baasradu"; +"Reveal in folder" = "Avalda kaustas"; + +"%@ of %@ used" = "%@ %@-st kasutatud"; +"Total: %@" = "Kokku: %@"; +"%@ item | " = "%@ objekt |"; +"%@ items | " = "%@ objekti |"; + +"{{itemCount}} items with {{totalSize}} total ({{fileCount}} files, {{folderCount}} folders)" = "{{itemCount}} items with {{totalSize}} total ({{fileCount}} files, {{folderCount}} folders)"; +"{{remaining}} available" = "{{remaining}} available"; + +"Show more results" = "Kuva rohkem tulemusi"; + +"An error occurred" = "Tekkis viga"; + +/* Item Layouts */ +"List" = "Nimekiri"; +"Grid" = "Grid"; +"Item grid" = "Item grid"; +"Image grid" = "Image grid"; + +/* Saved searches */ +"Saved searches" = "Saved searches"; +"Saved search templates" = "Saved search templates"; +"Save search" = "Save search"; +"Save as search template" = "Save as search template"; + +"Name of saved search" = "Name of saved search"; +"Saved search" = "Saved search"; +"Saved searches" = "Saved searches"; +"Name of template" = "Name of template"; +"Search template" = "Search template"; +"Search templates" = "Search templates"; + +/* Search scope */ +"Search scope" = "Otsingu ulatus"; +"Toggle Search Scope" = "Otsingu ulatuse muutmine"; + +/* Static Login Setup */ +"Server error" = "Serveri viga"; +"The server doesn't support any allowed authentication method." = "Server ei toeta ühtegi lubatud autentimismeetodit."; +"The server doesn't support any known and allowed authentication method found." = "Server ei toeta ühtegi teadaolevat ja lubatud autentimismeetodit."; +"Retry detection" = "Taaskorduse tuvastamine"; +"Enter your username and password" = "Sisestage oma kasutajanimi ja parool"; +"Please log in to authorize the app." = "Rakenduse autoriseerimiseks logige sisse."; +"Welcome to %@" = "Tere tulemast %@"; +"Please enter a server URL" = "Palun sisestage serveri URL"; +"Please pick a profile to begin setup" = "Palun valige profiil, et alustada seadistamist"; + +/* Single Account */ +"You are connected as\n%@" = "Olete ühendatud kui\n%@"; +"Login" = "Logi sisse"; +"Wrong URL" = "Vale URL"; +"Please enter a valid URL" = "Palun sisestage kehtiv URL"; +"Missing Profile URL" = "Profiili URL puudub"; +"The Profile '%@' does not have a URL configured.\nPlease provide a URL via configuration or MDM." = "Profiilil '%@' ei ole URL seadistatud.\nPalun esitage URL seadistuse või MDM-i kaudu."; + +/* Client Messages */ +"No contents" = "No contents"; +"This folder contains no files or folders." = "See kaust ei sisalda faile ega kaustu."; +"This folder is empty." = "This folder is empty."; +"This folder is empty. Fill it with content:" = "This folder is empty. Fill it with content:"; + +"Folder removed" = "Kaust on eemaldatud"; +"This folder no longer exists on the server." = "Seda kausta pole serveris enam olemas."; +"Are you sure you want to delete this item from the server?" = "Olete kindel, et soovite selle objekti serverist kustutada? "; +"Are you sure you want to delete these items from the server?" = "Olete kindel, et soovite neid objekte serverist kustutada? "; +"Multiple items" = "Mitu kirjet"; +"Deleting '%@'" = "'%@' kustutamine"; +"Renaming to %@" = "%@ ümbernimetamine"; +"Upload from your photo library" = "Laadige üles oma fotokogust"; +"Missing permissions" = "Puuduvad õigused"; +"This permission is needed to upload photos and videos from your photo library." = "See luba on vajalik fotode ja videote üleslaadimiseks teie fotokogust."; +"Not now" = "Mitte kohe"; +"Upload file" = "Laadi fail üles"; +"Upload files" = "Laadi faile üles"; + +"No matches" = "Vasteid ei ole"; +"The search term you entered did not match any item in the selected scope." = "The search term you entered did not match any item in the selected scope."; +"There are no results for this search term" = "Selle otsisõna kohta ei ole tulemusi"; +"No items found matching the search criteria." = "No items found matching the search criteria."; + +"Status" = "Staatus"; + +"Authorization failed" = "Autoriseerimine ebaõnnestus"; +"The account has been disabled." = "Sinu konto on välja lülitatud."; +"The server declined access with the credentials stored for this connection." = "Server keeldus juurdepääsust selle ühenduse jaoks salvestatud kasutajaandmetega."; +"Access denied" = "Ligipääs keelatud"; +"The connection's access token has expired or become invalid. Sign in again to re-gain access." = "Ühenduse juurdepääsutunnuse kehtivusaeg on lõppenud või muutunud kehtetuks. Juurdepääsu taastamiseks logige uuesti sisse."; +"No authentication data has been found for this connection." = "Selle ühenduse jaoks ei ole autentimisandmeid leitud."; +"Authentication with %@ is no longer allowed. Re-authentication needed." = "Autentimine %@-ga ei ole enam lubatud. Vajalik on uuesti autentimine."; +"Sign in" = "Logi sisse"; +"Continue offline" = "Jätka ilma võrguühenduseta"; +"Media upload in the previous session was incomplete since the application was terminated" = "Meedia üleslaadimine eelmises sessioonis oli mittetäielik, kuna rakendus lõpetati"; + +"Waiting for response from login session in external browser…" = "Vastuse ootamine välise brauseri sisselogimise sessioonilt... "; + +"All done" = "Tehtud"; +"No pending messages or ongoing actions." = "Ootel sõnumid või käimasolevad toimingud puuduvad."; + +"Show all" = "Näita kõiki"; +"+ %ld more" = "+ %ld veel"; +"Apply choice to all similar issues" = "Rakenda valik kõigi sarnaste probleemide puhul"; +"Apply to all" = "Rakenda kõigile"; + +/* Server List*/ +"Cancel" = "Loobu"; +"OK" = "OK"; + +"'%@' is currently locked" = "'%@' on praegu lukustatud"; +"An operation is currently performed that prevents connecting to '%@'. Please try again later." = "Hetkel käsil on käsil toiming, mis takistab ühenduse loomist '%@' -ga. Palun proovi hiljem uuesti."; +"Do you really want to disconnect from your '%@' account?" = "Kas soovite tõesti katkestada ühenduse oma kontoga '%@'?"; +"This will remove all locally stored file copies from your device." = "See eemaldab kõik kohalikus seadmes salvestatud failikoopiad teie seadmest."; +"Really delete '%@'?" = "Kas tõesti kustutada '%@'?"; +"Do you want to log out from '%@'?" = "Kas soovite välja logida '%@'-st?"; +"This will also delete all locally stored file copies." = "Sellega kustutatakse ka kõik lokaalselt salvestatud failikoopiad."; +"Delete" = "Kustuta"; +"Remove" = "Eemalda"; +"Removing of '%@' failed" = "'%@' eemaldamine ebaõnnestus"; +"Accounts" = "Kontod"; + +"Help" = "Abiinfo"; +"Feedback" = "Tagasiside"; +"Welcome" = "Tere tulemast"; +"Thanks for choosing %@! \n Start by adding your account." = "Täname, et valisid %@! \nAlusta oma konto lisamisega."; + +/* Settings Messages */ + +"Security" = "Turvalisus"; +"Passcode Lock" = "Pääsukoodi lukk"; +"Face ID" = "Face ID"; +"Touch ID" = "Touch ID"; +"Unlock using %@?" = "Ava kasutades %@?"; +"Videos" = "Videod"; +"Photos" = "Fotod"; +"Wifi only" = "Ainult Wifi"; +"Settings" = "Seaded"; +"Lock application" = "Lukusta rakendus"; +"Upload videos via WiFi only" = "Laadi videod üles ainult WiFi-t kasutades"; +"Upload pictures via WiFi only" = "Laadi pildid üles ainult WiFi-t kasutades"; +"More" = "Rohkem"; +"Documentation" = "Dokumentatsioon"; +"Send feedback" = "Saada tagasiside"; +"Recommend to a friend" = "Soovita sõbrale"; +"Privacy Policy" = "Privaatsus"; +"Terms Of Use" = "Kasutustingimused"; +"Acknowledgements" = "Kinnitused"; +"license" = "litsents"; +"Portions of this app may utilize the following copyrighted material, the use of which is hereby acknowledged." = "Selle rakenduse osad võivad kasutada järgmist autoriõigustega kaitstud materjali, mille kasutamist käesolevaga kinnitatakse."; +"Video upload path" = "Videote üleslaadimise asukoht"; +"Photo upload path" = "Fotode üleslaadimise rada"; +"Immediately" = "Koheselt"; +"After 1 minute" = "1 minuti pärast"; +"After 5 minutes" = "5 minuti pärast"; +"After 30 minutes" = "30 minuti pärast"; +"After %ld seconds" = "Pärast %ld sekundit"; +"If you choose \"Immediately\" the App will be locked, when it is no longer in foreground." = "Kui valite \"Koheselt\", lukustatakse rakendus, kui see ei ole enam esiplaanil."; +"Please configure an email account" = "Palun seadistage e-posti konto"; +"You need to configure an email account first to be able to send emails." = "E-kirjade saatmiseks peate kõigepealt e-posti konto seadistama."; +"Do you want to open the following URL?" = "Kas soovite avada järgmise URL-i?"; +"Passcode option" = "Pääsukoodi võimalus"; +"Please choose how many digits you want to use for the passcode lock?" = "Palun valige, mitmekohalist numbrit soovite pääsukoodina kasutada?"; +"%ld digit code" = "%ld-kohaline kood"; +"Enter a new code with %ld digits" = "Sisestage uus %ld-kohaline kood"; +"To use the app, you need to create a passcode." = "To use the app, you need to create a passcode."; +"Passcode setup" = "Passcode setup"; + +"%@%@ %@ version %@ build %@\n(app: %@, sdk: %@)" = "%@%@ %@ versioon %@ build %@\n(rakendus: %@, sdk: %@)"; +"beta" = "beeta"; +"release" = "väljalase"; +"App Version" = "Rakenduse versioon"; +"Version information were copied to the clipboard" = "Versiooni teave kopeeriti lõikelauale"; + + +/* User Interface Settings */ +"Theme" = "Teema"; +"User Interface" = "Kasutajaliides"; +"Dark" = "Tume"; +"Dark Blue" = "Tumesinine"; +"Dark Web" = "Dark Web"; +"Light" = "Hele"; +"Classic" = "Klassikaline"; +"System" = "Süsteem"; +"System Appeareance" = "Süsteemi välimus"; + +/* Log settings */ +"Log Files" = "Logifailid"; +"Browse log files" = "Sirvige logifaile"; +"Share" = "Jaga"; +"Delete all log files?" = "Kustutada kõik logifailid?"; +"Delete all" = "Kustuta kõik"; + +"Log Level" = "Logimise tase"; +"Log Destinations" = "Logi sihtkoht"; +"Privacy" = "Privaatsus"; + +"Logging" = "Logimine"; +"Enable logging" = "Luba logimine"; +"Options" = "Valikud"; + +"Off" = "Väljas"; +"Info" = "Info"; +"Default" = "Vaikeväärtus"; +"Warning" = "Hoiatus"; +"Error" = "Viga"; + +"Share log file" = "Jagage logifaili"; +"Reset log file" = "Lähtesta logifail"; + +"When activated, logs may impact performance and include sensitive information. However the logs are not subject to automatic submission to %@ servers. Sharing logs with others is sole user responsibility." = "Kui on aktiveeritud, võivad logid mõjutada jõudlust ja sisaldada tundlikku teavet. Logisid ei edastata siiski automaatselt %@ serveritele. Logide jagamine teistega on kasutaja ainuvastutus."; + +"The last 10 archived logs are kept on the device - with each log covering up to 24 hours of usage. When sharing please bear in mind that logs may contain sensitive information such as server URLs and user-specific information." = "Seadmes säilitatakse 10 viimast arhiveeritud logi, kusjuures iga logi hõlmab kuni 24 tunni kasutust. Jagamisel pidage meeles, et logid võivad sisaldada tundlikku teavet, näiteks serveri URL-id ja kasutajaspetsiifilised andmed."; + +"Mask private data" = "Privaatsete andmete varjamine"; +"Enabling this option will attempt to mask private data, so it does not become part of any log. Since logging is a development and debugging feature, though, we can't guarantee that the log file will be free of any private data even with this option enabled. Therefore, please look through any log file and verify its free of any data you're not comfortable sharing before sharing it with anybody." = "Selle valiku sisse lülitamisel üritatakse privaatseid andmeid varjata, nii et need ei kajastu üheski logis. Kuna logimine on aga arendus- ja silumisfunktsioon, ei saa me garanteerida, et logifail on privaatsete andmeteta isegi siis, kui see valik on sisse lülitatud. Seetõttu vaadake enne jagamist palun kõik logifailid läbi ja veenduge, et see ei sisaldaks andmeid, mida te ei soovi jagada."; + +"No log file found" = "Logifaili ei leitud"; +"The log file can't be shared because no log file could be found or the log file is empty." = "Logifaili ei saa jagada, sest logifaili ei leitud või logifail on tühi."; +"Enable log file" = "Luba logifaili kasutamine"; + +"Really reset log file?" = "Kas tõesti lähtestada logifail?"; +"This action can't be undone." = "Seda tegevust ei saa tühistada."; + +/* Data usage settings */ +"Data usage" = "Andmekasutus"; +"Delete unused local copies" = "Kustutada kasutamata kohalikud koopiad"; +"Time measured since uploading, editing, downloading or viewing the respective file through this device. Does not apply to files downloaded via the Available Offline feature. Local copies may be deleted before the given period of time has passed, f.ex. because there's a newer version of a file on the server - or through the manual deletion of offline copies. Also, local copies may not be deleted after the given period of time has passed, f.ex. if an action is performed on it, the file is still in use - or the account holding the file hasn't been used in the app." = "Aeg, mis on mõõdetud alates vastava faili selle seadme kaudu üleslaadimisest, redigeerimisest, allalaadimisest või vaatamisest. Ei kehti funktsiooni Available Offline kaudu alla laaditud failidele. Kohalikud koopiad võidakse kustutada enne etteantud aja möödumist, nt. kuna serveris on faili uuem versioon või võrguühenduseta koopiate käsitsi kustutamisel. Samuti ei tohi kohalikke koopiaid pärast etteantud aja möödumist kustutada, nt. kui sellega tehakse mõni toiming, fail endiselt kasutusel või faili hoidvat kontot pole rakenduses kasutatud."; +"never" = "mitte kunagi"; +"after %@" = "peale %@"; +"Decrease Slider Value" = "Vähendage liuguri väärtust"; +"Increase Slider Value" = "Suurendage liuguri väärtust"; + +"Cellular Data Usage" = "Mobiilside andmekasutus"; +"Some cellular data may still be used. To completely avoid the usage of cellular data, please turn off access to cellular for the entire app in the Settings app." = "Mõnda mobiilset andmesidet võidakse siiski kasutada. Mobiilse andmeside kasutamise täielikuks vältimiseks lülitage seadete rakenduses kogu rakenduse juurdepääs mobiilsidevõrgule välja."; +"Cellular Data Usage have been disabled via MDM configuration. Please contact your administrator for more information." = "Mobiilse andmeside kasutamine on MDM-i seadistuse kaudu keelatud. Lisateabe saamiseks võtke ühendust oma administraatoriga."; +"General" = "Üldine"; +"By feature" = "Funktsiooni järgi"; + +"off" = "väljas"; +"enabled" = "sisse lülitatud"; + +/* Display settings */ +"Advanced settings" = "Lisavalikud"; +"Show hidden files and folders" = "Näita peidetud faile ja kaustu"; +"Show folders on top" = "Näita kaustu üleval"; +"Disable gestures" = "Lülita žestid välja"; +"Prevent dragging of files and folders and multiselection using system defined gestures" = "Väldi failide, kaustade ja hulgivalikute lohistamist süseemseid žeste kasutades"; + +/* Advanced settings */ +"Enable diagnostics" = "Diagnostika sisselülitamine"; + +/* Passcode Messages */ + +"Enter code" = "Sisesta kood"; +"Repeat code" = "Korda koodi"; +"Delete code" = "Kustuta kood"; +"The entered codes are different" = "Sisestatud koodid on erinevad"; +"Incorrect code" = "Vale kood"; +"Please try again in %@" = "Palun proovige %@ pärast uuesti"; +"Unlock %@" = "Lukusta lahti %@"; +"Biometric authentication failed" = "Biomeetriline autentimine ebaõnnestus"; +"You are required to set the passcode" = "Peate määrama pääsukoodi"; + +/* Certificate management */ + +"Certificates" = "Sertifikaadid"; +"User-approved certificates" = "Kasutaja kinnitatud sertifikaadid"; + +"Approved" = "Heaks kiidetud"; +"Auto-approved" = "Automaatselt kinnitatud"; +"Revoke approval" = "Tühista kinnitus"; + +/* Actions */ + +"Forbidden Characters" = "Keelatud märgid"; +"File name cannot contain / or \\" = "Faili nimi ei tohi sisaldada / või \\"; +"Item with same name already exists" = "Samanimeline objekt on juba olemas"; +"An item with the same name already exists in this location." = "Samanimeline objekt on selles kohas juba olemas."; +"New Folder" = "Uus kaust"; +"Folder name" = "Kausta nimi"; +"Rename" ="Nimeta ümber"; +"Create folder" ="Loo kaust"; +"Duplicate" = "Klooni"; +"Move" = "Liiguta"; +"Move {{itemCount}} items" = "Move {{itemCount}} items"; +"Move \"{{itemName}}\"" = "Move \"{{itemName}}\""; +"Move here" = "Liiguta siia"; +"Select target." = "Select target."; +"Open in" = "Ava"; +"Copy" = "Kopeeri"; +"Copy {{itemCount}} items" = "Copy {{itemCount}} items"; +"Copy \"{{itemName}}\"" = "Copy \"{{itemName}}\""; +"Copy here" = "Kopeeri siia"; + +"Cannot connect to " = "Ei saa ühenduda"; +" couldn't download file(s)" = "faili(e) ei saanud alla laadida"; +"Actions" = "Tegevused"; +"copy" = "kopeeri"; +"Close Window" = "Sulge aken"; +"Open in a new Window" = "Ava uues aknas"; +"Open in Window" = "Ava aknas"; +"Take photo or video" = "Tehke foto või video"; +"%ld Item was copied to the clipboard" = "%ld Objekt kopeeriti lõikelauale"; +"%ld Items were copied to the clipboard" = "%ld Objektid kopeeriti lõikelauale"; +"Please note: Folders can only be pasted into the %@ app and the same account." = "Pange tähele: kaustu saab kleepida ainult rakendusse %@ ja samale kontole."; + +"Processing on server" = "Processing on server"; + +"Preparing…" = "Ettevalmistamine..."; + +"No actions available" = "Ühtegi toimingut pole saadaval"; +"No actions are available for this folder, possibly because of missing permissions." = "Selle kausta jaoks pole toimingud saadaval, tõenäoliselt puuduvate õiguste tõttu."; + +"Close actions menu" = "Sulgege toimingute menüü"; +"Favorited" = "Lemmikud"; +"Favorite item" = "Lisa lemmikuks"; +"Unfavorite item" = "Eemalda lemmikutest"; +"Presentation Mode" = "Esitlusrežiim"; +"Enabling presentation mode will prevent the display from sleep mode until the view is closed." = "Esitlusrežiimi sisselülitamine takistab ekraani puhkerežiimi kuni vaate sulgemiseni."; +"Enable" = "Lülita sisse"; +"Exit Full Screen" = "Välju täisekraanilt"; + +"Select one or more items." = "Select one or more items."; + +"Add item" = "Lisa kirje"; + +/* App Provider */ +"New document" = "Uus dokument"; +"Pick a name" = "Pick a name"; +"Pick a document type to create:" = "Vali loodava dokumendi tüüp:"; +"Error creating {{itemName}}" = "Error creating {{itemName}}"; + +"Opening…" = "Opening…"; + +"Open in {{appName}} (web)" = "Open in {{appName}} (web)"; +"Error opening {{itemName}} in {{appName}}" = "Error opening {{itemName}} in {{appName}}"; +"Opening documents is not supported by the app provider on this instance." = "Opening documents is not supported by the app provider on this instance."; + +/* Preview */ +"Open file" = "Ava fail"; +"Network unavailable" = "Võrguühendus pole saadaval"; +"Error" = "Viga"; +"Could not get the picture" = "Pildi hankimine ebaõnnestus"; +"Downloading" = "Allalaadimine"; +"File couldn't be opened" = "Faili ei saanud avada"; +"Connecting..." = "Ühendamine..."; + +"File" = "Fail"; +"%@ was updated" = "%@ uuendatud"; +"Would you like to view the updated version?" = "Kas soovite uuendatud versiooni vaadata?"; +"Show new version" = "Näita uut versiooni"; +"Refresh without asking" = "Värskendage ilma küsimata"; +"Ignore updates" = "Ignoreeri uuendusi"; + +/* PDF Viewer */ +"Resume" = "Jätka"; +"Go to page" = "Mine leheküljele"; +"Page" = "Lehekülg"; +"This document has %@ pages" = "Sellel dokumendil on %@ lehekülge"; +"%@ of %@" = "%@ %@-st"; +"Invalid Page" = "Vigane lehekülg"; +"The entered page number doesn't exist" = "Sisestatud leheküljenumbrit ei ole olemas"; +"Search PDF" = "Otsi PDF-ist"; +"Outline" = "Ülevaade"; +"Find Next" = "Leia järgmine"; +"Find Previous" = "Leia eelmine"; +"Close Search" = "Sulge otsing"; + +/* Photo Upload */ +"Upload" = "Lae üles"; +"Select All" = "Vali kõik"; +"Deselect All" = "Tühista valikud"; +"All Photos" = "Kõik fotod"; +"Albums" = "Albumid"; +"Importing from photo library" = "Importimine fotokogust"; +"Media import" = "Meedia importimine"; +"%@ of %@" = "%@ %@-st"; +"Selected folder lacks file creation permission" = "Valitud kaustal puudub faili loomise õigus"; +"%d Item" = "%d Objekt"; +"%d Items" = "%d Objekti"; +"Select Items" = "Vali objektid"; + +/* Scan */ +"Scan" = "Skanni"; +"Scans" = "Skannimised"; +"Scan document" = "Skanni dokument"; +"Saving" = "Salvestamine"; +"File format" = "Failivorming"; +"Name" = "Nimi"; +"Save as" = "Salvesta kui"; +"Options" = "Valikud"; +"Scan additional" = "Skanni lisaks"; +"Create one file per page" = "Loo üks fail lehekülje kohta"; + +/* Sharing */ +"Sharing requires an active connection." = "Sharing requires an active connection."; +"No items shared with you" = "No items shared with you"; +"No items shared by you" = "No items shared by you"; +"No items shared by link" = "No items shared by link"; + +"Searching Shares…" = "Jagamiste otsimine..."; +"Recipient" = "Saaja"; +"Recipients" = "Saajad"; +"Public Link" = "Avalik link"; +"Public Links" = "Avalikud lingid"; +"Shared by {{owner}}" = "Shared by {{owner}}"; +"Shared with {{recipient}}" = "Shared with {{recipient}}"; +"Shared with {{recipients}}" = "Shared with {{recipients}}"; +"Expires {{expirationDate}}" = "Expires {{expirationDate}}"; +"Share {{itemName}}" = "Share {{itemName}}"; +"Create link" = "Create link"; +"Invite" = "Kutsu"; +"Invite Recipient" = "Kutse saaja"; +"Recipients" = "Saajad"; +"Add email or name" = "Lisa e-post või nimi"; +"Users" = "Kasutajad"; +"Groups" = "Grupid"; +"Start typing to search users, groups and remote users." = "Alusta tippimist kasutajate, rühmade ja kaugkasutajate otsimiseks."; +"(Group)" = "(Grupp)"; +"Adding User to Share failed" = "Kasutaja lisamine jagamisele ebaõnnestus"; +"Permissions" = "Õigused"; +"Invited: %@" = "Kutsutud: %@"; +"Created: %@" = "Loodud: %@"; +"Allows the users you share with to re-share" = "Võimaldab kasutajatel, kellega te jagate, edasi jagada"; +"Allows the users you share with to edit your shared files, and to collaborate" = "Võimaldab kasutajatel, kellega jagate, teie jagatud faile redigeerida ja koostööd teha"; +"Allows the users you share with to create new files and add them to the share" = "Võimaldab kasutajatel, kellega te jagate, luua uusi faile ja lisada neid jagamistele"; +"Allows uploading a new version of a shared file and replacing it" = "Võimaldab jagatud faili uue versiooni üleslaadimist ja asendamist"; +"Allows the users you share with to delete shared files" = "Võimaldab kasutajatel, kellega te jagate, kustutada jagatud faile"; +"Setting permission failed" = "Õiguste seadistamine ebaõnnestus"; +"Shared with" = "Jagatud kasutajaga"; +"Remove Recipient failed" = "Vastuvõtja eemaldamine ebaõnnestus"; +"Remove Recipient" = "Eemalda saaja"; +"Create" = "Lisa"; +"Change" = "Muuda"; +"Recipients can view or download contents." = "Vastuvõtjad saavad sisu vaadata või alla laadida."; +"Recipients can view, download, edit, delete and upload contents." = "Vastuvõtjad saavad sisu vaadata, alla laadida, muuta, kustutada ja üles laadida."; +"Receive files from multiple recipients without revealing the contents of the folder." = "Failide vastuvõtmine mitmelt saatjalt ilma kausta sisu avalikustamata."; +"Download / View" = "Laadi alla / Vaata"; +"Download / View / Upload" = "Laadi alla / Vaata / Laadi üles"; +"Upload only (File Drop)" = "Ainult üleslaadimine (File Drop)"; +"Creating public link failed" = "Avaliku lingi loomine ebaõnnestus"; +"Create Public Link" = "Loo avalik link"; +"Links" = "Lingid"; +"Link" = "Link"; +"Setting expiration date failed" = "Aegumiskuupäeva määramine ebaõnnestus"; +"Expiration date" = "Aegumise kuupäev"; +"Copy Public Link" = "Kopeeri avalik link"; +"Delete Public Link" = "Kustuta avalik link"; +"Deleting Public Link failed" = "Avaliku lingi kustutamine ebaõnnestus"; +"Deleting password failed" = "Parooli kustutamine ebaõnnestus"; +"Setting password failed" = "Parooli määramine ebaõnnestus"; +"Type to update password" = "Parooli värskendamiseks tippige"; +"Cannot change permission" = "Õigust ei saa muuta"; +"Before you can set the permission\n%@,\n you must enter a password." = "Enne õiguste seadistamist\n%@, \npeate sisestama salasõna."; +"Password Protected" = "Parooliga kaitstud"; +"Pending Federated Invites" = "Ootel ühendatud kutsed"; +"Pending Invites" = "Ootel kutsed"; +"Shared with you" = "Sinuga jagatud"; +"Shared with others" = "Teistega jagatud"; +"Shares" = "Jagamised"; +"Copy Private Link" = "Kopeeri privaatne link"; +"Only people who have access to the file/folder can use it. Use it as a permanent link for yourself or to point others to files within shares." = "Ainult need inimesed, kellel on juurdepääs failile/kaustale, saavad seda kasutada. Kasutage seda püsilingina enda jaoks või teiste suunamiseks jagatud failidele."; +"Accept Share failed" = "Jagamise aktsepteerimine ebaõnnestus"; +"Decline Share failed" = "Jagamisest keeldumine ebaõnnestus"; +"Accept" = "Nõustu"; +"Decline" = "Lükka tagasi"; +"Declined" = "Tagasi lükatud"; +"Decline Share" = "Lükka jagamine tagasi"; +"Unshare" = "Lõpeta jagamine"; +"Unshare failed" = "Jagamise katkestamine ebaõnnestus"; +"Are you sure you want to unshare these items?" = "Kas olete kindel, et soovite nende objektide jagamise tühistada?"; +"Are you sure you want to unshare this item?" = "Kas olete kindel, et soovite selle objekti jagamise tühistada?"; +"Share" = "Jaga"; +"Read" = "Lugemine"; +"Can Share" = "Saab jagada"; +"Can Edit" = "Saab muuta"; +"Can Edit and Change" = "Saab Redigeerida ja Muuta"; +"Can Create" = "Saab Luua"; +"Can Change" = "Saab muuta"; +"Can Delete" = "Saab kustutada"; +"Accept Invite %@" = "Kutse aktsepteerimine %@"; +"Decline Invite %@" = "Kutsest keeldumine %@"; +"Decline cannot be undone." = "Keeldumist ei saa tagasi võtta."; +"Sharing" = "Jagamine"; +"You" = "Sina"; +"Share this file" = "Jaga seda faili"; +"Share this folder" = "Jaga seda kausta"; +"shared" = "jagatud"; +"Owner" = "Omanik"; +"Private Link" = "Privaatne link"; +"Created Public Link" = "Loo Avalik Link"; +"URL was copied to the clipboard" = "URL kopeeriti lõikelauale"; +"%@ is not available, when this account is offline. Please open the app and log into your account before you can do this action." = "%@ pole saadaval, kui see konto on võrguühenduseta. Enne selle toimingu tegemist avage rakendus ja logige sisse oma kontole."; +"%@ is not available for this item." = "%@ ei ole selle objekti puhul saadaval."; +"Share with user/group" = "Jagage kasutajaga/grupiga"; +"Share link" = "Jaga linki"; + +"Shared with me" = "Shared with me"; +"Shared by me" = "Shared by me"; +"Shared by link" = "Jagatud lingiga"; + +"Share with" = "Jaga"; +"Add" = "Lisa"; +"Save changes" = "Salvesta muudatused"; + +/* Quick Access view */ +"Quick Access" = "Kiire ligipääs"; +"Collection" = "Kogu"; +"Recents" = "Hiljutised"; +"Favorites"= "Lemmikud"; +"Images" = "Pildid"; +"PDF Documents" = "PDF dokumendid"; +"Text" = "Tekst"; +"Documents" = "Dokumendid"; +"Audio" = "Helid"; + +/* Favorites view */ +"No favorites found" = "No favorites found"; +"If you make an item a favorite, it will turn up here." = "If you make an item a favorite, it will turn up here."; + +/* Media files settings */ +"Media Files" = "Meediafailid"; +"Download instead of streaming" = "Voogesituse asemel laadige alla"; + +/* Photo upload settings */ +"Media Upload" = "Meedia üleslaadimine"; +"Media Export" = "Meedia eksport"; +"Auto Upload" = "Automaatne üleslaadimine"; +"Convert HEIC to JPEG" = "HEIC teisendamine JPEG-ks"; +"Convert videos to MP4" = "Videote teisendamine MP4-ks"; +"Preserve original media file names" = "Säilitage algsed meediafailide nimed"; +"Auto Upload Photos" = "Laadi fotod automaatselt üles"; +"Auto Upload Videos" = "Automaatne videote üleslaadimine"; +"Account" = "Konto"; +"Accounts" = "Kontod"; +"Select account" = "Vali konto"; +"Photo upload path" = "Fotode üleslaadimise rada"; +"Video upload path" = "Videote üleslaadimise asukoht"; +"Select Upload Path" = "Valige üleslaadimise rada"; +"Auto upload of media was disabled since configured account / folder was not found" = "Meedia automaatne üleslaadimine lülitati välja, kuna konfigureeritud kontot/kausta ei leitud"; +"Importing %@ media files for upload" = "%@ meediafaili importimine üleslaadimiseks"; +"Photo upload" = "Foto üleslaadimine"; +"Video upload" = "Video üleslaadimine"; + +/* Background media uploads */ +"Background uploads (Lab Version)" = "Taustal üleslaadimine (Lab Versioon)"; +"Background uploads" = "Üleslaadmised taustal"; +"Use background refresh" = "Kasutage taustavärskendust"; +"Allow this app to refresh the content when on Wi-Fi or mobile network in background." = "Luba sellel rakendusel uuendada sisu taustal, kui see on Wi-Fi's või mobiilivõrgus."; +"Enable background uploads" = "Luba taustal üleslaadimine"; +"Use background location updates" = "Kasutage asukohavärskendusi taustal"; +"Background upload notifications" = "Taustal üleslaadimise teavitused"; +"If you would like background media uploads to be more reliable, you should enable background location updates." = "Kui soovite, et meedia üleslaadimine taustal oleks usaldusväärsem, peaksite lubama asukohavärskendused taustal."; +"Background media uploads rely on location updates and will stop working if location acquisition permissions are revoked." = "Meedia üleslaadimine taustal tugineb asukohavärskendustel ja lakkab töötamast, kui asukoha hankimise õigused tühistatakse."; +"Otherwise background media uploads using background refresh technology would depend on how frequently you use the app." = "Vastasel juhul sõltuks meedia taustal üleslaadimine taustavärskendustehnoloogia abil sellest, kui sageli rakendust kasutatakse."; +"Location permission denied" = "Asukoha ligipääs keelatud"; +"Please re-enable location acquisition in system settings" = "Palun lülitage asukoha määramine süsteemi seadetes uuesti sisse"; +"Scheduled upload of %ld media assets" = "%ld meediavara ajastatud üleslaadimine"; + +/* Progress summarizer */ +"Creating %ld folders…" = "%ld kausta loomine…"; +"Moving %ld items…" = "%ld objekti teisaldamine…"; +"Copying %ld items…" = "%ld objekti kopeerimine…"; +"Deleting %ld items…" = "%ld objekti kustutamine…"; +"Uploading %ld files…" = "%ld faili üleslaadimine..."; +"Downloading %ld files…" = "%ld faili allalaadimine..."; +"Updating %ld items…" = "%ld objekti uuendamine…"; + +/* Offline storage management */ +"Free on %@" = "Vaba ruumi %@-l"; +"unknown" = "tundmatu"; +"Offline files use" = "Kohalikult salvestatud failid kasutavad "; +"Compacting of '%@' failed" = "%@ kokkupakkimine ebaõnnestus"; +"Include available offline files" = "Lisage saadaolevad kohalikult salvestatud failid"; +"Delete all Offline Files" = "Kustuta kõik kohalikult salvestatud failid"; +"Manage" = "Halda"; +"Storage" = "Salvestusmaht"; +"Compacting" = "Pakkimine"; + +"Really include available offline files?" = "Kas lisada saadaolevad kohalikult salvestatud failid?"; +"Files and folders marked as Available Offline will become unavailable. They will be re-downloaded next time you log into your account (connectivity required)." = "Failid ja kaustad, mis on märgitud kui Saadaval ilma Võrguühenduseta, muutuvad kättesaamatuks. Need laaditakse uuesti alla, kui järgmine kord oma kontole sisse logite (ühendus on vajalik)."; +"Removes downloaded files and local copies of items marked as Available Offline. The latter will be re-downloaded next time you log into your account (connectivity required)." = "Eemaldab allalaaditud failid ja nende objektide kohalikud koopiad, mis on märgitud kui Saadaval ilma Võrguühenduseta. Viimased laaditakse uuesti alla, kui järgmine kord kontole sisse logite (ühendus on vajalik)."; + +/* Diagnostic */ +"Diagnostic Overview" = "Diagnostika ülevaade"; +"Diagnostics" = "Diagnostika"; +"Share Diagnostics" = "Jagage diagnostikat"; +"Action executed" = "Toiming sooritatud"; + +"Update Status" = "Uuenda staatust"; +"Completed update scans" = "Lõpetatud uuenduste skaneerimisi"; +"Total update scans" = "Uuenduste skaneerimisi kokku"; + +/* Available offline */ +"Root folder" = "Peakaust"; +"at" = "kell"; +"(no match)" = "(pole vastet)"; + +"Make available offline" = "Teha kättesaadavaks võrguühenduseta"; +"Make unavailable offline" = "Tee saadavaks ilma võrguühenduseta"; + +"Available Offline" = "Saadaval ilma võrguühenduseta"; + +"No files available offline" = "No files available offline"; +"Files selected and downloaded for offline availability will show up here." = "Files selected and downloaded for offline availability will show up here."; + +"Locations" = "Locations"; +"Downloaded Files" = "Downloaded Files"; + +/* Single Account */ +"You are connected as\n%@" = "Olete ühendatud kui\n%@"; + +/* Release Notes */ +"Proceed" = "Jätka"; +"New in %@" = "Uut %@-s"; +"Thank you for using %@.\nIf you like our App, please leave an AppStore review.\n❤️" = "Täname, et kasutasite %@-i.\nKui teile meeldib meie rakendus, jätke AppStore'is arvustus.\n❤️"; +"Thank you for using %@.\n" = "Täname, et kasutasite %@-i.\n"; + +/* Key Commands */ +"Select Next" = "Vali järgmine"; +"Select Previous" = "Vali eelmine"; +"Open Selected" = "Ava vaitud"; +"Change Sort Order" = "Muuda sorteerimise järjekorda"; +"Search" = "Otsi"; +"Back" = "Tagasi"; +"Save" = "Salvesta"; +"Sort by %@" = "Sorteerida %@ järgi"; +"Tab %@" = "Tab %@"; +"Select Last Item on Page" = "Vali viimane kirje lehel"; +"Scroll to Top" = "Keri üles"; +"Scroll to Bottom" = "Keri alla"; +"Copy to Clipboard" = "Kopeeri lõikelauale"; +"Import from Clipboard" = "Impordi lõikelaualt"; +"Choose destination directory…" = "Vali sihtkaust"; +"Next" = "Järgmine"; +"Previous" = "Eelmine"; +"Favorite" = "Lemmik"; +"Cut" = "Lõika"; +"Paste" = "Kleebi"; +"Play/Pause" = "Esita/Paus"; +"Skip Back" = "Mine tagasi"; +"Skip Ahead" = "Mine edasi"; +"Go to Beginning" = "Mine algusesse"; +"Mute/Unmute" = "Vaigista/Tühista vaigistus"; +"Full Screen" = "Täisekraan"; +"Switch Theme Style" = "Vahetage teema stiili"; + +/* Markup */ +"How should this file be saved?" = "Kuidas seda faili salvestada?"; +"Overwrite original" = "Kirjuta algne üle"; +"Save as copy" = "Salvesta koopiana"; +"Discard changes" = "Loobu muudatustest"; +"Markup" = "Märgista"; +"Crop or Rotate" = "Lõika või pööra"; +"Saving edited file failed" = "Muudetud faili salvestamine ebaõnnestus"; +"File no longer exists" = "Faili pole enam olemas"; + +/* Licensing */ +"Unlocked" = "Avatud"; +"Unlock" = "Ava"; + +"Subscribe Now" = "Telli kohe"; +"Free" = "Vaba"; + +"%@ / %@" = "%@ / %@"; +"after free %@ trial" = "pärast tasuta %@ prooviperioodi"; + +/* Example usage: "Try [14 days] for free." */ +"Try %@ for free." = "Proovi %@ tasuta."; + +/* Example usage: "Then [1,99 €] / [year]." */ +"Then %@ / %@." = "Pärast %@ / %@."; + +/* Example usage: "[1,99 €] / [year] - starting immediately." */ +"%@ / %@ – starting immediately" = "%@ / %@ – alustades kohe"; + +/* Licensing: Pro Features */ +"Pro Features" = "Pro funktsioonid"; + +/* Licensing: App Store */ +"Restore purchases" = "Taasta ostud"; +"Restoring purchases…" = "Ostude taastamine..."; +"Error restoring purchases" = "Viga ostude taastamisel"; +"Error loading product info from App Store"= "Viga tooteinfo laadimisel App Store'ist"; +"Purchase failed" = "Ost ebaõnnestus"; +"More information" = "Rohkem informatsiooni"; + +/* Licensing: Enterprise */ +"Enterprise" = "Suurettevõte"; + +/* Licensing: Settings */ +"In-App Purchases" = "Rakendusesisesed ostud"; +"Purchases & Subscriptions" = "Purchases & Subscriptions"; +"Error fetching transactions" = "Viga tehingute hankimisel"; +"Document Scanner" = "Dokumendi skaneerija"; +"Scan documents and photos with your camera." = "Skaneerige dokumente ja fotosid oma kaameraga."; +"Shortcuts Actions" = "Otseteed Toimingud"; +"Use ownCloud actions in Shortcuts." = "Kasutage ownCloudi toiminguid otseteede all."; +"Markup Documents" = "Märgistage Dokumendid"; +"Markup photos and PDF files." = "Märgistage fotod ja PDF failid"; +"Unlock all Pro Features." = "Avage kõik Pro funktsioonid."; + +"Purchased App Version" = "Tasuline Rakenduse Versioon"; +"Receipt Date" = "Kviitungi kuupäev"; +"Manage subscription" = "Halda tellimusi"; +"Purchases are not allowed on this device." = "Selles seadmes ei ole ostud lubatud."; +"Type" = "Tüüp"; +"Quantity" = "Kogus"; +"Product" = "Toode"; +"Date" = "Kuupäev"; +"Cancelled" = "Tühistatud"; +"Ends" = "Lõpeb"; +"none" = "pole"; +"trial" = "prooviperiood"; +"purchase" = "osta"; +"subscription" = "tellimus"; +"Photo Pro Features" = "Foto Pro funktsioonid"; +"Image metadata, extended upload options" = "Foto metaandmed, laiendatud üleslaadimisvalikud"; +"Purchase" = "Osta"; +"Subscribe" = "Telli"; + +"No accounts found" = "Kontosid ei leitud"; +"In order to accurately determine your current licensing status, please add one or more accounts first." = "Praeguse litsentsi staatuse täpseks määramiseks lisage esmalt üks või mitu kontot."; + +/* Share Sheet */ +"Save File" = "Salvesta fail"; +"Choose an account and folder to import into." = "Valige importimiseks konto ja kaust."; +"No account configured.\nSetup an new account in the app to save to." = "Kontot pole seadistatud.\nSeadistage rakenduses uus konto, kuhu salvestada."; +"Importing item %ld of %ld" = "Objekti %ld importimine %ld-st"; +"Error importing %@" = "Viga importimisel %@"; +"Error loading item" = "Viga objekti laadimisel"; +"Preparing…" = "Ettevalmistamine..."; +"Import \"{{itemName}}\"" = "Import \"{{itemName}}\""; +"Import {{itemCount}} files" = "Import {{itemCount}} files"; +"Save here" = "Salvesta siia"; + +/* FileProvider */ +/* - Disabled */ +"File Provider access has been disabled by the administrator.\n\nPlease use the app to access your files." = "File Provider access has been disabled by the administrator.\n\nPlease use the app to access your files."; +"File Provider access has been disabled by the administrator. Please use the app to create new folders." = "File Provider access has been disabled by the administrator. Please use the app to create new folders."; +"File Provider access has been disabled by the administrator. Please use the share extension to import files." = "File Provider access has been disabled by the administrator. Please use the share extension to import files."; + +/* Disallowed Import Methods */ +/* - Share Extension */ +"Share Extension disabled" = "Jagamise laiendus välja lülitatud"; +"Importing files through the Share Extension is not allowed on this device." = "Failide importimine jagamislaienduse kaudu pole selles seadmes lubatud."; + +/* - Open with */ +"Opening not allowed" = "Avamine ei ole lubatud"; +"Importing files through opening is not allowed on this device." = "Failide importimine avamise kaudu pole selles seadmes lubatud."; + +/* - Files.app */ +"Import not allowed" = "Importimine pole lubatud"; +"Importing files through the File Provider is not allowed on this device." = "Failide importimine läbi FileProvideri ei ole sellel seadmel toetatud."; + +/* Misc. accessibility */ +"Enter multiple selection" = "Sisestage mitu valikut"; + +/* Universal Links */ +"Resolving link…" = "Lingi lahendamine..."; +"Link resolution failed" = "Lingi lahendamine ebaõnnestus"; +"Couldn't resolve a private link since you are offline and corresponding item is not cached locally." = "Privaatset linki ei saanud lahendada, kuna olete võrguühenduseta ja vastavat objekti ei ole kohalikus seadmes vahemällu salvestatud."; +"Couldn't resolve a private link since the item is not known to the server." = "Privaatset linki ei saanud lahendada, kuna objekt pole serverile teada."; +"Link points to an account bookmark which is not configured in the app." = "Link osutab konto järjehoidjale, mis ei ole rakenduses konfigureeritud."; + +/* Photo meta-data */ +"Image metadata" = "Pildi metadata"; +"Image details" = "Pildi üksikasjad"; +"Profile" = "Profiil"; +"Size" = "Suurus"; +"Density" = "Tihedus"; +"Color model" = "Värvimudel"; +"%@ (%d bits/channel)" = "%@ (%dbitti / kanal) "; +"Histogram" = "Histogramm"; + +"Camera details" = "Kaamera üksikasjad"; +"Capture settings" = "Jäädvustamise seaded"; +"Time" = "Aeg"; +"EXIF aux info" = "EXIF lisainfo"; +"Authoring" = "Loome"; +"TIFF" = "TIFF"; + +"Lens make" = "Objektiivi mark"; +"Lens model" = "Objektiivi mudel"; +"Lens info" = "Objektiivi info"; +"Focal length" = "Fookuskaugus"; +"Focal length @ 35 mm" = "Fookuskaugus @ 35 mm"; + +"Shutter speed" = "Säriaeg"; +"Aperture" = "Ava"; +"ISO" = "Valgustundlikkus"; + +/* EXIF: exposure program */ +"Program" = "Programm"; +"Not defined" = "Määratlemata"; +"Manual" = "Manuaalne"; +"Normal" = "Tavaline"; +"Aperture priority" = "Ava prioriteet"; +"Shutter priority" = "Katiku prioriteet"; +"Creative" = "Loominguline"; +"Action" = "Tegevus"; +"Portrait" = "Portree"; +"Landscape" = "Maastikuvaade"; + +/* EXIF: exposure metering mode */ +"Metering" = "Mõõtmine"; +"unknown" = "tundmatu"; +"Average" = "Keskmine"; +"CenterWeightedAverage" = "Kaalutud keskmine"; +"Spot" = "Punkt"; +"MultiSpot" = "MultiSpot"; +"Pattern" = "Muster"; +"Partial" = "Osaline"; + +/* EXIF: flash mode */ +"Flash" = "Välk"; +"Fired" = "Sähvas"; +"Didn't fire" = "Ei sähvanud"; +"No strobe return detection" = "Strobo tagastusvalguse tuvastus puudub"; +"Strobe return light not detected" = "Strobo tagastusvalgust ei tuvastatud"; +"Strobe return light detected" = "Strobo tagastusvalgus tuvastati"; +"Compulsory flash firing" = "Kohustuslik välgutamine"; +"Compulsory flash supression" = "Kohustuslik välgu summutamine"; +"Auto mode" = "Automaatrežiim"; +"Red eye detection supported" = "Punase silma tuvastamine toetatud"; +"not present" = "puudub"; + +/* EXIF: white balance */ +"White balance" = "Valge tasakaal"; +"Auto" = "Auto"; +"Manual" = "Manuaalne"; + +"Exposure bias" = "Säri kompensatsioon"; +"Uncalibrated" = "Kalibreerimata"; +"Original date" = "Algne kuupäev"; +"Digitized date" = "Digitaliseeritud kuupäev"; + +"Color space" = "Värviruum"; + +/* EXIF: aux info */ +"Lens ID" = "Objektiivi ID"; +"Lens serial" = "Objektiivi seerianumber"; +"Serial number" = "Seerianumber"; +"Flash compensation" = "Välgu kompenseerimine"; +"Owner" = "Omanik"; +"Firmware" = "Püsivara"; +"Keywords" = "Märksõnad"; +"Copyright" = "Autoriõigused"; + +/* IPTC meta data */ +"Contact info" = "Kontaktinfo"; +"Usage terms" = "Kasutustingimused"; +"Scene" = "Stseen"; +"Description" = "Kirjeldus"; + +/* IPTC photometric interpretation */ +"Photometric interpretation" = "Fotomeetriline interpretatsioon"; +"none" = "pole"; +"RGB" = "RGB"; +"YCbCr" = "YCbCr"; + +/* EXIF: gps position data */ +"GPS Location" = "GPS Asukoht"; +"Coordinates" = "Koordinaadid"; +"Altitude" = "Kõrgus merepinnast "; +"Place" = "Asukoht"; +"E" = "E"; +"W" = "W"; +"N" = "N"; +"S" = "S"; + +/* Pro photo upload settings */ +"Extended upload settings" = "Laiendatud üleslaadimise seaded"; +"Prefer unedited photos" = "Eelistage töötlemata fotosid"; +"Prefer RAW photos" = "Eelistage RAW-fotosid"; +"Prefer original videos" = "Eelistage originaalvideosid"; + +/* Limited photo library access */ +"Limited Photo Access" = "Piiratud juurdepääs fotodele"; +"Access for the media selected for upload is limited" = "Juurdepääs üleslaadimiseks valitud meediale on piiratud"; +"No Access to the media selected for upload" = "Juurdepääs üleslaadimiseks valitud meediale puudub"; + +/* macOS support */ +"Passcode protection is not supported on this device.\nPlease disable passcode lock in the app settings." = "See seade ei toeta pääsukoodide kaitset.\nKeelake rakenduse seadetes pääsukoodilukk."; + +/* MDM settings push */ +"New settings received from MDM" = "MDM-ist saadi uued seaded"; +"Tap to quit the app." = "Rakenduse lõpetamiseks puudutage valikut."; +"Tap to launch the app." = "Rakenduse avamiseks puudutage valikut."; + +/* Beta warning */ +"Beta Warning" = "Beta Warning"; +"\nThis is a BETA release that may - and likely will - still contain bugs.\n\nYOU SHOULD NOT USE THIS BETA VERSION WITH PRODUCTION SYSTEMS, PRODUCTION DATA OR DATA OF VALUE. YOU'RE USING THIS BETA AT YOUR OWN RISK.\n\nPlease let us know about any issues that come up via the \"Send Feedback\" option in the settings." = "\nThis is a BETA release that may - and likely will - still contain bugs.\n\nYOU SHOULD NOT USE THIS BETA VERSION WITH PRODUCTION SYSTEMS, PRODUCTION DATA OR DATA OF VALUE. YOU'RE USING THIS BETA AT YOUR OWN RISK.\n\nPlease let us know about any issues that come up via the \"Send Feedback\" option in the settings."; +"Agree" = "Agree"; diff --git a/ownCloud/Resources/eu.lproj/InfoPlist.strings b/ownCloud/Resources/eu.lproj/InfoPlist.strings index 641852852..169c66eb1 100644 Binary files a/ownCloud/Resources/eu.lproj/InfoPlist.strings and b/ownCloud/Resources/eu.lproj/InfoPlist.strings differ diff --git a/ownCloud/Resources/eu.lproj/Localizable.strings b/ownCloud/Resources/eu.lproj/Localizable.strings index 8ef50b094..4f775cf5b 100644 Binary files a/ownCloud/Resources/eu.lproj/Localizable.strings and b/ownCloud/Resources/eu.lproj/Localizable.strings differ diff --git a/ownCloud/Resources/fr.lproj/InfoPlist.strings b/ownCloud/Resources/fr.lproj/InfoPlist.strings index 871fc4f8a..f43ade8c6 100644 Binary files a/ownCloud/Resources/fr.lproj/InfoPlist.strings and b/ownCloud/Resources/fr.lproj/InfoPlist.strings differ diff --git a/ownCloud/Resources/fr.lproj/Localizable.strings b/ownCloud/Resources/fr.lproj/Localizable.strings index 759b58913..7900e732d 100644 Binary files a/ownCloud/Resources/fr.lproj/Localizable.strings and b/ownCloud/Resources/fr.lproj/Localizable.strings differ diff --git a/ownCloud/Resources/gl.lproj/InfoPlist.strings b/ownCloud/Resources/gl.lproj/InfoPlist.strings index d6b12ca14..f6682892d 100644 Binary files a/ownCloud/Resources/gl.lproj/InfoPlist.strings and b/ownCloud/Resources/gl.lproj/InfoPlist.strings differ diff --git a/ownCloud/Resources/gl.lproj/Localizable.strings b/ownCloud/Resources/gl.lproj/Localizable.strings index 8108795e5..3bc439d2b 100644 Binary files a/ownCloud/Resources/gl.lproj/Localizable.strings and b/ownCloud/Resources/gl.lproj/Localizable.strings differ diff --git a/ownCloud/Resources/he.lproj/InfoPlist.strings b/ownCloud/Resources/he.lproj/InfoPlist.strings index 0257cff0e..8a9541062 100644 Binary files a/ownCloud/Resources/he.lproj/InfoPlist.strings and b/ownCloud/Resources/he.lproj/InfoPlist.strings differ diff --git a/ownCloud/Resources/he.lproj/Localizable.strings b/ownCloud/Resources/he.lproj/Localizable.strings index 3714e3e1d..b7e78113f 100644 Binary files a/ownCloud/Resources/he.lproj/Localizable.strings and b/ownCloud/Resources/he.lproj/Localizable.strings differ diff --git a/ownCloud/Resources/id.lproj/InfoPlist.strings b/ownCloud/Resources/id.lproj/InfoPlist.strings index 294b3f69e..6bf9cc11a 100644 Binary files a/ownCloud/Resources/id.lproj/InfoPlist.strings and b/ownCloud/Resources/id.lproj/InfoPlist.strings differ diff --git a/ownCloud/Resources/is.lproj/InfoPlist.strings b/ownCloud/Resources/is.lproj/InfoPlist.strings index 604423c3b..2110238a5 100644 Binary files a/ownCloud/Resources/is.lproj/InfoPlist.strings and b/ownCloud/Resources/is.lproj/InfoPlist.strings differ diff --git a/ownCloud/Resources/is.lproj/Localizable.strings b/ownCloud/Resources/is.lproj/Localizable.strings index b1895e1d4..af14a38c4 100644 Binary files a/ownCloud/Resources/is.lproj/Localizable.strings and b/ownCloud/Resources/is.lproj/Localizable.strings differ diff --git a/ownCloud/Resources/ko.lproj/InfoPlist.strings b/ownCloud/Resources/ko.lproj/InfoPlist.strings index d478ca566..257dc025a 100644 Binary files a/ownCloud/Resources/ko.lproj/InfoPlist.strings and b/ownCloud/Resources/ko.lproj/InfoPlist.strings differ diff --git a/ownCloud/Resources/ko.lproj/Localizable.strings b/ownCloud/Resources/ko.lproj/Localizable.strings index 37de75560..32d205481 100644 Binary files a/ownCloud/Resources/ko.lproj/Localizable.strings and b/ownCloud/Resources/ko.lproj/Localizable.strings differ diff --git a/ownCloud/Resources/mk.lproj/InfoPlist.strings b/ownCloud/Resources/mk.lproj/InfoPlist.strings index d23d8fcd7..310496969 100644 Binary files a/ownCloud/Resources/mk.lproj/InfoPlist.strings and b/ownCloud/Resources/mk.lproj/InfoPlist.strings differ diff --git a/ownCloud/Resources/mk.lproj/Localizable.strings b/ownCloud/Resources/mk.lproj/Localizable.strings index 4dcd92dc9..dae34f3b6 100644 Binary files a/ownCloud/Resources/mk.lproj/Localizable.strings and b/ownCloud/Resources/mk.lproj/Localizable.strings differ diff --git a/ownCloud/Resources/nb-NO.lproj/InfoPlist.strings b/ownCloud/Resources/nb-NO.lproj/InfoPlist.strings index 23adcf156..e08fc761c 100644 Binary files a/ownCloud/Resources/nb-NO.lproj/InfoPlist.strings and b/ownCloud/Resources/nb-NO.lproj/InfoPlist.strings differ diff --git a/ownCloud/Resources/nl.lproj/InfoPlist.strings b/ownCloud/Resources/nl.lproj/InfoPlist.strings index 06efb85ec..7dc9e3a47 100644 Binary files a/ownCloud/Resources/nl.lproj/InfoPlist.strings and b/ownCloud/Resources/nl.lproj/InfoPlist.strings differ diff --git a/ownCloud/Resources/nl.lproj/Localizable.strings b/ownCloud/Resources/nl.lproj/Localizable.strings index 67258a521..0f28e05fe 100644 Binary files a/ownCloud/Resources/nl.lproj/Localizable.strings and b/ownCloud/Resources/nl.lproj/Localizable.strings differ diff --git a/ownCloud/Resources/nn-NO.lproj/InfoPlist.strings b/ownCloud/Resources/nn-NO.lproj/InfoPlist.strings index aadad51cc..41bcc01dc 100644 Binary files a/ownCloud/Resources/nn-NO.lproj/InfoPlist.strings and b/ownCloud/Resources/nn-NO.lproj/InfoPlist.strings differ diff --git a/ownCloud/Resources/pt-BR.lproj/InfoPlist.strings b/ownCloud/Resources/pt-BR.lproj/InfoPlist.strings index 9d54dfa13..47b708027 100644 Binary files a/ownCloud/Resources/pt-BR.lproj/InfoPlist.strings and b/ownCloud/Resources/pt-BR.lproj/InfoPlist.strings differ diff --git a/ownCloud/Resources/pt-BR.lproj/Localizable.strings b/ownCloud/Resources/pt-BR.lproj/Localizable.strings index 420fbb4ed..858b96e46 100644 Binary files a/ownCloud/Resources/pt-BR.lproj/Localizable.strings and b/ownCloud/Resources/pt-BR.lproj/Localizable.strings differ diff --git a/ownCloud/Resources/pt-PT.lproj/InfoPlist.strings b/ownCloud/Resources/pt-PT.lproj/InfoPlist.strings index 32fd6f0c9..932a0f871 100644 Binary files a/ownCloud/Resources/pt-PT.lproj/InfoPlist.strings and b/ownCloud/Resources/pt-PT.lproj/InfoPlist.strings differ diff --git a/ownCloud/Resources/ru.lproj/InfoPlist.strings b/ownCloud/Resources/ru.lproj/InfoPlist.strings index a021513a8..04d32c0d6 100644 Binary files a/ownCloud/Resources/ru.lproj/InfoPlist.strings and b/ownCloud/Resources/ru.lproj/InfoPlist.strings differ diff --git a/ownCloud/Resources/ru.lproj/Localizable.strings b/ownCloud/Resources/ru.lproj/Localizable.strings index 4b3cc3b3e..ed95dde9a 100644 Binary files a/ownCloud/Resources/ru.lproj/Localizable.strings and b/ownCloud/Resources/ru.lproj/Localizable.strings differ diff --git a/ownCloud/Resources/sq.lproj/InfoPlist.strings b/ownCloud/Resources/sq.lproj/InfoPlist.strings index f7508c4e3..13c3d54d3 100644 Binary files a/ownCloud/Resources/sq.lproj/InfoPlist.strings and b/ownCloud/Resources/sq.lproj/InfoPlist.strings differ diff --git a/ownCloud/Resources/sq.lproj/Localizable.strings b/ownCloud/Resources/sq.lproj/Localizable.strings index a2b156765..cb3fcc68a 100644 Binary files a/ownCloud/Resources/sq.lproj/Localizable.strings and b/ownCloud/Resources/sq.lproj/Localizable.strings differ diff --git a/ownCloud/Resources/th-TH.lproj/InfoPlist.strings b/ownCloud/Resources/th-TH.lproj/InfoPlist.strings index 9f50e3a4a..bf930021f 100644 Binary files a/ownCloud/Resources/th-TH.lproj/InfoPlist.strings and b/ownCloud/Resources/th-TH.lproj/InfoPlist.strings differ diff --git a/ownCloud/Resources/th-TH.lproj/Localizable.strings b/ownCloud/Resources/th-TH.lproj/Localizable.strings index 492427e1f..e8fafc7f4 100644 Binary files a/ownCloud/Resources/th-TH.lproj/Localizable.strings and b/ownCloud/Resources/th-TH.lproj/Localizable.strings differ diff --git a/ownCloud/Resources/tr.lproj/InfoPlist.strings b/ownCloud/Resources/tr.lproj/InfoPlist.strings index bc8b585ab..00de75866 100644 Binary files a/ownCloud/Resources/tr.lproj/InfoPlist.strings and b/ownCloud/Resources/tr.lproj/InfoPlist.strings differ diff --git a/ownCloud/Resources/tr.lproj/Localizable.strings b/ownCloud/Resources/tr.lproj/Localizable.strings index 2181c111a..825884800 100644 Binary files a/ownCloud/Resources/tr.lproj/Localizable.strings and b/ownCloud/Resources/tr.lproj/Localizable.strings differ diff --git a/ownCloud/Resources/zh-Hans.lproj/InfoPlist.strings b/ownCloud/Resources/zh-Hans.lproj/InfoPlist.strings index 76e7fe67b..0efdfb133 100644 Binary files a/ownCloud/Resources/zh-Hans.lproj/InfoPlist.strings and b/ownCloud/Resources/zh-Hans.lproj/InfoPlist.strings differ diff --git a/ownCloud/Resources/zh-Hans.lproj/Localizable.strings b/ownCloud/Resources/zh-Hans.lproj/Localizable.strings index 78d8c8f17..e74001cfd 100644 Binary files a/ownCloud/Resources/zh-Hans.lproj/Localizable.strings and b/ownCloud/Resources/zh-Hans.lproj/Localizable.strings differ diff --git a/ownCloud/Resources/zh_TW.lproj/InfoPlist.strings b/ownCloud/Resources/zh_TW.lproj/InfoPlist.strings index c919b9641..f10856a05 100644 Binary files a/ownCloud/Resources/zh_TW.lproj/InfoPlist.strings and b/ownCloud/Resources/zh_TW.lproj/InfoPlist.strings differ diff --git a/ownCloud/Resources/zh_TW.lproj/Localizable.strings b/ownCloud/Resources/zh_TW.lproj/Localizable.strings index 2ddb6c81d..0294e7653 100644 Binary files a/ownCloud/Resources/zh_TW.lproj/Localizable.strings and b/ownCloud/Resources/zh_TW.lproj/Localizable.strings differ diff --git a/ownCloudAppFramework/AppLock Settings/AppLockSettings.h b/ownCloudAppFramework/AppLock Settings/AppLockSettings.h index 941153565..2f12f5b02 100644 --- a/ownCloudAppFramework/AppLock Settings/AppLockSettings.h +++ b/ownCloudAppFramework/AppLock Settings/AppLockSettings.h @@ -44,6 +44,7 @@ NS_ASSUME_NONNULL_BEGIN extern OCClassSettingsIdentifier OCClassSettingsIdentifierPasscode; extern OCClassSettingsKey OCClassSettingsKeyPasscodeEnforced; +extern OCClassSettingsKey OCClassSettingsKeyPasscodeEnforcedByDevice; extern OCClassSettingsKey OCClassSettingsKeyRequiredPasscodeDigits; extern OCClassSettingsKey OCClassSettingsKeyMaximumPasscodeDigits; extern OCClassSettingsKey OCClassSettingsKeyPasscodeLockDelay; diff --git a/ownCloudAppFramework/AppLock Settings/AppLockSettings.m b/ownCloudAppFramework/AppLock Settings/AppLockSettings.m index 25c57542d..bcd3de2f5 100644 --- a/ownCloudAppFramework/AppLock Settings/AppLockSettings.m +++ b/ownCloudAppFramework/AppLock Settings/AppLockSettings.m @@ -18,6 +18,7 @@ #import "AppLockSettings.h" #import "Branding.h" +#import @implementation AppLockSettings @@ -53,6 +54,7 @@ + (OCClassSettingsIdentifier)classSettingsIdentifier { return (@{ OCClassSettingsKeyPasscodeEnforced : @(NO), + OCClassSettingsKeyPasscodeEnforcedByDevice : @(NO), OCClassSettingsKeyRequiredPasscodeDigits : @(4), OCClassSettingsKeyMaximumPasscodeDigits : @(6), OCClassSettingsKeyPasscodeUseBiometricalUnlock : @(NO), @@ -80,7 +82,14 @@ + (OCClassSettingsMetadataCollection)classSettingsMetadata OCClassSettingsMetadataKeyStatus : OCClassSettingsKeyStatusAdvanced, OCClassSettingsMetadataKeyCategory : @"Passcode" }, - + + OCClassSettingsKeyPasscodeEnforcedByDevice : @{ + OCClassSettingsMetadataKeyType : OCClassSettingsMetadataTypeBoolean, + OCClassSettingsMetadataKeyDescription : @"Controls wether the user MUST establish a passcode upon app installation, if NO device passcode protection is set.", + OCClassSettingsMetadataKeyStatus : OCClassSettingsKeyStatusAdvanced, + OCClassSettingsMetadataKeyCategory : @"Passcode" + }, + OCClassSettingsKeyRequiredPasscodeDigits : @{ OCClassSettingsMetadataKeyType : OCClassSettingsMetadataTypeInteger, OCClassSettingsMetadataKeyDescription : @"Controls how many passcode digits are at least required for passcode lock.", @@ -276,8 +285,14 @@ - (NSURL *)biometricalAuthenticationRedirectionTargetURL - (BOOL)isPasscodeEnforced { NSNumber *isPasscodeEnforced = [self classSettingForOCClassSettingsKey:OCClassSettingsKeyPasscodeEnforced]; - - if (isPasscodeEnforced != nil) { + NSNumber *isPasscodeEnforcedByDevice = [self classSettingForOCClassSettingsKey:OCClassSettingsKeyPasscodeEnforcedByDevice]; + + LAContext *context = [[LAContext alloc] init]; + NSError *error = nil; + + if (isPasscodeEnforcedByDevice != nil && isPasscodeEnforcedByDevice.boolValue == YES && [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication error:&error] == NO) { + return (YES); + } else if (isPasscodeEnforced != nil) { return (isPasscodeEnforced.boolValue); } @@ -316,6 +331,7 @@ - (BOOL)lockDelayUserSettable OCClassSettingsIdentifier OCClassSettingsIdentifierPasscode = @"passcode"; OCClassSettingsKey OCClassSettingsKeyPasscodeEnforced = @"enforced"; +OCClassSettingsKey OCClassSettingsKeyPasscodeEnforcedByDevice = @"enforced-by-device"; OCClassSettingsKey OCClassSettingsKeyRequiredPasscodeDigits = @"requiredPasscodeDigits"; OCClassSettingsKey OCClassSettingsKeyMaximumPasscodeDigits = @"maximumPasscodeDigits"; OCClassSettingsKey OCClassSettingsKeyPasscodeLockDelay = @"lockDelay"; diff --git a/ownCloudAppShared/AppLock/PasscodeSetupCoordinator.swift b/ownCloudAppShared/AppLock/PasscodeSetupCoordinator.swift index 6bb511841..b2eb8c21c 100644 --- a/ownCloudAppShared/AppLock/PasscodeSetupCoordinator.swift +++ b/ownCloudAppShared/AppLock/PasscodeSetupCoordinator.swift @@ -192,7 +192,15 @@ public class PasscodeSetupCoordinator { } public func showDigitsCountSelectionUI() { - let alertController = ThemedAlertController(title: "Passcode option".localized, message: "Please choose how many digits you want to use for the passcode lock?".localized, preferredStyle: .actionSheet) + var title = "Passcode option".localized + var message = "Please choose how many digits you want to use for the passcode lock?".localized + + if AppLockSettings.shared.isPasscodeEnforced { + title = "Passcode setup".localized + message = "To use the app, you need to create a passcode.".localized + "\n\n" + message + } + + let alertController = ThemedAlertController(title: title, message: message, preferredStyle: .alert) if let popoverController = alertController.popoverPresentationController { popoverController.sourceView = self.parentViewController.view diff --git a/ownCloudAppShared/Client/Account/Connection/AccountConnection.swift b/ownCloudAppShared/Client/Account/Connection/AccountConnection.swift index 94ab98b30..f981f0db6 100644 --- a/ownCloudAppShared/Client/Account/Connection/AccountConnection.swift +++ b/ownCloudAppShared/Client/Account/Connection/AccountConnection.swift @@ -408,7 +408,7 @@ open class AccountConnection: NSObject { var actionExtensions : [OCExtension] = [] if let core = core { - if let apps = core.appProvider?.apps { + if let appProvider = core.appProvider, appProvider.supportsOpen, let apps = appProvider.apps { for app in apps { // Pre-load app icon if let appIconRequest = app.iconResourceRequest { diff --git a/ownCloudAppShared/Client/Actions/Implementations/OpenInWebAppAction.swift b/ownCloudAppShared/Client/Actions/Implementations/OpenInWebAppAction.swift index 6bd498f96..bb42e5591 100644 --- a/ownCloudAppShared/Client/Actions/Implementations/OpenInWebAppAction.swift +++ b/ownCloudAppShared/Client/Actions/Implementations/OpenInWebAppAction.swift @@ -24,6 +24,7 @@ public extension OCClassSettingsKey { } public enum OpenInWebAppActionMode: String { + case auto = "auto" case defaultBrowser = "default-browser" case inApp = "in-app" case inAppWithDefaultBrowserOption = "in-app-with-default-browser-option" @@ -36,7 +37,7 @@ public class OpenInWebAppAction: Action { _classSettingsRegistered = true self.registerOCClassSettingsDefaults([ - .openInWebAppMode : OpenInWebAppActionMode.inApp.rawValue + .openInWebAppMode : OpenInWebAppActionMode.auto.rawValue ], metadata: [ .openInWebAppMode : [ .type : OCClassSettingsMetadataType.string, @@ -45,6 +46,10 @@ public class OpenInWebAppAction: Action { .status : OCClassSettingsKeyStatus.advanced, .category : "Actions", .possibleValues : [ + [ + OCClassSettingsMetadataKey.value : OpenInWebAppActionMode.auto.rawValue, + OCClassSettingsMetadataKey.description : "Open using `\(OpenInWebAppActionMode.inAppWithDefaultBrowserOption.rawValue)`, unless the respective endpoint is not available - in which case `\(OpenInWebAppActionMode.defaultBrowser.rawValue)` is used instead. If no endpoint to open the document is available, an error message is shown." + ], [ OCClassSettingsMetadataKey.value : OpenInWebAppActionMode.defaultBrowser.rawValue, OCClassSettingsMetadataKey.description : "Open in default browser app. May require user to sign in." @@ -137,12 +142,24 @@ public class OpenInWebAppAction: Action { // MARK: - Action implementation override public func run() { - var openMode : OpenInWebAppActionMode = .inApp + var openMode : OpenInWebAppActionMode = .auto if let openInWebAppMode = classSetting(forOCClassSettingsKey: .openInWebAppMode) as? String, let configuredOpenMode = OpenInWebAppActionMode(rawValue: openInWebAppMode) { openMode = configuredOpenMode } + if let appProvider = context.core?.appProvider, openMode == .auto { + if appProvider.supportsOpenDirect { + if appProvider.supportsOpenInWeb { + openMode = .inAppWithDefaultBrowserOption + } else { + openMode = .inApp + } + } else if appProvider.supportsOpenInWeb { + openMode = .defaultBrowser + } + } + switch openMode { case .defaultBrowser: openInExternalBrowser() @@ -152,6 +169,21 @@ public class OpenInWebAppAction: Action { case .inAppWithDefaultBrowserOption: openInInAppBrowser(withDefaultBrowserOption: true) + + case .auto: + // No open mode available, return error + if let viewController = context.viewController, let item = context.items.first { + let appName = self.app?.name ?? "app" + let itemName = item.name ?? "item" + + let alertController = ThemedAlertController( + with: "Error opening {{itemName}} in {{appName}}".localized(["itemName" : itemName, "appName" : appName]), + message: "Opening documents is not supported by the app provider on this instance.".localized, + okLabel: "OK".localized, + action: nil) + + viewController.present(alertController, animated: true) + } } } @@ -243,7 +275,8 @@ public class OpenInWebAppAction: Action { override public var icon: UIImage? { if let remoteIcon = (app?.iconResourceRequest?.resource as? OCResourceImage)?.image?.image { - return remoteIcon + let iconSize = CGSize(width: 32, height: 32) + return remoteIcon.scaledImageFitting(in: iconSize).paddedTo(width: iconSize.width, height: iconSize.height) } return super.icon diff --git a/ownCloudAppShared/Client/Sharing/ShareViewController.swift b/ownCloudAppShared/Client/Sharing/ShareViewController.swift index 332cc7ee6..08cae769e 100644 --- a/ownCloudAppShared/Client/Sharing/ShareViewController.swift +++ b/ownCloudAppShared/Client/Sharing/ShareViewController.swift @@ -124,6 +124,16 @@ open class ShareViewController: CollectionViewController, SearchViewControllerDe self.mode = mode self.completionHandler = completion + // Item section + if let item = item { + let itemSectionContext = ClientContext(with: clientContext, modifier: { context in + context.permissions = [] + }) + let itemSectionDatasource = OCDataSourceArray(items: [item]) + let itemSection = CollectionViewSection(identifier: "item", dataSource: itemSectionDatasource, cellStyle: .init(with: .header), cellLayout: .list(appearance: .plain, contentInsets: NSDirectionalEdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)), clientContext: itemSectionContext) + sections.append(itemSection) + } + // Managament section cell style let managementCellStyle: CollectionViewCellStyle = .init(with: .tableCell) managementCellStyle.options = [ @@ -220,19 +230,14 @@ open class ShareViewController: CollectionViewController, SearchViewControllerDe collectionView.contentInset = extraContentInset // Set navigation bar title - let itemDisplayName = location?.displayName(in: clientContext) var navigationTitle: String? - if let itemDisplayName { - navigationTitle = "Share {{itemName}}".localized([ "itemName" : itemDisplayName ]) - } else { - switch mode { - case .create: - navigationTitle = "Share".localized + switch mode { + case .create: + navigationTitle = (type == .link) ? "Create link".localized : "Invite".localized - case .edit: - navigationTitle = "Edit".localized - } + case .edit: + navigationTitle = "Edit".localized } navigationItem.titleLabelText = navigationTitle @@ -250,26 +255,11 @@ open class ShareViewController: CollectionViewController, SearchViewControllerDe bottomButtonBarViewController.view = bottomButtonBar // - Add delete button for existing shares - if mode == .edit, let bottomButtonBar { - var deleteButtonConfig = UIButton.Configuration.borderedProminent() - deleteButtonConfig.title = "Unshare".localized - deleteButtonConfig.cornerStyle = .large - deleteButtonConfig.baseBackgroundColor = .systemRed - deleteButtonConfig.baseForegroundColor = .white - - let deleteButton = UIButton() - deleteButton.translatesAutoresizingMaskIntoConstraints = false - deleteButton.configuration = deleteButtonConfig - deleteButton.addAction(UIAction(handler: { [weak self] action in - self?.deleteShare() - }), for: .primaryActionTriggered) - - bottomButtonBar.addSubview(deleteButton) + if mode == .edit { + let unshare = UIBarButtonItem(title: "Unshare".localized, style: .plain, target: self, action: #selector(deleteShare)) + unshare.tintColor = .red - NSLayoutConstraint.activate([ - deleteButton.leadingAnchor.constraint(equalTo: bottomButtonBar.leadingAnchor, constant: 20), - deleteButton.centerYAnchor.constraint(equalTo: bottomButtonBar.selectButton.centerYAnchor) - ]) + self.navigationItem.rightBarButtonItem = unshare } self.addStacked(child: bottomButtonBarViewController, position: .bottom) @@ -560,7 +550,7 @@ open class ShareViewController: CollectionViewController, SearchViewControllerDe details.append(SegmentViewItem(view: button)) } - let content = UniversalItemListCell.Content(with: .text("Password"), iconSymbolName: "key.fill", accessories: accessories) + let content = UniversalItemListCell.Content(with: .text("Password".localized), iconSymbolName: "key.fill", accessories: accessories) content.details = details if passwordOption == nil { @@ -647,7 +637,7 @@ open class ShareViewController: CollectionViewController, SearchViewControllerDe let passwordPrompt = UIAlertController(title: "Enter password".localized, message: nil, preferredStyle: .alert) passwordPrompt.addTextField(configurationHandler: { textField in - textField.placeholder = "password".localized + textField.placeholder = "Password".localized textField.isSecureTextEntry = true }) @@ -748,7 +738,7 @@ open class ShareViewController: CollectionViewController, SearchViewControllerDe } } - func deleteShare() { + @objc func deleteShare() { guard let core = clientContext?.core, let share else { self.showError(NSError(ocError: .internal)) return diff --git a/ownCloudAppShared/Client/Sharing/SharingViewController.swift b/ownCloudAppShared/Client/Sharing/SharingViewController.swift index 96df21297..cd77aad57 100644 --- a/ownCloudAppShared/Client/Sharing/SharingViewController.swift +++ b/ownCloudAppShared/Client/Sharing/SharingViewController.swift @@ -194,7 +194,7 @@ open class SharingViewController: CollectionViewController { if managementClientContext.core?.connection.capabilities?.supportsPrivateLinks == true { linkActions.append( - OCAction(title: "Copy Private Link", icon: OCSymbol.icon(forSymbolName: "list.clipboard"), action: { [weak self] _, _, completion in + OCAction(title: "Copy Private Link".localized, icon: OCSymbol.icon(forSymbolName: "list.clipboard"), action: { [weak self] _, _, completion in if let item = self?.item, let core = self?.clientContext?.core { core.retrievePrivateLink(for: item, completionHandler: { (error, url) in guard let url = url else { return } diff --git a/ownCloudAppShared/Client/User Interface/BottomButtonBar.swift b/ownCloudAppShared/Client/User Interface/BottomButtonBar.swift index 84e3b000a..ab36d4cd5 100644 --- a/ownCloudAppShared/Client/User Interface/BottomButtonBar.swift +++ b/ownCloudAppShared/Client/User Interface/BottomButtonBar.swift @@ -43,6 +43,9 @@ open class BottomButtonBar: ThemeCSSView { } } + open var promptText: String? + open var hasCancelButton: Bool + var activityIndicator: UIActivityIndicatorView? var showActivityIndicatorWhileModalActionRunning = true open var modalActionRunning: Bool = false { @@ -77,6 +80,7 @@ open class BottomButtonBar: ThemeCSSView { public init(prompt: String? = nil, selectButtonTitle: String, cancelButtonTitle: String? = "Cancel".localized, hasCancelButton: Bool, selectAction: UIAction?, cancelAction: UIAction?) { self.selectButtonTitle = selectButtonTitle + self.hasCancelButton = hasCancelButton super.init() @@ -90,8 +94,8 @@ open class BottomButtonBar: ThemeCSSView { promptLabel.translatesAutoresizingMaskIntoConstraints = false bottomSeparatorLine.translatesAutoresizingMaskIntoConstraints = false - var constraints: [NSLayoutConstraint] = [] - var leadingButtonAnchor = selectButton.leadingAnchor + selectButton.setContentCompressionResistancePriority(.required, for: .vertical) + cancelButton.setContentCompressionResistancePriority(.required, for: .vertical) var selectButtonConfig = UIButton.Configuration.borderedProminent() selectButtonConfig.title = selectButtonTitle @@ -111,42 +115,92 @@ open class BottomButtonBar: ThemeCSSView { } addSubview(cancelButton) - - leadingButtonAnchor = cancelButton.leadingAnchor - - constraints.append(contentsOf: [ - cancelButton.trailingAnchor.constraint(equalTo: selectButton.leadingAnchor, constant: -15), - cancelButton.centerYAnchor.constraint(equalTo: selectButton.centerYAnchor) - ]) } + promptText = prompt promptLabel.text = prompt addSubview(selectButton) addSubview(promptLabel) addSubview(bottomSeparatorLine) - constraints.append(contentsOf: [ - promptLabel.leadingAnchor.constraint(equalTo: safeAreaLayoutGuide.leadingAnchor, constant: 20), - promptLabel.trailingAnchor.constraint(lessThanOrEqualTo: leadingButtonAnchor, constant: -20), - promptLabel.centerYAnchor.constraint(equalTo: selectButton.centerYAnchor), + updateLayout() + } - selectButton.trailingAnchor.constraint(equalTo: safeAreaLayoutGuide.trailingAnchor, constant: -20), - selectButton.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor, constant: 20), - selectButton.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor, constant: -20), + var barConstraints: [NSLayoutConstraint]? { + willSet { + if let barConstraints { + NSLayoutConstraint.deactivate(barConstraints) + } + } + didSet { + if let barConstraints { + NSLayoutConstraint.activate(barConstraints) + } + } + } + + func updateLayout() { + var constraints: [NSLayoutConstraint] = [] + let isHorizontalLayout = (traitCollection.horizontalSizeClass == .regular) || (promptText == nil) + if isHorizontalLayout { + let leadingButtonAnchor = hasCancelButton ? cancelButton.leadingAnchor : selectButton.leadingAnchor + + constraints.append(contentsOf: [ + promptLabel.leadingAnchor.constraint(equalTo: safeAreaLayoutGuide.leadingAnchor, constant: 20), + promptLabel.trailingAnchor.constraint(lessThanOrEqualTo: leadingButtonAnchor, constant: -20), + promptLabel.centerYAnchor.constraint(equalTo: selectButton.centerYAnchor), + + selectButton.trailingAnchor.constraint(equalTo: safeAreaLayoutGuide.trailingAnchor, constant: -20), + selectButton.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor, constant: 20), + selectButton.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor, constant: -20) + ]) + } else { + constraints.append(contentsOf: [ + promptLabel.leadingAnchor.constraint(equalTo: safeAreaLayoutGuide.leadingAnchor, constant: 20), + promptLabel.trailingAnchor.constraint(lessThanOrEqualTo: safeAreaLayoutGuide.trailingAnchor, constant: -20), + promptLabel.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor, constant: 10), + + selectButton.leadingAnchor.constraint(equalTo: safeAreaLayoutGuide.leadingAnchor, constant: 20).with(priority: .defaultHigh), + selectButton.trailingAnchor.constraint(equalTo: safeAreaLayoutGuide.trailingAnchor, constant: -20), + selectButton.topAnchor.constraint(equalTo: promptLabel.bottomAnchor, constant: 10), + selectButton.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor, constant: -20) + ]) + + if hasCancelButton { + constraints.append( + cancelButton.leadingAnchor.constraint(equalTo: safeAreaLayoutGuide.leadingAnchor, constant: 20) + ) + } + } + + if hasCancelButton { + constraints.append(contentsOf: [ + cancelButton.trailingAnchor.constraint(equalTo: selectButton.leadingAnchor, constant: -15), + cancelButton.centerYAnchor.constraint(equalTo: selectButton.centerYAnchor) + ]) + } + + constraints.append(contentsOf: [ bottomSeparatorLine.leftAnchor.constraint(equalTo: leftAnchor), bottomSeparatorLine.rightAnchor.constraint(equalTo: rightAnchor), bottomSeparatorLine.topAnchor.constraint(equalTo: topAnchor), bottomSeparatorLine.heightAnchor.constraint(equalToConstant: 1) ]) - NSLayoutConstraint.activate(constraints) + barConstraints = constraints } required public init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + open override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + + updateLayout() + } } extension ThemeCSSSelector { diff --git a/ownCloudAppShared/Client/User Interface/ComposedMessageView.swift b/ownCloudAppShared/Client/User Interface/ComposedMessageView.swift index 398e32c44..b317f70b2 100644 --- a/ownCloudAppShared/Client/User Interface/ComposedMessageView.swift +++ b/ownCloudAppShared/Client/User Interface/ComposedMessageView.swift @@ -600,7 +600,7 @@ public extension ComposedMessageView { } } -extension ThemeCSSSelector { +public extension ThemeCSSSelector { static let infoBox = ThemeCSSSelector(rawValue: "infoBox") static let message = ThemeCSSSelector(rawValue: "message") } diff --git a/ownCloudAppShared/Client/User Interface/RoundCornerBackgroundView.swift b/ownCloudAppShared/Client/User Interface/RoundCornerBackgroundView.swift index 4601b7f82..0c62b439c 100644 --- a/ownCloudAppShared/Client/User Interface/RoundCornerBackgroundView.swift +++ b/ownCloudAppShared/Client/User Interface/RoundCornerBackgroundView.swift @@ -51,6 +51,11 @@ public class RoundCornerBackgroundView: UIView, Themeable { setNeedsDisplay() } } + public var fillImage: UIImage? { + didSet { + setNeedsDisplay() + } + } typealias ThemeColorPicker = (_ theme: Theme, _ collection: ThemeCollection, _ event: ThemeEvent) -> UIColor? @@ -75,6 +80,34 @@ public class RoundCornerBackgroundView: UIView, Themeable { public override func draw(_ rect: CGRect) { let bezierPath = UIBezierPath(roundedRect: bounds, byRoundingCorners: cornerRadius.corners, cornerRadii: cornerRadius.radii) + + if let fillImage { + func sizeThatFills(srcSize: CGSize, dstSize: CGSize) -> CGSize { + var fillSize = srcSize + + fillSize.height = fillSize.height * dstSize.width / fillSize.width + fillSize.width = dstSize.width + + if fillSize.height < dstSize.height { + fillSize.width = fillSize.width * dstSize.height / fillSize.height + fillSize.height = dstSize.height + } + + return fillSize + } + + let bounds = bounds + let fillSize = sizeThatFills(srcSize: fillImage.size, dstSize: bounds.size) + var drawRect: CGRect = CGRect() + + drawRect.origin.x = bounds.origin.x + (bounds.size.width - fillSize.width) / 2.0 + drawRect.origin.y = bounds.origin.y + (bounds.size.height - fillSize.height) / 2.0 + drawRect.size = fillSize + + bezierPath.addClip() + fillImage.draw(in: drawRect) + } + fillColor?.setFill() bezierPath.fill() } diff --git a/ownCloudAppShared/Client/View Controllers/ClientSidebarViewController.swift b/ownCloudAppShared/Client/View Controllers/ClientSidebarViewController.swift index ed61984a4..ce0730e25 100644 --- a/ownCloudAppShared/Client/View Controllers/ClientSidebarViewController.swift +++ b/ownCloudAppShared/Client/View Controllers/ClientSidebarViewController.swift @@ -52,7 +52,7 @@ public class ClientSidebarViewController: CollectionSidebarViewController, Navig super.viewDidLoad() // Set up AccountsControllerSource - accountsControllerSectionSource = OCDataSourceMapped(source: OCBookmarkManager.shared.bookmarksDatasource, creator: { [weak self] (_, bookmarkDataItem) in + accountsControllerSectionSource = OCDataSourceMapped(source: nil, creator: { [weak self] (_, bookmarkDataItem) in if let bookmark = bookmarkDataItem as? OCBookmark, let self = self, let clientContext = self.clientContext { let controller = AccountController(bookmark: bookmark, context: clientContext, configuration: self.controllerConfiguration) @@ -67,6 +67,9 @@ public class ClientSidebarViewController: CollectionSidebarViewController, Navig } }, queue: .main) + accountsControllerSectionSource?.trackItemVersions = true + accountsControllerSectionSource?.source = OCBookmarkManager.shared.bookmarksDatasource + // Combined data source if let accountsControllerSectionSource, let sidebarLinksDataSource = sidebarLinksDataSource { combinedSectionsDatasource = OCDataSourceComposition(sources: [ accountsControllerSectionSource, sidebarLinksDataSource ]) @@ -211,6 +214,7 @@ extension ClientSidebarViewController { let logoImage = UIImage(named: "branding-login-logo") let logoImageView = UIImageView(image: logoImage) logoImageView.cssSelector = .icon + logoImageView.accessibilityLabel = VendorServices.shared.appName logoImageView.contentMode = .scaleAspectFit logoImageView.translatesAutoresizingMaskIntoConstraints = false if let logoImage = logoImage { @@ -228,29 +232,39 @@ extension ClientSidebarViewController { let logoContainer = ThemeCSSView(withSelectors: [.logo]) logoContainer.translatesAutoresizingMaskIntoConstraints = false logoContainer.addSubview(logoImageView) - logoContainer.addSubview(logoLabel) logoContainer.setContentHuggingPriority(.required, for: .horizontal) logoContainer.setContentHuggingPriority(.required, for: .vertical) let logoWrapperView = ThemeCSSView() logoWrapperView.addSubview(logoContainer) - NSLayoutConstraint.activate([ - logoImageView.topAnchor.constraint(greaterThanOrEqualTo: logoContainer.topAnchor), - logoImageView.bottomAnchor.constraint(lessThanOrEqualTo: logoContainer.bottomAnchor), - logoImageView.centerYAnchor.constraint(equalTo: logoContainer.centerYAnchor), - logoLabel.topAnchor.constraint(greaterThanOrEqualTo: logoContainer.topAnchor), - logoLabel.bottomAnchor.constraint(lessThanOrEqualTo: logoContainer.bottomAnchor), - logoLabel.centerYAnchor.constraint(equalTo: logoContainer.centerYAnchor), - - logoImageView.leadingAnchor.constraint(equalTo: logoContainer.leadingAnchor), - logoLabel.leadingAnchor.constraint(equalToSystemSpacingAfter: logoImageView.trailingAnchor, multiplier: 1), - logoLabel.trailingAnchor.constraint(equalTo: logoContainer.trailingAnchor), - - logoContainer.topAnchor.constraint(equalTo: logoWrapperView.topAnchor), - logoContainer.bottomAnchor.constraint(equalTo: logoWrapperView.bottomAnchor), - logoContainer.centerXAnchor.constraint(equalTo: logoWrapperView.centerXAnchor) - ]) + if VendorServices.shared.isBranded { + NSLayoutConstraint.activate([ + logoImageView.topAnchor.constraint(greaterThanOrEqualTo: logoContainer.topAnchor), + logoImageView.bottomAnchor.constraint(lessThanOrEqualTo: logoContainer.bottomAnchor), + logoImageView.centerYAnchor.constraint(equalTo: logoContainer.centerYAnchor), + logoImageView.centerXAnchor.constraint(equalTo: logoContainer.centerXAnchor), + logoContainer.topAnchor.constraint(equalTo: logoWrapperView.topAnchor), + logoContainer.bottomAnchor.constraint(equalTo: logoWrapperView.bottomAnchor), + logoContainer.centerXAnchor.constraint(equalTo: logoWrapperView.centerXAnchor) + ]) + } else { + logoContainer.addSubview(logoLabel) + NSLayoutConstraint.activate([ + logoImageView.topAnchor.constraint(greaterThanOrEqualTo: logoContainer.topAnchor), + logoImageView.bottomAnchor.constraint(lessThanOrEqualTo: logoContainer.bottomAnchor), + logoImageView.centerYAnchor.constraint(equalTo: logoContainer.centerYAnchor), + logoLabel.topAnchor.constraint(greaterThanOrEqualTo: logoContainer.topAnchor), + logoLabel.bottomAnchor.constraint(lessThanOrEqualTo: logoContainer.bottomAnchor), + logoLabel.centerYAnchor.constraint(equalTo: logoContainer.centerYAnchor), + logoImageView.leadingAnchor.constraint(equalTo: logoContainer.leadingAnchor), + logoLabel.leadingAnchor.constraint(equalToSystemSpacingAfter: logoImageView.trailingAnchor, multiplier: 1), + logoLabel.trailingAnchor.constraint(equalTo: logoContainer.trailingAnchor), + logoContainer.topAnchor.constraint(equalTo: logoWrapperView.topAnchor), + logoContainer.bottomAnchor.constraint(equalTo: logoWrapperView.bottomAnchor), + logoContainer.centerXAnchor.constraint(equalTo: logoWrapperView.centerXAnchor) + ]) + } logoWrapperView.addThemeApplier({ (_, collection, _) in if !VendorServices.shared.isBranded, let logoColor = collection.css.getColor(.stroke, for: logoImageView) { diff --git a/ownCloudAppShared/UIKit Extension/UIColor+Extension.swift b/ownCloudAppShared/UIKit Extension/UIColor+Extension.swift index dda5fa3fe..26aceb687 100644 --- a/ownCloudAppShared/UIKit Extension/UIColor+Extension.swift +++ b/ownCloudAppShared/UIKit Extension/UIColor+Extension.swift @@ -28,6 +28,11 @@ extension UIColor { self.init(red: CGFloat(red) / 255.0, green: CGFloat(green) / 255.0, blue: CGFloat(blue) / 255.0, alpha: CGFloat(alpha)) } + public convenience init(hexa unsignedRGBAHex: UInt) { + let rgbaHex = Int(unsignedRGBAHex) + self.init(red: (rgbaHex >> 24) & 0xFF, green: (rgbaHex >> 16) & 0xFF, blue: (rgbaHex >> 8) & 0xFF, alpha: Float(rgbaHex & 0xFF) / 255.0) + } + public convenience init(hex unsignedRGBHex: UInt, alpha: Float = 1.0) { let rgbHex = Int(unsignedRGBHex) self.init(red: (rgbHex >> 16) & 0xFF, green: (rgbHex >> 8) & 0xFF, blue: (rgbHex & 0xFF), alpha: alpha) diff --git a/ownCloudAppShared/User Interface/Theme/CSS/ThemeCSS+SystemColors.swift b/ownCloudAppShared/User Interface/Theme/CSS/ThemeCSS+SystemColors.swift new file mode 100644 index 000000000..768c397cc --- /dev/null +++ b/ownCloudAppShared/User Interface/Theme/CSS/ThemeCSS+SystemColors.swift @@ -0,0 +1,66 @@ +// +// ThemeCSS+SystemColors.swift +// ownCloudAppShared +// +// Created by Felix Schwarz on 26.07.23. +// Copyright © 2023 ownCloud GmbH. All rights reserved. +// + +/* + * Copyright (C) 2023, ownCloud GmbH. + * + * This code is covered by the GNU Public License Version 3. + * + * For distribution utilizing Apple mechanisms please see https://owncloud.org/contribute/iOS-license-exception/ + * You should have received a copy of this license along with this program. If not, see . + * + */ + +import UIKit + +extension ThemeCSSAddress { + func cssComponents() -> ([ThemeCSSSelector]?, ThemeCSSProperty?) { + var components = components(separatedBy: ".") + if components.count >= 2 { + let propertyName = components.removeLast() + let selectors = components.compactMap { (str) in return ThemeCSSSelector(rawValue: str) } + + return (selectors, ThemeCSSProperty(rawValue: propertyName)) + } + + return (nil, nil) + } +} + +extension ThemeCSS { + func add(color: UIColor, address: ThemeCSSAddress) { + let (selectors, property) = address.cssComponents() + + if let selectors, let property { + self.add(record: ThemeCSSRecord(selectors: selectors, property: property, value: color)) + } + } + + func addSystemColors() { + add(color: .systemBlue, address: "os.color.blue") + add(color: .systemBrown, address: "os.color.brown") + add(color: .systemCyan, address: "os.color.cyan") + add(color: .systemGreen, address: "os.color.green") + add(color: .systemIndigo, address: "os.color.indigo") + add(color: .systemMint, address: "os.color.mint") + add(color: .systemOrange, address: "os.color.orange") + add(color: .systemPink, address: "os.color.pink") + add(color: .systemPurple, address: "os.color.purple") + add(color: .systemRed, address: "os.color.red") + add(color: .systemTeal, address: "os.color.teal") + add(color: .systemYellow, address: "os.color.yellow") + add(color: .systemGray, address: "os.color.gray1") + add(color: .systemGray2, address: "os.color.gray2") + add(color: .systemGray3, address: "os.color.gray3") + add(color: .systemGray4, address: "os.color.gray4") + add(color: .systemGray5, address: "os.color.gray5") + add(color: .systemGray6, address: "os.color.gray6") + + add(color: .clear, address: "os.color.clear") // transparent + } +} diff --git a/ownCloudAppShared/User Interface/Theme/CSS/ThemeCSS.swift b/ownCloudAppShared/User Interface/Theme/CSS/ThemeCSS.swift index 0ff61cc7a..aabb1e872 100644 --- a/ownCloudAppShared/User Interface/Theme/CSS/ThemeCSS.swift +++ b/ownCloudAppShared/User Interface/Theme/CSS/ThemeCSS.swift @@ -208,6 +208,8 @@ open class ThemeCSS: NSObject { return get(property, for: selectors) } + private var cssResolveRecursionTrack: [String] = [] + open func getColor(_ property: ThemeCSSProperty, selectors: [ThemeCSSSelector]? = nil, state stateSelectors: [ThemeCSSSelector]? = nil, for object: AnyObject?) -> UIColor? { let value = get(property, selectors: selectors, state: stateSelectors, for: object)?.value @@ -221,6 +223,25 @@ open class ThemeCSS: NSObject { } if let hexColor = string.colorFromHex { return hexColor + } else if string.hasPrefix("$") { + // Allow use of $cssSelectors.cssProperty to use the color resolved + var cssSelectors = string.replacingOccurrences(of: "$", with: "").components(separatedBy: ".") + + if cssResolveRecursionTrack.contains(string) { + Log.error("CSS themeing recursion detected: \(cssResolveRecursionTrack)") + return nil + } + + if cssSelectors.count >= 2 { + let property = ThemeCSSProperty(rawValue: cssSelectors.removeLast()) // use last string component as property + let themeCSSSelectors = cssSelectors.compactMap { cssSelector in return ThemeCSSSelector(rawValue: cssSelector) } // use the rest for CSS selectors + + cssResolveRecursionTrack.append(string) + let color = getColor(property, selectors: themeCSSSelectors, for: nil) + cssResolveRecursionTrack.removeAll(where: { (str) in str == string }) + + return color + } } } diff --git a/ownCloudAppShared/User Interface/Theme/NSObject+ThemeApplication.swift b/ownCloudAppShared/User Interface/Theme/NSObject+ThemeApplication.swift index e30e26bec..8f6a959ff 100644 --- a/ownCloudAppShared/User Interface/Theme/NSObject+ThemeApplication.swift +++ b/ownCloudAppShared/User Interface/Theme/NSObject+ThemeApplication.swift @@ -140,25 +140,7 @@ public extension NSObject { } if let label = self as? UILabel { - var normalColor : UIColor // = collection.tableRowColors.labelColor - var highlightColor : UIColor // = collection.tableRowHighlightColors.labelColor - let disabledColor : UIColor = css.getColor(.stroke, selectors: [.secondary], for: label) ?? .secondaryLabel // = collection.tableRowColors.secondaryLabelColor - - switch itemStyle { - case .message, .bigMessage, .systemSecondary(textStyle: _, weight: _): - normalColor = css.getColor(.stroke, selectors: [.secondary], for: label) ?? .secondaryLabel - highlightColor = css.getColor(.stroke, selectors: [.secondary], state: [.highlighted], for: label) ?? .tertiaryLabel - // normalColor = collection.tableRowColors.secondaryLabelColor - // highlightColor = collection.tableRowHighlightColors.secondaryLabelColor - - default: - // case .title, .bigTitle, .system(textStyle: _, weight: _): - normalColor = css.getColor(.stroke, selectors: [.primary], for: label) ?? .label - highlightColor = css.getColor(.stroke, selectors: [.primary], state: [.highlighted], for: label) ?? .label - // normalColor = collection.tableRowColors.labelColor - // highlightColor = collection.tableRowHighlightColors.labelColor - } - + // Change font for any type of label switch itemStyle { case .bigTitle: label.font = UIFont.boldSystemFont(ofSize: 34) @@ -184,15 +166,33 @@ public extension NSObject { break } - switch itemState { - case .normal: - label.textColor = normalColor + // Adapt color only for non-ThemeCSSLabel + let cssLabel = self as? ThemeCSSLabel + if cssLabel == nil { + var normalColor : UIColor + var highlightColor : UIColor + let disabledColor : UIColor = css.getColor(.stroke, selectors: [.secondary], for: label) ?? .secondaryLabel + + switch itemStyle { + case .message, .bigMessage, .systemSecondary(textStyle: _, weight: _): + normalColor = css.getColor(.stroke, selectors: [.secondary], for: label) ?? .secondaryLabel + highlightColor = css.getColor(.stroke, selectors: [.secondary], state: [.highlighted], for: label) ?? .tertiaryLabel + + default: + normalColor = css.getColor(.stroke, selectors: [.primary], for: label) ?? .label + highlightColor = css.getColor(.stroke, selectors: [.primary], state: [.highlighted], for: label) ?? .label + } + + switch itemState { + case .normal: + label.textColor = normalColor - case .highlighted: - label.textColor = highlightColor + case .highlighted: + label.textColor = highlightColor - case .disabled: - label.textColor = disabledColor + case .disabled: + label.textColor = disabledColor + } } } diff --git a/ownCloudAppShared/User Interface/Theme/ThemeCollection.swift b/ownCloudAppShared/User Interface/Theme/ThemeCollection.swift index a88e99d5b..3cf6a354c 100644 --- a/ownCloudAppShared/User Interface/Theme/ThemeCollection.swift +++ b/ownCloudAppShared/User Interface/Theme/ThemeCollection.swift @@ -390,7 +390,7 @@ public class ThemeCollection : NSObject { contentNavigationBarSet = cellSet contentToolbarSet = cellSet - sidebarCellStateSet = ThemeColorStateSet.from(colorSet: lightBrandSet, for: .light) + sidebarCellStateSet = ThemeColorStateSet.from(colorSet: cellSet, for: interfaceStyle) sidebarCellStateSet.regular.backgroundColor = .secondarySystemBackground.resolvedColor(with: styleTraitCollection) sidebarCellStateSet.selected.labelColor = .white sidebarCellStateSet.selected.iconColor = .white @@ -678,10 +678,13 @@ public class ThemeCollection : NSObject { ThemeCSSRecord(selectors: [.vectorImage, .symbolColor], property: .fill, value: iconSymbolColor), // Welcome screen - ThemeCSSRecord(selectors: [.welcome, .message, .background], property: .fill, value: lightBrandColor), - ThemeCSSRecord(selectors: [.welcome, .message, .title], property: .stroke, value: darkBrandSet.labelColor), - ThemeCSSRecord(selectors: [.welcome, .message, .button], property: .stroke, value: darkBrandSet.labelColor), + ThemeCSSRecord(selectors: [.welcome, .message, .background], property: .fill, value: UIColor(red: 0, green: 0, blue: 0, alpha: 0.2)), +// ThemeCSSRecord(selectors: [.welcome, .message, .title], property: .stroke, value: darkBrandSet.labelColor), +// ThemeCSSRecord(selectors: [.welcome, .message, .button], property: .stroke, value: darkBrandSet.labelColor), + ThemeCSSRecord(selectors: [.welcome, .message, .title], property: .stroke, value: UIColor.white), + ThemeCSSRecord(selectors: [.welcome, .message, .button], property: .stroke, value: UIColor.white), ThemeCSSRecord(selectors: [.welcome, .message, .button], property: .fill, value: darkBrandColor), + ThemeCSSRecord(selectors: [.welcome], property: .statusBarStyle, value: UIStatusBarStyle.lightContent), // Side Bar // - Interface Style @@ -737,6 +740,13 @@ public class ThemeCollection : NSObject { ThemeCSSRecord(selectors: [.content, .toolbar, .locationBar, .segments, .item, .separator], property: .stroke, value: contentToolbarSet.secondaryLabelColor), ThemeCSSRecord(selectors: [.content, .toolbar, .locationBar], property: .fill, value: contentToolbarSet.backgroundColor) ]) + + // System colors + css.addSystemColors() + + // Theme colors + css.add(color: lightBrandColor, address: "theme.color.light") + css.add(color: darkBrandColor, address: "theme.color.dark") } convenience override init() { diff --git a/ownCloudAppShared/User Interface/Theme/ThemeStyle.swift b/ownCloudAppShared/User Interface/Theme/ThemeStyle.swift index 7afd769a5..67548bb2f 100644 --- a/ownCloudAppShared/User Interface/Theme/ThemeStyle.swift +++ b/ownCloudAppShared/User Interface/Theme/ThemeStyle.swift @@ -64,6 +64,18 @@ public extension String { } } + if self.hasPrefix("#"), self.count == 9 { + // Format: #RRGGBBAA + if let hexRGB = UInt(self.replacingOccurrences(of: "#", with: ""), radix: 16) { + return UIColor(hexa: hexRGB) + } + } else if self.count == 8 { + // Format: RRGGBBAA + if let hexRGB = UInt(self, radix: 16) { + return UIColor(hexa: hexRGB) + } + } + return nil } } diff --git a/tools/gomplate/README.md b/tools/gomplate/README.md new file mode 100644 index 000000000..8caf6cd9a --- /dev/null +++ b/tools/gomplate/README.md @@ -0,0 +1,34 @@ +# Create Branding.plist from JSON + +Prerequisite: + +``` +brew install gomplate +``` + +Create `ownCloud/Resources/Theming/Branding.json` + + +``` +{ + "ios_branding.organization-name_text": "Example Cloud", + "ios_branding.profile-bookmark-name_text": "Example Cloud", + "ios_branding.theme-definitions$[0].darkBrandColor_color": "#5BB75B", + "ios_branding.theme-definitions$[0].lightBrandColor_color": "#000000", + "ios_branding.send-feedback-address_text": "mail@example.com", + "ios_branding.profile-url_text": "https://demo.owncloud.com", + "ios_branding.navigation.style_select": "colored", + "ios_branding.profile-help-url_text": "", + "ios_branding.profile-help-button-label_text": "", + "ios_branding.profile-open-help-message_text": "", + "ios_branding.style_select": "light", +} +``` + +Then execute `gomplate` to create your `ownCloud/Resources/Theming/Branding.plist` + +``` +gomplate --file ./tools/gomplate/Branding.plist.tmpl \ +--context config=./ownCloud/Resources/Theming/Branding.json \ +--out ./ownCloud/Resources/Theming/Branding.plist +``` diff --git a/tools/normalizestrings/normalizestrings.sh b/tools/normalizestrings/normalizestrings.sh new file mode 100755 index 000000000..3bf6e4f5f --- /dev/null +++ b/tools/normalizestrings/normalizestrings.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# Build ocstringstool +cd ../../ios-sdk/tools/ocstringstool/ +./build_tool.sh +cd ../normalizestrings +cd ../../../tools/normalizestrings/ +mv ../../ios-sdk/tools/ocstringstool/tool ./ocstringstool +chmod u+x ./ocstringstool + +# Perform normalization +echo "Normalizing…" +./ocstringstool normalize ../../ownCloud/Resources/ + +# Remove ocstringstool build +rm ./ocstringstool + +echo "Done." +