aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorCarl Schwan <carl@carlschwan.eu>2022-09-02 12:05:51 +0200
committerGitHub <noreply@github.com>2022-09-02 12:05:51 +0200
commit2397ea72196de42a79e2a5873dcbb07db698f348 (patch)
treecfe0e7e4f6ec10a665eb0a40eae103bafc49203f /lib
parent046d5451b15d46581cde846676e0b6d631fedbb7 (diff)
parent30be6ad605f3e451c17de3df74ec3832e1758afe (diff)
downloadnextcloud-server-2397ea72196de42a79e2a5873dcbb07db698f348.tar.gz
nextcloud-server-2397ea72196de42a79e2a5873dcbb07db698f348.zip
Merge branch 'master' into fix/setting/accessibility-title
Signed-off-by: Carl Schwan <carl@carlschwan.eu>
Diffstat (limited to 'lib')
-rw-r--r--lib/base.php83
-rw-r--r--lib/composer/composer/autoload_classmap.php49
-rw-r--r--lib/composer/composer/autoload_files.php10
-rw-r--r--lib/composer/composer/autoload_real.php19
-rw-r--r--lib/composer/composer/autoload_static.php53
-rw-r--r--lib/l10n/an.js19
-rw-r--r--lib/l10n/an.json17
-rw-r--r--lib/l10n/bg.js6
-rw-r--r--lib/l10n/bg.json6
-rw-r--r--lib/l10n/ca.js1
-rw-r--r--lib/l10n/ca.json1
-rw-r--r--lib/l10n/cs.js5
-rw-r--r--lib/l10n/cs.json5
-rw-r--r--lib/l10n/da.js2
-rw-r--r--lib/l10n/da.json2
-rw-r--r--lib/l10n/de.js59
-rw-r--r--lib/l10n/de.json59
-rw-r--r--lib/l10n/de_DE.js5
-rw-r--r--lib/l10n/de_DE.json5
-rw-r--r--lib/l10n/el.js3
-rw-r--r--lib/l10n/el.json3
-rw-r--r--lib/l10n/es.js29
-rw-r--r--lib/l10n/es.json29
-rw-r--r--lib/l10n/es_419.js22
-rw-r--r--lib/l10n/es_419.json22
-rw-r--r--lib/l10n/es_AR.js12
-rw-r--r--lib/l10n/es_AR.json12
-rw-r--r--lib/l10n/es_CL.js22
-rw-r--r--lib/l10n/es_CL.json22
-rw-r--r--lib/l10n/es_CO.js22
-rw-r--r--lib/l10n/es_CO.json22
-rw-r--r--lib/l10n/es_CR.js22
-rw-r--r--lib/l10n/es_CR.json22
-rw-r--r--lib/l10n/es_DO.js22
-rw-r--r--lib/l10n/es_DO.json22
-rw-r--r--lib/l10n/es_EC.js22
-rw-r--r--lib/l10n/es_EC.json22
-rw-r--r--lib/l10n/es_GT.js22
-rw-r--r--lib/l10n/es_GT.json22
-rw-r--r--lib/l10n/es_HN.js22
-rw-r--r--lib/l10n/es_HN.json22
-rw-r--r--lib/l10n/es_MX.js22
-rw-r--r--lib/l10n/es_MX.json22
-rw-r--r--lib/l10n/es_NI.js22
-rw-r--r--lib/l10n/es_NI.json22
-rw-r--r--lib/l10n/es_PA.js22
-rw-r--r--lib/l10n/es_PA.json22
-rw-r--r--lib/l10n/es_PE.js22
-rw-r--r--lib/l10n/es_PE.json22
-rw-r--r--lib/l10n/es_PR.js22
-rw-r--r--lib/l10n/es_PR.json22
-rw-r--r--lib/l10n/es_PY.js22
-rw-r--r--lib/l10n/es_PY.json22
-rw-r--r--lib/l10n/es_SV.js22
-rw-r--r--lib/l10n/es_SV.json22
-rw-r--r--lib/l10n/es_UY.js22
-rw-r--r--lib/l10n/es_UY.json22
-rw-r--r--lib/l10n/eu.js6
-rw-r--r--lib/l10n/eu.json6
-rw-r--r--lib/l10n/fa.js1
-rw-r--r--lib/l10n/fa.json1
-rw-r--r--lib/l10n/fi.js8
-rw-r--r--lib/l10n/fi.json8
-rw-r--r--lib/l10n/fr.js28
-rw-r--r--lib/l10n/fr.json28
-rw-r--r--lib/l10n/hr.js2
-rw-r--r--lib/l10n/hr.json2
-rw-r--r--lib/l10n/hu.js4
-rw-r--r--lib/l10n/hu.json4
-rw-r--r--lib/l10n/id.js3
-rw-r--r--lib/l10n/id.json3
-rw-r--r--lib/l10n/is.js1
-rw-r--r--lib/l10n/is.json1
-rw-r--r--lib/l10n/it.js26
-rw-r--r--lib/l10n/it.json26
-rw-r--r--lib/l10n/ja.js4
-rw-r--r--lib/l10n/ja.json4
-rw-r--r--lib/l10n/ko.js1
-rw-r--r--lib/l10n/ko.json1
-rw-r--r--lib/l10n/mk.js2
-rw-r--r--lib/l10n/mk.json2
-rw-r--r--lib/l10n/nb.js16
-rw-r--r--lib/l10n/nb.json16
-rw-r--r--lib/l10n/nl.js2
-rw-r--r--lib/l10n/nl.json2
-rw-r--r--lib/l10n/nn_NO.js7
-rw-r--r--lib/l10n/nn_NO.json7
-rw-r--r--lib/l10n/pl.js7
-rw-r--r--lib/l10n/pl.json7
-rw-r--r--lib/l10n/pt_BR.js28
-rw-r--r--lib/l10n/pt_BR.json28
-rw-r--r--lib/l10n/pt_PT.js24
-rw-r--r--lib/l10n/pt_PT.json24
-rw-r--r--lib/l10n/ro.js3
-rw-r--r--lib/l10n/ro.json3
-rw-r--r--lib/l10n/ru.js8
-rw-r--r--lib/l10n/ru.json8
-rw-r--r--lib/l10n/sc.js2
-rw-r--r--lib/l10n/sc.json2
-rw-r--r--lib/l10n/sk.js4
-rw-r--r--lib/l10n/sk.json4
-rw-r--r--lib/l10n/sl.js3
-rw-r--r--lib/l10n/sl.json3
-rw-r--r--lib/l10n/sv.js2
-rw-r--r--lib/l10n/sv.json2
-rw-r--r--lib/l10n/th.js2
-rw-r--r--lib/l10n/th.json2
-rw-r--r--lib/l10n/tr.js11
-rw-r--r--lib/l10n/tr.json11
-rw-r--r--lib/l10n/uk.js6
-rw-r--r--lib/l10n/uk.json6
-rw-r--r--lib/l10n/zh_CN.js2
-rw-r--r--lib/l10n/zh_CN.json2
-rw-r--r--lib/l10n/zh_HK.js5
-rw-r--r--lib/l10n/zh_HK.json5
-rw-r--r--lib/l10n/zh_TW.js5
-rw-r--r--lib/l10n/zh_TW.json5
-rw-r--r--lib/private/Accounts/AccountManager.php66
-rw-r--r--lib/private/AllConfig.php6
-rw-r--r--lib/private/App/AppStore/Bundles/HubBundle.php4
-rw-r--r--lib/private/App/CompareVersion.php2
-rw-r--r--lib/private/App/Platform.php38
-rw-r--r--lib/private/App/PlatformRepository.php23
-rw-r--r--lib/private/AppFramework/App.php2
-rw-r--r--lib/private/AppFramework/Bootstrap/Coordinator.php2
-rw-r--r--lib/private/AppFramework/Bootstrap/RegistrationContext.php49
-rw-r--r--lib/private/AppFramework/Http/Dispatcher.php4
-rw-r--r--lib/private/AppFramework/Http/Request.php35
-rw-r--r--lib/private/AppFramework/Middleware/MiddlewareDispatcher.php2
-rw-r--r--lib/private/AppFramework/Middleware/Security/BruteForceMiddleware.php14
-rw-r--r--lib/private/AppFramework/Middleware/SessionMiddleware.php4
-rw-r--r--lib/private/Authentication/Listeners/UserDeletedFilesCleanupListener.php2
-rw-r--r--lib/private/Authentication/LoginCredentials/Store.php2
-rw-r--r--lib/private/Authentication/Token/IProvider.php2
-rw-r--r--lib/private/Authentication/Token/PublicKeyTokenProvider.php20
-rw-r--r--lib/private/Authentication/Token/TokenCleanupJob.php41
-rw-r--r--lib/private/Avatar/Avatar.php95
-rw-r--r--lib/private/Avatar/AvatarManager.php25
-rw-r--r--lib/private/Avatar/GuestAvatar.php27
-rw-r--r--lib/private/Avatar/PlaceholderAvatar.php25
-rw-r--r--lib/private/Avatar/UserAvatar.php55
-rw-r--r--lib/private/BackgroundJob/Job.php4
-rw-r--r--lib/private/BackgroundJob/JobList.php117
-rw-r--r--lib/private/BinaryFinder.php71
-rw-r--r--lib/private/Cache/CappedMemoryCache.php1
-rw-r--r--lib/private/Calendar/Manager.php153
-rw-r--r--lib/private/Collaboration/Collaborators/UserPlugin.php2
-rw-r--r--lib/private/Collaboration/Reference/File/FileReferenceEventListener.php61
-rw-r--r--lib/private/Collaboration/Reference/File/FileReferenceProvider.php153
-rw-r--r--lib/private/Collaboration/Reference/LinkReferenceProvider.php162
-rw-r--r--lib/private/Collaboration/Reference/Reference.php163
-rw-r--r--lib/private/Collaboration/Reference/ReferenceManager.php169
-rw-r--r--lib/private/Collaboration/Resources/Listener.php33
-rw-r--r--lib/private/Command/ClosureJob.php5
-rw-r--r--lib/private/Command/CommandJob.php2
-rw-r--r--lib/private/Command/CronBus.php5
-rw-r--r--lib/private/Comments/Comment.php24
-rw-r--r--lib/private/Comments/Manager.php33
-rw-r--r--lib/private/Config.php8
-rw-r--r--lib/private/Console/Application.php6
-rw-r--r--lib/private/Contacts/ContactsMenu/ContactsStore.php4
-rw-r--r--lib/private/ContactsManager.php3
-rw-r--r--lib/private/DB/Connection.php2
-rw-r--r--lib/private/DB/MigrationService.php35
-rw-r--r--lib/private/DB/Migrator.php37
-rw-r--r--lib/private/DB/MigratorExecuteSqlEvent.php55
-rw-r--r--lib/private/DB/QueryBuilder/QueryBuilder.php4
-rw-r--r--lib/private/Dashboard/Manager.php35
-rw-r--r--lib/private/Diagnostics/QueryLogger.php2
-rw-r--r--lib/private/Encryption/File.php29
-rw-r--r--lib/private/Encryption/HookManager.php38
-rw-r--r--lib/private/Encryption/Util.php78
-rw-r--r--lib/private/Federation/CloudFederationProviderManager.php3
-rw-r--r--lib/private/Federation/CloudIdManager.php79
-rw-r--r--lib/private/Files/AppData/AppData.php2
-rw-r--r--lib/private/Files/Cache/Cache.php61
-rw-r--r--lib/private/Files/Cache/CacheEntry.php8
-rw-r--r--lib/private/Files/Cache/CacheQueryBuilder.php14
-rw-r--r--lib/private/Files/Cache/Propagator.php17
-rw-r--r--lib/private/Files/Cache/QuerySearchHelper.php23
-rw-r--r--lib/private/Files/Cache/Scanner.php13
-rw-r--r--lib/private/Files/Cache/SearchBuilder.php10
-rw-r--r--lib/private/Files/Cache/Storage.php4
-rw-r--r--lib/private/Files/Cache/StorageGlobal.php2
-rw-r--r--lib/private/Files/Cache/Updater.php4
-rw-r--r--lib/private/Files/Cache/Wrapper/CacheJail.php22
-rw-r--r--lib/private/Files/Cache/Wrapper/CacheWrapper.php2
-rw-r--r--lib/private/Files/Config/UserMountCache.php11
-rw-r--r--lib/private/Files/FileInfo.php29
-rw-r--r--lib/private/Files/Filesystem.php2
-rw-r--r--lib/private/Files/Mount/Manager.php2
-rw-r--r--lib/private/Files/Mount/MoveableMount.php1
-rw-r--r--lib/private/Files/Node/File.php1
-rw-r--r--lib/private/Files/Node/Folder.php19
-rw-r--r--lib/private/Files/Node/Node.php21
-rw-r--r--lib/private/Files/Node/Root.php4
-rw-r--r--lib/private/Files/ObjectStore/NoopScanner.php2
-rw-r--r--lib/private/Files/ObjectStore/ObjectStoreStorage.php2
-rw-r--r--lib/private/Files/ObjectStore/S3ConnectionTrait.php31
-rw-r--r--lib/private/Files/ObjectStore/S3ObjectTrait.php10
-rw-r--r--lib/private/Files/SetupManager.php43
-rw-r--r--lib/private/Files/SimpleFS/NewSimpleFile.php38
-rw-r--r--lib/private/Files/SimpleFS/SimpleFile.php44
-rw-r--r--lib/private/Files/SimpleFS/SimpleFolder.php13
-rw-r--r--lib/private/Files/Storage/Common.php1
-rw-r--r--lib/private/Files/Storage/Local.php14
-rw-r--r--lib/private/Files/Storage/Wrapper/Encoding.php2
-rw-r--r--lib/private/Files/Storage/Wrapper/Encryption.php98
-rw-r--r--lib/private/Files/Stream/HashWrapper.php6
-rw-r--r--lib/private/Files/Stream/SeekableHttpStream.php13
-rw-r--r--lib/private/Files/View.php11
-rw-r--r--lib/private/HintException.php2
-rw-r--r--lib/private/Http/Client/Client.php2
-rw-r--r--lib/private/Http/Client/LocalAddressChecker.php22
-rw-r--r--lib/private/IntegrityCheck/Checker.php3
-rw-r--r--lib/private/L10N/L10N.php2
-rwxr-xr-xlib/private/LargeFileHelper.php4
-rw-r--r--lib/private/Log.php73
-rw-r--r--lib/private/Log/ExceptionSerializer.php25
-rw-r--r--lib/private/Log/LogDetails.php4
-rw-r--r--lib/private/Log/Rotate.php2
-rw-r--r--lib/private/Mail/EMailTemplate.php2
-rw-r--r--lib/private/Mail/Mailer.php21
-rw-r--r--lib/private/Memcache/Memcached.php30
-rw-r--r--lib/private/MemoryInfo.php2
-rw-r--r--lib/private/Metadata/IMetadataManager.php2
-rw-r--r--lib/private/Metadata/Provider/ExifProvider.php2
-rw-r--r--lib/private/Migration/BackgroundRepair.php45
-rw-r--r--lib/private/NavigationManager.php40
-rw-r--r--lib/private/Notification/Manager.php10
-rw-r--r--lib/private/OCS/DiscoveryService.php2
-rw-r--r--lib/private/Preview/Bitmap.php2
-rw-r--r--lib/private/Preview/Bundled.php2
-rw-r--r--lib/private/Preview/Generator.php27
-rw-r--r--lib/private/Preview/HEIC.php2
-rw-r--r--lib/private/Preview/Image.php2
-rw-r--r--lib/private/Preview/MP3.php2
-rw-r--r--lib/private/Preview/MarkDown.php2
-rw-r--r--lib/private/Preview/Movie.php2
-rw-r--r--lib/private/Preview/Office.php2
-rw-r--r--lib/private/Preview/ProviderV1Adapter.php2
-rw-r--r--lib/private/Preview/SVG.php2
-rw-r--r--lib/private/Preview/TXT.php2
-rw-r--r--lib/private/PreviewManager.php113
-rw-r--r--lib/private/Profile/ProfileManager.php13
-rw-r--r--lib/private/Repair.php72
-rw-r--r--lib/private/Repair/Events/RepairAdvanceEvent.php48
-rw-r--r--lib/private/Repair/Events/RepairErrorEvent.php (renamed from lib/public/BackgroundJob.php)38
-rw-r--r--lib/private/Repair/Events/RepairFinishEvent.php30
-rw-r--r--lib/private/Repair/Events/RepairInfoEvent.php (renamed from lib/private/BackgroundJob/Legacy/RegularJob.php)36
-rw-r--r--lib/private/Repair/Events/RepairStartEvent.php (renamed from lib/private/BackgroundJob/Legacy/QueuedJob.php)42
-rw-r--r--lib/private/Repair/Events/RepairStepEvent.php41
-rw-r--r--lib/private/Repair/Events/RepairWarningEvent.php41
-rw-r--r--lib/private/Repair/NC22/LookupServerSendCheck.php8
-rw-r--r--lib/private/Repair/NC24/AddTokenCleanupJob.php47
-rw-r--r--lib/private/Repair/RemoveLinkShares.php2
-rw-r--r--lib/private/Repair/RepairMimeTypes.php13
-rw-r--r--lib/private/Route/Router.php2
-rw-r--r--lib/private/Security/Bruteforce/Throttler.php16
-rw-r--r--lib/private/Security/CSP/ContentSecurityPolicyNonceManager.php6
-rw-r--r--lib/private/Security/TrustedDomainHelper.php2
-rw-r--r--lib/private/Security/VerificationToken/CleanUpJob.php31
-rw-r--r--lib/private/Server.php97
-rw-r--r--lib/private/Session/CryptoSessionData.php17
-rw-r--r--lib/private/Session/Internal.php25
-rw-r--r--lib/private/Session/Memory.php6
-rw-r--r--lib/private/Settings/Manager.php9
-rw-r--r--lib/private/Setup.php2
-rw-r--r--lib/private/Setup/AbstractDatabase.php4
-rw-r--r--lib/private/Setup/MySQL.php26
-rw-r--r--lib/private/Share/Constants.php2
-rw-r--r--lib/private/Share/Share.php4
-rw-r--r--lib/private/Share20/DefaultShareProvider.php100
-rw-r--r--lib/private/Share20/Manager.php17
-rw-r--r--lib/private/Share20/Share.php29
-rw-r--r--lib/private/Share20/ShareAttributes.php73
-rw-r--r--lib/private/Streamer.php4
-rw-r--r--lib/private/Template/JSConfigHelper.php89
-rw-r--r--lib/private/Template/JSResourceLocator.php4
-rw-r--r--lib/private/TemplateLayout.php4
-rw-r--r--lib/private/URLGenerator.php39
-rw-r--r--lib/private/Updater.php142
-rw-r--r--lib/private/User/Database.php10
-rw-r--r--lib/private/User/DisplayNameCache.php4
-rw-r--r--lib/private/User/LazyUser.php9
-rw-r--r--lib/private/User/Listeners/UserChangedListener.php62
-rw-r--r--lib/private/User/Listeners/UserDeletedListener.php65
-rw-r--r--lib/private/User/Manager.php41
-rw-r--r--lib/private/User/Session.php18
-rw-r--r--lib/private/User/User.php56
-rw-r--r--lib/private/legacy/OC_App.php38
-rw-r--r--lib/private/legacy/OC_Files.php33
-rw-r--r--lib/private/legacy/OC_Helper.php44
-rw-r--r--lib/private/legacy/OC_Image.php38
-rw-r--r--lib/private/legacy/OC_User.php9
-rw-r--r--lib/public/Accounts/IAccountManager.php36
-rw-r--r--lib/public/App.php104
-rw-r--r--lib/public/App/IAppManager.php3
-rw-r--r--lib/public/AppFramework/Bootstrap/IRegistrationContext.php21
-rw-r--r--lib/public/AppFramework/Db/Entity.php3
-rw-r--r--lib/public/AppFramework/Http/JSONResponse.php8
-rw-r--r--lib/public/AppFramework/Http/TemplateResponse.php9
-rw-r--r--lib/public/AppFramework/Http/ZipResponse.php2
-rw-r--r--lib/public/Authentication/IProvideUserSecretBackend.php41
-rw-r--r--lib/public/BackgroundJob/IJob.php21
-rw-r--r--lib/public/BackgroundJob/IJobList.php54
-rw-r--r--lib/public/BackgroundJob/Job.php31
-rw-r--r--lib/public/BackgroundJob/QueuedJob.php15
-rw-r--r--lib/public/BackgroundJob/TimedJob.php20
-rw-r--r--lib/public/Cache/CappedMemoryCache.php160
-rw-r--r--lib/public/Calendar/ICreateFromString.php9
-rw-r--r--lib/public/Calendar/IManager.php14
-rw-r--r--lib/public/Collaboration/Reference/IReference.php130
-rw-r--r--lib/public/Collaboration/Reference/IReferenceManager.php70
-rw-r--r--lib/public/Collaboration/Reference/IReferenceProvider.php63
-rw-r--r--lib/public/Collaboration/Reference/RenderReferenceEvent.php36
-rw-r--r--lib/public/Collaboration/Resources/LoadAdditionalScriptsEvent.php40
-rw-r--r--lib/public/Color.php142
-rw-r--r--lib/public/Comments/IComment.php21
-rw-r--r--lib/public/Comments/ICommentsManager.php11
-rw-r--r--lib/public/Config/BeforePreferenceDeletedEvent.php83
-rw-r--r--lib/public/Config/BeforePreferenceSetEvent.php92
-rw-r--r--lib/public/Contacts/IManager.php2
-rw-r--r--lib/public/DB/QueryBuilder/IQueryBuilder.php2
-rw-r--r--lib/public/Dashboard/IManager.php2
-rw-r--r--lib/public/Federation/Events/TrustedServerRemovedEvent.php48
-rw-r--r--lib/public/Files/Cache/ICache.php2
-rw-r--r--lib/public/Files/Cache/ICacheEntry.php10
-rw-r--r--lib/public/Files/DavUtil.php89
-rw-r--r--lib/public/Files/Events/BeforeDirectFileDownloadEvent.php84
-rw-r--r--lib/public/Files/Events/BeforeZipCreatedEvent.php91
-rw-r--r--lib/public/Files/Mount/ISystemMountPoint.php34
-rw-r--r--lib/public/Files/SimpleFS/ISimpleFile.php22
-rw-r--r--lib/public/Files/SimpleFS/ISimpleFolder.php17
-rw-r--r--lib/public/Files/SimpleFS/ISimpleRoot.php4
-rw-r--r--lib/public/Files/SimpleFS/InMemoryFile.php24
-rw-r--r--lib/public/FullTextSearch/Model/ISearchRequest.php2
-rw-r--r--lib/public/IAddressBook.php4
-rw-r--r--lib/public/IAvatar.php44
-rw-r--r--lib/public/IAvatarManager.php7
-rw-r--r--lib/public/IBinaryFinder.php41
-rw-r--r--lib/public/IDBConnection.php2
-rw-r--r--lib/public/IEventSource.php2
-rw-r--r--lib/public/ISession.php8
-rw-r--r--lib/public/IURLGenerator.php10
-rw-r--r--lib/public/IUser.php6
-rw-r--r--lib/public/IUserManager.php17
-rw-r--r--lib/public/Log/functions.php73
-rw-r--r--lib/public/Migration/IOutput.php6
-rw-r--r--lib/public/Security/Bruteforce/IThrottler.php126
-rw-r--r--lib/public/Share/IAttributes.php68
-rw-r--r--lib/public/Share/IManager.php3
-rw-r--r--lib/public/Share/IShare.php31
-rw-r--r--lib/public/Share/IShareProvider.php3
-rw-r--r--lib/public/User/Backend/ICheckPasswordBackend.php2
-rw-r--r--lib/public/User/Backend/ISetDisplayNameBackend.php3
-rw-r--r--lib/public/User/Events/UserChangedEvent.php10
-rw-r--r--lib/public/UserMigration/ISizeEstimationMigrator.php43
-rw-r--r--lib/public/UserStatus/IUserStatus.php12
-rw-r--r--lib/public/Util.php24
360 files changed, 6233 insertions, 2414 deletions
diff --git a/lib/base.php b/lib/base.php
index 377644c70a2..055cc6786f0 100644
--- a/lib/base.php
+++ b/lib/base.php
@@ -62,9 +62,11 @@
*
*/
+use OC\EventDispatcher\SymfonyAdapter;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Group\Events\UserRemovedEvent;
use OCP\ILogger;
+use OCP\Server;
use OCP\Share;
use OC\Encryption\HookManager;
use OC\Files\Filesystem;
@@ -141,7 +143,7 @@ class OC {
public static function initPaths() {
if (defined('PHPUNIT_CONFIG_DIR')) {
self::$configDir = OC::$SERVERROOT . '/' . PHPUNIT_CONFIG_DIR . '/';
- } elseif (defined('PHPUNIT_RUN') && PHPUNIT_RUN && is_dir(OC::$SERVERROOT . '/tests/config/')) {
+ } elseif (defined('PHPUNIT_RUN') and PHPUNIT_RUN and is_dir(OC::$SERVERROOT . '/tests/config/')) {
self::$configDir = OC::$SERVERROOT . '/tests/config/';
} elseif ($dir = getenv('NEXTCLOUD_CONFIG_DIR')) {
self::$configDir = rtrim($dir, '/') . '/';
@@ -293,6 +295,7 @@ class OC {
if (((bool) $systemConfig->getValue('maintenance', false)) && OC::$SUBURI != '/core/ajax/update.php') {
// send http status 503
http_response_code(503);
+ header('X-Nextcloud-Maintenance-Mode: 1');
header('Retry-After: 120');
// render error page
@@ -442,7 +445,9 @@ class OC {
die();
}
+ //try to set the session lifetime
$sessionLifeTime = self::getSessionLifeTime();
+ @ini_set('gc_maxlifetime', (string)$sessionLifeTime);
// session timeout
if ($session->exists('LAST_ACTIVITY') && (time() - $session->get('LAST_ACTIVITY') > $sessionLifeTime)) {
@@ -452,7 +457,10 @@ class OC {
\OC::$server->getUserSession()->logout();
}
- $session->set('LAST_ACTIVITY', time());
+ if (!self::hasSessionRelaxedExpiry()) {
+ $session->set('LAST_ACTIVITY', time());
+ }
+ $session->close();
}
/**
@@ -463,6 +471,21 @@ class OC {
}
/**
+ * @return bool true if the session expiry should only be done by gc instead of an explicit timeout
+ */
+ public static function hasSessionRelaxedExpiry(): bool {
+ return \OC::$server->getConfig()->getSystemValue('session_relaxed_expiry', false);
+ }
+
+ /**
+ * Try to set some values to the required Nextcloud default
+ */
+ public static function setRequiredIniValues() {
+ @ini_set('default_charset', 'UTF-8');
+ @ini_set('gd.jpeg_ignore_warning', '1');
+ }
+
+ /**
* Send the same site cookies
*/
private static function sendSameSiteCookies() {
@@ -595,12 +618,7 @@ class OC {
$eventLogger = \OC::$server->getEventLogger();
$eventLogger->log('autoloader', 'Autoloader', $loaderStart, $loaderEnd);
- $eventLogger->start('request', 'Full request after autoloading');
- register_shutdown_function(function () use ($eventLogger) {
- $eventLogger->end('request');
- });
$eventLogger->start('boot', 'Initialize');
- $eventLogger->start('runtime', 'Runtime (total - autoloader)');
// Override php.ini and log everything if we're troubleshooting
if (self::$config->getValue('loglevel') === ILogger::DEBUG) {
@@ -615,17 +633,25 @@ class OC {
throw new \RuntimeException('Could not set timezone to UTC');
}
+
//try to configure php to enable big file uploads.
- //this doesn´t work always depending on the web server and php configuration.
- //Let´s try to overwrite some defaults anyway
+ //this doesn´t work always depending on the webserver and php configuration.
+ //Let´s try to overwrite some defaults if they are smaller than 1 hour
+
+ if (intval(@ini_get('max_execution_time') ?? 0) < 3600) {
+ @ini_set('max_execution_time', strval(3600));
+ }
- //try to set the maximum execution time to 60min
+ if (intval(@ini_get('max_input_time') ?? 0) < 3600) {
+ @ini_set('max_input_time', strval(3600));
+ }
+
+ //try to set the maximum execution time to the largest time limit we have
if (strpos(@ini_get('disable_functions'), 'set_time_limit') === false) {
- @set_time_limit(3600);
+ @set_time_limit(max(intval(@ini_get('max_execution_time')), intval(@ini_get('max_input_time'))));
}
- @ini_set('max_execution_time', '3600');
- @ini_set('max_input_time', '3600');
+ self::setRequiredIniValues();
self::handleAuthHeaders();
$systemConfig = \OC::$server->get(\OC\SystemConfig::class);
self::registerAutoloaderCache($systemConfig);
@@ -693,9 +719,6 @@ class OC {
$config->deleteAppValue('core', 'cronErrors');
}
}
- //try to set the session lifetime
- $sessionLifeTime = self::getSessionLifeTime();
- @ini_set('gc_maxlifetime', (string)$sessionLifeTime);
// User and Groups
if (!$systemConfig->getValue("installed", false)) {
@@ -727,6 +750,7 @@ class OC {
self::registerEncryptionWrapperAndHooks();
self::registerAccountHooks();
self::registerResourceCollectionHooks();
+ self::registerFileReferenceEventListener();
self::registerAppRestrictionsHooks();
// Make sure that the application class is not loaded before the database is setup
@@ -797,6 +821,11 @@ class OC {
}
$eventLogger->end('boot');
$eventLogger->log('init', 'OC::init', $loaderStart, microtime(true));
+ $eventLogger->start('runtime', 'Runtime');
+ $eventLogger->start('request', 'Full request after boot');
+ register_shutdown_function(function () use ($eventLogger) {
+ $eventLogger->end('request');
+ });
}
/**
@@ -881,7 +910,11 @@ class OC {
}
private static function registerResourceCollectionHooks() {
- \OC\Collaboration\Resources\Listener::register(\OC::$server->getEventDispatcher());
+ \OC\Collaboration\Resources\Listener::register(Server::get(SymfonyAdapter::class), Server::get(IEventDispatcher::class));
+ }
+
+ private static function registerFileReferenceEventListener() {
+ \OC\Collaboration\Reference\File\FileReferenceEventListener::register(Server::get(IEventDispatcher::class));
}
/**
@@ -1030,6 +1063,22 @@ class OC {
return;
}
+ // Handle requests for JSON or XML
+ $acceptHeader = $request->getHeader('Accept');
+ if (in_array($acceptHeader, ['application/json', 'application/xml'], true)) {
+ http_response_code(404);
+ return;
+ }
+
+ // Handle resources that can't be found
+ // This prevents browsers from redirecting to the default page and then
+ // attempting to parse HTML as CSS and similar.
+ $destinationHeader = $request->getHeader('Sec-Fetch-Dest');
+ if (in_array($destinationHeader, ['font', 'script', 'style'])) {
+ http_response_code(404);
+ return;
+ }
+
// Someone is logged in
if (\OC::$server->getUserSession()->isLoggedIn()) {
OC_App::loadApps();
diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php
index 520d2097918..47ffd81f751 100644
--- a/lib/composer/composer/autoload_classmap.php
+++ b/lib/composer/composer/autoload_classmap.php
@@ -21,7 +21,6 @@ return array(
'OCP\\Activity\\IManager' => $baseDir . '/lib/public/Activity/IManager.php',
'OCP\\Activity\\IProvider' => $baseDir . '/lib/public/Activity/IProvider.php',
'OCP\\Activity\\ISetting' => $baseDir . '/lib/public/Activity/ISetting.php',
- 'OCP\\App' => $baseDir . '/lib/public/App.php',
'OCP\\AppFramework\\ApiController' => $baseDir . '/lib/public/AppFramework/ApiController.php',
'OCP\\AppFramework\\App' => $baseDir . '/lib/public/AppFramework/App.php',
'OCP\\AppFramework\\AuthPublicShareController' => $baseDir . '/lib/public/AppFramework/AuthPublicShareController.php',
@@ -90,6 +89,7 @@ return array(
'OCP\\Authentication\\Exceptions\\PasswordUnavailableException' => $baseDir . '/lib/public/Authentication/Exceptions/PasswordUnavailableException.php',
'OCP\\Authentication\\IAlternativeLogin' => $baseDir . '/lib/public/Authentication/IAlternativeLogin.php',
'OCP\\Authentication\\IApacheBackend' => $baseDir . '/lib/public/Authentication/IApacheBackend.php',
+ 'OCP\\Authentication\\IProvideUserSecretBackend' => $baseDir . '/lib/public/Authentication/IProvideUserSecretBackend.php',
'OCP\\Authentication\\LoginCredentials\\ICredentials' => $baseDir . '/lib/public/Authentication/LoginCredentials/ICredentials.php',
'OCP\\Authentication\\LoginCredentials\\IStore' => $baseDir . '/lib/public/Authentication/LoginCredentials/IStore.php',
'OCP\\Authentication\\TwoFactorAuth\\ALoginSetupController' => $baseDir . '/lib/public/Authentication/TwoFactorAuth/ALoginSetupController.php',
@@ -109,13 +109,13 @@ return array(
'OCP\\Authentication\\TwoFactorAuth\\TwoFactorProviderForUserDisabled' => $baseDir . '/lib/public/Authentication/TwoFactorAuth/TwoFactorProviderForUserDisabled.php',
'OCP\\Authentication\\TwoFactorAuth\\TwoFactorProviderForUserEnabled' => $baseDir . '/lib/public/Authentication/TwoFactorAuth/TwoFactorProviderForUserEnabled.php',
'OCP\\AutoloadNotAllowedException' => $baseDir . '/lib/public/AutoloadNotAllowedException.php',
- 'OCP\\BackgroundJob' => $baseDir . '/lib/public/BackgroundJob.php',
'OCP\\BackgroundJob\\IJob' => $baseDir . '/lib/public/BackgroundJob/IJob.php',
'OCP\\BackgroundJob\\IJobList' => $baseDir . '/lib/public/BackgroundJob/IJobList.php',
'OCP\\BackgroundJob\\Job' => $baseDir . '/lib/public/BackgroundJob/Job.php',
'OCP\\BackgroundJob\\QueuedJob' => $baseDir . '/lib/public/BackgroundJob/QueuedJob.php',
'OCP\\BackgroundJob\\TimedJob' => $baseDir . '/lib/public/BackgroundJob/TimedJob.php',
'OCP\\Broadcast\\Events\\IBroadcastEvent' => $baseDir . '/lib/public/Broadcast/Events/IBroadcastEvent.php',
+ 'OCP\\Cache\\CappedMemoryCache' => $baseDir . '/lib/public/Cache/CappedMemoryCache.php',
'OCP\\Calendar\\BackendTemporarilyUnavailableException' => $baseDir . '/lib/public/Calendar/BackendTemporarilyUnavailableException.php',
'OCP\\Calendar\\Exceptions\\CalendarException' => $baseDir . '/lib/public/Calendar/Exceptions/CalendarException.php',
'OCP\\Calendar\\ICalendar' => $baseDir . '/lib/public/Calendar/ICalendar.php',
@@ -142,13 +142,19 @@ return array(
'OCP\\Collaboration\\Collaborators\\ISearchPlugin' => $baseDir . '/lib/public/Collaboration/Collaborators/ISearchPlugin.php',
'OCP\\Collaboration\\Collaborators\\ISearchResult' => $baseDir . '/lib/public/Collaboration/Collaborators/ISearchResult.php',
'OCP\\Collaboration\\Collaborators\\SearchResultType' => $baseDir . '/lib/public/Collaboration/Collaborators/SearchResultType.php',
+ 'OCP\\Collaboration\\Reference\\IReference' => $baseDir . '/lib/public/Collaboration/Reference/IReference.php',
+ 'OCP\\Collaboration\\Reference\\IReferenceManager' => $baseDir . '/lib/public/Collaboration/Reference/IReferenceManager.php',
+ 'OCP\\Collaboration\\Reference\\IReferenceProvider' => $baseDir . '/lib/public/Collaboration/Reference/IReferenceProvider.php',
+ 'OCP\\Collaboration\\Reference\\RenderReferenceEvent' => $baseDir . '/lib/public/Collaboration/Reference/RenderReferenceEvent.php',
'OCP\\Collaboration\\Resources\\CollectionException' => $baseDir . '/lib/public/Collaboration/Resources/CollectionException.php',
'OCP\\Collaboration\\Resources\\ICollection' => $baseDir . '/lib/public/Collaboration/Resources/ICollection.php',
'OCP\\Collaboration\\Resources\\IManager' => $baseDir . '/lib/public/Collaboration/Resources/IManager.php',
'OCP\\Collaboration\\Resources\\IProvider' => $baseDir . '/lib/public/Collaboration/Resources/IProvider.php',
'OCP\\Collaboration\\Resources\\IProviderManager' => $baseDir . '/lib/public/Collaboration/Resources/IProviderManager.php',
'OCP\\Collaboration\\Resources\\IResource' => $baseDir . '/lib/public/Collaboration/Resources/IResource.php',
+ 'OCP\\Collaboration\\Resources\\LoadAdditionalScriptsEvent' => $baseDir . '/lib/public/Collaboration/Resources/LoadAdditionalScriptsEvent.php',
'OCP\\Collaboration\\Resources\\ResourceException' => $baseDir . '/lib/public/Collaboration/Resources/ResourceException.php',
+ 'OCP\\Color' => $baseDir . '/lib/public/Color.php',
'OCP\\Command\\IBus' => $baseDir . '/lib/public/Command/IBus.php',
'OCP\\Command\\ICommand' => $baseDir . '/lib/public/Command/ICommand.php',
'OCP\\Comments\\CommentsEntityEvent' => $baseDir . '/lib/public/Comments/CommentsEntityEvent.php',
@@ -160,6 +166,8 @@ return array(
'OCP\\Comments\\IllegalIDChangeException' => $baseDir . '/lib/public/Comments/IllegalIDChangeException.php',
'OCP\\Comments\\MessageTooLongException' => $baseDir . '/lib/public/Comments/MessageTooLongException.php',
'OCP\\Comments\\NotFoundException' => $baseDir . '/lib/public/Comments/NotFoundException.php',
+ 'OCP\\Config\\BeforePreferenceDeletedEvent' => $baseDir . '/lib/public/Config/BeforePreferenceDeletedEvent.php',
+ 'OCP\\Config\\BeforePreferenceSetEvent' => $baseDir . '/lib/public/Config/BeforePreferenceSetEvent.php',
'OCP\\Console\\ConsoleEvent' => $baseDir . '/lib/public/Console/ConsoleEvent.php',
'OCP\\Constants' => $baseDir . '/lib/public/Constants.php',
'OCP\\Contacts\\ContactsMenu\\IAction' => $baseDir . '/lib/public/Contacts/ContactsMenu/IAction.php',
@@ -221,6 +229,7 @@ return array(
'OCP\\EventDispatcher\\GenericEvent' => $baseDir . '/lib/public/EventDispatcher/GenericEvent.php',
'OCP\\EventDispatcher\\IEventDispatcher' => $baseDir . '/lib/public/EventDispatcher/IEventDispatcher.php',
'OCP\\EventDispatcher\\IEventListener' => $baseDir . '/lib/public/EventDispatcher/IEventListener.php',
+ 'OCP\\Federation\\Events\\TrustedServerRemovedEvent' => $baseDir . '/lib/public/Federation/Events/TrustedServerRemovedEvent.php',
'OCP\\Federation\\Exceptions\\ActionNotSupportedException' => $baseDir . '/lib/public/Federation/Exceptions/ActionNotSupportedException.php',
'OCP\\Federation\\Exceptions\\AuthenticationFailedException' => $baseDir . '/lib/public/Federation/Exceptions/AuthenticationFailedException.php',
'OCP\\Federation\\Exceptions\\BadRequestException' => $baseDir . '/lib/public/Federation/Exceptions/BadRequestException.php',
@@ -257,10 +266,13 @@ return array(
'OCP\\Files\\Config\\IMountProviderCollection' => $baseDir . '/lib/public/Files/Config/IMountProviderCollection.php',
'OCP\\Files\\Config\\IRootMountProvider' => $baseDir . '/lib/public/Files/Config/IRootMountProvider.php',
'OCP\\Files\\Config\\IUserMountCache' => $baseDir . '/lib/public/Files/Config/IUserMountCache.php',
+ 'OCP\\Files\\DavUtil' => $baseDir . '/lib/public/Files/DavUtil.php',
'OCP\\Files\\EmptyFileNameException' => $baseDir . '/lib/public/Files/EmptyFileNameException.php',
'OCP\\Files\\EntityTooLargeException' => $baseDir . '/lib/public/Files/EntityTooLargeException.php',
+ 'OCP\\Files\\Events\\BeforeDirectFileDownloadEvent' => $baseDir . '/lib/public/Files/Events/BeforeDirectFileDownloadEvent.php',
'OCP\\Files\\Events\\BeforeFileScannedEvent' => $baseDir . '/lib/public/Files/Events/BeforeFileScannedEvent.php',
'OCP\\Files\\Events\\BeforeFolderScannedEvent' => $baseDir . '/lib/public/Files/Events/BeforeFolderScannedEvent.php',
+ 'OCP\\Files\\Events\\BeforeZipCreatedEvent' => $baseDir . '/lib/public/Files/Events/BeforeZipCreatedEvent.php',
'OCP\\Files\\Events\\FileCacheUpdated' => $baseDir . '/lib/public/Files/Events/FileCacheUpdated.php',
'OCP\\Files\\Events\\FileScannedEvent' => $baseDir . '/lib/public/Files/Events/FileScannedEvent.php',
'OCP\\Files\\Events\\FolderScannedEvent' => $baseDir . '/lib/public/Files/Events/FolderScannedEvent.php',
@@ -307,6 +319,7 @@ return array(
'OCP\\Files\\Lock\\OwnerLockedException' => $baseDir . '/lib/public/Files/Lock/OwnerLockedException.php',
'OCP\\Files\\Mount\\IMountManager' => $baseDir . '/lib/public/Files/Mount/IMountManager.php',
'OCP\\Files\\Mount\\IMountPoint' => $baseDir . '/lib/public/Files/Mount/IMountPoint.php',
+ 'OCP\\Files\\Mount\\ISystemMountPoint' => $baseDir . '/lib/public/Files/Mount/ISystemMountPoint.php',
'OCP\\Files\\Node' => $baseDir . '/lib/public/Files/Node.php',
'OCP\\Files\\NotEnoughSpaceException' => $baseDir . '/lib/public/Files/NotEnoughSpaceException.php',
'OCP\\Files\\NotFoundException' => $baseDir . '/lib/public/Files/NotFoundException.php',
@@ -403,6 +416,7 @@ return array(
'OCP\\IAppConfig' => $baseDir . '/lib/public/IAppConfig.php',
'OCP\\IAvatar' => $baseDir . '/lib/public/IAvatar.php',
'OCP\\IAvatarManager' => $baseDir . '/lib/public/IAvatarManager.php',
+ 'OCP\\IBinaryFinder' => $baseDir . '/lib/public/IBinaryFinder.php',
'OCP\\ICache' => $baseDir . '/lib/public/ICache.php',
'OCP\\ICacheFactory' => $baseDir . '/lib/public/ICacheFactory.php',
'OCP\\ICertificate' => $baseDir . '/lib/public/ICertificate.php',
@@ -503,6 +517,7 @@ return array(
'OCP\\Search\\Result' => $baseDir . '/lib/public/Search/Result.php',
'OCP\\Search\\SearchResult' => $baseDir . '/lib/public/Search/SearchResult.php',
'OCP\\Search\\SearchResultEntry' => $baseDir . '/lib/public/Search/SearchResultEntry.php',
+ 'OCP\\Security\\Bruteforce\\IThrottler' => $baseDir . '/lib/public/Security/Bruteforce/IThrottler.php',
'OCP\\Security\\Bruteforce\\MaxDelayReached' => $baseDir . '/lib/public/Security/Bruteforce/MaxDelayReached.php',
'OCP\\Security\\CSP\\AddContentSecurityPolicyEvent' => $baseDir . '/lib/public/Security/CSP/AddContentSecurityPolicyEvent.php',
'OCP\\Security\\Events\\GenerateSecurePasswordEvent' => $baseDir . '/lib/public/Security/Events/GenerateSecurePasswordEvent.php',
@@ -531,6 +546,7 @@ return array(
'OCP\\Share\\Exceptions\\GenericShareException' => $baseDir . '/lib/public/Share/Exceptions/GenericShareException.php',
'OCP\\Share\\Exceptions\\IllegalIDChangeException' => $baseDir . '/lib/public/Share/Exceptions/IllegalIDChangeException.php',
'OCP\\Share\\Exceptions\\ShareNotFound' => $baseDir . '/lib/public/Share/Exceptions/ShareNotFound.php',
+ 'OCP\\Share\\IAttributes' => $baseDir . '/lib/public/Share/IAttributes.php',
'OCP\\Share\\IManager' => $baseDir . '/lib/public/Share/IManager.php',
'OCP\\Share\\IProviderFactory' => $baseDir . '/lib/public/Share/IProviderFactory.php',
'OCP\\Share\\IShare' => $baseDir . '/lib/public/Share/IShare.php',
@@ -566,6 +582,7 @@ return array(
'OCP\\UserMigration\\IExportDestination' => $baseDir . '/lib/public/UserMigration/IExportDestination.php',
'OCP\\UserMigration\\IImportSource' => $baseDir . '/lib/public/UserMigration/IImportSource.php',
'OCP\\UserMigration\\IMigrator' => $baseDir . '/lib/public/UserMigration/IMigrator.php',
+ 'OCP\\UserMigration\\ISizeEstimationMigrator' => $baseDir . '/lib/public/UserMigration/ISizeEstimationMigrator.php',
'OCP\\UserMigration\\TMigratorBasicVersionHandling' => $baseDir . '/lib/public/UserMigration/TMigratorBasicVersionHandling.php',
'OCP\\UserMigration\\UserMigrationException' => $baseDir . '/lib/public/UserMigration/UserMigrationException.php',
'OCP\\UserStatus\\IManager' => $baseDir . '/lib/public/UserStatus/IManager.php',
@@ -770,6 +787,7 @@ return array(
'OC\\Authentication\\Token\\PublicKeyTokenMapper' => $baseDir . '/lib/private/Authentication/Token/PublicKeyTokenMapper.php',
'OC\\Authentication\\Token\\PublicKeyTokenProvider' => $baseDir . '/lib/private/Authentication/Token/PublicKeyTokenProvider.php',
'OC\\Authentication\\Token\\RemoteWipe' => $baseDir . '/lib/private/Authentication/Token/RemoteWipe.php',
+ 'OC\\Authentication\\Token\\TokenCleanupJob' => $baseDir . '/lib/private/Authentication/Token/TokenCleanupJob.php',
'OC\\Authentication\\TwoFactorAuth\\Db\\ProviderUserAssignmentDao' => $baseDir . '/lib/private/Authentication/TwoFactorAuth/Db/ProviderUserAssignmentDao.php',
'OC\\Authentication\\TwoFactorAuth\\EnforcementState' => $baseDir . '/lib/private/Authentication/TwoFactorAuth/EnforcementState.php',
'OC\\Authentication\\TwoFactorAuth\\Manager' => $baseDir . '/lib/private/Authentication/TwoFactorAuth/Manager.php',
@@ -789,10 +807,9 @@ return array(
'OC\\Avatar\\UserAvatar' => $baseDir . '/lib/private/Avatar/UserAvatar.php',
'OC\\BackgroundJob\\Job' => $baseDir . '/lib/private/BackgroundJob/Job.php',
'OC\\BackgroundJob\\JobList' => $baseDir . '/lib/private/BackgroundJob/JobList.php',
- 'OC\\BackgroundJob\\Legacy\\QueuedJob' => $baseDir . '/lib/private/BackgroundJob/Legacy/QueuedJob.php',
- 'OC\\BackgroundJob\\Legacy\\RegularJob' => $baseDir . '/lib/private/BackgroundJob/Legacy/RegularJob.php',
'OC\\BackgroundJob\\QueuedJob' => $baseDir . '/lib/private/BackgroundJob/QueuedJob.php',
'OC\\BackgroundJob\\TimedJob' => $baseDir . '/lib/private/BackgroundJob/TimedJob.php',
+ 'OC\\BinaryFinder' => $baseDir . '/lib/private/BinaryFinder.php',
'OC\\Broadcast\\Events\\BroadcastEvent' => $baseDir . '/lib/private/Broadcast/Events/BroadcastEvent.php',
'OC\\Cache\\CappedMemoryCache' => $baseDir . '/lib/private/Cache/CappedMemoryCache.php',
'OC\\Cache\\File' => $baseDir . '/lib/private/Cache/File.php',
@@ -810,6 +827,11 @@ return array(
'OC\\Collaboration\\Collaborators\\Search' => $baseDir . '/lib/private/Collaboration/Collaborators/Search.php',
'OC\\Collaboration\\Collaborators\\SearchResult' => $baseDir . '/lib/private/Collaboration/Collaborators/SearchResult.php',
'OC\\Collaboration\\Collaborators\\UserPlugin' => $baseDir . '/lib/private/Collaboration/Collaborators/UserPlugin.php',
+ 'OC\\Collaboration\\Reference\\File\\FileReferenceEventListener' => $baseDir . '/lib/private/Collaboration/Reference/File/FileReferenceEventListener.php',
+ 'OC\\Collaboration\\Reference\\File\\FileReferenceProvider' => $baseDir . '/lib/private/Collaboration/Reference/File/FileReferenceProvider.php',
+ 'OC\\Collaboration\\Reference\\LinkReferenceProvider' => $baseDir . '/lib/private/Collaboration/Reference/LinkReferenceProvider.php',
+ 'OC\\Collaboration\\Reference\\Reference' => $baseDir . '/lib/private/Collaboration/Reference/Reference.php',
+ 'OC\\Collaboration\\Reference\\ReferenceManager' => $baseDir . '/lib/private/Collaboration/Reference/ReferenceManager.php',
'OC\\Collaboration\\Resources\\Collection' => $baseDir . '/lib/private/Collaboration/Resources/Collection.php',
'OC\\Collaboration\\Resources\\Listener' => $baseDir . '/lib/private/Collaboration/Resources/Listener.php',
'OC\\Collaboration\\Resources\\Manager' => $baseDir . '/lib/private/Collaboration/Resources/Manager.php',
@@ -854,6 +876,7 @@ return array(
'OC\\Core\\Command\\Background\\Base' => $baseDir . '/core/Command/Background/Base.php',
'OC\\Core\\Command\\Background\\Cron' => $baseDir . '/core/Command/Background/Cron.php',
'OC\\Core\\Command\\Background\\Job' => $baseDir . '/core/Command/Background/Job.php',
+ 'OC\\Core\\Command\\Background\\ListCommand' => $baseDir . '/core/Command/Background/ListCommand.php',
'OC\\Core\\Command\\Background\\WebCron' => $baseDir . '/core/Command/Background/WebCron.php',
'OC\\Core\\Command\\Base' => $baseDir . '/core/Command/Base.php',
'OC\\Core\\Command\\Broadcast\\Test' => $baseDir . '/core/Command/Broadcast/Test.php',
@@ -961,6 +984,8 @@ return array(
'OC\\Core\\Controller\\ProfileApiController' => $baseDir . '/core/Controller/ProfileApiController.php',
'OC\\Core\\Controller\\ProfilePageController' => $baseDir . '/core/Controller/ProfilePageController.php',
'OC\\Core\\Controller\\RecommendedAppsController' => $baseDir . '/core/Controller/RecommendedAppsController.php',
+ 'OC\\Core\\Controller\\ReferenceApiController' => $baseDir . '/core/Controller/ReferenceApiController.php',
+ 'OC\\Core\\Controller\\ReferenceController' => $baseDir . '/core/Controller/ReferenceController.php',
'OC\\Core\\Controller\\SearchController' => $baseDir . '/core/Controller/SearchController.php',
'OC\\Core\\Controller\\SetupController' => $baseDir . '/core/Controller/SetupController.php',
'OC\\Core\\Controller\\TwoFactorChallengeController' => $baseDir . '/core/Controller/TwoFactorChallengeController.php',
@@ -977,6 +1002,8 @@ return array(
'OC\\Core\\Db\\LoginFlowV2Mapper' => $baseDir . '/core/Db/LoginFlowV2Mapper.php',
'OC\\Core\\Db\\ProfileConfig' => $baseDir . '/core/Db/ProfileConfig.php',
'OC\\Core\\Db\\ProfileConfigMapper' => $baseDir . '/core/Db/ProfileConfigMapper.php',
+ 'OC\\Core\\Events\\BeforePasswordResetEvent' => $baseDir . '/core/Events/BeforePasswordResetEvent.php',
+ 'OC\\Core\\Events\\PasswordResetEvent' => $baseDir . '/core/Events/PasswordResetEvent.php',
'OC\\Core\\Exception\\LoginFlowV2NotFoundException' => $baseDir . '/core/Exception/LoginFlowV2NotFoundException.php',
'OC\\Core\\Exception\\ResetPasswordException' => $baseDir . '/core/Exception/ResetPasswordException.php',
'OC\\Core\\Middleware\\TwoFactorMiddleware' => $baseDir . '/core/Middleware/TwoFactorMiddleware.php',
@@ -1029,6 +1056,8 @@ return array(
'OC\\Core\\Migrations\\Version24000Date20220202150027' => $baseDir . '/core/Migrations/Version24000Date20220202150027.php',
'OC\\Core\\Migrations\\Version24000Date20220404230027' => $baseDir . '/core/Migrations/Version24000Date20220404230027.php',
'OC\\Core\\Migrations\\Version24000Date20220425072957' => $baseDir . '/core/Migrations/Version24000Date20220425072957.php',
+ 'OC\\Core\\Migrations\\Version25000Date20220515204012' => $baseDir . '/core/Migrations/Version25000Date20220515204012.php',
+ 'OC\\Core\\Migrations\\Version25000Date20220602190540' => $baseDir . '/core/Migrations/Version25000Date20220602190540.php',
'OC\\Core\\Notification\\CoreNotifier' => $baseDir . '/core/Notification/CoreNotifier.php',
'OC\\Core\\Service\\LoginFlowV2Service' => $baseDir . '/core/Service/LoginFlowV2Service.php',
'OC\\DB\\Adapter' => $baseDir . '/lib/private/DB/Adapter.php',
@@ -1045,6 +1074,7 @@ return array(
'OC\\DB\\MigrationException' => $baseDir . '/lib/private/DB/MigrationException.php',
'OC\\DB\\MigrationService' => $baseDir . '/lib/private/DB/MigrationService.php',
'OC\\DB\\Migrator' => $baseDir . '/lib/private/DB/Migrator.php',
+ 'OC\\DB\\MigratorExecuteSqlEvent' => $baseDir . '/lib/private/DB/MigratorExecuteSqlEvent.php',
'OC\\DB\\MissingColumnInformation' => $baseDir . '/lib/private/DB/MissingColumnInformation.php',
'OC\\DB\\MissingIndexInformation' => $baseDir . '/lib/private/DB/MissingIndexInformation.php',
'OC\\DB\\MissingPrimaryKeyInformation' => $baseDir . '/lib/private/DB/MissingPrimaryKeyInformation.php',
@@ -1399,6 +1429,13 @@ return array(
'OC\\Repair\\ClearFrontendCaches' => $baseDir . '/lib/private/Repair/ClearFrontendCaches.php',
'OC\\Repair\\ClearGeneratedAvatarCache' => $baseDir . '/lib/private/Repair/ClearGeneratedAvatarCache.php',
'OC\\Repair\\Collation' => $baseDir . '/lib/private/Repair/Collation.php',
+ 'OC\\Repair\\Events\\RepairAdvanceEvent' => $baseDir . '/lib/private/Repair/Events/RepairAdvanceEvent.php',
+ 'OC\\Repair\\Events\\RepairErrorEvent' => $baseDir . '/lib/private/Repair/Events/RepairErrorEvent.php',
+ 'OC\\Repair\\Events\\RepairFinishEvent' => $baseDir . '/lib/private/Repair/Events/RepairFinishEvent.php',
+ 'OC\\Repair\\Events\\RepairInfoEvent' => $baseDir . '/lib/private/Repair/Events/RepairInfoEvent.php',
+ 'OC\\Repair\\Events\\RepairStartEvent' => $baseDir . '/lib/private/Repair/Events/RepairStartEvent.php',
+ 'OC\\Repair\\Events\\RepairStepEvent' => $baseDir . '/lib/private/Repair/Events/RepairStepEvent.php',
+ 'OC\\Repair\\Events\\RepairWarningEvent' => $baseDir . '/lib/private/Repair/Events/RepairWarningEvent.php',
'OC\\Repair\\MoveUpdaterStepFile' => $baseDir . '/lib/private/Repair/MoveUpdaterStepFile.php',
'OC\\Repair\\NC11\\FixMountStorages' => $baseDir . '/lib/private/Repair/NC11/FixMountStorages.php',
'OC\\Repair\\NC13\\AddLogRotateJob' => $baseDir . '/lib/private/Repair/NC13/AddLogRotateJob.php',
@@ -1413,6 +1450,7 @@ return array(
'OC\\Repair\\NC21\\AddCheckForUserCertificatesJob' => $baseDir . '/lib/private/Repair/NC21/AddCheckForUserCertificatesJob.php',
'OC\\Repair\\NC21\\ValidatePhoneNumber' => $baseDir . '/lib/private/Repair/NC21/ValidatePhoneNumber.php',
'OC\\Repair\\NC22\\LookupServerSendCheck' => $baseDir . '/lib/private/Repair/NC22/LookupServerSendCheck.php',
+ 'OC\\Repair\\NC24\\AddTokenCleanupJob' => $baseDir . '/lib/private/Repair/NC24/AddTokenCleanupJob.php',
'OC\\Repair\\OldGroupMembershipShares' => $baseDir . '/lib/private/Repair/OldGroupMembershipShares.php',
'OC\\Repair\\Owncloud\\CleanPreviews' => $baseDir . '/lib/private/Repair/Owncloud/CleanPreviews.php',
'OC\\Repair\\Owncloud\\CleanPreviewsBackgroundJob' => $baseDir . '/lib/private/Repair/Owncloud/CleanPreviewsBackgroundJob.php',
@@ -1498,6 +1536,7 @@ return array(
'OC\\Share20\\Manager' => $baseDir . '/lib/private/Share20/Manager.php',
'OC\\Share20\\ProviderFactory' => $baseDir . '/lib/private/Share20/ProviderFactory.php',
'OC\\Share20\\Share' => $baseDir . '/lib/private/Share20/Share.php',
+ 'OC\\Share20\\ShareAttributes' => $baseDir . '/lib/private/Share20/ShareAttributes.php',
'OC\\Share20\\ShareHelper' => $baseDir . '/lib/private/Share20/ShareHelper.php',
'OC\\Share20\\UserRemovedListener' => $baseDir . '/lib/private/Share20/UserRemovedListener.php',
'OC\\Share\\Constants' => $baseDir . '/lib/private/Share/Constants.php',
@@ -1541,6 +1580,8 @@ return array(
'OC\\User\\Database' => $baseDir . '/lib/private/User/Database.php',
'OC\\User\\DisplayNameCache' => $baseDir . '/lib/private/User/DisplayNameCache.php',
'OC\\User\\LazyUser' => $baseDir . '/lib/private/User/LazyUser.php',
+ 'OC\\User\\Listeners\\UserChangedListener' => $baseDir . '/lib/private/User/Listeners/UserChangedListener.php',
+ 'OC\\User\\Listeners\\UserDeletedListener' => $baseDir . '/lib/private/User/Listeners/UserDeletedListener.php',
'OC\\User\\LoginException' => $baseDir . '/lib/private/User/LoginException.php',
'OC\\User\\Manager' => $baseDir . '/lib/private/User/Manager.php',
'OC\\User\\NoUserException' => $baseDir . '/lib/private/User/NoUserException.php',
diff --git a/lib/composer/composer/autoload_files.php b/lib/composer/composer/autoload_files.php
new file mode 100644
index 00000000000..16e661d85de
--- /dev/null
+++ b/lib/composer/composer/autoload_files.php
@@ -0,0 +1,10 @@
+<?php
+
+// autoload_files.php @generated by Composer
+
+$vendorDir = dirname(__DIR__);
+$baseDir = dirname(dirname($vendorDir));
+
+return array(
+ '03ae51fe9694f2f597f918142c49ff7a' => $baseDir . '/lib/public/Log/functions.php',
+);
diff --git a/lib/composer/composer/autoload_real.php b/lib/composer/composer/autoload_real.php
index 27e0b34bf5b..6b98041770d 100644
--- a/lib/composer/composer/autoload_real.php
+++ b/lib/composer/composer/autoload_real.php
@@ -31,6 +31,25 @@ class ComposerAutoloaderInit749170dad3f5e7f9ca158f5a9f04f6a2
$loader->register(true);
+ $includeFiles = \Composer\Autoload\ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2::$files;
+ foreach ($includeFiles as $fileIdentifier => $file) {
+ composerRequire749170dad3f5e7f9ca158f5a9f04f6a2($fileIdentifier, $file);
+ }
+
return $loader;
}
}
+
+/**
+ * @param string $fileIdentifier
+ * @param string $file
+ * @return void
+ */
+function composerRequire749170dad3f5e7f9ca158f5a9f04f6a2($fileIdentifier, $file)
+{
+ if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
+ $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
+
+ require $file;
+ }
+}
diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php
index 294969211ec..a84bab472a5 100644
--- a/lib/composer/composer/autoload_static.php
+++ b/lib/composer/composer/autoload_static.php
@@ -6,6 +6,10 @@ namespace Composer\Autoload;
class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
{
+ public static $files = array (
+ '03ae51fe9694f2f597f918142c49ff7a' => __DIR__ . '/../../..' . '/lib/public/Log/functions.php',
+ );
+
public static $prefixLengthsPsr4 = array (
'O' =>
array (
@@ -50,7 +54,6 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\Activity\\IManager' => __DIR__ . '/../../..' . '/lib/public/Activity/IManager.php',
'OCP\\Activity\\IProvider' => __DIR__ . '/../../..' . '/lib/public/Activity/IProvider.php',
'OCP\\Activity\\ISetting' => __DIR__ . '/../../..' . '/lib/public/Activity/ISetting.php',
- 'OCP\\App' => __DIR__ . '/../../..' . '/lib/public/App.php',
'OCP\\AppFramework\\ApiController' => __DIR__ . '/../../..' . '/lib/public/AppFramework/ApiController.php',
'OCP\\AppFramework\\App' => __DIR__ . '/../../..' . '/lib/public/AppFramework/App.php',
'OCP\\AppFramework\\AuthPublicShareController' => __DIR__ . '/../../..' . '/lib/public/AppFramework/AuthPublicShareController.php',
@@ -119,6 +122,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\Authentication\\Exceptions\\PasswordUnavailableException' => __DIR__ . '/../../..' . '/lib/public/Authentication/Exceptions/PasswordUnavailableException.php',
'OCP\\Authentication\\IAlternativeLogin' => __DIR__ . '/../../..' . '/lib/public/Authentication/IAlternativeLogin.php',
'OCP\\Authentication\\IApacheBackend' => __DIR__ . '/../../..' . '/lib/public/Authentication/IApacheBackend.php',
+ 'OCP\\Authentication\\IProvideUserSecretBackend' => __DIR__ . '/../../..' . '/lib/public/Authentication/IProvideUserSecretBackend.php',
'OCP\\Authentication\\LoginCredentials\\ICredentials' => __DIR__ . '/../../..' . '/lib/public/Authentication/LoginCredentials/ICredentials.php',
'OCP\\Authentication\\LoginCredentials\\IStore' => __DIR__ . '/../../..' . '/lib/public/Authentication/LoginCredentials/IStore.php',
'OCP\\Authentication\\TwoFactorAuth\\ALoginSetupController' => __DIR__ . '/../../..' . '/lib/public/Authentication/TwoFactorAuth/ALoginSetupController.php',
@@ -138,13 +142,13 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\Authentication\\TwoFactorAuth\\TwoFactorProviderForUserDisabled' => __DIR__ . '/../../..' . '/lib/public/Authentication/TwoFactorAuth/TwoFactorProviderForUserDisabled.php',
'OCP\\Authentication\\TwoFactorAuth\\TwoFactorProviderForUserEnabled' => __DIR__ . '/../../..' . '/lib/public/Authentication/TwoFactorAuth/TwoFactorProviderForUserEnabled.php',
'OCP\\AutoloadNotAllowedException' => __DIR__ . '/../../..' . '/lib/public/AutoloadNotAllowedException.php',
- 'OCP\\BackgroundJob' => __DIR__ . '/../../..' . '/lib/public/BackgroundJob.php',
'OCP\\BackgroundJob\\IJob' => __DIR__ . '/../../..' . '/lib/public/BackgroundJob/IJob.php',
'OCP\\BackgroundJob\\IJobList' => __DIR__ . '/../../..' . '/lib/public/BackgroundJob/IJobList.php',
'OCP\\BackgroundJob\\Job' => __DIR__ . '/../../..' . '/lib/public/BackgroundJob/Job.php',
'OCP\\BackgroundJob\\QueuedJob' => __DIR__ . '/../../..' . '/lib/public/BackgroundJob/QueuedJob.php',
'OCP\\BackgroundJob\\TimedJob' => __DIR__ . '/../../..' . '/lib/public/BackgroundJob/TimedJob.php',
'OCP\\Broadcast\\Events\\IBroadcastEvent' => __DIR__ . '/../../..' . '/lib/public/Broadcast/Events/IBroadcastEvent.php',
+ 'OCP\\Cache\\CappedMemoryCache' => __DIR__ . '/../../..' . '/lib/public/Cache/CappedMemoryCache.php',
'OCP\\Calendar\\BackendTemporarilyUnavailableException' => __DIR__ . '/../../..' . '/lib/public/Calendar/BackendTemporarilyUnavailableException.php',
'OCP\\Calendar\\Exceptions\\CalendarException' => __DIR__ . '/../../..' . '/lib/public/Calendar/Exceptions/CalendarException.php',
'OCP\\Calendar\\ICalendar' => __DIR__ . '/../../..' . '/lib/public/Calendar/ICalendar.php',
@@ -171,13 +175,19 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\Collaboration\\Collaborators\\ISearchPlugin' => __DIR__ . '/../../..' . '/lib/public/Collaboration/Collaborators/ISearchPlugin.php',
'OCP\\Collaboration\\Collaborators\\ISearchResult' => __DIR__ . '/../../..' . '/lib/public/Collaboration/Collaborators/ISearchResult.php',
'OCP\\Collaboration\\Collaborators\\SearchResultType' => __DIR__ . '/../../..' . '/lib/public/Collaboration/Collaborators/SearchResultType.php',
+ 'OCP\\Collaboration\\Reference\\IReference' => __DIR__ . '/../../..' . '/lib/public/Collaboration/Reference/IReference.php',
+ 'OCP\\Collaboration\\Reference\\IReferenceManager' => __DIR__ . '/../../..' . '/lib/public/Collaboration/Reference/IReferenceManager.php',
+ 'OCP\\Collaboration\\Reference\\IReferenceProvider' => __DIR__ . '/../../..' . '/lib/public/Collaboration/Reference/IReferenceProvider.php',
+ 'OCP\\Collaboration\\Reference\\RenderReferenceEvent' => __DIR__ . '/../../..' . '/lib/public/Collaboration/Reference/RenderReferenceEvent.php',
'OCP\\Collaboration\\Resources\\CollectionException' => __DIR__ . '/../../..' . '/lib/public/Collaboration/Resources/CollectionException.php',
'OCP\\Collaboration\\Resources\\ICollection' => __DIR__ . '/../../..' . '/lib/public/Collaboration/Resources/ICollection.php',
'OCP\\Collaboration\\Resources\\IManager' => __DIR__ . '/../../..' . '/lib/public/Collaboration/Resources/IManager.php',
'OCP\\Collaboration\\Resources\\IProvider' => __DIR__ . '/../../..' . '/lib/public/Collaboration/Resources/IProvider.php',
'OCP\\Collaboration\\Resources\\IProviderManager' => __DIR__ . '/../../..' . '/lib/public/Collaboration/Resources/IProviderManager.php',
'OCP\\Collaboration\\Resources\\IResource' => __DIR__ . '/../../..' . '/lib/public/Collaboration/Resources/IResource.php',
+ 'OCP\\Collaboration\\Resources\\LoadAdditionalScriptsEvent' => __DIR__ . '/../../..' . '/lib/public/Collaboration/Resources/LoadAdditionalScriptsEvent.php',
'OCP\\Collaboration\\Resources\\ResourceException' => __DIR__ . '/../../..' . '/lib/public/Collaboration/Resources/ResourceException.php',
+ 'OCP\\Color' => __DIR__ . '/../../..' . '/lib/public/Color.php',
'OCP\\Command\\IBus' => __DIR__ . '/../../..' . '/lib/public/Command/IBus.php',
'OCP\\Command\\ICommand' => __DIR__ . '/../../..' . '/lib/public/Command/ICommand.php',
'OCP\\Comments\\CommentsEntityEvent' => __DIR__ . '/../../..' . '/lib/public/Comments/CommentsEntityEvent.php',
@@ -189,6 +199,8 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\Comments\\IllegalIDChangeException' => __DIR__ . '/../../..' . '/lib/public/Comments/IllegalIDChangeException.php',
'OCP\\Comments\\MessageTooLongException' => __DIR__ . '/../../..' . '/lib/public/Comments/MessageTooLongException.php',
'OCP\\Comments\\NotFoundException' => __DIR__ . '/../../..' . '/lib/public/Comments/NotFoundException.php',
+ 'OCP\\Config\\BeforePreferenceDeletedEvent' => __DIR__ . '/../../..' . '/lib/public/Config/BeforePreferenceDeletedEvent.php',
+ 'OCP\\Config\\BeforePreferenceSetEvent' => __DIR__ . '/../../..' . '/lib/public/Config/BeforePreferenceSetEvent.php',
'OCP\\Console\\ConsoleEvent' => __DIR__ . '/../../..' . '/lib/public/Console/ConsoleEvent.php',
'OCP\\Constants' => __DIR__ . '/../../..' . '/lib/public/Constants.php',
'OCP\\Contacts\\ContactsMenu\\IAction' => __DIR__ . '/../../..' . '/lib/public/Contacts/ContactsMenu/IAction.php',
@@ -250,6 +262,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\EventDispatcher\\GenericEvent' => __DIR__ . '/../../..' . '/lib/public/EventDispatcher/GenericEvent.php',
'OCP\\EventDispatcher\\IEventDispatcher' => __DIR__ . '/../../..' . '/lib/public/EventDispatcher/IEventDispatcher.php',
'OCP\\EventDispatcher\\IEventListener' => __DIR__ . '/../../..' . '/lib/public/EventDispatcher/IEventListener.php',
+ 'OCP\\Federation\\Events\\TrustedServerRemovedEvent' => __DIR__ . '/../../..' . '/lib/public/Federation/Events/TrustedServerRemovedEvent.php',
'OCP\\Federation\\Exceptions\\ActionNotSupportedException' => __DIR__ . '/../../..' . '/lib/public/Federation/Exceptions/ActionNotSupportedException.php',
'OCP\\Federation\\Exceptions\\AuthenticationFailedException' => __DIR__ . '/../../..' . '/lib/public/Federation/Exceptions/AuthenticationFailedException.php',
'OCP\\Federation\\Exceptions\\BadRequestException' => __DIR__ . '/../../..' . '/lib/public/Federation/Exceptions/BadRequestException.php',
@@ -286,10 +299,13 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\Files\\Config\\IMountProviderCollection' => __DIR__ . '/../../..' . '/lib/public/Files/Config/IMountProviderCollection.php',
'OCP\\Files\\Config\\IRootMountProvider' => __DIR__ . '/../../..' . '/lib/public/Files/Config/IRootMountProvider.php',
'OCP\\Files\\Config\\IUserMountCache' => __DIR__ . '/../../..' . '/lib/public/Files/Config/IUserMountCache.php',
+ 'OCP\\Files\\DavUtil' => __DIR__ . '/../../..' . '/lib/public/Files/DavUtil.php',
'OCP\\Files\\EmptyFileNameException' => __DIR__ . '/../../..' . '/lib/public/Files/EmptyFileNameException.php',
'OCP\\Files\\EntityTooLargeException' => __DIR__ . '/../../..' . '/lib/public/Files/EntityTooLargeException.php',
+ 'OCP\\Files\\Events\\BeforeDirectFileDownloadEvent' => __DIR__ . '/../../..' . '/lib/public/Files/Events/BeforeDirectFileDownloadEvent.php',
'OCP\\Files\\Events\\BeforeFileScannedEvent' => __DIR__ . '/../../..' . '/lib/public/Files/Events/BeforeFileScannedEvent.php',
'OCP\\Files\\Events\\BeforeFolderScannedEvent' => __DIR__ . '/../../..' . '/lib/public/Files/Events/BeforeFolderScannedEvent.php',
+ 'OCP\\Files\\Events\\BeforeZipCreatedEvent' => __DIR__ . '/../../..' . '/lib/public/Files/Events/BeforeZipCreatedEvent.php',
'OCP\\Files\\Events\\FileCacheUpdated' => __DIR__ . '/../../..' . '/lib/public/Files/Events/FileCacheUpdated.php',
'OCP\\Files\\Events\\FileScannedEvent' => __DIR__ . '/../../..' . '/lib/public/Files/Events/FileScannedEvent.php',
'OCP\\Files\\Events\\FolderScannedEvent' => __DIR__ . '/../../..' . '/lib/public/Files/Events/FolderScannedEvent.php',
@@ -336,6 +352,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\Files\\Lock\\OwnerLockedException' => __DIR__ . '/../../..' . '/lib/public/Files/Lock/OwnerLockedException.php',
'OCP\\Files\\Mount\\IMountManager' => __DIR__ . '/../../..' . '/lib/public/Files/Mount/IMountManager.php',
'OCP\\Files\\Mount\\IMountPoint' => __DIR__ . '/../../..' . '/lib/public/Files/Mount/IMountPoint.php',
+ 'OCP\\Files\\Mount\\ISystemMountPoint' => __DIR__ . '/../../..' . '/lib/public/Files/Mount/ISystemMountPoint.php',
'OCP\\Files\\Node' => __DIR__ . '/../../..' . '/lib/public/Files/Node.php',
'OCP\\Files\\NotEnoughSpaceException' => __DIR__ . '/../../..' . '/lib/public/Files/NotEnoughSpaceException.php',
'OCP\\Files\\NotFoundException' => __DIR__ . '/../../..' . '/lib/public/Files/NotFoundException.php',
@@ -432,6 +449,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\IAppConfig' => __DIR__ . '/../../..' . '/lib/public/IAppConfig.php',
'OCP\\IAvatar' => __DIR__ . '/../../..' . '/lib/public/IAvatar.php',
'OCP\\IAvatarManager' => __DIR__ . '/../../..' . '/lib/public/IAvatarManager.php',
+ 'OCP\\IBinaryFinder' => __DIR__ . '/../../..' . '/lib/public/IBinaryFinder.php',
'OCP\\ICache' => __DIR__ . '/../../..' . '/lib/public/ICache.php',
'OCP\\ICacheFactory' => __DIR__ . '/../../..' . '/lib/public/ICacheFactory.php',
'OCP\\ICertificate' => __DIR__ . '/../../..' . '/lib/public/ICertificate.php',
@@ -532,6 +550,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\Search\\Result' => __DIR__ . '/../../..' . '/lib/public/Search/Result.php',
'OCP\\Search\\SearchResult' => __DIR__ . '/../../..' . '/lib/public/Search/SearchResult.php',
'OCP\\Search\\SearchResultEntry' => __DIR__ . '/../../..' . '/lib/public/Search/SearchResultEntry.php',
+ 'OCP\\Security\\Bruteforce\\IThrottler' => __DIR__ . '/../../..' . '/lib/public/Security/Bruteforce/IThrottler.php',
'OCP\\Security\\Bruteforce\\MaxDelayReached' => __DIR__ . '/../../..' . '/lib/public/Security/Bruteforce/MaxDelayReached.php',
'OCP\\Security\\CSP\\AddContentSecurityPolicyEvent' => __DIR__ . '/../../..' . '/lib/public/Security/CSP/AddContentSecurityPolicyEvent.php',
'OCP\\Security\\Events\\GenerateSecurePasswordEvent' => __DIR__ . '/../../..' . '/lib/public/Security/Events/GenerateSecurePasswordEvent.php',
@@ -560,6 +579,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\Share\\Exceptions\\GenericShareException' => __DIR__ . '/../../..' . '/lib/public/Share/Exceptions/GenericShareException.php',
'OCP\\Share\\Exceptions\\IllegalIDChangeException' => __DIR__ . '/../../..' . '/lib/public/Share/Exceptions/IllegalIDChangeException.php',
'OCP\\Share\\Exceptions\\ShareNotFound' => __DIR__ . '/../../..' . '/lib/public/Share/Exceptions/ShareNotFound.php',
+ 'OCP\\Share\\IAttributes' => __DIR__ . '/../../..' . '/lib/public/Share/IAttributes.php',
'OCP\\Share\\IManager' => __DIR__ . '/../../..' . '/lib/public/Share/IManager.php',
'OCP\\Share\\IProviderFactory' => __DIR__ . '/../../..' . '/lib/public/Share/IProviderFactory.php',
'OCP\\Share\\IShare' => __DIR__ . '/../../..' . '/lib/public/Share/IShare.php',
@@ -595,6 +615,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\UserMigration\\IExportDestination' => __DIR__ . '/../../..' . '/lib/public/UserMigration/IExportDestination.php',
'OCP\\UserMigration\\IImportSource' => __DIR__ . '/../../..' . '/lib/public/UserMigration/IImportSource.php',
'OCP\\UserMigration\\IMigrator' => __DIR__ . '/../../..' . '/lib/public/UserMigration/IMigrator.php',
+ 'OCP\\UserMigration\\ISizeEstimationMigrator' => __DIR__ . '/../../..' . '/lib/public/UserMigration/ISizeEstimationMigrator.php',
'OCP\\UserMigration\\TMigratorBasicVersionHandling' => __DIR__ . '/../../..' . '/lib/public/UserMigration/TMigratorBasicVersionHandling.php',
'OCP\\UserMigration\\UserMigrationException' => __DIR__ . '/../../..' . '/lib/public/UserMigration/UserMigrationException.php',
'OCP\\UserStatus\\IManager' => __DIR__ . '/../../..' . '/lib/public/UserStatus/IManager.php',
@@ -799,6 +820,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Authentication\\Token\\PublicKeyTokenMapper' => __DIR__ . '/../../..' . '/lib/private/Authentication/Token/PublicKeyTokenMapper.php',
'OC\\Authentication\\Token\\PublicKeyTokenProvider' => __DIR__ . '/../../..' . '/lib/private/Authentication/Token/PublicKeyTokenProvider.php',
'OC\\Authentication\\Token\\RemoteWipe' => __DIR__ . '/../../..' . '/lib/private/Authentication/Token/RemoteWipe.php',
+ 'OC\\Authentication\\Token\\TokenCleanupJob' => __DIR__ . '/../../..' . '/lib/private/Authentication/Token/TokenCleanupJob.php',
'OC\\Authentication\\TwoFactorAuth\\Db\\ProviderUserAssignmentDao' => __DIR__ . '/../../..' . '/lib/private/Authentication/TwoFactorAuth/Db/ProviderUserAssignmentDao.php',
'OC\\Authentication\\TwoFactorAuth\\EnforcementState' => __DIR__ . '/../../..' . '/lib/private/Authentication/TwoFactorAuth/EnforcementState.php',
'OC\\Authentication\\TwoFactorAuth\\Manager' => __DIR__ . '/../../..' . '/lib/private/Authentication/TwoFactorAuth/Manager.php',
@@ -818,10 +840,9 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Avatar\\UserAvatar' => __DIR__ . '/../../..' . '/lib/private/Avatar/UserAvatar.php',
'OC\\BackgroundJob\\Job' => __DIR__ . '/../../..' . '/lib/private/BackgroundJob/Job.php',
'OC\\BackgroundJob\\JobList' => __DIR__ . '/../../..' . '/lib/private/BackgroundJob/JobList.php',
- 'OC\\BackgroundJob\\Legacy\\QueuedJob' => __DIR__ . '/../../..' . '/lib/private/BackgroundJob/Legacy/QueuedJob.php',
- 'OC\\BackgroundJob\\Legacy\\RegularJob' => __DIR__ . '/../../..' . '/lib/private/BackgroundJob/Legacy/RegularJob.php',
'OC\\BackgroundJob\\QueuedJob' => __DIR__ . '/../../..' . '/lib/private/BackgroundJob/QueuedJob.php',
'OC\\BackgroundJob\\TimedJob' => __DIR__ . '/../../..' . '/lib/private/BackgroundJob/TimedJob.php',
+ 'OC\\BinaryFinder' => __DIR__ . '/../../..' . '/lib/private/BinaryFinder.php',
'OC\\Broadcast\\Events\\BroadcastEvent' => __DIR__ . '/../../..' . '/lib/private/Broadcast/Events/BroadcastEvent.php',
'OC\\Cache\\CappedMemoryCache' => __DIR__ . '/../../..' . '/lib/private/Cache/CappedMemoryCache.php',
'OC\\Cache\\File' => __DIR__ . '/../../..' . '/lib/private/Cache/File.php',
@@ -839,6 +860,11 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Collaboration\\Collaborators\\Search' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Collaborators/Search.php',
'OC\\Collaboration\\Collaborators\\SearchResult' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Collaborators/SearchResult.php',
'OC\\Collaboration\\Collaborators\\UserPlugin' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Collaborators/UserPlugin.php',
+ 'OC\\Collaboration\\Reference\\File\\FileReferenceEventListener' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Reference/File/FileReferenceEventListener.php',
+ 'OC\\Collaboration\\Reference\\File\\FileReferenceProvider' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Reference/File/FileReferenceProvider.php',
+ 'OC\\Collaboration\\Reference\\LinkReferenceProvider' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Reference/LinkReferenceProvider.php',
+ 'OC\\Collaboration\\Reference\\Reference' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Reference/Reference.php',
+ 'OC\\Collaboration\\Reference\\ReferenceManager' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Reference/ReferenceManager.php',
'OC\\Collaboration\\Resources\\Collection' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Resources/Collection.php',
'OC\\Collaboration\\Resources\\Listener' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Resources/Listener.php',
'OC\\Collaboration\\Resources\\Manager' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Resources/Manager.php',
@@ -883,6 +909,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Core\\Command\\Background\\Base' => __DIR__ . '/../../..' . '/core/Command/Background/Base.php',
'OC\\Core\\Command\\Background\\Cron' => __DIR__ . '/../../..' . '/core/Command/Background/Cron.php',
'OC\\Core\\Command\\Background\\Job' => __DIR__ . '/../../..' . '/core/Command/Background/Job.php',
+ 'OC\\Core\\Command\\Background\\ListCommand' => __DIR__ . '/../../..' . '/core/Command/Background/ListCommand.php',
'OC\\Core\\Command\\Background\\WebCron' => __DIR__ . '/../../..' . '/core/Command/Background/WebCron.php',
'OC\\Core\\Command\\Base' => __DIR__ . '/../../..' . '/core/Command/Base.php',
'OC\\Core\\Command\\Broadcast\\Test' => __DIR__ . '/../../..' . '/core/Command/Broadcast/Test.php',
@@ -990,6 +1017,8 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Core\\Controller\\ProfileApiController' => __DIR__ . '/../../..' . '/core/Controller/ProfileApiController.php',
'OC\\Core\\Controller\\ProfilePageController' => __DIR__ . '/../../..' . '/core/Controller/ProfilePageController.php',
'OC\\Core\\Controller\\RecommendedAppsController' => __DIR__ . '/../../..' . '/core/Controller/RecommendedAppsController.php',
+ 'OC\\Core\\Controller\\ReferenceApiController' => __DIR__ . '/../../..' . '/core/Controller/ReferenceApiController.php',
+ 'OC\\Core\\Controller\\ReferenceController' => __DIR__ . '/../../..' . '/core/Controller/ReferenceController.php',
'OC\\Core\\Controller\\SearchController' => __DIR__ . '/../../..' . '/core/Controller/SearchController.php',
'OC\\Core\\Controller\\SetupController' => __DIR__ . '/../../..' . '/core/Controller/SetupController.php',
'OC\\Core\\Controller\\TwoFactorChallengeController' => __DIR__ . '/../../..' . '/core/Controller/TwoFactorChallengeController.php',
@@ -1006,6 +1035,8 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Core\\Db\\LoginFlowV2Mapper' => __DIR__ . '/../../..' . '/core/Db/LoginFlowV2Mapper.php',
'OC\\Core\\Db\\ProfileConfig' => __DIR__ . '/../../..' . '/core/Db/ProfileConfig.php',
'OC\\Core\\Db\\ProfileConfigMapper' => __DIR__ . '/../../..' . '/core/Db/ProfileConfigMapper.php',
+ 'OC\\Core\\Events\\BeforePasswordResetEvent' => __DIR__ . '/../../..' . '/core/Events/BeforePasswordResetEvent.php',
+ 'OC\\Core\\Events\\PasswordResetEvent' => __DIR__ . '/../../..' . '/core/Events/PasswordResetEvent.php',
'OC\\Core\\Exception\\LoginFlowV2NotFoundException' => __DIR__ . '/../../..' . '/core/Exception/LoginFlowV2NotFoundException.php',
'OC\\Core\\Exception\\ResetPasswordException' => __DIR__ . '/../../..' . '/core/Exception/ResetPasswordException.php',
'OC\\Core\\Middleware\\TwoFactorMiddleware' => __DIR__ . '/../../..' . '/core/Middleware/TwoFactorMiddleware.php',
@@ -1058,6 +1089,8 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Core\\Migrations\\Version24000Date20220202150027' => __DIR__ . '/../../..' . '/core/Migrations/Version24000Date20220202150027.php',
'OC\\Core\\Migrations\\Version24000Date20220404230027' => __DIR__ . '/../../..' . '/core/Migrations/Version24000Date20220404230027.php',
'OC\\Core\\Migrations\\Version24000Date20220425072957' => __DIR__ . '/../../..' . '/core/Migrations/Version24000Date20220425072957.php',
+ 'OC\\Core\\Migrations\\Version25000Date20220515204012' => __DIR__ . '/../../..' . '/core/Migrations/Version25000Date20220515204012.php',
+ 'OC\\Core\\Migrations\\Version25000Date20220602190540' => __DIR__ . '/../../..' . '/core/Migrations/Version25000Date20220602190540.php',
'OC\\Core\\Notification\\CoreNotifier' => __DIR__ . '/../../..' . '/core/Notification/CoreNotifier.php',
'OC\\Core\\Service\\LoginFlowV2Service' => __DIR__ . '/../../..' . '/core/Service/LoginFlowV2Service.php',
'OC\\DB\\Adapter' => __DIR__ . '/../../..' . '/lib/private/DB/Adapter.php',
@@ -1074,6 +1107,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\DB\\MigrationException' => __DIR__ . '/../../..' . '/lib/private/DB/MigrationException.php',
'OC\\DB\\MigrationService' => __DIR__ . '/../../..' . '/lib/private/DB/MigrationService.php',
'OC\\DB\\Migrator' => __DIR__ . '/../../..' . '/lib/private/DB/Migrator.php',
+ 'OC\\DB\\MigratorExecuteSqlEvent' => __DIR__ . '/../../..' . '/lib/private/DB/MigratorExecuteSqlEvent.php',
'OC\\DB\\MissingColumnInformation' => __DIR__ . '/../../..' . '/lib/private/DB/MissingColumnInformation.php',
'OC\\DB\\MissingIndexInformation' => __DIR__ . '/../../..' . '/lib/private/DB/MissingIndexInformation.php',
'OC\\DB\\MissingPrimaryKeyInformation' => __DIR__ . '/../../..' . '/lib/private/DB/MissingPrimaryKeyInformation.php',
@@ -1428,6 +1462,13 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Repair\\ClearFrontendCaches' => __DIR__ . '/../../..' . '/lib/private/Repair/ClearFrontendCaches.php',
'OC\\Repair\\ClearGeneratedAvatarCache' => __DIR__ . '/../../..' . '/lib/private/Repair/ClearGeneratedAvatarCache.php',
'OC\\Repair\\Collation' => __DIR__ . '/../../..' . '/lib/private/Repair/Collation.php',
+ 'OC\\Repair\\Events\\RepairAdvanceEvent' => __DIR__ . '/../../..' . '/lib/private/Repair/Events/RepairAdvanceEvent.php',
+ 'OC\\Repair\\Events\\RepairErrorEvent' => __DIR__ . '/../../..' . '/lib/private/Repair/Events/RepairErrorEvent.php',
+ 'OC\\Repair\\Events\\RepairFinishEvent' => __DIR__ . '/../../..' . '/lib/private/Repair/Events/RepairFinishEvent.php',
+ 'OC\\Repair\\Events\\RepairInfoEvent' => __DIR__ . '/../../..' . '/lib/private/Repair/Events/RepairInfoEvent.php',
+ 'OC\\Repair\\Events\\RepairStartEvent' => __DIR__ . '/../../..' . '/lib/private/Repair/Events/RepairStartEvent.php',
+ 'OC\\Repair\\Events\\RepairStepEvent' => __DIR__ . '/../../..' . '/lib/private/Repair/Events/RepairStepEvent.php',
+ 'OC\\Repair\\Events\\RepairWarningEvent' => __DIR__ . '/../../..' . '/lib/private/Repair/Events/RepairWarningEvent.php',
'OC\\Repair\\MoveUpdaterStepFile' => __DIR__ . '/../../..' . '/lib/private/Repair/MoveUpdaterStepFile.php',
'OC\\Repair\\NC11\\FixMountStorages' => __DIR__ . '/../../..' . '/lib/private/Repair/NC11/FixMountStorages.php',
'OC\\Repair\\NC13\\AddLogRotateJob' => __DIR__ . '/../../..' . '/lib/private/Repair/NC13/AddLogRotateJob.php',
@@ -1442,6 +1483,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Repair\\NC21\\AddCheckForUserCertificatesJob' => __DIR__ . '/../../..' . '/lib/private/Repair/NC21/AddCheckForUserCertificatesJob.php',
'OC\\Repair\\NC21\\ValidatePhoneNumber' => __DIR__ . '/../../..' . '/lib/private/Repair/NC21/ValidatePhoneNumber.php',
'OC\\Repair\\NC22\\LookupServerSendCheck' => __DIR__ . '/../../..' . '/lib/private/Repair/NC22/LookupServerSendCheck.php',
+ 'OC\\Repair\\NC24\\AddTokenCleanupJob' => __DIR__ . '/../../..' . '/lib/private/Repair/NC24/AddTokenCleanupJob.php',
'OC\\Repair\\OldGroupMembershipShares' => __DIR__ . '/../../..' . '/lib/private/Repair/OldGroupMembershipShares.php',
'OC\\Repair\\Owncloud\\CleanPreviews' => __DIR__ . '/../../..' . '/lib/private/Repair/Owncloud/CleanPreviews.php',
'OC\\Repair\\Owncloud\\CleanPreviewsBackgroundJob' => __DIR__ . '/../../..' . '/lib/private/Repair/Owncloud/CleanPreviewsBackgroundJob.php',
@@ -1527,6 +1569,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Share20\\Manager' => __DIR__ . '/../../..' . '/lib/private/Share20/Manager.php',
'OC\\Share20\\ProviderFactory' => __DIR__ . '/../../..' . '/lib/private/Share20/ProviderFactory.php',
'OC\\Share20\\Share' => __DIR__ . '/../../..' . '/lib/private/Share20/Share.php',
+ 'OC\\Share20\\ShareAttributes' => __DIR__ . '/../../..' . '/lib/private/Share20/ShareAttributes.php',
'OC\\Share20\\ShareHelper' => __DIR__ . '/../../..' . '/lib/private/Share20/ShareHelper.php',
'OC\\Share20\\UserRemovedListener' => __DIR__ . '/../../..' . '/lib/private/Share20/UserRemovedListener.php',
'OC\\Share\\Constants' => __DIR__ . '/../../..' . '/lib/private/Share/Constants.php',
@@ -1570,6 +1613,8 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\User\\Database' => __DIR__ . '/../../..' . '/lib/private/User/Database.php',
'OC\\User\\DisplayNameCache' => __DIR__ . '/../../..' . '/lib/private/User/DisplayNameCache.php',
'OC\\User\\LazyUser' => __DIR__ . '/../../..' . '/lib/private/User/LazyUser.php',
+ 'OC\\User\\Listeners\\UserChangedListener' => __DIR__ . '/../../..' . '/lib/private/User/Listeners/UserChangedListener.php',
+ 'OC\\User\\Listeners\\UserDeletedListener' => __DIR__ . '/../../..' . '/lib/private/User/Listeners/UserDeletedListener.php',
'OC\\User\\LoginException' => __DIR__ . '/../../..' . '/lib/private/User/LoginException.php',
'OC\\User\\Manager' => __DIR__ . '/../../..' . '/lib/private/User/Manager.php',
'OC\\User\\NoUserException' => __DIR__ . '/../../..' . '/lib/private/User/NoUserException.php',
diff --git a/lib/l10n/an.js b/lib/l10n/an.js
new file mode 100644
index 00000000000..49222b95d78
--- /dev/null
+++ b/lib/l10n/an.js
@@ -0,0 +1,19 @@
+OC.L10N.register(
+ "lib",
+ {
+ "See %s" : "Veyer %s",
+ "Unknown filetype" : "Tipo de fichero esconoxiu",
+ "Invalid image" : "Imachen no valida",
+ "today" : "Hue",
+ "yesterday" : "Ayer",
+ "last month" : "Zaguero mes",
+ "last year" : "Zaguero año",
+ "Help" : "Aduya",
+ "Apps" : "Aplicazions",
+ "Settings" : "Configurazión",
+ "Users" : "Usuarios",
+ "Email" : "Correu electronico",
+ "Full name" : "Nombre completo",
+ "PostgreSQL >= 9 required" : "PostgreSQL >= 9 requeriu"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/lib/l10n/an.json b/lib/l10n/an.json
new file mode 100644
index 00000000000..3ee3c9bb029
--- /dev/null
+++ b/lib/l10n/an.json
@@ -0,0 +1,17 @@
+{ "translations": {
+ "See %s" : "Veyer %s",
+ "Unknown filetype" : "Tipo de fichero esconoxiu",
+ "Invalid image" : "Imachen no valida",
+ "today" : "Hue",
+ "yesterday" : "Ayer",
+ "last month" : "Zaguero mes",
+ "last year" : "Zaguero año",
+ "Help" : "Aduya",
+ "Apps" : "Aplicazions",
+ "Settings" : "Configurazión",
+ "Users" : "Usuarios",
+ "Email" : "Correu electronico",
+ "Full name" : "Nombre completo",
+ "PostgreSQL >= 9 required" : "PostgreSQL >= 9 requeriu"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/lib/l10n/bg.js b/lib/l10n/bg.js
index 84f67dd760a..a0be77e48fa 100644
--- a/lib/l10n/bg.js
+++ b/lib/l10n/bg.js
@@ -91,6 +91,7 @@ OC.L10N.register(
"This is an automatically sent email, please do not reply." : "Имейлът е генериран автоматично, моля не отговаряйте.",
"Help" : "Помощ",
"Apps" : "Приложения",
+ "Personal settings" : "Лични настройки",
"Settings" : "Настройки",
"Log out" : "Отписване",
"Users" : "Потребители",
@@ -207,7 +208,7 @@ OC.L10N.register(
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "Приложението „%1$s“ не може да бъде инсталирано, защото следните зависимости не са изпълнени: %2$s",
"a safe home for all your data" : "безопасен дом за всички ваши данни",
"File is currently busy, please try again later" : "Файлът в момента е зает, моля, опитайте отново по-късно",
- "Cannot read file" : "Не може да се прочете файл",
+ "Cannot download file" : "Файлът не можа да бъде изтеглен",
"Application is not enabled" : "Приложението не е включено",
"Authentication error" : "Грешка при удостоверяването",
"Token expired. Please reload page." : "Изтекла сесия. Моля, презареди страницата.",
@@ -235,6 +236,8 @@ OC.L10N.register(
"This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Това може да се дължи на cache/accelerator като Zend OPache или eAccelerator.",
"PHP modules have been installed, but they are still listed as missing?" : "PHP модулите са инсталирани, но все още се обявяват като липсващи?",
"Please ask your server administrator to restart the web server." : "Моля, поискай от своя администратор да рестартира уеб сървъра.",
+ "The required %s config variable is not configured in the config.php file." : "Необходимата %s конфигурационна променлива не е конфигурирана във файла config.php.",
+ "Please ask your server administrator to check the Nextcloud configuration." : "Моля, помолете администратора на вашия сървър да провери конфигурацията на Nextcloud.",
"PostgreSQL >= 9 required." : "Нужно е PostgreSQL >= 9",
"Please upgrade your database version." : "Моля, надстройте версията на вашата база данни.",
"Your data directory is readable by other users." : "Вашата директория с данни може да се чете от други потребители.",
@@ -255,6 +258,7 @@ OC.L10N.register(
"Storage is temporarily not available" : "Временно хранилището не е налично",
"Storage connection timeout. %s" : "Време за изчакване при свързването с хранилище. %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Това обикновено може да бъде оправено като, се даде достъп на уеб сървъра да записва в config директорията.",
+ "Cannot read file" : "Не може да се прочете файл",
"Cannot write into \"config\" directory" : "Неуспешен опит за запис в \"config\" папката.",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Това обикновено може да бъде оправено като, се даде достъп на уеб сървъра да записва в config директорията. Погледнете %s",
"Cannot write into \"apps\" directory" : "Писането в папка приложения не е възможно",
diff --git a/lib/l10n/bg.json b/lib/l10n/bg.json
index 27b6ca57155..52faf059f8c 100644
--- a/lib/l10n/bg.json
+++ b/lib/l10n/bg.json
@@ -89,6 +89,7 @@
"This is an automatically sent email, please do not reply." : "Имейлът е генериран автоматично, моля не отговаряйте.",
"Help" : "Помощ",
"Apps" : "Приложения",
+ "Personal settings" : "Лични настройки",
"Settings" : "Настройки",
"Log out" : "Отписване",
"Users" : "Потребители",
@@ -205,7 +206,7 @@
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "Приложението „%1$s“ не може да бъде инсталирано, защото следните зависимости не са изпълнени: %2$s",
"a safe home for all your data" : "безопасен дом за всички ваши данни",
"File is currently busy, please try again later" : "Файлът в момента е зает, моля, опитайте отново по-късно",
- "Cannot read file" : "Не може да се прочете файл",
+ "Cannot download file" : "Файлът не можа да бъде изтеглен",
"Application is not enabled" : "Приложението не е включено",
"Authentication error" : "Грешка при удостоверяването",
"Token expired. Please reload page." : "Изтекла сесия. Моля, презареди страницата.",
@@ -233,6 +234,8 @@
"This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Това може да се дължи на cache/accelerator като Zend OPache или eAccelerator.",
"PHP modules have been installed, but they are still listed as missing?" : "PHP модулите са инсталирани, но все още се обявяват като липсващи?",
"Please ask your server administrator to restart the web server." : "Моля, поискай от своя администратор да рестартира уеб сървъра.",
+ "The required %s config variable is not configured in the config.php file." : "Необходимата %s конфигурационна променлива не е конфигурирана във файла config.php.",
+ "Please ask your server administrator to check the Nextcloud configuration." : "Моля, помолете администратора на вашия сървър да провери конфигурацията на Nextcloud.",
"PostgreSQL >= 9 required." : "Нужно е PostgreSQL >= 9",
"Please upgrade your database version." : "Моля, надстройте версията на вашата база данни.",
"Your data directory is readable by other users." : "Вашата директория с данни може да се чете от други потребители.",
@@ -253,6 +256,7 @@
"Storage is temporarily not available" : "Временно хранилището не е налично",
"Storage connection timeout. %s" : "Време за изчакване при свързването с хранилище. %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Това обикновено може да бъде оправено като, се даде достъп на уеб сървъра да записва в config директорията.",
+ "Cannot read file" : "Не може да се прочете файл",
"Cannot write into \"config\" directory" : "Неуспешен опит за запис в \"config\" папката.",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Това обикновено може да бъде оправено като, се даде достъп на уеб сървъра да записва в config директорията. Погледнете %s",
"Cannot write into \"apps\" directory" : "Писането в папка приложения не е възможно",
diff --git a/lib/l10n/ca.js b/lib/l10n/ca.js
index ac957865e6c..9ac9151fb6d 100644
--- a/lib/l10n/ca.js
+++ b/lib/l10n/ca.js
@@ -46,6 +46,7 @@ OC.L10N.register(
"Unknown filetype" : "Tipus de fitxer desconegut",
"Invalid image" : "Imatge no vàlida",
"Avatar image is not square" : "La imatge de perfil no és quadrada",
+ "View profile" : "Visualitza el perfil",
"today" : "avui",
"tomorrow" : "demà",
"yesterday" : "ahir",
diff --git a/lib/l10n/ca.json b/lib/l10n/ca.json
index 97f3a6df60b..dc152ed98a9 100644
--- a/lib/l10n/ca.json
+++ b/lib/l10n/ca.json
@@ -44,6 +44,7 @@
"Unknown filetype" : "Tipus de fitxer desconegut",
"Invalid image" : "Imatge no vàlida",
"Avatar image is not square" : "La imatge de perfil no és quadrada",
+ "View profile" : "Visualitza el perfil",
"today" : "avui",
"tomorrow" : "demà",
"yesterday" : "ahir",
diff --git a/lib/l10n/cs.js b/lib/l10n/cs.js
index 9f5de98a9e6..b6cafff5fb0 100644
--- a/lib/l10n/cs.js
+++ b/lib/l10n/cs.js
@@ -91,6 +91,8 @@ OC.L10N.register(
"This is an automatically sent email, please do not reply." : "Toto je automaticky odesílaný e-mail, neodpovídejte na něj.",
"Help" : "Nápověda",
"Apps" : "Aplikace",
+ "Personal settings" : "Osobní nastavení",
+ "Admin settings" : "Nastavení pro správu",
"Settings" : "Nastavení",
"Log out" : "Odhlásit se",
"Users" : "Uživatelé",
@@ -207,7 +209,7 @@ OC.L10N.register(
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "Aplikaci „%1$s“ nelze nainstalovat, protože nejsou splněny následující závislosti: %2$s",
"a safe home for all your data" : "bezpečný domov pro všechna vaše data",
"File is currently busy, please try again later" : "Soubor je nyní používán, zkuste to později",
- "Cannot read file" : "Nelze přečíst soubor",
+ "Cannot download file" : "Soubor se nedaří stáhnout",
"Application is not enabled" : "Aplikace není povolena",
"Authentication error" : "Chyba při ověřování se",
"Token expired. Please reload page." : "Platnost tokenu skončila. Načtěte stránku znovu.",
@@ -257,6 +259,7 @@ OC.L10N.register(
"Storage is temporarily not available" : "Úložiště je dočasně nedostupné",
"Storage connection timeout. %s" : "Překročen časový limit připojování k úložišti. %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Toto je obvykle možné vyřešit udělením webovému serveru oprávnění k zápisu do adresáře s nastaveními.",
+ "Cannot read file" : "Nelze přečíst soubor",
"Cannot write into \"config\" directory" : "Nelze zapisovat do adresáře „config“",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Toto obvykle lze vyřešit udělením oprávnění k zápisu do kořenové složky webu pro účet, pod kterým je provozován webový server. Viz %s",
"Cannot write into \"apps\" directory" : "Nedaří se zapisovat do adresáře „apps“",
diff --git a/lib/l10n/cs.json b/lib/l10n/cs.json
index 1401735b826..1eeacdc2749 100644
--- a/lib/l10n/cs.json
+++ b/lib/l10n/cs.json
@@ -89,6 +89,8 @@
"This is an automatically sent email, please do not reply." : "Toto je automaticky odesílaný e-mail, neodpovídejte na něj.",
"Help" : "Nápověda",
"Apps" : "Aplikace",
+ "Personal settings" : "Osobní nastavení",
+ "Admin settings" : "Nastavení pro správu",
"Settings" : "Nastavení",
"Log out" : "Odhlásit se",
"Users" : "Uživatelé",
@@ -205,7 +207,7 @@
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "Aplikaci „%1$s“ nelze nainstalovat, protože nejsou splněny následující závislosti: %2$s",
"a safe home for all your data" : "bezpečný domov pro všechna vaše data",
"File is currently busy, please try again later" : "Soubor je nyní používán, zkuste to později",
- "Cannot read file" : "Nelze přečíst soubor",
+ "Cannot download file" : "Soubor se nedaří stáhnout",
"Application is not enabled" : "Aplikace není povolena",
"Authentication error" : "Chyba při ověřování se",
"Token expired. Please reload page." : "Platnost tokenu skončila. Načtěte stránku znovu.",
@@ -255,6 +257,7 @@
"Storage is temporarily not available" : "Úložiště je dočasně nedostupné",
"Storage connection timeout. %s" : "Překročen časový limit připojování k úložišti. %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Toto je obvykle možné vyřešit udělením webovému serveru oprávnění k zápisu do adresáře s nastaveními.",
+ "Cannot read file" : "Nelze přečíst soubor",
"Cannot write into \"config\" directory" : "Nelze zapisovat do adresáře „config“",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Toto obvykle lze vyřešit udělením oprávnění k zápisu do kořenové složky webu pro účet, pod kterým je provozován webový server. Viz %s",
"Cannot write into \"apps\" directory" : "Nedaří se zapisovat do adresáře „apps“",
diff --git a/lib/l10n/da.js b/lib/l10n/da.js
index cbcf6cd32c7..c5bd2b5215b 100644
--- a/lib/l10n/da.js
+++ b/lib/l10n/da.js
@@ -5,6 +5,7 @@ OC.L10N.register(
"See %s" : "Se %s",
"Sample configuration detected" : "Eksempel for konfiguration registreret",
"It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Der er registreret at eksempel for konfiguration er blevet kopieret. Dette kan ødelægge din installation og understøttes ikke. Læs venligst dokumentationen før der foretages ændringer i config.php",
+ "Other activities" : "Andre aktiviteter",
"%1$s and %2$s" : "%1$s og %2$s",
"%1$s, %2$s and %3$s" : "%1$s, %2$s og %3$s",
"%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s og %4$s",
@@ -18,6 +19,7 @@ OC.L10N.register(
"Authentication" : "Godkendelse",
"Unknown filetype" : "Ukendt filtype",
"Invalid image" : "Ugyldigt billede",
+ "View profile" : "Vis profil",
"today" : "i dag",
"tomorrow" : "i morgen",
"yesterday" : "i går",
diff --git a/lib/l10n/da.json b/lib/l10n/da.json
index 8e18ffdb314..7b1840e39ef 100644
--- a/lib/l10n/da.json
+++ b/lib/l10n/da.json
@@ -3,6 +3,7 @@
"See %s" : "Se %s",
"Sample configuration detected" : "Eksempel for konfiguration registreret",
"It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Der er registreret at eksempel for konfiguration er blevet kopieret. Dette kan ødelægge din installation og understøttes ikke. Læs venligst dokumentationen før der foretages ændringer i config.php",
+ "Other activities" : "Andre aktiviteter",
"%1$s and %2$s" : "%1$s og %2$s",
"%1$s, %2$s and %3$s" : "%1$s, %2$s og %3$s",
"%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s og %4$s",
@@ -16,6 +17,7 @@
"Authentication" : "Godkendelse",
"Unknown filetype" : "Ukendt filtype",
"Invalid image" : "Ugyldigt billede",
+ "View profile" : "Vis profil",
"today" : "i dag",
"tomorrow" : "i morgen",
"yesterday" : "i går",
diff --git a/lib/l10n/de.js b/lib/l10n/de.js
index c8c03b8028f..941beea683a 100644
--- a/lib/l10n/de.js
+++ b/lib/l10n/de.js
@@ -3,16 +3,16 @@ OC.L10N.register(
{
"Cannot write into \"config\" directory!" : "Das Schreiben in das „config“-Verzeichnis ist nicht möglich!",
"This can usually be fixed by giving the web server write access to the config directory." : "Dies kann normalerweise behoben werden, indem dem Webserver Schreibzugriff auf das config-Verzeichnis gegeben wird.",
- "But, if you prefer to keep config.php file read only, set the option \"config_is_read_only\" to true in it." : "Wenn Du jedoch möchtest dass die Datei config.php schreibgeschützt bleiben soll, dann setze die Option \"config_is_read_only\" in der Datei auf true.",
+ "But, if you prefer to keep config.php file read only, set the option \"config_is_read_only\" to true in it." : "Wenn du jedoch möchtest dass die Datei config.php schreibgeschützt bleiben soll, dann setze die Option \"config_is_read_only\" in der Datei auf true.",
"See %s" : "Siehe %s",
"The files of the app %1$s were not replaced correctly. Make sure it is a version compatible with the server." : "Die Dateien der App %1$swurden nicht korrekt ersetzt. Stelle sicher, dass es sich um eine mit dem Server kompatible Version handelt.",
"Sample configuration detected" : "Beispielkonfiguration gefunden",
- "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Es wurde festgestellt, dass die Beispielkonfiguration kopiert wurde. Dies kann Deine Installation zerstören und wird nicht unterstützt. Bitte die Dokumentation lesen, bevor Änderungen an der config.php vorgenommen werden.",
+ "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Es wurde festgestellt, dass die Beispielkonfiguration kopiert wurde. Dies kann deine Installation zerstören und wird nicht unterstützt. Bitte die Dokumentation lesen, bevor Änderungen an der config.php vorgenommen werden.",
"%s email verification" : "%s E-Mail-Überprüfung",
"Email verification" : "E-Mail-Überprüfung",
- "Click the following button to confirm your email." : "Klicke die folgende Schaltfläche, um Deine E-Mail-Adresse zu bestätigen.",
- "Click the following link to confirm your email." : "Klicke den nachfolgenden Link, um Deine E-Mail-Adresse zu bestätigen",
- "Confirm your email" : "Bestätige Deine E-Mail-Adresse",
+ "Click the following button to confirm your email." : "Klicke die folgende Schaltfläche, um deine E-Mail-Adresse zu bestätigen.",
+ "Click the following link to confirm your email." : "Klicke den nachfolgenden Link, um deine E-Mail-Adresse zu bestätigen",
+ "Confirm your email" : "Bestätige deine E-Mail-Adresse",
"Other activities" : "Andere Aktivitäten",
"%1$s and %2$s" : "%1$s und %2$s",
"%1$s, %2$s and %3$s" : "%1$s, %2$s und %3$s",
@@ -91,6 +91,7 @@ OC.L10N.register(
"This is an automatically sent email, please do not reply." : "Dies ist eine automatisch versandte E-Mail, bitte nicht antworten.",
"Help" : "Hilfe",
"Apps" : "Apps",
+ "Personal settings" : "Persönliche Einstellungen",
"Settings" : "Einstellungen",
"Log out" : "Abmelden",
"Users" : "Benutzer",
@@ -123,19 +124,19 @@ OC.L10N.register(
"Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X wird nicht unterstützt und %s wird auf dieser Plattform nicht richtig funktionieren. Die Benutzung erfolgt auf eigene Gefahr!",
"For the best results, please consider using a GNU/Linux server instead." : "Zur Gewährleistung eines optimalen Betriebs sollte stattdessen ein GNU/Linux-Server verwendet werden.",
"It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Es scheint, dass diese %s-Instanz unter einer 32-Bit-PHP-Umgebung läuft und open_basedir in der Datei php.ini konfiguriert worden ist. Von einem solchen Betrieb wird dringend abgeraten, weil es dabei zu Problemen mit Dateien kommt, deren Größe 4 GB übersteigt.",
- "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Bitte entferne die open_basedir-Einstellung in Deiner php.ini oder wechsele zu 64-Bit-PHP.",
+ "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Bitte entferne die open_basedir-Einstellung in deiner php.ini oder wechsele zu 64-Bit-PHP.",
"Set an admin username." : "Einen Administrator-Benutzernamen setzen.",
"Set an admin password." : "Ein Administrator-Passwort setzen.",
"Cannot create or write into the data directory %s" : "Das Datenverzeichnis %s kann nicht erstellt oder es kann darin nicht geschrieben werden.",
"Sharing backend %s must implement the interface OCP\\Share_Backend" : "Freigabe-Backend %s muss in der OCP\\Share_Backend - Schnittstelle implementiert werden",
"Sharing backend %s not found" : "Freigabe-Backend %s nicht gefunden",
"Sharing backend for %s not found" : "Freigabe-Backend für %s nicht gefunden",
- "%1$s shared »%2$s« with you and wants to add:" : "%1$shat » %2$s«  mit Dir geteilt und möchte folgendes hinzufügen:",
- "%1$s shared »%2$s« with you and wants to add" : "%1$shat »%2$s« mit Dir geteilt und möchte folgendes hinzufügen",
- "»%s« added a note to a file shared with you" : "»%s« hat eine Bemerkung zu einer mit Dir geteilten Datei hinzugefügt",
+ "%1$s shared »%2$s« with you and wants to add:" : "%1$shat » %2$s«  mit dir geteilt und möchte folgendes hinzufügen:",
+ "%1$s shared »%2$s« with you and wants to add" : "%1$shat »%2$s« mit dir geteilt und möchte folgendes hinzufügen",
+ "»%s« added a note to a file shared with you" : "»%s« hat eine Bemerkung zu einer mit dir geteilten Datei hinzugefügt",
"Open »%s«" : "»%s« öffnen",
"%1$s via %2$s" : "%1$s über %2$s",
- "You are not allowed to share %s" : "Die Freigabe von %s ist Dir nicht erlaubt",
+ "You are not allowed to share %s" : "Die Freigabe von %s ist dir nicht erlaubt",
"Cannot increase permissions of %s" : "Kann die Berechtigungen von %s nicht erhöhen",
"Files cannot be shared with delete permissions" : "Dateien mit Lösch-Berechtigungen können nicht geteilt werden",
"Files cannot be shared with create permissions" : "Dateien mit Erstell-Berechtigungen können nicht geteilt werden",
@@ -143,8 +144,8 @@ OC.L10N.register(
"_Cannot set expiration date more than %n day in the future_::_Cannot set expiration date more than %n days in the future_" : ["Das Ablaufdatum kann nicht mehr als %n Tag in der Zukunft liegen","Das Ablaufdatum kann nicht mehr als %n Tage in der Zukunft liegen"],
"Sharing is only allowed with group members" : "Teilen ist nur mit Gruppenmitgliedern erlaubt",
"Sharing %s failed, because this item is already shared with user %s" : "Freigabe von %s fehlgeschlagen, da dieses Element schon mit dem Benutzer %s geteilt wird",
- "%1$s shared »%2$s« with you" : "%1$s hat »%2$s« mit Dir geteilt",
- "%1$s shared »%2$s« with you." : "%1$s hat »%2$s« mit Dir geteilt.",
+ "%1$s shared »%2$s« with you" : "%1$s hat »%2$s« mit dir geteilt",
+ "%1$s shared »%2$s« with you." : "%1$s hat »%2$s« mit dir geteilt.",
"Click the button below to open it." : "Klicke zum Öffnen auf die untere Schaltfläche.",
"The requested share does not exist anymore" : "Die angeforderte Freigabe existiert nicht mehr",
"Could not find category \"%s\"" : "Die Kategorie \"%s“ konnte nicht gefunden werden",
@@ -205,16 +206,15 @@ OC.L10N.register(
"User disabled" : "Benutzer deaktiviert",
"Login canceled by app" : "Anmeldung durch die App abgebrochen",
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "Die App „%1$s“ kann nicht installiert werden, da die folgenden Abhängigkeiten nicht erfüllt sind: %2$s",
- "a safe home for all your data" : "ein sicherer Ort für all Deine Daten",
+ "a safe home for all your data" : "ein sicherer Ort für all deine Daten",
"File is currently busy, please try again later" : "Die Datei ist in Benutzung, bitte versuche es später noch einmal",
- "Cannot read file" : "Datei kann nicht gelesen werden",
"Application is not enabled" : "Die Anwendung ist nicht aktiviert",
"Authentication error" : "Authentifizierungsfehler",
"Token expired. Please reload page." : "Token abgelaufen. Bitte lade die Seite neu.",
"No database drivers (sqlite, mysql, or postgresql) installed." : "Keine Datenbanktreiber (SQLite, MySQL oder PostgreSQL) installiert.",
"Cannot write into \"config\" directory." : "Schreiben in das „config“-Verzeichnis ist nicht möglich.",
"This can usually be fixed by giving the web server write access to the config directory. See %s" : "Dies kann zumeist behoben werden, indem dem Webserver Schreibzugriff auf das Konfigurationsverzeichnis eingeräumt wird. Siehe auch %s",
- "Or, if you prefer to keep config.php file read only, set the option \"config_is_read_only\" to true in it. See %s" : "Oder wenn Du lieber möchtest, dass die Datei config.php schreibgeschützt bleiben soll, dann setze die Option \"config_is_read_only\" in der Datei auf true. Siehe %s",
+ "Or, if you prefer to keep config.php file read only, set the option \"config_is_read_only\" to true in it. See %s" : "Oder wenn du lieber möchtest, dass die Datei config.php schreibgeschützt bleiben soll, dann setze die Option \"config_is_read_only\" in der Datei auf true. Siehe %s",
"Cannot write into \"apps\" directory." : "Schreiben in das „apps“-Verzeichnis ist nicht möglich.",
"This can usually be fixed by giving the web server write access to the apps directory or disabling the App Store in the config file." : "Dies kann normalerweise behoben werden, indem dem Webserver Schreibzugriff auf das App-Verzeichnis gegeben wird oder der App-Store in der Konfigurationsdatei deaktiviert wird.",
"Cannot create \"data\" directory." : "Kann das \"Daten\"-Verzeichnis nicht erstellen.",
@@ -222,27 +222,27 @@ OC.L10N.register(
"Permissions can usually be fixed by giving the web server write access to the root directory. See %s." : "Berechtigungen können zumeist korrigiert werden, indem dem Webserver Schreibzugriff auf das Wurzel-Verzeichnis eingeräumt wird. Siehe auch %s. ",
"Your data directory is not writable." : "Dein Datenverzeichnis ist schreibgeschützt.",
"Setting locale to %s failed." : "Das Setzen der Spracheeinstellung auf %s ist fehlgeschlagen.",
- "Please install one of these locales on your system and restart your web server." : "Bitte installiere eine dieser Sprachen auf Deinem System und starte den Webserver neu.",
+ "Please install one of these locales on your system and restart your web server." : "Bitte installiere eine dieser Sprachen auf deinem System und starte den Webserver neu.",
"PHP module %s not installed." : "PHP-Modul %s nicht installiert.",
- "Please ask your server administrator to install the module." : "Bitte für die Installation des Moduls Deinen Server-Administrator kontaktieren.",
+ "Please ask your server administrator to install the module." : "Bitte für die Installation des Moduls deinen Server-Administrator kontaktieren.",
"PHP setting \"%s\" is not set to \"%s\"." : "PHP-Einstellung „%s“ ist nicht auf „%s“ gesetzt.",
- "Adjusting this setting in php.ini will make Nextcloud run again" : "Eine Änderung dieser Einstellung in der php.ini kann Deine Nextcloud wieder lauffähig machen.",
+ "Adjusting this setting in php.ini will make Nextcloud run again" : "Eine Änderung dieser Einstellung in der php.ini kann deine Nextcloud wieder lauffähig machen.",
"<code>mbstring.func_overload</code> is set to <code>%s</code> instead of the expected value <code>0</code>." : "<code>mbstring.func_overload</code> ist auf <code>%s</code> gesetzt und nicht auf den erwarteten Wert <code>0</code>.",
- "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini." : "Bitte setze zum Beheben dieses Problems <code>mbstring.func_overload</code> in Deiner php.ini auf <code>0</code>.",
+ "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini." : "Bitte setze zum Beheben dieses Problems <code>mbstring.func_overload</code> in deiner php.ini auf <code>0</code>.",
"libxml2 2.7.0 is at least required. Currently %s is installed." : "libxml2 2.7.0 ist mindestestens erforderlich. Im Moment ist %s installiert.",
- "To fix this issue update your libxml2 version and restart your web server." : "Um den Fehler zu beheben, musst Du die libxml2 Version aktualisieren und den Webserver neustarten.",
+ "To fix this issue update your libxml2 version and restart your web server." : "Um den Fehler zu beheben, musst du die libxml2 Version aktualisieren und den Webserver neustarten.",
"PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP ist offenbar so konfiguriert, dass PHPDoc-Blöcke in der Anweisung entfernt werden. Dadurch sind mehrere Kern-Apps nicht erreichbar.",
"This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Dies wird wahrscheinlich durch Zwischenspeicher/Beschleuniger wie etwa Zend OPcache oder eAccelerator verursacht.",
"PHP modules have been installed, but they are still listed as missing?" : "PHP-Module wurden installiert, werden aber als noch fehlend gelistet?",
- "Please ask your server administrator to restart the web server." : "Bitte kontaktiere Deinen Server-Administrator und bitte um den Neustart des Webservers.",
+ "Please ask your server administrator to restart the web server." : "Bitte kontaktiere deinen Server-Administrator und bitte um den Neustart des Webservers.",
"The required %s config variable is not configured in the config.php file." : "Die erforderliche %s Konfigurationsvariable ist in der config.php nicht konfiguriert.",
- "Please ask your server administrator to check the Nextcloud configuration." : "Bitte Deinen Server-Administrator, die Nextcloud-Konfiguration zu überprüfen.",
+ "Please ask your server administrator to check the Nextcloud configuration." : "Bitte deinen Server-Administrator, die Nextcloud-Konfiguration zu überprüfen.",
"PostgreSQL >= 9 required." : "PostgreSQL >= 9 benötigt",
- "Please upgrade your database version." : "Bitte aktualisiere Deine Datenbankversion",
+ "Please upgrade your database version." : "Bitte aktualisiere deine Datenbankversion",
"Your data directory is readable by other users." : "Dein Datenverzeichnis kann von anderen Benutzern gelesen werden",
"Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Bitte ändere die Berechtigungen auf 0770, sodass das Verzeichnis nicht von anderen Benutzern angezeigt werden kann.",
"Your data directory must be an absolute path." : "Dein Datenverzeichnis muss einen eindeutigen Pfad haben",
- "Check the value of \"datadirectory\" in your configuration." : "Überprüfe bitte die Angabe unter „datadirectory“ in Deiner Konfiguration",
+ "Check the value of \"datadirectory\" in your configuration." : "Überprüfe bitte die Angabe unter „datadirectory“ in deiner Konfiguration",
"Your data directory is invalid." : "Dein Datenverzeichnis ist ungültig",
"Ensure there is a file called \".ocdata\" in the root of the data directory." : "Stelle sicher, dass eine Datei \".ocdata\" im Wurzelverzeichnis des data-Verzeichnisses existiert.",
"Action \"%s\" not supported or implemented." : "Aktion \"%s\" wird nicht unterstützt oder ist nicht implementiert.",
@@ -257,22 +257,23 @@ OC.L10N.register(
"Storage is temporarily not available" : "Speicher ist vorübergehend nicht verfügbar",
"Storage connection timeout. %s" : "Zeitüberschreitung der Verbindung zum Speicherplatz. %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Dies kann normalerweise behoben werden, indem dem Webserver Schreibzugriff auf das config-Verzeichnis gegeben wird.",
+ "Cannot read file" : "Datei kann nicht gelesen werden",
"Cannot write into \"config\" directory" : "Schreiben in das „config“-Verzeichnis ist nicht möglich",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Dies kann zumeist behoben werden, indem dem Web-Server Schreibzugriff auf das Konfigurationsverzeichnis eingeräumt wird. Siehe auch %s",
"Cannot write into \"apps\" directory" : "Schreiben in das „apps“-Verzeichnis ist nicht möglich",
"This can usually be fixed by giving the webserver write access to the apps directory or disabling the App Store in the config file." : "Dies kann normalerweise behoben werden, indem dem Webserver Schreibzugriff auf das App-Verzeichnis gegeben wird oder der App-Store in der Konfigurationsdatei deaktiviert wird.",
- "Cannot create \"data\" directory" : "Kann das \"Daten\"-Verzeichnis nicht erstellen",
+ "Cannot create \"data\" directory" : "Kann das \"Daten\"-Verzeichnis nicht erstellen.",
"This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Dies kann zumeist behoben werden, indem dem Web-Server Schreibzugriff auf das Wurzel-Verzeichnis eingeräumt wird. Siehe auch %s",
"Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Berechtigungen können zumeist korrigiert werden indem dem Web-Server Schreibzugriff auf das Wurzel-Verzeichnis eingeräumt wird. Siehe auch %s.",
"Setting locale to %s failed" : "Das Setzen der Umgebungslokale auf %s ist fehlgeschlagen",
- "Please install one of these locales on your system and restart your webserver." : "Bitte installiere eine dieser Sprachen auf Deinem System und starte den Webserver neu.",
+ "Please install one of these locales on your system and restart your webserver." : "Bitte installiere eine dieser Sprachen auf deinem System und starte den Webserver neu.",
"mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload ist nicht auf den erwarteten Wert „0“, sondern stattdessen auf „%s“ gesetzt",
- "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Bitte setze zum Beheben dieses Problems <code>mbstring.func_overload</code> in Deiner php.ini auf <code>0</code>.",
+ "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Bitte setze zum Beheben dieses Problems <code>mbstring.func_overload</code> in deiner php.ini auf <code>0</code>.",
"PostgreSQL >= 9 required" : "PostgreSQL >= 9 benötigt",
- "Please upgrade your database version" : "Bitte aktualisiere Deine Datenbankversion",
+ "Please upgrade your database version" : "Bitte aktualisiere deine Datenbankversion",
"Your data directory is readable by other users" : "Dein Datenverzeichnis kann von anderen Benutzern gelesen werden",
"Your data directory must be an absolute path" : "Dein Datenverzeichnis muss einen eindeutigen Pfad haben",
- "Check the value of \"datadirectory\" in your configuration" : "Überprüfe bitte die Angabe unter „datadirectory“ in Deiner Konfiguration",
+ "Check the value of \"datadirectory\" in your configuration" : "Überprüfe bitte die Angabe unter „datadirectory“ in deiner Konfiguration",
"Your data directory is invalid" : "Dein Datenverzeichnis ist ungültig"
},
"nplurals=2; plural=(n != 1);");
diff --git a/lib/l10n/de.json b/lib/l10n/de.json
index a3dec1fe691..0c59b2523c6 100644
--- a/lib/l10n/de.json
+++ b/lib/l10n/de.json
@@ -1,16 +1,16 @@
{ "translations": {
"Cannot write into \"config\" directory!" : "Das Schreiben in das „config“-Verzeichnis ist nicht möglich!",
"This can usually be fixed by giving the web server write access to the config directory." : "Dies kann normalerweise behoben werden, indem dem Webserver Schreibzugriff auf das config-Verzeichnis gegeben wird.",
- "But, if you prefer to keep config.php file read only, set the option \"config_is_read_only\" to true in it." : "Wenn Du jedoch möchtest dass die Datei config.php schreibgeschützt bleiben soll, dann setze die Option \"config_is_read_only\" in der Datei auf true.",
+ "But, if you prefer to keep config.php file read only, set the option \"config_is_read_only\" to true in it." : "Wenn du jedoch möchtest dass die Datei config.php schreibgeschützt bleiben soll, dann setze die Option \"config_is_read_only\" in der Datei auf true.",
"See %s" : "Siehe %s",
"The files of the app %1$s were not replaced correctly. Make sure it is a version compatible with the server." : "Die Dateien der App %1$swurden nicht korrekt ersetzt. Stelle sicher, dass es sich um eine mit dem Server kompatible Version handelt.",
"Sample configuration detected" : "Beispielkonfiguration gefunden",
- "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Es wurde festgestellt, dass die Beispielkonfiguration kopiert wurde. Dies kann Deine Installation zerstören und wird nicht unterstützt. Bitte die Dokumentation lesen, bevor Änderungen an der config.php vorgenommen werden.",
+ "It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Es wurde festgestellt, dass die Beispielkonfiguration kopiert wurde. Dies kann deine Installation zerstören und wird nicht unterstützt. Bitte die Dokumentation lesen, bevor Änderungen an der config.php vorgenommen werden.",
"%s email verification" : "%s E-Mail-Überprüfung",
"Email verification" : "E-Mail-Überprüfung",
- "Click the following button to confirm your email." : "Klicke die folgende Schaltfläche, um Deine E-Mail-Adresse zu bestätigen.",
- "Click the following link to confirm your email." : "Klicke den nachfolgenden Link, um Deine E-Mail-Adresse zu bestätigen",
- "Confirm your email" : "Bestätige Deine E-Mail-Adresse",
+ "Click the following button to confirm your email." : "Klicke die folgende Schaltfläche, um deine E-Mail-Adresse zu bestätigen.",
+ "Click the following link to confirm your email." : "Klicke den nachfolgenden Link, um deine E-Mail-Adresse zu bestätigen",
+ "Confirm your email" : "Bestätige deine E-Mail-Adresse",
"Other activities" : "Andere Aktivitäten",
"%1$s and %2$s" : "%1$s und %2$s",
"%1$s, %2$s and %3$s" : "%1$s, %2$s und %3$s",
@@ -89,6 +89,7 @@
"This is an automatically sent email, please do not reply." : "Dies ist eine automatisch versandte E-Mail, bitte nicht antworten.",
"Help" : "Hilfe",
"Apps" : "Apps",
+ "Personal settings" : "Persönliche Einstellungen",
"Settings" : "Einstellungen",
"Log out" : "Abmelden",
"Users" : "Benutzer",
@@ -121,19 +122,19 @@
"Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X wird nicht unterstützt und %s wird auf dieser Plattform nicht richtig funktionieren. Die Benutzung erfolgt auf eigene Gefahr!",
"For the best results, please consider using a GNU/Linux server instead." : "Zur Gewährleistung eines optimalen Betriebs sollte stattdessen ein GNU/Linux-Server verwendet werden.",
"It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. This will lead to problems with files over 4 GB and is highly discouraged." : "Es scheint, dass diese %s-Instanz unter einer 32-Bit-PHP-Umgebung läuft und open_basedir in der Datei php.ini konfiguriert worden ist. Von einem solchen Betrieb wird dringend abgeraten, weil es dabei zu Problemen mit Dateien kommt, deren Größe 4 GB übersteigt.",
- "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Bitte entferne die open_basedir-Einstellung in Deiner php.ini oder wechsele zu 64-Bit-PHP.",
+ "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "Bitte entferne die open_basedir-Einstellung in deiner php.ini oder wechsele zu 64-Bit-PHP.",
"Set an admin username." : "Einen Administrator-Benutzernamen setzen.",
"Set an admin password." : "Ein Administrator-Passwort setzen.",
"Cannot create or write into the data directory %s" : "Das Datenverzeichnis %s kann nicht erstellt oder es kann darin nicht geschrieben werden.",
"Sharing backend %s must implement the interface OCP\\Share_Backend" : "Freigabe-Backend %s muss in der OCP\\Share_Backend - Schnittstelle implementiert werden",
"Sharing backend %s not found" : "Freigabe-Backend %s nicht gefunden",
"Sharing backend for %s not found" : "Freigabe-Backend für %s nicht gefunden",
- "%1$s shared »%2$s« with you and wants to add:" : "%1$shat » %2$s«  mit Dir geteilt und möchte folgendes hinzufügen:",
- "%1$s shared »%2$s« with you and wants to add" : "%1$shat »%2$s« mit Dir geteilt und möchte folgendes hinzufügen",
- "»%s« added a note to a file shared with you" : "»%s« hat eine Bemerkung zu einer mit Dir geteilten Datei hinzugefügt",
+ "%1$s shared »%2$s« with you and wants to add:" : "%1$shat » %2$s«  mit dir geteilt und möchte folgendes hinzufügen:",
+ "%1$s shared »%2$s« with you and wants to add" : "%1$shat »%2$s« mit dir geteilt und möchte folgendes hinzufügen",
+ "»%s« added a note to a file shared with you" : "»%s« hat eine Bemerkung zu einer mit dir geteilten Datei hinzugefügt",
"Open »%s«" : "»%s« öffnen",
"%1$s via %2$s" : "%1$s über %2$s",
- "You are not allowed to share %s" : "Die Freigabe von %s ist Dir nicht erlaubt",
+ "You are not allowed to share %s" : "Die Freigabe von %s ist dir nicht erlaubt",
"Cannot increase permissions of %s" : "Kann die Berechtigungen von %s nicht erhöhen",
"Files cannot be shared with delete permissions" : "Dateien mit Lösch-Berechtigungen können nicht geteilt werden",
"Files cannot be shared with create permissions" : "Dateien mit Erstell-Berechtigungen können nicht geteilt werden",
@@ -141,8 +142,8 @@
"_Cannot set expiration date more than %n day in the future_::_Cannot set expiration date more than %n days in the future_" : ["Das Ablaufdatum kann nicht mehr als %n Tag in der Zukunft liegen","Das Ablaufdatum kann nicht mehr als %n Tage in der Zukunft liegen"],
"Sharing is only allowed with group members" : "Teilen ist nur mit Gruppenmitgliedern erlaubt",
"Sharing %s failed, because this item is already shared with user %s" : "Freigabe von %s fehlgeschlagen, da dieses Element schon mit dem Benutzer %s geteilt wird",
- "%1$s shared »%2$s« with you" : "%1$s hat »%2$s« mit Dir geteilt",
- "%1$s shared »%2$s« with you." : "%1$s hat »%2$s« mit Dir geteilt.",
+ "%1$s shared »%2$s« with you" : "%1$s hat »%2$s« mit dir geteilt",
+ "%1$s shared »%2$s« with you." : "%1$s hat »%2$s« mit dir geteilt.",
"Click the button below to open it." : "Klicke zum Öffnen auf die untere Schaltfläche.",
"The requested share does not exist anymore" : "Die angeforderte Freigabe existiert nicht mehr",
"Could not find category \"%s\"" : "Die Kategorie \"%s“ konnte nicht gefunden werden",
@@ -203,16 +204,15 @@
"User disabled" : "Benutzer deaktiviert",
"Login canceled by app" : "Anmeldung durch die App abgebrochen",
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "Die App „%1$s“ kann nicht installiert werden, da die folgenden Abhängigkeiten nicht erfüllt sind: %2$s",
- "a safe home for all your data" : "ein sicherer Ort für all Deine Daten",
+ "a safe home for all your data" : "ein sicherer Ort für all deine Daten",
"File is currently busy, please try again later" : "Die Datei ist in Benutzung, bitte versuche es später noch einmal",
- "Cannot read file" : "Datei kann nicht gelesen werden",
"Application is not enabled" : "Die Anwendung ist nicht aktiviert",
"Authentication error" : "Authentifizierungsfehler",
"Token expired. Please reload page." : "Token abgelaufen. Bitte lade die Seite neu.",
"No database drivers (sqlite, mysql, or postgresql) installed." : "Keine Datenbanktreiber (SQLite, MySQL oder PostgreSQL) installiert.",
"Cannot write into \"config\" directory." : "Schreiben in das „config“-Verzeichnis ist nicht möglich.",
"This can usually be fixed by giving the web server write access to the config directory. See %s" : "Dies kann zumeist behoben werden, indem dem Webserver Schreibzugriff auf das Konfigurationsverzeichnis eingeräumt wird. Siehe auch %s",
- "Or, if you prefer to keep config.php file read only, set the option \"config_is_read_only\" to true in it. See %s" : "Oder wenn Du lieber möchtest, dass die Datei config.php schreibgeschützt bleiben soll, dann setze die Option \"config_is_read_only\" in der Datei auf true. Siehe %s",
+ "Or, if you prefer to keep config.php file read only, set the option \"config_is_read_only\" to true in it. See %s" : "Oder wenn du lieber möchtest, dass die Datei config.php schreibgeschützt bleiben soll, dann setze die Option \"config_is_read_only\" in der Datei auf true. Siehe %s",
"Cannot write into \"apps\" directory." : "Schreiben in das „apps“-Verzeichnis ist nicht möglich.",
"This can usually be fixed by giving the web server write access to the apps directory or disabling the App Store in the config file." : "Dies kann normalerweise behoben werden, indem dem Webserver Schreibzugriff auf das App-Verzeichnis gegeben wird oder der App-Store in der Konfigurationsdatei deaktiviert wird.",
"Cannot create \"data\" directory." : "Kann das \"Daten\"-Verzeichnis nicht erstellen.",
@@ -220,27 +220,27 @@
"Permissions can usually be fixed by giving the web server write access to the root directory. See %s." : "Berechtigungen können zumeist korrigiert werden, indem dem Webserver Schreibzugriff auf das Wurzel-Verzeichnis eingeräumt wird. Siehe auch %s. ",
"Your data directory is not writable." : "Dein Datenverzeichnis ist schreibgeschützt.",
"Setting locale to %s failed." : "Das Setzen der Spracheeinstellung auf %s ist fehlgeschlagen.",
- "Please install one of these locales on your system and restart your web server." : "Bitte installiere eine dieser Sprachen auf Deinem System und starte den Webserver neu.",
+ "Please install one of these locales on your system and restart your web server." : "Bitte installiere eine dieser Sprachen auf deinem System und starte den Webserver neu.",
"PHP module %s not installed." : "PHP-Modul %s nicht installiert.",
- "Please ask your server administrator to install the module." : "Bitte für die Installation des Moduls Deinen Server-Administrator kontaktieren.",
+ "Please ask your server administrator to install the module." : "Bitte für die Installation des Moduls deinen Server-Administrator kontaktieren.",
"PHP setting \"%s\" is not set to \"%s\"." : "PHP-Einstellung „%s“ ist nicht auf „%s“ gesetzt.",
- "Adjusting this setting in php.ini will make Nextcloud run again" : "Eine Änderung dieser Einstellung in der php.ini kann Deine Nextcloud wieder lauffähig machen.",
+ "Adjusting this setting in php.ini will make Nextcloud run again" : "Eine Änderung dieser Einstellung in der php.ini kann deine Nextcloud wieder lauffähig machen.",
"<code>mbstring.func_overload</code> is set to <code>%s</code> instead of the expected value <code>0</code>." : "<code>mbstring.func_overload</code> ist auf <code>%s</code> gesetzt und nicht auf den erwarteten Wert <code>0</code>.",
- "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini." : "Bitte setze zum Beheben dieses Problems <code>mbstring.func_overload</code> in Deiner php.ini auf <code>0</code>.",
+ "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini." : "Bitte setze zum Beheben dieses Problems <code>mbstring.func_overload</code> in deiner php.ini auf <code>0</code>.",
"libxml2 2.7.0 is at least required. Currently %s is installed." : "libxml2 2.7.0 ist mindestestens erforderlich. Im Moment ist %s installiert.",
- "To fix this issue update your libxml2 version and restart your web server." : "Um den Fehler zu beheben, musst Du die libxml2 Version aktualisieren und den Webserver neustarten.",
+ "To fix this issue update your libxml2 version and restart your web server." : "Um den Fehler zu beheben, musst du die libxml2 Version aktualisieren und den Webserver neustarten.",
"PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "PHP ist offenbar so konfiguriert, dass PHPDoc-Blöcke in der Anweisung entfernt werden. Dadurch sind mehrere Kern-Apps nicht erreichbar.",
"This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Dies wird wahrscheinlich durch Zwischenspeicher/Beschleuniger wie etwa Zend OPcache oder eAccelerator verursacht.",
"PHP modules have been installed, but they are still listed as missing?" : "PHP-Module wurden installiert, werden aber als noch fehlend gelistet?",
- "Please ask your server administrator to restart the web server." : "Bitte kontaktiere Deinen Server-Administrator und bitte um den Neustart des Webservers.",
+ "Please ask your server administrator to restart the web server." : "Bitte kontaktiere deinen Server-Administrator und bitte um den Neustart des Webservers.",
"The required %s config variable is not configured in the config.php file." : "Die erforderliche %s Konfigurationsvariable ist in der config.php nicht konfiguriert.",
- "Please ask your server administrator to check the Nextcloud configuration." : "Bitte Deinen Server-Administrator, die Nextcloud-Konfiguration zu überprüfen.",
+ "Please ask your server administrator to check the Nextcloud configuration." : "Bitte deinen Server-Administrator, die Nextcloud-Konfiguration zu überprüfen.",
"PostgreSQL >= 9 required." : "PostgreSQL >= 9 benötigt",
- "Please upgrade your database version." : "Bitte aktualisiere Deine Datenbankversion",
+ "Please upgrade your database version." : "Bitte aktualisiere deine Datenbankversion",
"Your data directory is readable by other users." : "Dein Datenverzeichnis kann von anderen Benutzern gelesen werden",
"Please change the permissions to 0770 so that the directory cannot be listed by other users." : "Bitte ändere die Berechtigungen auf 0770, sodass das Verzeichnis nicht von anderen Benutzern angezeigt werden kann.",
"Your data directory must be an absolute path." : "Dein Datenverzeichnis muss einen eindeutigen Pfad haben",
- "Check the value of \"datadirectory\" in your configuration." : "Überprüfe bitte die Angabe unter „datadirectory“ in Deiner Konfiguration",
+ "Check the value of \"datadirectory\" in your configuration." : "Überprüfe bitte die Angabe unter „datadirectory“ in deiner Konfiguration",
"Your data directory is invalid." : "Dein Datenverzeichnis ist ungültig",
"Ensure there is a file called \".ocdata\" in the root of the data directory." : "Stelle sicher, dass eine Datei \".ocdata\" im Wurzelverzeichnis des data-Verzeichnisses existiert.",
"Action \"%s\" not supported or implemented." : "Aktion \"%s\" wird nicht unterstützt oder ist nicht implementiert.",
@@ -255,22 +255,23 @@
"Storage is temporarily not available" : "Speicher ist vorübergehend nicht verfügbar",
"Storage connection timeout. %s" : "Zeitüberschreitung der Verbindung zum Speicherplatz. %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Dies kann normalerweise behoben werden, indem dem Webserver Schreibzugriff auf das config-Verzeichnis gegeben wird.",
+ "Cannot read file" : "Datei kann nicht gelesen werden",
"Cannot write into \"config\" directory" : "Schreiben in das „config“-Verzeichnis ist nicht möglich",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Dies kann zumeist behoben werden, indem dem Web-Server Schreibzugriff auf das Konfigurationsverzeichnis eingeräumt wird. Siehe auch %s",
"Cannot write into \"apps\" directory" : "Schreiben in das „apps“-Verzeichnis ist nicht möglich",
"This can usually be fixed by giving the webserver write access to the apps directory or disabling the App Store in the config file." : "Dies kann normalerweise behoben werden, indem dem Webserver Schreibzugriff auf das App-Verzeichnis gegeben wird oder der App-Store in der Konfigurationsdatei deaktiviert wird.",
- "Cannot create \"data\" directory" : "Kann das \"Daten\"-Verzeichnis nicht erstellen",
+ "Cannot create \"data\" directory" : "Kann das \"Daten\"-Verzeichnis nicht erstellen.",
"This can usually be fixed by giving the webserver write access to the root directory. See %s" : "Dies kann zumeist behoben werden, indem dem Web-Server Schreibzugriff auf das Wurzel-Verzeichnis eingeräumt wird. Siehe auch %s",
"Permissions can usually be fixed by giving the webserver write access to the root directory. See %s." : "Berechtigungen können zumeist korrigiert werden indem dem Web-Server Schreibzugriff auf das Wurzel-Verzeichnis eingeräumt wird. Siehe auch %s.",
"Setting locale to %s failed" : "Das Setzen der Umgebungslokale auf %s ist fehlgeschlagen",
- "Please install one of these locales on your system and restart your webserver." : "Bitte installiere eine dieser Sprachen auf Deinem System und starte den Webserver neu.",
+ "Please install one of these locales on your system and restart your webserver." : "Bitte installiere eine dieser Sprachen auf deinem System und starte den Webserver neu.",
"mbstring.func_overload is set to \"%s\" instead of the expected value \"0\"" : "mbstring.func_overload ist nicht auf den erwarteten Wert „0“, sondern stattdessen auf „%s“ gesetzt",
- "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Bitte setze zum Beheben dieses Problems <code>mbstring.func_overload</code> in Deiner php.ini auf <code>0</code>.",
+ "To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini" : "Bitte setze zum Beheben dieses Problems <code>mbstring.func_overload</code> in deiner php.ini auf <code>0</code>.",
"PostgreSQL >= 9 required" : "PostgreSQL >= 9 benötigt",
- "Please upgrade your database version" : "Bitte aktualisiere Deine Datenbankversion",
+ "Please upgrade your database version" : "Bitte aktualisiere deine Datenbankversion",
"Your data directory is readable by other users" : "Dein Datenverzeichnis kann von anderen Benutzern gelesen werden",
"Your data directory must be an absolute path" : "Dein Datenverzeichnis muss einen eindeutigen Pfad haben",
- "Check the value of \"datadirectory\" in your configuration" : "Überprüfe bitte die Angabe unter „datadirectory“ in Deiner Konfiguration",
+ "Check the value of \"datadirectory\" in your configuration" : "Überprüfe bitte die Angabe unter „datadirectory“ in deiner Konfiguration",
"Your data directory is invalid" : "Dein Datenverzeichnis ist ungültig"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
} \ No newline at end of file
diff --git a/lib/l10n/de_DE.js b/lib/l10n/de_DE.js
index d7b983dba66..d024251197f 100644
--- a/lib/l10n/de_DE.js
+++ b/lib/l10n/de_DE.js
@@ -91,6 +91,8 @@ OC.L10N.register(
"This is an automatically sent email, please do not reply." : "Dies ist eine automatisch versandte E-Mail, bitte nicht antworten.",
"Help" : "Hilfe",
"Apps" : "Apps",
+ "Personal settings" : "Persönliche Einstellungen",
+ "Admin settings" : "Administrations-Einstellungen",
"Settings" : "Einstellungen",
"Log out" : "Abmelden",
"Users" : "Benutzer",
@@ -207,7 +209,7 @@ OC.L10N.register(
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "Die App „%1$s“ kann nicht installiert werden, da die folgenden Abhängigkeiten nicht erfüllt sind: %2$s",
"a safe home for all your data" : "ein sicherer Ort für all Ihre Daten",
"File is currently busy, please try again later" : "Die Datei ist in Benutzung, bitte versuchen Sie es später noch einmal",
- "Cannot read file" : "Datei kann nicht gelesen werden",
+ "Cannot download file" : "Datei kann nicht heruntergeladen werden",
"Application is not enabled" : "Die Anwendung ist nicht aktiviert",
"Authentication error" : "Authentifizierungsfehler",
"Token expired. Please reload page." : "Token abgelaufen. Bitte laden Sie die Seite neu.",
@@ -257,6 +259,7 @@ OC.L10N.register(
"Storage is temporarily not available" : "Speicher ist vorübergehend nicht verfügbar",
"Storage connection timeout. %s" : "Zeitüberschreitung der Verbindung zum Speicherplatz. %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Dies kann normalerweise behoben werden, indem dem Webserver Schreibzugriff auf das config-Verzeichnis gegeben wird.",
+ "Cannot read file" : "Datei kann nicht gelesen werden",
"Cannot write into \"config\" directory" : "Schreiben in das „config“-Verzeichnis ist nicht möglich",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Dies kann zumeist behoben werden, indem dem Web-Server Schreibzugriff auf das Konfigurationsverzeichnis eingeräumt wird. Siehe auch %s",
"Cannot write into \"apps\" directory" : "Schreiben in das „apps“-Verzeichnis ist nicht möglich",
diff --git a/lib/l10n/de_DE.json b/lib/l10n/de_DE.json
index c456ac01d75..5dd0d2e1250 100644
--- a/lib/l10n/de_DE.json
+++ b/lib/l10n/de_DE.json
@@ -89,6 +89,8 @@
"This is an automatically sent email, please do not reply." : "Dies ist eine automatisch versandte E-Mail, bitte nicht antworten.",
"Help" : "Hilfe",
"Apps" : "Apps",
+ "Personal settings" : "Persönliche Einstellungen",
+ "Admin settings" : "Administrations-Einstellungen",
"Settings" : "Einstellungen",
"Log out" : "Abmelden",
"Users" : "Benutzer",
@@ -205,7 +207,7 @@
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "Die App „%1$s“ kann nicht installiert werden, da die folgenden Abhängigkeiten nicht erfüllt sind: %2$s",
"a safe home for all your data" : "ein sicherer Ort für all Ihre Daten",
"File is currently busy, please try again later" : "Die Datei ist in Benutzung, bitte versuchen Sie es später noch einmal",
- "Cannot read file" : "Datei kann nicht gelesen werden",
+ "Cannot download file" : "Datei kann nicht heruntergeladen werden",
"Application is not enabled" : "Die Anwendung ist nicht aktiviert",
"Authentication error" : "Authentifizierungsfehler",
"Token expired. Please reload page." : "Token abgelaufen. Bitte laden Sie die Seite neu.",
@@ -255,6 +257,7 @@
"Storage is temporarily not available" : "Speicher ist vorübergehend nicht verfügbar",
"Storage connection timeout. %s" : "Zeitüberschreitung der Verbindung zum Speicherplatz. %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Dies kann normalerweise behoben werden, indem dem Webserver Schreibzugriff auf das config-Verzeichnis gegeben wird.",
+ "Cannot read file" : "Datei kann nicht gelesen werden",
"Cannot write into \"config\" directory" : "Schreiben in das „config“-Verzeichnis ist nicht möglich",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Dies kann zumeist behoben werden, indem dem Web-Server Schreibzugriff auf das Konfigurationsverzeichnis eingeräumt wird. Siehe auch %s",
"Cannot write into \"apps\" directory" : "Schreiben in das „apps“-Verzeichnis ist nicht möglich",
diff --git a/lib/l10n/el.js b/lib/l10n/el.js
index 84bd8b77d65..1d758d80473 100644
--- a/lib/l10n/el.js
+++ b/lib/l10n/el.js
@@ -91,6 +91,7 @@ OC.L10N.register(
"This is an automatically sent email, please do not reply." : "Αυτό είναι ένα μήνυμα ηλεκτρονικού ταχυδρομείου που στάλθηκε αυτόματα, παρακαλούμε μην απαντήσετε.",
"Help" : "Βοήθεια",
"Apps" : "Εφαρμογές",
+ "Personal settings" : "Προσωπικές ρυθμίσεις",
"Settings" : "Ρυθμίσεις",
"Log out" : "Έξοδος",
"Users" : "Χρήστες",
@@ -207,7 +208,6 @@ OC.L10N.register(
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "Η εφαρμογή \"%1$s\" δεν μπορεί να εγκατασταθεί επειδή δεν πληρούνται τα προαπαιτούμενα: %2$s",
"a safe home for all your data" : "ένα ασφαλές μέρος για όλα τα δεδομένα σας",
"File is currently busy, please try again later" : "Το αρχείο χρησιμοποιείται αυτή τη στιγμή, παρακαλούμε προσπαθήστε αργότερα",
- "Cannot read file" : "Δεν είναι δυνατή η ανάγνωση του αρχείου",
"Application is not enabled" : "Δεν ενεργοποιήθηκε η εφαρμογή",
"Authentication error" : "Σφάλμα πιστοποίησης",
"Token expired. Please reload page." : "Το αναγνωριστικό έληξε. Παρακαλούμε φορτώστε ξανά την σελίδα.",
@@ -255,6 +255,7 @@ OC.L10N.register(
"Storage is temporarily not available" : "Ο χώρος αποθήκευσης δεν είναι διαθέσιμος προσωρινά",
"Storage connection timeout. %s" : "Λήξη χρονικού ορίου σύνδεσης με αποθηκευτικό χώρο.%s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Αυτό μπορεί συνήθως να διορθωθεί παρέχοντας δικαιώματα εγγραφής για το φάκελο config στον διακομιστή ιστού.",
+ "Cannot read file" : "Δεν είναι δυνατή η ανάγνωση του αρχείου",
"Cannot write into \"config\" directory" : "Αδυναμία εγγραφής στον κατάλογο \"config\"",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Αυτό μπορεί συνήθως να διορθωθεί δίνοντας στον διακομιστή ιστού δικαιώματα εγγραφής στον κατάλογο config. Δείτε το%s",
"Cannot write into \"apps\" directory" : "Αδυναμία εγγραφής στον κατάλογο \"apps\"",
diff --git a/lib/l10n/el.json b/lib/l10n/el.json
index 4b947c61b57..88b0022aa60 100644
--- a/lib/l10n/el.json
+++ b/lib/l10n/el.json
@@ -89,6 +89,7 @@
"This is an automatically sent email, please do not reply." : "Αυτό είναι ένα μήνυμα ηλεκτρονικού ταχυδρομείου που στάλθηκε αυτόματα, παρακαλούμε μην απαντήσετε.",
"Help" : "Βοήθεια",
"Apps" : "Εφαρμογές",
+ "Personal settings" : "Προσωπικές ρυθμίσεις",
"Settings" : "Ρυθμίσεις",
"Log out" : "Έξοδος",
"Users" : "Χρήστες",
@@ -205,7 +206,6 @@
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "Η εφαρμογή \"%1$s\" δεν μπορεί να εγκατασταθεί επειδή δεν πληρούνται τα προαπαιτούμενα: %2$s",
"a safe home for all your data" : "ένα ασφαλές μέρος για όλα τα δεδομένα σας",
"File is currently busy, please try again later" : "Το αρχείο χρησιμοποιείται αυτή τη στιγμή, παρακαλούμε προσπαθήστε αργότερα",
- "Cannot read file" : "Δεν είναι δυνατή η ανάγνωση του αρχείου",
"Application is not enabled" : "Δεν ενεργοποιήθηκε η εφαρμογή",
"Authentication error" : "Σφάλμα πιστοποίησης",
"Token expired. Please reload page." : "Το αναγνωριστικό έληξε. Παρακαλούμε φορτώστε ξανά την σελίδα.",
@@ -253,6 +253,7 @@
"Storage is temporarily not available" : "Ο χώρος αποθήκευσης δεν είναι διαθέσιμος προσωρινά",
"Storage connection timeout. %s" : "Λήξη χρονικού ορίου σύνδεσης με αποθηκευτικό χώρο.%s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Αυτό μπορεί συνήθως να διορθωθεί παρέχοντας δικαιώματα εγγραφής για το φάκελο config στον διακομιστή ιστού.",
+ "Cannot read file" : "Δεν είναι δυνατή η ανάγνωση του αρχείου",
"Cannot write into \"config\" directory" : "Αδυναμία εγγραφής στον κατάλογο \"config\"",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Αυτό μπορεί συνήθως να διορθωθεί δίνοντας στον διακομιστή ιστού δικαιώματα εγγραφής στον κατάλογο config. Δείτε το%s",
"Cannot write into \"apps\" directory" : "Αδυναμία εγγραφής στον κατάλογο \"apps\"",
diff --git a/lib/l10n/es.js b/lib/l10n/es.js
index 52b3902e16f..ffa65189c8e 100644
--- a/lib/l10n/es.js
+++ b/lib/l10n/es.js
@@ -58,20 +58,20 @@ OC.L10N.register(
"today" : "hoy",
"tomorrow" : "mañana",
"yesterday" : "ayer",
- "_in %n day_::_in %n days_" : ["dentro de %n día","dentro de %n días"],
- "_%n day ago_::_%n days ago_" : ["Hace %n día","hace %n días"],
+ "_in %n day_::_in %n days_" : ["dentro de %n día","dentro de %n días","dentro de %n días"],
+ "_%n day ago_::_%n days ago_" : ["Hace %n día","hace %n días","hace %n días"],
"next month" : "mes siguiente",
"last month" : "mes pasado",
- "_in %n month_::_in %n months_" : ["dentro de %n mes","dentro de %n meses"],
- "_%n month ago_::_%n months ago_" : ["Hace %n mes","hace %n meses"],
+ "_in %n month_::_in %n months_" : ["dentro de %n mes","dentro de %n meses","dentro de %n meses"],
+ "_%n month ago_::_%n months ago_" : ["Hace %n mes","hace %n meses","hace %n meses"],
"next year" : "año que viene",
"last year" : "año pasado",
- "_in %n year_::_in %n years_" : ["dentro de %n año","dentro de %n años"],
- "_%n year ago_::_%n years ago_" : ["Hace %n año","hace %n años"],
- "_in %n hour_::_in %n hours_" : ["dentro de %n hora","dentro de %n horas"],
- "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","hace %n horas"],
- "_in %n minute_::_in %n minutes_" : ["dentro de %n minuto","dentro de %n minutos"],
- "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","hace %n minutos"],
+ "_in %n year_::_in %n years_" : ["dentro de %n año","dentro de %n años","dentro de %n años"],
+ "_%n year ago_::_%n years ago_" : ["Hace %n año","hace %n años","hace %n años"],
+ "_in %n hour_::_in %n hours_" : ["dentro de %n hora","dentro de %n horas","dentro de %n horas"],
+ "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","hace %n horas","hace %n horas"],
+ "_in %n minute_::_in %n minutes_" : ["dentro de %n minuto","dentro de %n minutos","dentro de %n minutos"],
+ "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","hace %n minutos","hace %n minutos"],
"in a few seconds" : "en unos segundos",
"seconds ago" : "hace segundos",
"Empty file" : "Archivo vacío",
@@ -91,6 +91,7 @@ OC.L10N.register(
"This is an automatically sent email, please do not reply." : "Este es un correo enviado automáticamente, por favor no responda.",
"Help" : "Ayuda",
"Apps" : "Aplicaciones",
+ "Personal settings" : "Ajustes personales",
"Settings" : "Ajustes",
"Log out" : "Cerrar sesión",
"Users" : "Usuarios",
@@ -140,7 +141,7 @@ OC.L10N.register(
"Files cannot be shared with delete permissions" : "Los archivos no se pueden compartir con permisos de borrado",
"Files cannot be shared with create permissions" : "Los archivos no se pueden compartir con permisos de creación",
"Expiration date is in the past" : "Ha pasado la fecha de caducidad",
- "_Cannot set expiration date more than %n day in the future_::_Cannot set expiration date more than %n days in the future_" : ["No se puede fijar la fecha de caducidad más de %n día en el futuro.","No se puede fijar la fecha de caducidad más de %n días en el futuro."],
+ "_Cannot set expiration date more than %n day in the future_::_Cannot set expiration date more than %n days in the future_" : ["No se puede fijar la fecha de caducidad más de %n día en el futuro.","No se puede fijar la fecha de caducidad más de %n días en el futuro.","No se puede fijar la fecha de caducidad más de %n días en el futuro."],
"Sharing is only allowed with group members" : "Sólo está permitido compartir a los integrantes del grupo",
"Sharing %s failed, because this item is already shared with user %s" : "No se pudo compartir %s, porque este elemento ya está compartido con el usuario %s",
"%1$s shared »%2$s« with you" : "%1$s ha compartido «%2$s» contigo",
@@ -207,7 +208,6 @@ OC.L10N.register(
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "No se ha podido instlaar la app «%1$s» porque no se cumplen las siguientes dependencias: %2$s",
"a safe home for all your data" : "un hogar seguro para todos tus datos",
"File is currently busy, please try again later" : "El archivo se encuentra actualmente ocupado, por favor inténtelo de nuevo más tarde",
- "Cannot read file" : "No se puede leer archivo",
"Application is not enabled" : "La aplicación no está habilitada",
"Authentication error" : "Error de autenticación",
"Token expired. Please reload page." : "Token caducado. Por favor, recarge la página.",
@@ -235,6 +235,8 @@ OC.L10N.register(
"This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Probablemente esto venga a causa de la caché o un acelerador, tales como Zend OPcache o eAccelerator.",
"PHP modules have been installed, but they are still listed as missing?" : "Los módulos PHP se han instalado, pero aparecen listados como si faltaran",
"Please ask your server administrator to restart the web server." : "Consulte al administrador de su servidor para reiniciar el servidor web.",
+ "The required %s config variable is not configured in the config.php file." : "La variable de configuración %s requerida no está configurada en el archivo config.php.",
+ "Please ask your server administrator to check the Nextcloud configuration." : "Por favor, pida al administrador de su servidor que compruebe la configuración de Nextcloud.",
"PostgreSQL >= 9 required." : "PostgreSQL >= 9 requerido.",
"Please upgrade your database version." : "Por favor, actualiza la versión de tu base de datos.",
"Your data directory is readable by other users." : "Tu carpeta de datos puede ser leído por otros usuarios.",
@@ -255,6 +257,7 @@ OC.L10N.register(
"Storage is temporarily not available" : "El almacenamiento no esta disponible temporalmente",
"Storage connection timeout. %s" : "Tiempo de conexión de almacenamiento agotado. %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Se podría solucionar esto dándole al servidor permisos de escritura del directorio de configuración.",
+ "Cannot read file" : "No se puede leer archivo",
"Cannot write into \"config\" directory" : "No se puede escribir el el directorio de configuración",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Se podría solucionar esto dándole al servidor permisos de escritura del directorio de configuración. Ver %s",
"Cannot write into \"apps\" directory" : "No se puede escribir en el directorio de \"apps\"",
@@ -273,4 +276,4 @@ OC.L10N.register(
"Check the value of \"datadirectory\" in your configuration" : "Compruebe el valor de \"datadirectory\" en su configuración.",
"Your data directory is invalid" : "Tu directorio de datos es inválido"
},
-"nplurals=2; plural=(n != 1);");
+"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/lib/l10n/es.json b/lib/l10n/es.json
index ab972b7a7d4..24809243385 100644
--- a/lib/l10n/es.json
+++ b/lib/l10n/es.json
@@ -56,20 +56,20 @@
"today" : "hoy",
"tomorrow" : "mañana",
"yesterday" : "ayer",
- "_in %n day_::_in %n days_" : ["dentro de %n día","dentro de %n días"],
- "_%n day ago_::_%n days ago_" : ["Hace %n día","hace %n días"],
+ "_in %n day_::_in %n days_" : ["dentro de %n día","dentro de %n días","dentro de %n días"],
+ "_%n day ago_::_%n days ago_" : ["Hace %n día","hace %n días","hace %n días"],
"next month" : "mes siguiente",
"last month" : "mes pasado",
- "_in %n month_::_in %n months_" : ["dentro de %n mes","dentro de %n meses"],
- "_%n month ago_::_%n months ago_" : ["Hace %n mes","hace %n meses"],
+ "_in %n month_::_in %n months_" : ["dentro de %n mes","dentro de %n meses","dentro de %n meses"],
+ "_%n month ago_::_%n months ago_" : ["Hace %n mes","hace %n meses","hace %n meses"],
"next year" : "año que viene",
"last year" : "año pasado",
- "_in %n year_::_in %n years_" : ["dentro de %n año","dentro de %n años"],
- "_%n year ago_::_%n years ago_" : ["Hace %n año","hace %n años"],
- "_in %n hour_::_in %n hours_" : ["dentro de %n hora","dentro de %n horas"],
- "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","hace %n horas"],
- "_in %n minute_::_in %n minutes_" : ["dentro de %n minuto","dentro de %n minutos"],
- "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","hace %n minutos"],
+ "_in %n year_::_in %n years_" : ["dentro de %n año","dentro de %n años","dentro de %n años"],
+ "_%n year ago_::_%n years ago_" : ["Hace %n año","hace %n años","hace %n años"],
+ "_in %n hour_::_in %n hours_" : ["dentro de %n hora","dentro de %n horas","dentro de %n horas"],
+ "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","hace %n horas","hace %n horas"],
+ "_in %n minute_::_in %n minutes_" : ["dentro de %n minuto","dentro de %n minutos","dentro de %n minutos"],
+ "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","hace %n minutos","hace %n minutos"],
"in a few seconds" : "en unos segundos",
"seconds ago" : "hace segundos",
"Empty file" : "Archivo vacío",
@@ -89,6 +89,7 @@
"This is an automatically sent email, please do not reply." : "Este es un correo enviado automáticamente, por favor no responda.",
"Help" : "Ayuda",
"Apps" : "Aplicaciones",
+ "Personal settings" : "Ajustes personales",
"Settings" : "Ajustes",
"Log out" : "Cerrar sesión",
"Users" : "Usuarios",
@@ -138,7 +139,7 @@
"Files cannot be shared with delete permissions" : "Los archivos no se pueden compartir con permisos de borrado",
"Files cannot be shared with create permissions" : "Los archivos no se pueden compartir con permisos de creación",
"Expiration date is in the past" : "Ha pasado la fecha de caducidad",
- "_Cannot set expiration date more than %n day in the future_::_Cannot set expiration date more than %n days in the future_" : ["No se puede fijar la fecha de caducidad más de %n día en el futuro.","No se puede fijar la fecha de caducidad más de %n días en el futuro."],
+ "_Cannot set expiration date more than %n day in the future_::_Cannot set expiration date more than %n days in the future_" : ["No se puede fijar la fecha de caducidad más de %n día en el futuro.","No se puede fijar la fecha de caducidad más de %n días en el futuro.","No se puede fijar la fecha de caducidad más de %n días en el futuro."],
"Sharing is only allowed with group members" : "Sólo está permitido compartir a los integrantes del grupo",
"Sharing %s failed, because this item is already shared with user %s" : "No se pudo compartir %s, porque este elemento ya está compartido con el usuario %s",
"%1$s shared »%2$s« with you" : "%1$s ha compartido «%2$s» contigo",
@@ -205,7 +206,6 @@
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "No se ha podido instlaar la app «%1$s» porque no se cumplen las siguientes dependencias: %2$s",
"a safe home for all your data" : "un hogar seguro para todos tus datos",
"File is currently busy, please try again later" : "El archivo se encuentra actualmente ocupado, por favor inténtelo de nuevo más tarde",
- "Cannot read file" : "No se puede leer archivo",
"Application is not enabled" : "La aplicación no está habilitada",
"Authentication error" : "Error de autenticación",
"Token expired. Please reload page." : "Token caducado. Por favor, recarge la página.",
@@ -233,6 +233,8 @@
"This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Probablemente esto venga a causa de la caché o un acelerador, tales como Zend OPcache o eAccelerator.",
"PHP modules have been installed, but they are still listed as missing?" : "Los módulos PHP se han instalado, pero aparecen listados como si faltaran",
"Please ask your server administrator to restart the web server." : "Consulte al administrador de su servidor para reiniciar el servidor web.",
+ "The required %s config variable is not configured in the config.php file." : "La variable de configuración %s requerida no está configurada en el archivo config.php.",
+ "Please ask your server administrator to check the Nextcloud configuration." : "Por favor, pida al administrador de su servidor que compruebe la configuración de Nextcloud.",
"PostgreSQL >= 9 required." : "PostgreSQL >= 9 requerido.",
"Please upgrade your database version." : "Por favor, actualiza la versión de tu base de datos.",
"Your data directory is readable by other users." : "Tu carpeta de datos puede ser leído por otros usuarios.",
@@ -253,6 +255,7 @@
"Storage is temporarily not available" : "El almacenamiento no esta disponible temporalmente",
"Storage connection timeout. %s" : "Tiempo de conexión de almacenamiento agotado. %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Se podría solucionar esto dándole al servidor permisos de escritura del directorio de configuración.",
+ "Cannot read file" : "No se puede leer archivo",
"Cannot write into \"config\" directory" : "No se puede escribir el el directorio de configuración",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Se podría solucionar esto dándole al servidor permisos de escritura del directorio de configuración. Ver %s",
"Cannot write into \"apps\" directory" : "No se puede escribir en el directorio de \"apps\"",
@@ -270,5 +273,5 @@
"Your data directory must be an absolute path" : "Su directorio data debe ser una ruta absoluta",
"Check the value of \"datadirectory\" in your configuration" : "Compruebe el valor de \"datadirectory\" en su configuración.",
"Your data directory is invalid" : "Tu directorio de datos es inválido"
-},"pluralForm" :"nplurals=2; plural=(n != 1);"
+},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
} \ No newline at end of file
diff --git a/lib/l10n/es_419.js b/lib/l10n/es_419.js
index 4dd8bd93c30..b483e623154 100644
--- a/lib/l10n/es_419.js
+++ b/lib/l10n/es_419.js
@@ -27,20 +27,20 @@ OC.L10N.register(
"today" : "hoy",
"tomorrow" : "mañana",
"yesterday" : "ayer",
- "_in %n day_::_in %n days_" : ["en %n día","en %n días"],
- "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días"],
+ "_in %n day_::_in %n days_" : ["en %n día","en %n días","en %n días"],
+ "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días","hace %n días"],
"next month" : "próximo mes",
"last month" : "mes pasado",
- "_in %n month_::_in %n months_" : ["en %n mes","en %n meses"],
- "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"],
+ "_in %n month_::_in %n months_" : ["en %n mes","en %n meses","en %n meses"],
+ "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses","Hace %n meses"],
"next year" : "próximo año",
"last year" : "año pasado",
- "_in %n year_::_in %n years_" : ["en %n año","en %n años"],
- "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años"],
- "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas"],
- "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"],
- "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos"],
- "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"],
+ "_in %n year_::_in %n years_" : ["en %n año","en %n años","en %n años"],
+ "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años","hace %n años"],
+ "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas","en %n horas"],
+ "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas","Hace %n horas"],
+ "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos","en %n minutos"],
+ "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos","Hace %n minutos"],
"in a few seconds" : "en algunos segundos",
"seconds ago" : "hace segundos",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Por favor hablíitalo en tus configuraciones de aplicación o contacta a tu administrador. ",
@@ -189,4 +189,4 @@ OC.L10N.register(
"Check the value of \"datadirectory\" in your configuration" : "Verifica el valor de \"datadirectory\" en tu configuración",
"Your data directory is invalid" : "Tu directorio de datos es inválido"
},
-"nplurals=2; plural=(n != 1);");
+"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/lib/l10n/es_419.json b/lib/l10n/es_419.json
index 58e7959cca4..d05cc6895aa 100644
--- a/lib/l10n/es_419.json
+++ b/lib/l10n/es_419.json
@@ -25,20 +25,20 @@
"today" : "hoy",
"tomorrow" : "mañana",
"yesterday" : "ayer",
- "_in %n day_::_in %n days_" : ["en %n día","en %n días"],
- "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días"],
+ "_in %n day_::_in %n days_" : ["en %n día","en %n días","en %n días"],
+ "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días","hace %n días"],
"next month" : "próximo mes",
"last month" : "mes pasado",
- "_in %n month_::_in %n months_" : ["en %n mes","en %n meses"],
- "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"],
+ "_in %n month_::_in %n months_" : ["en %n mes","en %n meses","en %n meses"],
+ "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses","Hace %n meses"],
"next year" : "próximo año",
"last year" : "año pasado",
- "_in %n year_::_in %n years_" : ["en %n año","en %n años"],
- "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años"],
- "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas"],
- "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"],
- "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos"],
- "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"],
+ "_in %n year_::_in %n years_" : ["en %n año","en %n años","en %n años"],
+ "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años","hace %n años"],
+ "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas","en %n horas"],
+ "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas","Hace %n horas"],
+ "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos","en %n minutos"],
+ "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos","Hace %n minutos"],
"in a few seconds" : "en algunos segundos",
"seconds ago" : "hace segundos",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Por favor hablíitalo en tus configuraciones de aplicación o contacta a tu administrador. ",
@@ -186,5 +186,5 @@
"Your data directory must be an absolute path" : "Tu directorio data debe ser una ruta absoluta",
"Check the value of \"datadirectory\" in your configuration" : "Verifica el valor de \"datadirectory\" en tu configuración",
"Your data directory is invalid" : "Tu directorio de datos es inválido"
-},"pluralForm" :"nplurals=2; plural=(n != 1);"
+},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
} \ No newline at end of file
diff --git a/lib/l10n/es_AR.js b/lib/l10n/es_AR.js
index 2ac8cc15e4e..dd2a959c33b 100644
--- a/lib/l10n/es_AR.js
+++ b/lib/l10n/es_AR.js
@@ -25,13 +25,13 @@ OC.L10N.register(
"Avatar image is not square" : "La imagen del avatar no es un cuadrado",
"today" : "hoy",
"yesterday" : "ayer",
- "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días"],
+ "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días","hace %n días"],
"last month" : "mes pasado",
- "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"],
+ "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses","Hace %n meses"],
"last year" : "año pasado",
- "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años"],
- "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"],
- "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"],
+ "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años","hace %n años"],
+ "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas","Hace %n horas"],
+ "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos","Hace %n minutos"],
"seconds ago" : "hace segundos",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Favor de habilitarlo en sus configuraciones de aplicación o contacte a su administrador. ",
"File name is a reserved word" : "Nombre de archivo es una palabra reservada",
@@ -175,4 +175,4 @@ OC.L10N.register(
"Check the value of \"datadirectory\" in your configuration" : "Verifique el valor de \"datadirectory\" en su configuración",
"Your data directory is invalid" : "Su directorio de datos es inválido"
},
-"nplurals=2; plural=(n != 1);");
+"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/lib/l10n/es_AR.json b/lib/l10n/es_AR.json
index fd497701b36..4d7ea16672f 100644
--- a/lib/l10n/es_AR.json
+++ b/lib/l10n/es_AR.json
@@ -23,13 +23,13 @@
"Avatar image is not square" : "La imagen del avatar no es un cuadrado",
"today" : "hoy",
"yesterday" : "ayer",
- "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días"],
+ "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días","hace %n días"],
"last month" : "mes pasado",
- "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"],
+ "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses","Hace %n meses"],
"last year" : "año pasado",
- "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años"],
- "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"],
- "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"],
+ "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años","hace %n años"],
+ "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas","Hace %n horas"],
+ "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos","Hace %n minutos"],
"seconds ago" : "hace segundos",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Favor de habilitarlo en sus configuraciones de aplicación o contacte a su administrador. ",
"File name is a reserved word" : "Nombre de archivo es una palabra reservada",
@@ -172,5 +172,5 @@
"Your data directory must be an absolute path" : "Su direcctorio data debe ser una ruta absoluta",
"Check the value of \"datadirectory\" in your configuration" : "Verifique el valor de \"datadirectory\" en su configuración",
"Your data directory is invalid" : "Su directorio de datos es inválido"
-},"pluralForm" :"nplurals=2; plural=(n != 1);"
+},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
} \ No newline at end of file
diff --git a/lib/l10n/es_CL.js b/lib/l10n/es_CL.js
index ad95ed44897..3762954bc8f 100644
--- a/lib/l10n/es_CL.js
+++ b/lib/l10n/es_CL.js
@@ -28,20 +28,20 @@ OC.L10N.register(
"today" : "hoy",
"tomorrow" : "mañana",
"yesterday" : "ayer",
- "_in %n day_::_in %n days_" : ["en %n día","en %n días"],
- "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días"],
+ "_in %n day_::_in %n days_" : ["en %n día","en %n días","en %n días"],
+ "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días","hace %n días"],
"next month" : "próximo mes",
"last month" : "mes pasado",
- "_in %n month_::_in %n months_" : ["en %n mes","en %n meses"],
- "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"],
+ "_in %n month_::_in %n months_" : ["en %n mes","en %n meses","en %n meses"],
+ "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses","Hace %n meses"],
"next year" : "próximo año",
"last year" : "año pasado",
- "_in %n year_::_in %n years_" : ["en %n año","en %n años"],
- "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años"],
- "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas"],
- "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"],
- "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos"],
- "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"],
+ "_in %n year_::_in %n years_" : ["en %n año","en %n años","en %n años"],
+ "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años","hace %n años"],
+ "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas","en %n horas"],
+ "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas","Hace %n horas"],
+ "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos","en %n minutos"],
+ "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos","Hace %n minutos"],
"in a few seconds" : "en algunos segundos",
"seconds ago" : "hace segundos",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Por favor hablíitalo en tus configuraciones de aplicación o contacta a tu administrador. ",
@@ -190,4 +190,4 @@ OC.L10N.register(
"Check the value of \"datadirectory\" in your configuration" : "Verifica el valor de \"datadirectory\" en tu configuración",
"Your data directory is invalid" : "Tu directorio de datos es inválido"
},
-"nplurals=2; plural=(n != 1);");
+"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/lib/l10n/es_CL.json b/lib/l10n/es_CL.json
index e996e30e807..5ba63a54295 100644
--- a/lib/l10n/es_CL.json
+++ b/lib/l10n/es_CL.json
@@ -26,20 +26,20 @@
"today" : "hoy",
"tomorrow" : "mañana",
"yesterday" : "ayer",
- "_in %n day_::_in %n days_" : ["en %n día","en %n días"],
- "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días"],
+ "_in %n day_::_in %n days_" : ["en %n día","en %n días","en %n días"],
+ "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días","hace %n días"],
"next month" : "próximo mes",
"last month" : "mes pasado",
- "_in %n month_::_in %n months_" : ["en %n mes","en %n meses"],
- "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"],
+ "_in %n month_::_in %n months_" : ["en %n mes","en %n meses","en %n meses"],
+ "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses","Hace %n meses"],
"next year" : "próximo año",
"last year" : "año pasado",
- "_in %n year_::_in %n years_" : ["en %n año","en %n años"],
- "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años"],
- "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas"],
- "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"],
- "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos"],
- "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"],
+ "_in %n year_::_in %n years_" : ["en %n año","en %n años","en %n años"],
+ "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años","hace %n años"],
+ "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas","en %n horas"],
+ "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas","Hace %n horas"],
+ "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos","en %n minutos"],
+ "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos","Hace %n minutos"],
"in a few seconds" : "en algunos segundos",
"seconds ago" : "hace segundos",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Por favor hablíitalo en tus configuraciones de aplicación o contacta a tu administrador. ",
@@ -187,5 +187,5 @@
"Your data directory must be an absolute path" : "Tu directorio data debe ser una ruta absoluta",
"Check the value of \"datadirectory\" in your configuration" : "Verifica el valor de \"datadirectory\" en tu configuración",
"Your data directory is invalid" : "Tu directorio de datos es inválido"
-},"pluralForm" :"nplurals=2; plural=(n != 1);"
+},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
} \ No newline at end of file
diff --git a/lib/l10n/es_CO.js b/lib/l10n/es_CO.js
index 8838bfab177..91ee8fedd38 100644
--- a/lib/l10n/es_CO.js
+++ b/lib/l10n/es_CO.js
@@ -28,20 +28,20 @@ OC.L10N.register(
"today" : "hoy",
"tomorrow" : "mañana",
"yesterday" : "ayer",
- "_in %n day_::_in %n days_" : ["en %n día","en %n días"],
- "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días"],
+ "_in %n day_::_in %n days_" : ["en %n día","en %n días","en %n días"],
+ "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días","hace %n días"],
"next month" : "próximo mes",
"last month" : "mes pasado",
- "_in %n month_::_in %n months_" : ["en %n mes","en %n meses"],
- "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"],
+ "_in %n month_::_in %n months_" : ["en %n mes","en %n meses","en %n meses"],
+ "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses","Hace %n meses"],
"next year" : "próximo año",
"last year" : "año pasado",
- "_in %n year_::_in %n years_" : ["en %n año","en %n años"],
- "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años"],
- "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas"],
- "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"],
- "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos"],
- "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"],
+ "_in %n year_::_in %n years_" : ["en %n año","en %n años","en %n años"],
+ "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años","hace %n años"],
+ "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas","en %n horas"],
+ "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas","Hace %n horas"],
+ "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos","en %n minutos"],
+ "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos","Hace %n minutos"],
"in a few seconds" : "en algunos segundos",
"seconds ago" : "hace segundos",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Por favor hablíitalo en tus configuraciones de aplicación o contacta a tu administrador. ",
@@ -190,4 +190,4 @@ OC.L10N.register(
"Check the value of \"datadirectory\" in your configuration" : "Verifica el valor de \"datadirectory\" en tu configuración",
"Your data directory is invalid" : "Tu directorio de datos es inválido"
},
-"nplurals=2; plural=(n != 1);");
+"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/lib/l10n/es_CO.json b/lib/l10n/es_CO.json
index 99218d58628..ea7ef3ec112 100644
--- a/lib/l10n/es_CO.json
+++ b/lib/l10n/es_CO.json
@@ -26,20 +26,20 @@
"today" : "hoy",
"tomorrow" : "mañana",
"yesterday" : "ayer",
- "_in %n day_::_in %n days_" : ["en %n día","en %n días"],
- "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días"],
+ "_in %n day_::_in %n days_" : ["en %n día","en %n días","en %n días"],
+ "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días","hace %n días"],
"next month" : "próximo mes",
"last month" : "mes pasado",
- "_in %n month_::_in %n months_" : ["en %n mes","en %n meses"],
- "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"],
+ "_in %n month_::_in %n months_" : ["en %n mes","en %n meses","en %n meses"],
+ "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses","Hace %n meses"],
"next year" : "próximo año",
"last year" : "año pasado",
- "_in %n year_::_in %n years_" : ["en %n año","en %n años"],
- "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años"],
- "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas"],
- "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"],
- "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos"],
- "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"],
+ "_in %n year_::_in %n years_" : ["en %n año","en %n años","en %n años"],
+ "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años","hace %n años"],
+ "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas","en %n horas"],
+ "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas","Hace %n horas"],
+ "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos","en %n minutos"],
+ "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos","Hace %n minutos"],
"in a few seconds" : "en algunos segundos",
"seconds ago" : "hace segundos",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Por favor hablíitalo en tus configuraciones de aplicación o contacta a tu administrador. ",
@@ -187,5 +187,5 @@
"Your data directory must be an absolute path" : "Tu directorio data debe ser una ruta absoluta",
"Check the value of \"datadirectory\" in your configuration" : "Verifica el valor de \"datadirectory\" en tu configuración",
"Your data directory is invalid" : "Tu directorio de datos es inválido"
-},"pluralForm" :"nplurals=2; plural=(n != 1);"
+},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
} \ No newline at end of file
diff --git a/lib/l10n/es_CR.js b/lib/l10n/es_CR.js
index 0d8294160ef..c049eb26853 100644
--- a/lib/l10n/es_CR.js
+++ b/lib/l10n/es_CR.js
@@ -28,20 +28,20 @@ OC.L10N.register(
"today" : "hoy",
"tomorrow" : "mañana",
"yesterday" : "ayer",
- "_in %n day_::_in %n days_" : ["en %n día","en %n días"],
- "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días"],
+ "_in %n day_::_in %n days_" : ["en %n día","en %n días","en %n días"],
+ "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días","hace %n días"],
"next month" : "próximo mes",
"last month" : "mes pasado",
- "_in %n month_::_in %n months_" : ["en %n mes","en %n meses"],
- "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"],
+ "_in %n month_::_in %n months_" : ["en %n mes","en %n meses","en %n meses"],
+ "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses","Hace %n meses"],
"next year" : "próximo año",
"last year" : "año pasado",
- "_in %n year_::_in %n years_" : ["en %n año","en %n años"],
- "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años"],
- "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas"],
- "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"],
- "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos"],
- "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"],
+ "_in %n year_::_in %n years_" : ["en %n año","en %n años","en %n años"],
+ "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años","hace %n años"],
+ "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas","en %n horas"],
+ "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas","Hace %n horas"],
+ "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos","en %n minutos"],
+ "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos","Hace %n minutos"],
"in a few seconds" : "en algunos segundos",
"seconds ago" : "hace segundos",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Por favor hablíitalo en tus configuraciones de aplicación o contacta a tu administrador. ",
@@ -190,4 +190,4 @@ OC.L10N.register(
"Check the value of \"datadirectory\" in your configuration" : "Verifica el valor de \"datadirectory\" en tu configuración",
"Your data directory is invalid" : "Tu directorio de datos es inválido"
},
-"nplurals=2; plural=(n != 1);");
+"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/lib/l10n/es_CR.json b/lib/l10n/es_CR.json
index 93d7c533a73..2a3bb425cfd 100644
--- a/lib/l10n/es_CR.json
+++ b/lib/l10n/es_CR.json
@@ -26,20 +26,20 @@
"today" : "hoy",
"tomorrow" : "mañana",
"yesterday" : "ayer",
- "_in %n day_::_in %n days_" : ["en %n día","en %n días"],
- "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días"],
+ "_in %n day_::_in %n days_" : ["en %n día","en %n días","en %n días"],
+ "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días","hace %n días"],
"next month" : "próximo mes",
"last month" : "mes pasado",
- "_in %n month_::_in %n months_" : ["en %n mes","en %n meses"],
- "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"],
+ "_in %n month_::_in %n months_" : ["en %n mes","en %n meses","en %n meses"],
+ "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses","Hace %n meses"],
"next year" : "próximo año",
"last year" : "año pasado",
- "_in %n year_::_in %n years_" : ["en %n año","en %n años"],
- "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años"],
- "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas"],
- "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"],
- "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos"],
- "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"],
+ "_in %n year_::_in %n years_" : ["en %n año","en %n años","en %n años"],
+ "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años","hace %n años"],
+ "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas","en %n horas"],
+ "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas","Hace %n horas"],
+ "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos","en %n minutos"],
+ "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos","Hace %n minutos"],
"in a few seconds" : "en algunos segundos",
"seconds ago" : "hace segundos",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Por favor hablíitalo en tus configuraciones de aplicación o contacta a tu administrador. ",
@@ -187,5 +187,5 @@
"Your data directory must be an absolute path" : "Tu directorio data debe ser una ruta absoluta",
"Check the value of \"datadirectory\" in your configuration" : "Verifica el valor de \"datadirectory\" en tu configuración",
"Your data directory is invalid" : "Tu directorio de datos es inválido"
-},"pluralForm" :"nplurals=2; plural=(n != 1);"
+},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
} \ No newline at end of file
diff --git a/lib/l10n/es_DO.js b/lib/l10n/es_DO.js
index 8dee29ff4cd..0aea42a6656 100644
--- a/lib/l10n/es_DO.js
+++ b/lib/l10n/es_DO.js
@@ -28,20 +28,20 @@ OC.L10N.register(
"today" : "hoy",
"tomorrow" : "mañana",
"yesterday" : "ayer",
- "_in %n day_::_in %n days_" : ["en %n día","en %n días"],
- "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días"],
+ "_in %n day_::_in %n days_" : ["en %n día","en %n días","en %n días"],
+ "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días","hace %n días"],
"next month" : "próximo mes",
"last month" : "mes pasado",
- "_in %n month_::_in %n months_" : ["en %n mes","en %n meses"],
- "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"],
+ "_in %n month_::_in %n months_" : ["en %n mes","en %n meses","en %n meses"],
+ "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses","Hace %n meses"],
"next year" : "próximo año",
"last year" : "año pasado",
- "_in %n year_::_in %n years_" : ["en %n año","en %n años"],
- "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años"],
- "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas"],
- "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"],
- "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos"],
- "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"],
+ "_in %n year_::_in %n years_" : ["en %n año","en %n años","en %n años"],
+ "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años","hace %n años"],
+ "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas","en %n horas"],
+ "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas","Hace %n horas"],
+ "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos","en %n minutos"],
+ "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos","Hace %n minutos"],
"in a few seconds" : "en algunos segundos",
"seconds ago" : "hace segundos",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Por favor hablíitalo en tus configuraciones de aplicación o contacta a tu administrador. ",
@@ -190,4 +190,4 @@ OC.L10N.register(
"Check the value of \"datadirectory\" in your configuration" : "Verifica el valor de \"datadirectory\" en tu configuración",
"Your data directory is invalid" : "Tu directorio de datos es inválido"
},
-"nplurals=2; plural=(n != 1);");
+"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/lib/l10n/es_DO.json b/lib/l10n/es_DO.json
index 7e0dd5f06bd..08fda06066c 100644
--- a/lib/l10n/es_DO.json
+++ b/lib/l10n/es_DO.json
@@ -26,20 +26,20 @@
"today" : "hoy",
"tomorrow" : "mañana",
"yesterday" : "ayer",
- "_in %n day_::_in %n days_" : ["en %n día","en %n días"],
- "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días"],
+ "_in %n day_::_in %n days_" : ["en %n día","en %n días","en %n días"],
+ "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días","hace %n días"],
"next month" : "próximo mes",
"last month" : "mes pasado",
- "_in %n month_::_in %n months_" : ["en %n mes","en %n meses"],
- "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"],
+ "_in %n month_::_in %n months_" : ["en %n mes","en %n meses","en %n meses"],
+ "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses","Hace %n meses"],
"next year" : "próximo año",
"last year" : "año pasado",
- "_in %n year_::_in %n years_" : ["en %n año","en %n años"],
- "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años"],
- "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas"],
- "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"],
- "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos"],
- "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"],
+ "_in %n year_::_in %n years_" : ["en %n año","en %n años","en %n años"],
+ "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años","hace %n años"],
+ "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas","en %n horas"],
+ "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas","Hace %n horas"],
+ "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos","en %n minutos"],
+ "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos","Hace %n minutos"],
"in a few seconds" : "en algunos segundos",
"seconds ago" : "hace segundos",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Por favor hablíitalo en tus configuraciones de aplicación o contacta a tu administrador. ",
@@ -187,5 +187,5 @@
"Your data directory must be an absolute path" : "Tu directorio data debe ser una ruta absoluta",
"Check the value of \"datadirectory\" in your configuration" : "Verifica el valor de \"datadirectory\" en tu configuración",
"Your data directory is invalid" : "Tu directorio de datos es inválido"
-},"pluralForm" :"nplurals=2; plural=(n != 1);"
+},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
} \ No newline at end of file
diff --git a/lib/l10n/es_EC.js b/lib/l10n/es_EC.js
index 34b163fa9e2..5a2b63b9aff 100644
--- a/lib/l10n/es_EC.js
+++ b/lib/l10n/es_EC.js
@@ -28,20 +28,20 @@ OC.L10N.register(
"today" : "hoy",
"tomorrow" : "mañana",
"yesterday" : "ayer",
- "_in %n day_::_in %n days_" : ["en %n día","en %n días"],
- "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días"],
+ "_in %n day_::_in %n days_" : ["en %n día","en %n días","en %n días"],
+ "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días","hace %n días"],
"next month" : "próximo mes",
"last month" : "mes pasado",
- "_in %n month_::_in %n months_" : ["en %n mes","en %n meses"],
- "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"],
+ "_in %n month_::_in %n months_" : ["en %n mes","en %n meses","en %n meses"],
+ "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses","Hace %n meses"],
"next year" : "próximo año",
"last year" : "año pasado",
- "_in %n year_::_in %n years_" : ["en %n año","en %n años"],
- "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años"],
- "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas"],
- "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"],
- "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos"],
- "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"],
+ "_in %n year_::_in %n years_" : ["en %n año","en %n años","en %n años"],
+ "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años","hace %n años"],
+ "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas","en %n horas"],
+ "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas","Hace %n horas"],
+ "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos","en %n minutos"],
+ "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos","Hace %n minutos"],
"in a few seconds" : "en algunos segundos",
"seconds ago" : "hace segundos",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Por favor hablíitalo en tus configuraciones de aplicación o contacta a tu administrador. ",
@@ -190,4 +190,4 @@ OC.L10N.register(
"Check the value of \"datadirectory\" in your configuration" : "Verifica el valor de \"datadirectory\" en tu configuración",
"Your data directory is invalid" : "Tu directorio de datos es inválido"
},
-"nplurals=2; plural=(n != 1);");
+"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/lib/l10n/es_EC.json b/lib/l10n/es_EC.json
index 903df1d2dda..6fcd9e0468e 100644
--- a/lib/l10n/es_EC.json
+++ b/lib/l10n/es_EC.json
@@ -26,20 +26,20 @@
"today" : "hoy",
"tomorrow" : "mañana",
"yesterday" : "ayer",
- "_in %n day_::_in %n days_" : ["en %n día","en %n días"],
- "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días"],
+ "_in %n day_::_in %n days_" : ["en %n día","en %n días","en %n días"],
+ "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días","hace %n días"],
"next month" : "próximo mes",
"last month" : "mes pasado",
- "_in %n month_::_in %n months_" : ["en %n mes","en %n meses"],
- "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"],
+ "_in %n month_::_in %n months_" : ["en %n mes","en %n meses","en %n meses"],
+ "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses","Hace %n meses"],
"next year" : "próximo año",
"last year" : "año pasado",
- "_in %n year_::_in %n years_" : ["en %n año","en %n años"],
- "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años"],
- "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas"],
- "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"],
- "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos"],
- "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"],
+ "_in %n year_::_in %n years_" : ["en %n año","en %n años","en %n años"],
+ "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años","hace %n años"],
+ "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas","en %n horas"],
+ "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas","Hace %n horas"],
+ "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos","en %n minutos"],
+ "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos","Hace %n minutos"],
"in a few seconds" : "en algunos segundos",
"seconds ago" : "hace segundos",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Por favor hablíitalo en tus configuraciones de aplicación o contacta a tu administrador. ",
@@ -187,5 +187,5 @@
"Your data directory must be an absolute path" : "Tu directorio data debe ser una ruta absoluta",
"Check the value of \"datadirectory\" in your configuration" : "Verifica el valor de \"datadirectory\" en tu configuración",
"Your data directory is invalid" : "Tu directorio de datos es inválido"
-},"pluralForm" :"nplurals=2; plural=(n != 1);"
+},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
} \ No newline at end of file
diff --git a/lib/l10n/es_GT.js b/lib/l10n/es_GT.js
index f029d14ad40..24d85bc24cd 100644
--- a/lib/l10n/es_GT.js
+++ b/lib/l10n/es_GT.js
@@ -28,20 +28,20 @@ OC.L10N.register(
"today" : "hoy",
"tomorrow" : "mañana",
"yesterday" : "ayer",
- "_in %n day_::_in %n days_" : ["en %n día","en %n días"],
- "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días"],
+ "_in %n day_::_in %n days_" : ["en %n día","en %n días","en %n días"],
+ "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días","hace %n días"],
"next month" : "próximo mes",
"last month" : "mes pasado",
- "_in %n month_::_in %n months_" : ["en %n mes","en %n meses"],
- "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"],
+ "_in %n month_::_in %n months_" : ["en %n mes","en %n meses","en %n meses"],
+ "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses","Hace %n meses"],
"next year" : "próximo año",
"last year" : "año pasado",
- "_in %n year_::_in %n years_" : ["en %n año","en %n años"],
- "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años"],
- "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas"],
- "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"],
- "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos"],
- "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"],
+ "_in %n year_::_in %n years_" : ["en %n año","en %n años","en %n años"],
+ "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años","hace %n años"],
+ "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas","en %n horas"],
+ "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas","Hace %n horas"],
+ "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos","en %n minutos"],
+ "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos","Hace %n minutos"],
"in a few seconds" : "en algunos segundos",
"seconds ago" : "hace segundos",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Por favor hablíitalo en tus configuraciones de aplicación o contacta a tu administrador. ",
@@ -190,4 +190,4 @@ OC.L10N.register(
"Check the value of \"datadirectory\" in your configuration" : "Verifica el valor de \"datadirectory\" en tu configuración",
"Your data directory is invalid" : "Tu directorio de datos es inválido"
},
-"nplurals=2; plural=(n != 1);");
+"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/lib/l10n/es_GT.json b/lib/l10n/es_GT.json
index fe49e40a39f..de5c6d6638d 100644
--- a/lib/l10n/es_GT.json
+++ b/lib/l10n/es_GT.json
@@ -26,20 +26,20 @@
"today" : "hoy",
"tomorrow" : "mañana",
"yesterday" : "ayer",
- "_in %n day_::_in %n days_" : ["en %n día","en %n días"],
- "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días"],
+ "_in %n day_::_in %n days_" : ["en %n día","en %n días","en %n días"],
+ "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días","hace %n días"],
"next month" : "próximo mes",
"last month" : "mes pasado",
- "_in %n month_::_in %n months_" : ["en %n mes","en %n meses"],
- "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"],
+ "_in %n month_::_in %n months_" : ["en %n mes","en %n meses","en %n meses"],
+ "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses","Hace %n meses"],
"next year" : "próximo año",
"last year" : "año pasado",
- "_in %n year_::_in %n years_" : ["en %n año","en %n años"],
- "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años"],
- "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas"],
- "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"],
- "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos"],
- "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"],
+ "_in %n year_::_in %n years_" : ["en %n año","en %n años","en %n años"],
+ "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años","hace %n años"],
+ "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas","en %n horas"],
+ "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas","Hace %n horas"],
+ "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos","en %n minutos"],
+ "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos","Hace %n minutos"],
"in a few seconds" : "en algunos segundos",
"seconds ago" : "hace segundos",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Por favor hablíitalo en tus configuraciones de aplicación o contacta a tu administrador. ",
@@ -187,5 +187,5 @@
"Your data directory must be an absolute path" : "Tu directorio data debe ser una ruta absoluta",
"Check the value of \"datadirectory\" in your configuration" : "Verifica el valor de \"datadirectory\" en tu configuración",
"Your data directory is invalid" : "Tu directorio de datos es inválido"
-},"pluralForm" :"nplurals=2; plural=(n != 1);"
+},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
} \ No newline at end of file
diff --git a/lib/l10n/es_HN.js b/lib/l10n/es_HN.js
index bbc3e0bd6c1..e2af5b57461 100644
--- a/lib/l10n/es_HN.js
+++ b/lib/l10n/es_HN.js
@@ -27,20 +27,20 @@ OC.L10N.register(
"today" : "hoy",
"tomorrow" : "mañana",
"yesterday" : "ayer",
- "_in %n day_::_in %n days_" : ["en %n día","en %n días"],
- "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días"],
+ "_in %n day_::_in %n days_" : ["en %n día","en %n días","en %n días"],
+ "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días","hace %n días"],
"next month" : "próximo mes",
"last month" : "mes pasado",
- "_in %n month_::_in %n months_" : ["en %n mes","en %n meses"],
- "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"],
+ "_in %n month_::_in %n months_" : ["en %n mes","en %n meses","en %n meses"],
+ "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses","Hace %n meses"],
"next year" : "próximo año",
"last year" : "año pasado",
- "_in %n year_::_in %n years_" : ["en %n año","en %n años"],
- "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años"],
- "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas"],
- "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"],
- "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos"],
- "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"],
+ "_in %n year_::_in %n years_" : ["en %n año","en %n años","en %n años"],
+ "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años","hace %n años"],
+ "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas","en %n horas"],
+ "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas","Hace %n horas"],
+ "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos","en %n minutos"],
+ "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos","Hace %n minutos"],
"in a few seconds" : "en algunos segundos",
"seconds ago" : "hace segundos",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Por favor hablíitalo en tus configuraciones de aplicación o contacta a tu administrador. ",
@@ -189,4 +189,4 @@ OC.L10N.register(
"Check the value of \"datadirectory\" in your configuration" : "Verifica el valor de \"datadirectory\" en tu configuración",
"Your data directory is invalid" : "Tu directorio de datos es inválido"
},
-"nplurals=2; plural=(n != 1);");
+"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/lib/l10n/es_HN.json b/lib/l10n/es_HN.json
index bd48a18b3c4..ebbdbdf588d 100644
--- a/lib/l10n/es_HN.json
+++ b/lib/l10n/es_HN.json
@@ -25,20 +25,20 @@
"today" : "hoy",
"tomorrow" : "mañana",
"yesterday" : "ayer",
- "_in %n day_::_in %n days_" : ["en %n día","en %n días"],
- "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días"],
+ "_in %n day_::_in %n days_" : ["en %n día","en %n días","en %n días"],
+ "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días","hace %n días"],
"next month" : "próximo mes",
"last month" : "mes pasado",
- "_in %n month_::_in %n months_" : ["en %n mes","en %n meses"],
- "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"],
+ "_in %n month_::_in %n months_" : ["en %n mes","en %n meses","en %n meses"],
+ "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses","Hace %n meses"],
"next year" : "próximo año",
"last year" : "año pasado",
- "_in %n year_::_in %n years_" : ["en %n año","en %n años"],
- "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años"],
- "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas"],
- "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"],
- "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos"],
- "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"],
+ "_in %n year_::_in %n years_" : ["en %n año","en %n años","en %n años"],
+ "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años","hace %n años"],
+ "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas","en %n horas"],
+ "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas","Hace %n horas"],
+ "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos","en %n minutos"],
+ "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos","Hace %n minutos"],
"in a few seconds" : "en algunos segundos",
"seconds ago" : "hace segundos",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Por favor hablíitalo en tus configuraciones de aplicación o contacta a tu administrador. ",
@@ -186,5 +186,5 @@
"Your data directory must be an absolute path" : "Tu directorio data debe ser una ruta absoluta",
"Check the value of \"datadirectory\" in your configuration" : "Verifica el valor de \"datadirectory\" en tu configuración",
"Your data directory is invalid" : "Tu directorio de datos es inválido"
-},"pluralForm" :"nplurals=2; plural=(n != 1);"
+},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
} \ No newline at end of file
diff --git a/lib/l10n/es_MX.js b/lib/l10n/es_MX.js
index d4156f349fa..2693ae01512 100644
--- a/lib/l10n/es_MX.js
+++ b/lib/l10n/es_MX.js
@@ -28,20 +28,20 @@ OC.L10N.register(
"today" : "hoy",
"tomorrow" : "mañana",
"yesterday" : "ayer",
- "_in %n day_::_in %n days_" : ["en %n día","en %n días"],
- "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días"],
+ "_in %n day_::_in %n days_" : ["en %n día","en %n días","en %n días"],
+ "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días","hace %n días"],
"next month" : "próximo mes",
"last month" : "mes pasado",
- "_in %n month_::_in %n months_" : ["en %n mes","en %n meses"],
- "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"],
+ "_in %n month_::_in %n months_" : ["en %n mes","en %n meses","en %n meses"],
+ "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses","Hace %n meses"],
"next year" : "próximo año",
"last year" : "año pasado",
- "_in %n year_::_in %n years_" : ["en %n año","en %n años"],
- "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años"],
- "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas"],
- "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"],
- "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos"],
- "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"],
+ "_in %n year_::_in %n years_" : ["en %n año","en %n años","en %n años"],
+ "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años","hace %n años"],
+ "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas","en %n horas"],
+ "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas","Hace %n horas"],
+ "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos","en %n minutos"],
+ "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos","Hace %n minutos"],
"in a few seconds" : "en algunos segundos",
"seconds ago" : "hace segundos",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Por favor hablíitalo en tus configuraciones de aplicación o contacta a tu administrador. ",
@@ -190,4 +190,4 @@ OC.L10N.register(
"Check the value of \"datadirectory\" in your configuration" : "Verifica el valor de \"datadirectory\" en tu configuración",
"Your data directory is invalid" : "Tu directorio de datos es inválido"
},
-"nplurals=2; plural=(n != 1);");
+"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/lib/l10n/es_MX.json b/lib/l10n/es_MX.json
index 69b7d9014f5..2beebf60c3f 100644
--- a/lib/l10n/es_MX.json
+++ b/lib/l10n/es_MX.json
@@ -26,20 +26,20 @@
"today" : "hoy",
"tomorrow" : "mañana",
"yesterday" : "ayer",
- "_in %n day_::_in %n days_" : ["en %n día","en %n días"],
- "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días"],
+ "_in %n day_::_in %n days_" : ["en %n día","en %n días","en %n días"],
+ "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días","hace %n días"],
"next month" : "próximo mes",
"last month" : "mes pasado",
- "_in %n month_::_in %n months_" : ["en %n mes","en %n meses"],
- "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"],
+ "_in %n month_::_in %n months_" : ["en %n mes","en %n meses","en %n meses"],
+ "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses","Hace %n meses"],
"next year" : "próximo año",
"last year" : "año pasado",
- "_in %n year_::_in %n years_" : ["en %n año","en %n años"],
- "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años"],
- "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas"],
- "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"],
- "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos"],
- "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"],
+ "_in %n year_::_in %n years_" : ["en %n año","en %n años","en %n años"],
+ "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años","hace %n años"],
+ "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas","en %n horas"],
+ "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas","Hace %n horas"],
+ "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos","en %n minutos"],
+ "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos","Hace %n minutos"],
"in a few seconds" : "en algunos segundos",
"seconds ago" : "hace segundos",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Por favor hablíitalo en tus configuraciones de aplicación o contacta a tu administrador. ",
@@ -187,5 +187,5 @@
"Your data directory must be an absolute path" : "Tu directorio data debe ser una ruta absoluta",
"Check the value of \"datadirectory\" in your configuration" : "Verifica el valor de \"datadirectory\" en tu configuración",
"Your data directory is invalid" : "Tu directorio de datos es inválido"
-},"pluralForm" :"nplurals=2; plural=(n != 1);"
+},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
} \ No newline at end of file
diff --git a/lib/l10n/es_NI.js b/lib/l10n/es_NI.js
index 8fcf7aa15ad..051762ee07c 100644
--- a/lib/l10n/es_NI.js
+++ b/lib/l10n/es_NI.js
@@ -27,20 +27,20 @@ OC.L10N.register(
"today" : "hoy",
"tomorrow" : "mañana",
"yesterday" : "ayer",
- "_in %n day_::_in %n days_" : ["en %n día","en %n días"],
- "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días"],
+ "_in %n day_::_in %n days_" : ["en %n día","en %n días","en %n días"],
+ "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días","hace %n días"],
"next month" : "próximo mes",
"last month" : "mes pasado",
- "_in %n month_::_in %n months_" : ["en %n mes","en %n meses"],
- "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"],
+ "_in %n month_::_in %n months_" : ["en %n mes","en %n meses","en %n meses"],
+ "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses","Hace %n meses"],
"next year" : "próximo año",
"last year" : "año pasado",
- "_in %n year_::_in %n years_" : ["en %n año","en %n años"],
- "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años"],
- "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas"],
- "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"],
- "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos"],
- "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"],
+ "_in %n year_::_in %n years_" : ["en %n año","en %n años","en %n años"],
+ "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años","hace %n años"],
+ "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas","en %n horas"],
+ "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas","Hace %n horas"],
+ "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos","en %n minutos"],
+ "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos","Hace %n minutos"],
"in a few seconds" : "en algunos segundos",
"seconds ago" : "hace segundos",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Por favor hablíitalo en tus configuraciones de aplicación o contacta a tu administrador. ",
@@ -189,4 +189,4 @@ OC.L10N.register(
"Check the value of \"datadirectory\" in your configuration" : "Verifica el valor de \"datadirectory\" en tu configuración",
"Your data directory is invalid" : "Tu directorio de datos es inválido"
},
-"nplurals=2; plural=(n != 1);");
+"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/lib/l10n/es_NI.json b/lib/l10n/es_NI.json
index 272097aea29..0095526e3a3 100644
--- a/lib/l10n/es_NI.json
+++ b/lib/l10n/es_NI.json
@@ -25,20 +25,20 @@
"today" : "hoy",
"tomorrow" : "mañana",
"yesterday" : "ayer",
- "_in %n day_::_in %n days_" : ["en %n día","en %n días"],
- "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días"],
+ "_in %n day_::_in %n days_" : ["en %n día","en %n días","en %n días"],
+ "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días","hace %n días"],
"next month" : "próximo mes",
"last month" : "mes pasado",
- "_in %n month_::_in %n months_" : ["en %n mes","en %n meses"],
- "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"],
+ "_in %n month_::_in %n months_" : ["en %n mes","en %n meses","en %n meses"],
+ "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses","Hace %n meses"],
"next year" : "próximo año",
"last year" : "año pasado",
- "_in %n year_::_in %n years_" : ["en %n año","en %n años"],
- "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años"],
- "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas"],
- "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"],
- "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos"],
- "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"],
+ "_in %n year_::_in %n years_" : ["en %n año","en %n años","en %n años"],
+ "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años","hace %n años"],
+ "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas","en %n horas"],
+ "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas","Hace %n horas"],
+ "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos","en %n minutos"],
+ "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos","Hace %n minutos"],
"in a few seconds" : "en algunos segundos",
"seconds ago" : "hace segundos",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Por favor hablíitalo en tus configuraciones de aplicación o contacta a tu administrador. ",
@@ -186,5 +186,5 @@
"Your data directory must be an absolute path" : "Tu directorio data debe ser una ruta absoluta",
"Check the value of \"datadirectory\" in your configuration" : "Verifica el valor de \"datadirectory\" en tu configuración",
"Your data directory is invalid" : "Tu directorio de datos es inválido"
-},"pluralForm" :"nplurals=2; plural=(n != 1);"
+},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
} \ No newline at end of file
diff --git a/lib/l10n/es_PA.js b/lib/l10n/es_PA.js
index 7bb2cd2cde7..6a97d9115ea 100644
--- a/lib/l10n/es_PA.js
+++ b/lib/l10n/es_PA.js
@@ -27,20 +27,20 @@ OC.L10N.register(
"today" : "hoy",
"tomorrow" : "mañana",
"yesterday" : "ayer",
- "_in %n day_::_in %n days_" : ["en %n día","en %n días"],
- "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días"],
+ "_in %n day_::_in %n days_" : ["en %n día","en %n días","en %n días"],
+ "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días","hace %n días"],
"next month" : "próximo mes",
"last month" : "mes pasado",
- "_in %n month_::_in %n months_" : ["en %n mes","en %n meses"],
- "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"],
+ "_in %n month_::_in %n months_" : ["en %n mes","en %n meses","en %n meses"],
+ "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses","Hace %n meses"],
"next year" : "próximo año",
"last year" : "año pasado",
- "_in %n year_::_in %n years_" : ["en %n año","en %n años"],
- "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años"],
- "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas"],
- "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"],
- "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos"],
- "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"],
+ "_in %n year_::_in %n years_" : ["en %n año","en %n años","en %n años"],
+ "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años","hace %n años"],
+ "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas","en %n horas"],
+ "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas","Hace %n horas"],
+ "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos","en %n minutos"],
+ "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos","Hace %n minutos"],
"in a few seconds" : "en algunos segundos",
"seconds ago" : "hace segundos",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Por favor hablíitalo en tus configuraciones de aplicación o contacta a tu administrador. ",
@@ -189,4 +189,4 @@ OC.L10N.register(
"Check the value of \"datadirectory\" in your configuration" : "Verifica el valor de \"datadirectory\" en tu configuración",
"Your data directory is invalid" : "Tu directorio de datos es inválido"
},
-"nplurals=2; plural=(n != 1);");
+"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/lib/l10n/es_PA.json b/lib/l10n/es_PA.json
index 13b17f37b5f..cae1186af7b 100644
--- a/lib/l10n/es_PA.json
+++ b/lib/l10n/es_PA.json
@@ -25,20 +25,20 @@
"today" : "hoy",
"tomorrow" : "mañana",
"yesterday" : "ayer",
- "_in %n day_::_in %n days_" : ["en %n día","en %n días"],
- "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días"],
+ "_in %n day_::_in %n days_" : ["en %n día","en %n días","en %n días"],
+ "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días","hace %n días"],
"next month" : "próximo mes",
"last month" : "mes pasado",
- "_in %n month_::_in %n months_" : ["en %n mes","en %n meses"],
- "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"],
+ "_in %n month_::_in %n months_" : ["en %n mes","en %n meses","en %n meses"],
+ "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses","Hace %n meses"],
"next year" : "próximo año",
"last year" : "año pasado",
- "_in %n year_::_in %n years_" : ["en %n año","en %n años"],
- "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años"],
- "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas"],
- "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"],
- "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos"],
- "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"],
+ "_in %n year_::_in %n years_" : ["en %n año","en %n años","en %n años"],
+ "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años","hace %n años"],
+ "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas","en %n horas"],
+ "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas","Hace %n horas"],
+ "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos","en %n minutos"],
+ "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos","Hace %n minutos"],
"in a few seconds" : "en algunos segundos",
"seconds ago" : "hace segundos",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Por favor hablíitalo en tus configuraciones de aplicación o contacta a tu administrador. ",
@@ -186,5 +186,5 @@
"Your data directory must be an absolute path" : "Tu directorio data debe ser una ruta absoluta",
"Check the value of \"datadirectory\" in your configuration" : "Verifica el valor de \"datadirectory\" en tu configuración",
"Your data directory is invalid" : "Tu directorio de datos es inválido"
-},"pluralForm" :"nplurals=2; plural=(n != 1);"
+},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
} \ No newline at end of file
diff --git a/lib/l10n/es_PE.js b/lib/l10n/es_PE.js
index 217f458ecb4..67e7dc14095 100644
--- a/lib/l10n/es_PE.js
+++ b/lib/l10n/es_PE.js
@@ -27,20 +27,20 @@ OC.L10N.register(
"today" : "hoy",
"tomorrow" : "mañana",
"yesterday" : "ayer",
- "_in %n day_::_in %n days_" : ["en %n día","en %n días"],
- "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días"],
+ "_in %n day_::_in %n days_" : ["en %n día","en %n días","en %n días"],
+ "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días","hace %n días"],
"next month" : "próximo mes",
"last month" : "mes pasado",
- "_in %n month_::_in %n months_" : ["en %n mes","en %n meses"],
- "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"],
+ "_in %n month_::_in %n months_" : ["en %n mes","en %n meses","en %n meses"],
+ "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses","Hace %n meses"],
"next year" : "próximo año",
"last year" : "año pasado",
- "_in %n year_::_in %n years_" : ["en %n año","en %n años"],
- "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años"],
- "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas"],
- "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"],
- "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos"],
- "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"],
+ "_in %n year_::_in %n years_" : ["en %n año","en %n años","en %n años"],
+ "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años","hace %n años"],
+ "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas","en %n horas"],
+ "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas","Hace %n horas"],
+ "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos","en %n minutos"],
+ "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos","Hace %n minutos"],
"in a few seconds" : "en algunos segundos",
"seconds ago" : "hace segundos",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Por favor hablíitalo en tus configuraciones de aplicación o contacta a tu administrador. ",
@@ -189,4 +189,4 @@ OC.L10N.register(
"Check the value of \"datadirectory\" in your configuration" : "Verifica el valor de \"datadirectory\" en tu configuración",
"Your data directory is invalid" : "Tu directorio de datos es inválido"
},
-"nplurals=2; plural=(n != 1);");
+"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/lib/l10n/es_PE.json b/lib/l10n/es_PE.json
index bdaaab523fa..737f934d10a 100644
--- a/lib/l10n/es_PE.json
+++ b/lib/l10n/es_PE.json
@@ -25,20 +25,20 @@
"today" : "hoy",
"tomorrow" : "mañana",
"yesterday" : "ayer",
- "_in %n day_::_in %n days_" : ["en %n día","en %n días"],
- "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días"],
+ "_in %n day_::_in %n days_" : ["en %n día","en %n días","en %n días"],
+ "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días","hace %n días"],
"next month" : "próximo mes",
"last month" : "mes pasado",
- "_in %n month_::_in %n months_" : ["en %n mes","en %n meses"],
- "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"],
+ "_in %n month_::_in %n months_" : ["en %n mes","en %n meses","en %n meses"],
+ "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses","Hace %n meses"],
"next year" : "próximo año",
"last year" : "año pasado",
- "_in %n year_::_in %n years_" : ["en %n año","en %n años"],
- "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años"],
- "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas"],
- "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"],
- "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos"],
- "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"],
+ "_in %n year_::_in %n years_" : ["en %n año","en %n años","en %n años"],
+ "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años","hace %n años"],
+ "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas","en %n horas"],
+ "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas","Hace %n horas"],
+ "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos","en %n minutos"],
+ "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos","Hace %n minutos"],
"in a few seconds" : "en algunos segundos",
"seconds ago" : "hace segundos",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Por favor hablíitalo en tus configuraciones de aplicación o contacta a tu administrador. ",
@@ -186,5 +186,5 @@
"Your data directory must be an absolute path" : "Tu directorio data debe ser una ruta absoluta",
"Check the value of \"datadirectory\" in your configuration" : "Verifica el valor de \"datadirectory\" en tu configuración",
"Your data directory is invalid" : "Tu directorio de datos es inválido"
-},"pluralForm" :"nplurals=2; plural=(n != 1);"
+},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
} \ No newline at end of file
diff --git a/lib/l10n/es_PR.js b/lib/l10n/es_PR.js
index 1526c425fb0..2af553a2f58 100644
--- a/lib/l10n/es_PR.js
+++ b/lib/l10n/es_PR.js
@@ -27,20 +27,20 @@ OC.L10N.register(
"today" : "hoy",
"tomorrow" : "mañana",
"yesterday" : "ayer",
- "_in %n day_::_in %n days_" : ["en %n día","en %n días"],
- "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días"],
+ "_in %n day_::_in %n days_" : ["en %n día","en %n días","en %n días"],
+ "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días","hace %n días"],
"next month" : "próximo mes",
"last month" : "mes pasado",
- "_in %n month_::_in %n months_" : ["en %n mes","en %n meses"],
- "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"],
+ "_in %n month_::_in %n months_" : ["en %n mes","en %n meses","en %n meses"],
+ "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses","Hace %n meses"],
"next year" : "próximo año",
"last year" : "año pasado",
- "_in %n year_::_in %n years_" : ["en %n año","en %n años"],
- "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años"],
- "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas"],
- "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"],
- "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos"],
- "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"],
+ "_in %n year_::_in %n years_" : ["en %n año","en %n años","en %n años"],
+ "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años","hace %n años"],
+ "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas","en %n horas"],
+ "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas","Hace %n horas"],
+ "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos","en %n minutos"],
+ "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos","Hace %n minutos"],
"in a few seconds" : "en algunos segundos",
"seconds ago" : "hace segundos",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Por favor hablíitalo en tus configuraciones de aplicación o contacta a tu administrador. ",
@@ -189,4 +189,4 @@ OC.L10N.register(
"Check the value of \"datadirectory\" in your configuration" : "Verifica el valor de \"datadirectory\" en tu configuración",
"Your data directory is invalid" : "Tu directorio de datos es inválido"
},
-"nplurals=2; plural=(n != 1);");
+"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/lib/l10n/es_PR.json b/lib/l10n/es_PR.json
index f325d0c98b0..620eeb7c134 100644
--- a/lib/l10n/es_PR.json
+++ b/lib/l10n/es_PR.json
@@ -25,20 +25,20 @@
"today" : "hoy",
"tomorrow" : "mañana",
"yesterday" : "ayer",
- "_in %n day_::_in %n days_" : ["en %n día","en %n días"],
- "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días"],
+ "_in %n day_::_in %n days_" : ["en %n día","en %n días","en %n días"],
+ "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días","hace %n días"],
"next month" : "próximo mes",
"last month" : "mes pasado",
- "_in %n month_::_in %n months_" : ["en %n mes","en %n meses"],
- "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"],
+ "_in %n month_::_in %n months_" : ["en %n mes","en %n meses","en %n meses"],
+ "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses","Hace %n meses"],
"next year" : "próximo año",
"last year" : "año pasado",
- "_in %n year_::_in %n years_" : ["en %n año","en %n años"],
- "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años"],
- "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas"],
- "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"],
- "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos"],
- "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"],
+ "_in %n year_::_in %n years_" : ["en %n año","en %n años","en %n años"],
+ "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años","hace %n años"],
+ "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas","en %n horas"],
+ "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas","Hace %n horas"],
+ "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos","en %n minutos"],
+ "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos","Hace %n minutos"],
"in a few seconds" : "en algunos segundos",
"seconds ago" : "hace segundos",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Por favor hablíitalo en tus configuraciones de aplicación o contacta a tu administrador. ",
@@ -186,5 +186,5 @@
"Your data directory must be an absolute path" : "Tu directorio data debe ser una ruta absoluta",
"Check the value of \"datadirectory\" in your configuration" : "Verifica el valor de \"datadirectory\" en tu configuración",
"Your data directory is invalid" : "Tu directorio de datos es inválido"
-},"pluralForm" :"nplurals=2; plural=(n != 1);"
+},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
} \ No newline at end of file
diff --git a/lib/l10n/es_PY.js b/lib/l10n/es_PY.js
index f7bafd23ddc..26a1fdbfb3e 100644
--- a/lib/l10n/es_PY.js
+++ b/lib/l10n/es_PY.js
@@ -27,20 +27,20 @@ OC.L10N.register(
"today" : "hoy",
"tomorrow" : "mañana",
"yesterday" : "ayer",
- "_in %n day_::_in %n days_" : ["en %n día","en %n días"],
- "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días"],
+ "_in %n day_::_in %n days_" : ["en %n día","en %n días","en %n días"],
+ "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días","hace %n días"],
"next month" : "próximo mes",
"last month" : "mes pasado",
- "_in %n month_::_in %n months_" : ["en %n mes","en %n meses"],
- "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"],
+ "_in %n month_::_in %n months_" : ["en %n mes","en %n meses","en %n meses"],
+ "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses","Hace %n meses"],
"next year" : "próximo año",
"last year" : "año pasado",
- "_in %n year_::_in %n years_" : ["en %n año","en %n años"],
- "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años"],
- "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas"],
- "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"],
- "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos"],
- "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"],
+ "_in %n year_::_in %n years_" : ["en %n año","en %n años","en %n años"],
+ "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años","hace %n años"],
+ "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas","en %n horas"],
+ "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas","Hace %n horas"],
+ "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos","en %n minutos"],
+ "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos","Hace %n minutos"],
"in a few seconds" : "en algunos segundos",
"seconds ago" : "hace segundos",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Por favor hablíitalo en tus configuraciones de aplicación o contacta a tu administrador. ",
@@ -189,4 +189,4 @@ OC.L10N.register(
"Check the value of \"datadirectory\" in your configuration" : "Verifica el valor de \"datadirectory\" en tu configuración",
"Your data directory is invalid" : "Tu directorio de datos es inválido"
},
-"nplurals=2; plural=(n != 1);");
+"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/lib/l10n/es_PY.json b/lib/l10n/es_PY.json
index 4f4378b9e2d..64b9486042c 100644
--- a/lib/l10n/es_PY.json
+++ b/lib/l10n/es_PY.json
@@ -25,20 +25,20 @@
"today" : "hoy",
"tomorrow" : "mañana",
"yesterday" : "ayer",
- "_in %n day_::_in %n days_" : ["en %n día","en %n días"],
- "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días"],
+ "_in %n day_::_in %n days_" : ["en %n día","en %n días","en %n días"],
+ "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días","hace %n días"],
"next month" : "próximo mes",
"last month" : "mes pasado",
- "_in %n month_::_in %n months_" : ["en %n mes","en %n meses"],
- "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"],
+ "_in %n month_::_in %n months_" : ["en %n mes","en %n meses","en %n meses"],
+ "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses","Hace %n meses"],
"next year" : "próximo año",
"last year" : "año pasado",
- "_in %n year_::_in %n years_" : ["en %n año","en %n años"],
- "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años"],
- "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas"],
- "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"],
- "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos"],
- "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"],
+ "_in %n year_::_in %n years_" : ["en %n año","en %n años","en %n años"],
+ "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años","hace %n años"],
+ "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas","en %n horas"],
+ "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas","Hace %n horas"],
+ "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos","en %n minutos"],
+ "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos","Hace %n minutos"],
"in a few seconds" : "en algunos segundos",
"seconds ago" : "hace segundos",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Por favor hablíitalo en tus configuraciones de aplicación o contacta a tu administrador. ",
@@ -186,5 +186,5 @@
"Your data directory must be an absolute path" : "Tu directorio data debe ser una ruta absoluta",
"Check the value of \"datadirectory\" in your configuration" : "Verifica el valor de \"datadirectory\" en tu configuración",
"Your data directory is invalid" : "Tu directorio de datos es inválido"
-},"pluralForm" :"nplurals=2; plural=(n != 1);"
+},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
} \ No newline at end of file
diff --git a/lib/l10n/es_SV.js b/lib/l10n/es_SV.js
index 2ac7487e946..774e0b91879 100644
--- a/lib/l10n/es_SV.js
+++ b/lib/l10n/es_SV.js
@@ -28,20 +28,20 @@ OC.L10N.register(
"today" : "hoy",
"tomorrow" : "mañana",
"yesterday" : "ayer",
- "_in %n day_::_in %n days_" : ["en %n día","en %n días"],
- "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días"],
+ "_in %n day_::_in %n days_" : ["en %n día","en %n días","en %n días"],
+ "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días","hace %n días"],
"next month" : "próximo mes",
"last month" : "mes pasado",
- "_in %n month_::_in %n months_" : ["en %n mes","en %n meses"],
- "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"],
+ "_in %n month_::_in %n months_" : ["en %n mes","en %n meses","en %n meses"],
+ "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses","Hace %n meses"],
"next year" : "próximo año",
"last year" : "año pasado",
- "_in %n year_::_in %n years_" : ["en %n año","en %n años"],
- "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años"],
- "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas"],
- "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"],
- "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos"],
- "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"],
+ "_in %n year_::_in %n years_" : ["en %n año","en %n años","en %n años"],
+ "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años","hace %n años"],
+ "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas","en %n horas"],
+ "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas","Hace %n horas"],
+ "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos","en %n minutos"],
+ "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos","Hace %n minutos"],
"in a few seconds" : "en algunos segundos",
"seconds ago" : "hace segundos",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Por favor hablíitalo en tus configuraciones de aplicación o contacta a tu administrador. ",
@@ -190,4 +190,4 @@ OC.L10N.register(
"Check the value of \"datadirectory\" in your configuration" : "Verifica el valor de \"datadirectory\" en tu configuración",
"Your data directory is invalid" : "Tu directorio de datos es inválido"
},
-"nplurals=2; plural=(n != 1);");
+"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/lib/l10n/es_SV.json b/lib/l10n/es_SV.json
index ff98ae2fd25..5d7f1d512ba 100644
--- a/lib/l10n/es_SV.json
+++ b/lib/l10n/es_SV.json
@@ -26,20 +26,20 @@
"today" : "hoy",
"tomorrow" : "mañana",
"yesterday" : "ayer",
- "_in %n day_::_in %n days_" : ["en %n día","en %n días"],
- "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días"],
+ "_in %n day_::_in %n days_" : ["en %n día","en %n días","en %n días"],
+ "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días","hace %n días"],
"next month" : "próximo mes",
"last month" : "mes pasado",
- "_in %n month_::_in %n months_" : ["en %n mes","en %n meses"],
- "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"],
+ "_in %n month_::_in %n months_" : ["en %n mes","en %n meses","en %n meses"],
+ "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses","Hace %n meses"],
"next year" : "próximo año",
"last year" : "año pasado",
- "_in %n year_::_in %n years_" : ["en %n año","en %n años"],
- "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años"],
- "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas"],
- "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"],
- "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos"],
- "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"],
+ "_in %n year_::_in %n years_" : ["en %n año","en %n años","en %n años"],
+ "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años","hace %n años"],
+ "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas","en %n horas"],
+ "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas","Hace %n horas"],
+ "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos","en %n minutos"],
+ "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos","Hace %n minutos"],
"in a few seconds" : "en algunos segundos",
"seconds ago" : "hace segundos",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Por favor hablíitalo en tus configuraciones de aplicación o contacta a tu administrador. ",
@@ -187,5 +187,5 @@
"Your data directory must be an absolute path" : "Tu directorio data debe ser una ruta absoluta",
"Check the value of \"datadirectory\" in your configuration" : "Verifica el valor de \"datadirectory\" en tu configuración",
"Your data directory is invalid" : "Tu directorio de datos es inválido"
-},"pluralForm" :"nplurals=2; plural=(n != 1);"
+},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
} \ No newline at end of file
diff --git a/lib/l10n/es_UY.js b/lib/l10n/es_UY.js
index ac274bd7964..dc99fd41307 100644
--- a/lib/l10n/es_UY.js
+++ b/lib/l10n/es_UY.js
@@ -27,20 +27,20 @@ OC.L10N.register(
"today" : "hoy",
"tomorrow" : "mañana",
"yesterday" : "ayer",
- "_in %n day_::_in %n days_" : ["en %n día","en %n días"],
- "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días"],
+ "_in %n day_::_in %n days_" : ["en %n día","en %n días","en %n días"],
+ "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días","hace %n días"],
"next month" : "próximo mes",
"last month" : "mes pasado",
- "_in %n month_::_in %n months_" : ["en %n mes","en %n meses"],
- "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"],
+ "_in %n month_::_in %n months_" : ["en %n mes","en %n meses","en %n meses"],
+ "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses","Hace %n meses"],
"next year" : "próximo año",
"last year" : "año pasado",
- "_in %n year_::_in %n years_" : ["en %n año","en %n años"],
- "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años"],
- "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas"],
- "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"],
- "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos"],
- "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"],
+ "_in %n year_::_in %n years_" : ["en %n año","en %n años","en %n años"],
+ "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años","hace %n años"],
+ "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas","en %n horas"],
+ "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas","Hace %n horas"],
+ "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos","en %n minutos"],
+ "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos","Hace %n minutos"],
"in a few seconds" : "en algunos segundos",
"seconds ago" : "hace segundos",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Por favor hablíitalo en tus configuraciones de aplicación o contacta a tu administrador. ",
@@ -189,4 +189,4 @@ OC.L10N.register(
"Check the value of \"datadirectory\" in your configuration" : "Verifica el valor de \"datadirectory\" en tu configuración",
"Your data directory is invalid" : "Tu directorio de datos es inválido"
},
-"nplurals=2; plural=(n != 1);");
+"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/lib/l10n/es_UY.json b/lib/l10n/es_UY.json
index 57fe8a5fe94..47fed462c2f 100644
--- a/lib/l10n/es_UY.json
+++ b/lib/l10n/es_UY.json
@@ -25,20 +25,20 @@
"today" : "hoy",
"tomorrow" : "mañana",
"yesterday" : "ayer",
- "_in %n day_::_in %n days_" : ["en %n día","en %n días"],
- "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días"],
+ "_in %n day_::_in %n days_" : ["en %n día","en %n días","en %n días"],
+ "_%n day ago_::_%n days ago_" : ["hace %n día","hace %n días","hace %n días"],
"next month" : "próximo mes",
"last month" : "mes pasado",
- "_in %n month_::_in %n months_" : ["en %n mes","en %n meses"],
- "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses"],
+ "_in %n month_::_in %n months_" : ["en %n mes","en %n meses","en %n meses"],
+ "_%n month ago_::_%n months ago_" : ["Hace %n mes","Hace %n meses","Hace %n meses"],
"next year" : "próximo año",
"last year" : "año pasado",
- "_in %n year_::_in %n years_" : ["en %n año","en %n años"],
- "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años"],
- "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas"],
- "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas"],
- "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos"],
- "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos"],
+ "_in %n year_::_in %n years_" : ["en %n año","en %n años","en %n años"],
+ "_%n year ago_::_%n years ago_" : ["hace %n año","hace %n años","hace %n años"],
+ "_in %n hour_::_in %n hours_" : ["en %n hora","en %n horas","en %n horas"],
+ "_%n hour ago_::_%n hours ago_" : ["Hace %n hora","Hace %n horas","Hace %n horas"],
+ "_in %n minute_::_in %n minutes_" : ["en %n minuto","en %n minutos","en %n minutos"],
+ "_%n minute ago_::_%n minutes ago_" : ["Hace %n minuto","Hace %n minutos","Hace %n minutos"],
"in a few seconds" : "en algunos segundos",
"seconds ago" : "hace segundos",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "El módulo con ID: %sno existe. Por favor hablíitalo en tus configuraciones de aplicación o contacta a tu administrador. ",
@@ -186,5 +186,5 @@
"Your data directory must be an absolute path" : "Tu directorio data debe ser una ruta absoluta",
"Check the value of \"datadirectory\" in your configuration" : "Verifica el valor de \"datadirectory\" en tu configuración",
"Your data directory is invalid" : "Tu directorio de datos es inválido"
-},"pluralForm" :"nplurals=2; plural=(n != 1);"
+},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
} \ No newline at end of file
diff --git a/lib/l10n/eu.js b/lib/l10n/eu.js
index 232592ad6e3..bbef4fead98 100644
--- a/lib/l10n/eu.js
+++ b/lib/l10n/eu.js
@@ -91,6 +91,7 @@ OC.L10N.register(
"This is an automatically sent email, please do not reply." : "Hau automatikoki bidalitako e-posta bat da, ez erantzun mesedez.",
"Help" : "Laguntza",
"Apps" : "Aplikazioak",
+ "Personal settings" : "Ezarpen pertsonalak",
"Settings" : "Ezarpenak",
"Log out" : "Amaitu saioa",
"Users" : "Erabiltzaileak",
@@ -207,7 +208,7 @@ OC.L10N.register(
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "\"%1$s\" aplikazioa ezin da instalatu, menpekotasun hauek betetzen ez direlako:%2$s",
"a safe home for all your data" : "zure datu guztientzako toki segurua",
"File is currently busy, please try again later" : "Fitxategia lanpetuta dago, saiatu berriro geroago",
- "Cannot read file" : "Ezin da fitxategia irakurri",
+ "Cannot download file" : "Ezin da fitxategia deskargatu",
"Application is not enabled" : "Aplikazioa ez dago gaituta",
"Authentication error" : "Autentifikazio errorea",
"Token expired. Please reload page." : "Tokena iraungitu da. Mesedez birkargatu orria.",
@@ -235,6 +236,8 @@ OC.L10N.register(
"This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Hau ziur aski cache/accelerator batek eragin du, hala nola Zend OPcache edo eAccelerator.",
"PHP modules have been installed, but they are still listed as missing?" : "PHP moduluak instalatu dira, baina oraindik faltan bezala markatuta daude?",
"Please ask your server administrator to restart the web server." : "Mesedez eskatu zerbitzariaren kudeatzaileari web zerbitzaria berrabiarazteko.",
+ "The required %s config variable is not configured in the config.php file." : "Beharrezko %s config aldagaia ez dago konfiguratuta config.php fitxategian.",
+ "Please ask your server administrator to check the Nextcloud configuration." : "Mesedez, eskatu zure zerbitzari-administratzaileari Nextcloud-en konfigurazioa egiaztatzeko.",
"PostgreSQL >= 9 required." : "PostgreSQL >= 9 behar da",
"Please upgrade your database version." : "Mesedez eguneratu zure datu-basearen bertsioa.",
"Your data directory is readable by other users." : "Zure datuen karpeta beste erabiltzaileek irakur dezakete.",
@@ -255,6 +258,7 @@ OC.L10N.register(
"Storage is temporarily not available" : "Biltegia ez dago erabilgarri aldi baterako",
"Storage connection timeout. %s" : "Biltegiratze-konexioa denboraz kanpo geratu da. %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Hau normalean web zerbitzarira config karpetan idazteko baimenak emanez konpondu daiteke.",
+ "Cannot read file" : "Ezin da fitxategia irakurri",
"Cannot write into \"config\" directory" : "Ezin da idatzi \"config\" karpetan",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Hau normalean konpondu daiteke web zerbitzariari konfigurazio direktoriorako sarbidea emanez. Ikus %s",
"Cannot write into \"apps\" directory" : "Ezin da idatzi \"apps\" karpetan",
diff --git a/lib/l10n/eu.json b/lib/l10n/eu.json
index 3a5e2d9e84c..1afbdf9cf14 100644
--- a/lib/l10n/eu.json
+++ b/lib/l10n/eu.json
@@ -89,6 +89,7 @@
"This is an automatically sent email, please do not reply." : "Hau automatikoki bidalitako e-posta bat da, ez erantzun mesedez.",
"Help" : "Laguntza",
"Apps" : "Aplikazioak",
+ "Personal settings" : "Ezarpen pertsonalak",
"Settings" : "Ezarpenak",
"Log out" : "Amaitu saioa",
"Users" : "Erabiltzaileak",
@@ -205,7 +206,7 @@
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "\"%1$s\" aplikazioa ezin da instalatu, menpekotasun hauek betetzen ez direlako:%2$s",
"a safe home for all your data" : "zure datu guztientzako toki segurua",
"File is currently busy, please try again later" : "Fitxategia lanpetuta dago, saiatu berriro geroago",
- "Cannot read file" : "Ezin da fitxategia irakurri",
+ "Cannot download file" : "Ezin da fitxategia deskargatu",
"Application is not enabled" : "Aplikazioa ez dago gaituta",
"Authentication error" : "Autentifikazio errorea",
"Token expired. Please reload page." : "Tokena iraungitu da. Mesedez birkargatu orria.",
@@ -233,6 +234,8 @@
"This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Hau ziur aski cache/accelerator batek eragin du, hala nola Zend OPcache edo eAccelerator.",
"PHP modules have been installed, but they are still listed as missing?" : "PHP moduluak instalatu dira, baina oraindik faltan bezala markatuta daude?",
"Please ask your server administrator to restart the web server." : "Mesedez eskatu zerbitzariaren kudeatzaileari web zerbitzaria berrabiarazteko.",
+ "The required %s config variable is not configured in the config.php file." : "Beharrezko %s config aldagaia ez dago konfiguratuta config.php fitxategian.",
+ "Please ask your server administrator to check the Nextcloud configuration." : "Mesedez, eskatu zure zerbitzari-administratzaileari Nextcloud-en konfigurazioa egiaztatzeko.",
"PostgreSQL >= 9 required." : "PostgreSQL >= 9 behar da",
"Please upgrade your database version." : "Mesedez eguneratu zure datu-basearen bertsioa.",
"Your data directory is readable by other users." : "Zure datuen karpeta beste erabiltzaileek irakur dezakete.",
@@ -253,6 +256,7 @@
"Storage is temporarily not available" : "Biltegia ez dago erabilgarri aldi baterako",
"Storage connection timeout. %s" : "Biltegiratze-konexioa denboraz kanpo geratu da. %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Hau normalean web zerbitzarira config karpetan idazteko baimenak emanez konpondu daiteke.",
+ "Cannot read file" : "Ezin da fitxategia irakurri",
"Cannot write into \"config\" directory" : "Ezin da idatzi \"config\" karpetan",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Hau normalean konpondu daiteke web zerbitzariari konfigurazio direktoriorako sarbidea emanez. Ikus %s",
"Cannot write into \"apps\" directory" : "Ezin da idatzi \"apps\" karpetan",
diff --git a/lib/l10n/fa.js b/lib/l10n/fa.js
index d4bb5db0142..95a78ce1015 100644
--- a/lib/l10n/fa.js
+++ b/lib/l10n/fa.js
@@ -42,6 +42,7 @@ OC.L10N.register(
"Unknown filetype" : "نوع فایل ناشناخته",
"Invalid image" : "عکس نامعتبر",
"Avatar image is not square" : "تصویر آواتار مربع نیست",
+ "View profile" : "مشاهده پروفایل",
"today" : "امروز",
"tomorrow" : "فردا",
"yesterday" : "دیروز",
diff --git a/lib/l10n/fa.json b/lib/l10n/fa.json
index d8dabdaed32..3fbaf99cfa9 100644
--- a/lib/l10n/fa.json
+++ b/lib/l10n/fa.json
@@ -40,6 +40,7 @@
"Unknown filetype" : "نوع فایل ناشناخته",
"Invalid image" : "عکس نامعتبر",
"Avatar image is not square" : "تصویر آواتار مربع نیست",
+ "View profile" : "مشاهده پروفایل",
"today" : "امروز",
"tomorrow" : "فردا",
"yesterday" : "دیروز",
diff --git a/lib/l10n/fi.js b/lib/l10n/fi.js
index 93c4d632aef..f01cae12c98 100644
--- a/lib/l10n/fi.js
+++ b/lib/l10n/fi.js
@@ -79,6 +79,7 @@ OC.L10N.register(
"This is an automatically sent email, please do not reply." : "Tämä on automaattisesti lähetetty viesti. Älä vastaa tähän viestiin.",
"Help" : "Ohje",
"Apps" : "Sovellukset",
+ "Personal settings" : "Henkilökohtaiset asetukset",
"Settings" : "Asetukset",
"Log out" : "Kirjaudu ulos",
"Users" : "Käyttäjät",
@@ -116,7 +117,11 @@ OC.L10N.register(
"Set an admin password." : "Aseta ylläpitäjän salasana.",
"Sharing backend %s not found" : "Jakamisen taustaosaa %s ei löytynyt",
"Sharing backend for %s not found" : "Jakamisen taustaosaa kohteelle %s ei löytynyt",
+ "%1$s shared »%2$s« with you and wants to add:" : "%1$s jakoi kohteen »%2$s« kanssasi ja haluaa lisätä:",
+ "%1$s shared »%2$s« with you and wants to add" : "%1$s jakoi kohteen »%2$s« kanssasi ja haluaa lisätä",
+ "»%s« added a note to a file shared with you" : "»%s« lisäsi huomion jakamaasi tiedostoon",
"Open »%s«" : "Avaa »%s«",
+ "%1$s via %2$s" : "%1$s palvelun %2$s kautta",
"You are not allowed to share %s" : "Oikeutesi eivät riitä kohteen %s jakamiseen.",
"Cannot increase permissions of %s" : "Kohteen %s käyttöoikeuksien lisääminen ei onnistu",
"Files cannot be shared with delete permissions" : "Tiedostoja ei voi jakaa poistamisoikeuksilla",
@@ -189,7 +194,7 @@ OC.L10N.register(
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "Sovellusta \"%1$s\" ei voi asentaa, koska seuraavat riippuvuudet eivät täyty: %2$s",
"a safe home for all your data" : "turvallinen koti kaikille tiedostoillesi",
"File is currently busy, please try again later" : "Tiedosto on parhaillaan käytössä, yritä myöhemmin uudelleen",
- "Cannot read file" : "Tiedostoa ei voi lukea",
+ "Cannot download file" : "Tiedostoa ei voi ladata",
"Application is not enabled" : "Sovellusta ei ole otettu käyttöön",
"Authentication error" : "Tunnistautumisvirhe",
"Token expired. Please reload page." : "Valtuutus vanheni. Lataa sivu uudelleen.",
@@ -221,6 +226,7 @@ OC.L10N.register(
"Storage connection error. %s" : "Tallennustilan yhteysvirhe. %s",
"Storage is temporarily not available" : "Tallennustila on tilapäisesti pois käytöstä",
"Storage connection timeout. %s" : "Tallennustilan yhteyden aikakatkaisu. %s",
+ "Cannot read file" : "Tiedostoa ei voi lukea",
"Cannot write into \"config\" directory" : "Hakemistoon \"config\" kirjoittaminen ei onnistu",
"Cannot write into \"apps\" directory" : "Hakemistoon \"apps\" kirjoittaminen ei onnistu",
"Cannot create \"data\" directory" : "Hakemiston \"data\" luominen ei onnistu",
diff --git a/lib/l10n/fi.json b/lib/l10n/fi.json
index 02470bb0054..fc4606693d9 100644
--- a/lib/l10n/fi.json
+++ b/lib/l10n/fi.json
@@ -77,6 +77,7 @@
"This is an automatically sent email, please do not reply." : "Tämä on automaattisesti lähetetty viesti. Älä vastaa tähän viestiin.",
"Help" : "Ohje",
"Apps" : "Sovellukset",
+ "Personal settings" : "Henkilökohtaiset asetukset",
"Settings" : "Asetukset",
"Log out" : "Kirjaudu ulos",
"Users" : "Käyttäjät",
@@ -114,7 +115,11 @@
"Set an admin password." : "Aseta ylläpitäjän salasana.",
"Sharing backend %s not found" : "Jakamisen taustaosaa %s ei löytynyt",
"Sharing backend for %s not found" : "Jakamisen taustaosaa kohteelle %s ei löytynyt",
+ "%1$s shared »%2$s« with you and wants to add:" : "%1$s jakoi kohteen »%2$s« kanssasi ja haluaa lisätä:",
+ "%1$s shared »%2$s« with you and wants to add" : "%1$s jakoi kohteen »%2$s« kanssasi ja haluaa lisätä",
+ "»%s« added a note to a file shared with you" : "»%s« lisäsi huomion jakamaasi tiedostoon",
"Open »%s«" : "Avaa »%s«",
+ "%1$s via %2$s" : "%1$s palvelun %2$s kautta",
"You are not allowed to share %s" : "Oikeutesi eivät riitä kohteen %s jakamiseen.",
"Cannot increase permissions of %s" : "Kohteen %s käyttöoikeuksien lisääminen ei onnistu",
"Files cannot be shared with delete permissions" : "Tiedostoja ei voi jakaa poistamisoikeuksilla",
@@ -187,7 +192,7 @@
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "Sovellusta \"%1$s\" ei voi asentaa, koska seuraavat riippuvuudet eivät täyty: %2$s",
"a safe home for all your data" : "turvallinen koti kaikille tiedostoillesi",
"File is currently busy, please try again later" : "Tiedosto on parhaillaan käytössä, yritä myöhemmin uudelleen",
- "Cannot read file" : "Tiedostoa ei voi lukea",
+ "Cannot download file" : "Tiedostoa ei voi ladata",
"Application is not enabled" : "Sovellusta ei ole otettu käyttöön",
"Authentication error" : "Tunnistautumisvirhe",
"Token expired. Please reload page." : "Valtuutus vanheni. Lataa sivu uudelleen.",
@@ -219,6 +224,7 @@
"Storage connection error. %s" : "Tallennustilan yhteysvirhe. %s",
"Storage is temporarily not available" : "Tallennustila on tilapäisesti pois käytöstä",
"Storage connection timeout. %s" : "Tallennustilan yhteyden aikakatkaisu. %s",
+ "Cannot read file" : "Tiedostoa ei voi lukea",
"Cannot write into \"config\" directory" : "Hakemistoon \"config\" kirjoittaminen ei onnistu",
"Cannot write into \"apps\" directory" : "Hakemistoon \"apps\" kirjoittaminen ei onnistu",
"Cannot create \"data\" directory" : "Hakemiston \"data\" luominen ei onnistu",
diff --git a/lib/l10n/fr.js b/lib/l10n/fr.js
index de641540de1..7a2c82ebbbf 100644
--- a/lib/l10n/fr.js
+++ b/lib/l10n/fr.js
@@ -58,20 +58,20 @@ OC.L10N.register(
"today" : "aujourd'hui",
"tomorrow" : "demain",
"yesterday" : "hier",
- "_in %n day_::_in %n days_" : ["dans %n jour","dans %n jours"],
- "_%n day ago_::_%n days ago_" : ["il y a %n jour","il y a %n jours"],
+ "_in %n day_::_in %n days_" : ["dans %n jour","dans %n jours","dans %n jours"],
+ "_%n day ago_::_%n days ago_" : ["il y a %n jour","il y a %n jours","il y a %n jours"],
"next month" : "mois suivant",
"last month" : "le mois dernier",
- "_in %n month_::_in %n months_" : ["dans %n mois","dans %n mois"],
- "_%n month ago_::_%n months ago_" : ["Il y a %n mois","Il y a %n mois"],
+ "_in %n month_::_in %n months_" : ["dans %n mois","dans %n mois","dans %n mois"],
+ "_%n month ago_::_%n months ago_" : ["Il y a %n mois","Il y a %n mois","Il y a %n mois"],
"next year" : "année suivante",
"last year" : "l'année dernière",
- "_in %n year_::_in %n years_" : ["dans %n an","dans %n ans"],
- "_%n year ago_::_%n years ago_" : ["il y a %n an","il y a %n ans"],
- "_in %n hour_::_in %n hours_" : ["dans %n heure","dans %n heures"],
- "_%n hour ago_::_%n hours ago_" : ["Il y a %n heure","Il y a %n heures"],
- "_in %n minute_::_in %n minutes_" : ["dans %n minute","dans %n minutes"],
- "_%n minute ago_::_%n minutes ago_" : ["il y a %n minute","il y a %n minutes"],
+ "_in %n year_::_in %n years_" : ["dans %n an","dans %n ans","dans %n ans"],
+ "_%n year ago_::_%n years ago_" : ["il y a %n an","il y a %n ans","il y a %n ans"],
+ "_in %n hour_::_in %n hours_" : ["dans %n heure","dans %n heures","dans %n heures"],
+ "_%n hour ago_::_%n hours ago_" : ["Il y a %n heure","Il y a %n heures","Il y a %n heures"],
+ "_in %n minute_::_in %n minutes_" : ["dans %n minute","dans %n minutes","dans %n minutes"],
+ "_%n minute ago_::_%n minutes ago_" : ["il y a %n minute","il y a %n minutes","il y a %n minutes"],
"in a few seconds" : "dans quelques secondes",
"seconds ago" : "il y a quelques secondes",
"Empty file" : "Fichier vide",
@@ -91,6 +91,7 @@ OC.L10N.register(
"This is an automatically sent email, please do not reply." : "Ceci est un e-mail envoyé automatiquement, veuillez ne pas y répondre.",
"Help" : "Aide",
"Apps" : "Applications",
+ "Personal settings" : "Paramètres personnels",
"Settings" : "Paramètres",
"Log out" : "Se déconnecter",
"Users" : "Utilisateurs",
@@ -140,7 +141,7 @@ OC.L10N.register(
"Files cannot be shared with delete permissions" : "Les fichiers ne peuvent pas être partagés avec les autorisations de suppression",
"Files cannot be shared with create permissions" : "Les fichiers ne peuvent pas être partagés avec les autorisations de création",
"Expiration date is in the past" : "La date d'expiration est dans le passé",
- "_Cannot set expiration date more than %n day in the future_::_Cannot set expiration date more than %n days in the future_" : ["Impossible de définir la date d'expiration à dans plus de %s jour","Impossible de définir la date d'expiration à dans plus de %s jours"],
+ "_Cannot set expiration date more than %n day in the future_::_Cannot set expiration date more than %n days in the future_" : ["Impossible de définir la date d'expiration à dans plus de %s jour","Impossible de définir la date d'expiration à dans plus de %s jours","Impossible de définir la date d'expiration à dans plus de %s jours"],
"Sharing is only allowed with group members" : "Le partage n'est que possible qu'avec les membres du groupe",
"Sharing %s failed, because this item is already shared with user %s" : "Impossible de partager %s car il est déjà partagé avec l'utilisateur %s",
"%1$s shared »%2$s« with you" : "%1$s a partagé « %2$s » avec vous",
@@ -207,7 +208,7 @@ OC.L10N.register(
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "L'application \"%1$s\" ne peut pas être installée à cause des dépendances suivantes non satisfaites : %2$s",
"a safe home for all your data" : "un lieu sûr pour toutes vos données",
"File is currently busy, please try again later" : "Le fichier est actuellement utilisé, veuillez réessayer plus tard",
- "Cannot read file" : "Impossible de lire le fichier",
+ "Cannot download file" : "Impossible de télécharger le fichier",
"Application is not enabled" : "L'application n'est pas activée",
"Authentication error" : "Erreur d'authentification",
"Token expired. Please reload page." : "La session a expiré. Veuillez recharger la page.",
@@ -257,6 +258,7 @@ OC.L10N.register(
"Storage is temporarily not available" : "Le support de stockage est temporairement indisponible",
"Storage connection timeout. %s" : "Le délai d'attente pour la connexion à l'espace de stockage a été dépassé. %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Ce problème est généralement résolu en donnant au serveur web un accès en écriture au répertoire de configuration.",
+ "Cannot read file" : "Impossible de lire le fichier",
"Cannot write into \"config\" directory" : "Impossible d’écrire dans le répertoire \"config\"",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Ce problème est généralement résolu en donnant au serveur web un accès en écriture au répertoire \"config\". Voir %s",
"Cannot write into \"apps\" directory" : "Impossible d’écrire dans le répertoire \"apps\"",
@@ -275,4 +277,4 @@ OC.L10N.register(
"Check the value of \"datadirectory\" in your configuration" : "Verifiez la valeur de \"datadirectory\" dans votre configuration",
"Your data directory is invalid" : "Votre répertoire n'est pas valide"
},
-"nplurals=2; plural=(n > 1);");
+"nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/lib/l10n/fr.json b/lib/l10n/fr.json
index 89e6fa76aea..3c560d11f55 100644
--- a/lib/l10n/fr.json
+++ b/lib/l10n/fr.json
@@ -56,20 +56,20 @@
"today" : "aujourd'hui",
"tomorrow" : "demain",
"yesterday" : "hier",
- "_in %n day_::_in %n days_" : ["dans %n jour","dans %n jours"],
- "_%n day ago_::_%n days ago_" : ["il y a %n jour","il y a %n jours"],
+ "_in %n day_::_in %n days_" : ["dans %n jour","dans %n jours","dans %n jours"],
+ "_%n day ago_::_%n days ago_" : ["il y a %n jour","il y a %n jours","il y a %n jours"],
"next month" : "mois suivant",
"last month" : "le mois dernier",
- "_in %n month_::_in %n months_" : ["dans %n mois","dans %n mois"],
- "_%n month ago_::_%n months ago_" : ["Il y a %n mois","Il y a %n mois"],
+ "_in %n month_::_in %n months_" : ["dans %n mois","dans %n mois","dans %n mois"],
+ "_%n month ago_::_%n months ago_" : ["Il y a %n mois","Il y a %n mois","Il y a %n mois"],
"next year" : "année suivante",
"last year" : "l'année dernière",
- "_in %n year_::_in %n years_" : ["dans %n an","dans %n ans"],
- "_%n year ago_::_%n years ago_" : ["il y a %n an","il y a %n ans"],
- "_in %n hour_::_in %n hours_" : ["dans %n heure","dans %n heures"],
- "_%n hour ago_::_%n hours ago_" : ["Il y a %n heure","Il y a %n heures"],
- "_in %n minute_::_in %n minutes_" : ["dans %n minute","dans %n minutes"],
- "_%n minute ago_::_%n minutes ago_" : ["il y a %n minute","il y a %n minutes"],
+ "_in %n year_::_in %n years_" : ["dans %n an","dans %n ans","dans %n ans"],
+ "_%n year ago_::_%n years ago_" : ["il y a %n an","il y a %n ans","il y a %n ans"],
+ "_in %n hour_::_in %n hours_" : ["dans %n heure","dans %n heures","dans %n heures"],
+ "_%n hour ago_::_%n hours ago_" : ["Il y a %n heure","Il y a %n heures","Il y a %n heures"],
+ "_in %n minute_::_in %n minutes_" : ["dans %n minute","dans %n minutes","dans %n minutes"],
+ "_%n minute ago_::_%n minutes ago_" : ["il y a %n minute","il y a %n minutes","il y a %n minutes"],
"in a few seconds" : "dans quelques secondes",
"seconds ago" : "il y a quelques secondes",
"Empty file" : "Fichier vide",
@@ -89,6 +89,7 @@
"This is an automatically sent email, please do not reply." : "Ceci est un e-mail envoyé automatiquement, veuillez ne pas y répondre.",
"Help" : "Aide",
"Apps" : "Applications",
+ "Personal settings" : "Paramètres personnels",
"Settings" : "Paramètres",
"Log out" : "Se déconnecter",
"Users" : "Utilisateurs",
@@ -138,7 +139,7 @@
"Files cannot be shared with delete permissions" : "Les fichiers ne peuvent pas être partagés avec les autorisations de suppression",
"Files cannot be shared with create permissions" : "Les fichiers ne peuvent pas être partagés avec les autorisations de création",
"Expiration date is in the past" : "La date d'expiration est dans le passé",
- "_Cannot set expiration date more than %n day in the future_::_Cannot set expiration date more than %n days in the future_" : ["Impossible de définir la date d'expiration à dans plus de %s jour","Impossible de définir la date d'expiration à dans plus de %s jours"],
+ "_Cannot set expiration date more than %n day in the future_::_Cannot set expiration date more than %n days in the future_" : ["Impossible de définir la date d'expiration à dans plus de %s jour","Impossible de définir la date d'expiration à dans plus de %s jours","Impossible de définir la date d'expiration à dans plus de %s jours"],
"Sharing is only allowed with group members" : "Le partage n'est que possible qu'avec les membres du groupe",
"Sharing %s failed, because this item is already shared with user %s" : "Impossible de partager %s car il est déjà partagé avec l'utilisateur %s",
"%1$s shared »%2$s« with you" : "%1$s a partagé « %2$s » avec vous",
@@ -205,7 +206,7 @@
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "L'application \"%1$s\" ne peut pas être installée à cause des dépendances suivantes non satisfaites : %2$s",
"a safe home for all your data" : "un lieu sûr pour toutes vos données",
"File is currently busy, please try again later" : "Le fichier est actuellement utilisé, veuillez réessayer plus tard",
- "Cannot read file" : "Impossible de lire le fichier",
+ "Cannot download file" : "Impossible de télécharger le fichier",
"Application is not enabled" : "L'application n'est pas activée",
"Authentication error" : "Erreur d'authentification",
"Token expired. Please reload page." : "La session a expiré. Veuillez recharger la page.",
@@ -255,6 +256,7 @@
"Storage is temporarily not available" : "Le support de stockage est temporairement indisponible",
"Storage connection timeout. %s" : "Le délai d'attente pour la connexion à l'espace de stockage a été dépassé. %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Ce problème est généralement résolu en donnant au serveur web un accès en écriture au répertoire de configuration.",
+ "Cannot read file" : "Impossible de lire le fichier",
"Cannot write into \"config\" directory" : "Impossible d’écrire dans le répertoire \"config\"",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Ce problème est généralement résolu en donnant au serveur web un accès en écriture au répertoire \"config\". Voir %s",
"Cannot write into \"apps\" directory" : "Impossible d’écrire dans le répertoire \"apps\"",
@@ -272,5 +274,5 @@
"Your data directory must be an absolute path" : "Le chemin de votre répertoire doit être un lien absolu",
"Check the value of \"datadirectory\" in your configuration" : "Verifiez la valeur de \"datadirectory\" dans votre configuration",
"Your data directory is invalid" : "Votre répertoire n'est pas valide"
-},"pluralForm" :"nplurals=2; plural=(n > 1);"
+},"pluralForm" :"nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
} \ No newline at end of file
diff --git a/lib/l10n/hr.js b/lib/l10n/hr.js
index 60e5aa3e240..e448f3fd030 100644
--- a/lib/l10n/hr.js
+++ b/lib/l10n/hr.js
@@ -206,7 +206,6 @@ OC.L10N.register(
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "Aplikaciju „%1$s” nije moguće instalirati jer nisu ispunjene sljedeće ovisnosti: %2$s",
"a safe home for all your data" : "siguran dom za sve vaše podatke",
"File is currently busy, please try again later" : "Datoteka je trenutno zauzeta, pokušajte ponovo kasnije",
- "Cannot read file" : "Datoteku nije moguće pročitati",
"Application is not enabled" : "Aplikacija nije omogućena",
"Authentication error" : "Pogrešna autentifikacija",
"Token expired. Please reload page." : "Token je istekao. Ponovno učitajte stranicu.",
@@ -236,6 +235,7 @@ OC.L10N.register(
"Storage is temporarily not available" : "Pohrana privremeno nije dostupna",
"Storage connection timeout. %s" : "Istek veze pohrane. %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Ovo se obično može ispraviti tako da se web-poslužitelju dopusti pristup za pisanje u direktoriju config.",
+ "Cannot read file" : "Datoteku nije moguće pročitati",
"Cannot write into \"config\" directory" : "Pisanje u direktorij „config” nije moguće",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Ovo se obično može popraviti tako da se web poslužitelju dopusti pristup za pisanje u konfiguracijski direktorij. Pogledajte %s",
"Cannot write into \"apps\" directory" : "Nije moguće pisati u direktorij „apps”",
diff --git a/lib/l10n/hr.json b/lib/l10n/hr.json
index d78f720c81f..2760ab38adb 100644
--- a/lib/l10n/hr.json
+++ b/lib/l10n/hr.json
@@ -204,7 +204,6 @@
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "Aplikaciju „%1$s” nije moguće instalirati jer nisu ispunjene sljedeće ovisnosti: %2$s",
"a safe home for all your data" : "siguran dom za sve vaše podatke",
"File is currently busy, please try again later" : "Datoteka je trenutno zauzeta, pokušajte ponovo kasnije",
- "Cannot read file" : "Datoteku nije moguće pročitati",
"Application is not enabled" : "Aplikacija nije omogućena",
"Authentication error" : "Pogrešna autentifikacija",
"Token expired. Please reload page." : "Token je istekao. Ponovno učitajte stranicu.",
@@ -234,6 +233,7 @@
"Storage is temporarily not available" : "Pohrana privremeno nije dostupna",
"Storage connection timeout. %s" : "Istek veze pohrane. %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Ovo se obično može ispraviti tako da se web-poslužitelju dopusti pristup za pisanje u direktoriju config.",
+ "Cannot read file" : "Datoteku nije moguće pročitati",
"Cannot write into \"config\" directory" : "Pisanje u direktorij „config” nije moguće",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Ovo se obično može popraviti tako da se web poslužitelju dopusti pristup za pisanje u konfiguracijski direktorij. Pogledajte %s",
"Cannot write into \"apps\" directory" : "Nije moguće pisati u direktorij „apps”",
diff --git a/lib/l10n/hu.js b/lib/l10n/hu.js
index 6e4113507b5..86d8c7e3b9f 100644
--- a/lib/l10n/hu.js
+++ b/lib/l10n/hu.js
@@ -91,6 +91,7 @@ OC.L10N.register(
"This is an automatically sent email, please do not reply." : "Ez egy automatikusan küldött levél, ne válaszoljon rá.",
"Help" : "Súgó",
"Apps" : "Alkalmazások",
+ "Personal settings" : "Személyes beállítások",
"Settings" : "Beállítások",
"Log out" : "Kijelentkezés",
"Users" : "Felhasználók",
@@ -207,7 +208,7 @@ OC.L10N.register(
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "A(z) „%1$s” alkalmazást nem lehet telepíteni, mert a következő függőségek nem teljesülnek: %2$s",
"a safe home for all your data" : "egy biztonságos hely az adataidnak",
"File is currently busy, please try again later" : "A fájl jelenleg foglalt, próbálja újra később",
- "Cannot read file" : "A fájl nem olvasható",
+ "Cannot download file" : "A fájl nem tölthető le",
"Application is not enabled" : "Az alkalmazás nincs engedélyezve",
"Authentication error" : "Hitelesítési hiba",
"Token expired. Please reload page." : "A token lejárt. Frissítse az oldalt.",
@@ -257,6 +258,7 @@ OC.L10N.register(
"Storage is temporarily not available" : "A tároló átmenetileg nem érhető el",
"Storage connection timeout. %s" : "Időtúllépés a tárolókapcsolatban. %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Ez általában úgy javítható, hogy a webkiszolgálónak írási hozzáférést biztosít a konfigurációs könyvtárhoz.",
+ "Cannot read file" : "A fájl nem olvasható",
"Cannot write into \"config\" directory" : "A „config” könyvtár nem írható",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Ez általában úgy javítható, hogy a webkiszolgálónak írási hozzáférést biztosít a konfigurációs könyvtárhoz. Lásd: %s.",
"Cannot write into \"apps\" directory" : "Az „apps” könyvtár nem írható",
diff --git a/lib/l10n/hu.json b/lib/l10n/hu.json
index 01d41e53248..09e0e6db90b 100644
--- a/lib/l10n/hu.json
+++ b/lib/l10n/hu.json
@@ -89,6 +89,7 @@
"This is an automatically sent email, please do not reply." : "Ez egy automatikusan küldött levél, ne válaszoljon rá.",
"Help" : "Súgó",
"Apps" : "Alkalmazások",
+ "Personal settings" : "Személyes beállítások",
"Settings" : "Beállítások",
"Log out" : "Kijelentkezés",
"Users" : "Felhasználók",
@@ -205,7 +206,7 @@
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "A(z) „%1$s” alkalmazást nem lehet telepíteni, mert a következő függőségek nem teljesülnek: %2$s",
"a safe home for all your data" : "egy biztonságos hely az adataidnak",
"File is currently busy, please try again later" : "A fájl jelenleg foglalt, próbálja újra később",
- "Cannot read file" : "A fájl nem olvasható",
+ "Cannot download file" : "A fájl nem tölthető le",
"Application is not enabled" : "Az alkalmazás nincs engedélyezve",
"Authentication error" : "Hitelesítési hiba",
"Token expired. Please reload page." : "A token lejárt. Frissítse az oldalt.",
@@ -255,6 +256,7 @@
"Storage is temporarily not available" : "A tároló átmenetileg nem érhető el",
"Storage connection timeout. %s" : "Időtúllépés a tárolókapcsolatban. %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Ez általában úgy javítható, hogy a webkiszolgálónak írási hozzáférést biztosít a konfigurációs könyvtárhoz.",
+ "Cannot read file" : "A fájl nem olvasható",
"Cannot write into \"config\" directory" : "A „config” könyvtár nem írható",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Ez általában úgy javítható, hogy a webkiszolgálónak írási hozzáférést biztosít a konfigurációs könyvtárhoz. Lásd: %s.",
"Cannot write into \"apps\" directory" : "Az „apps” könyvtár nem írható",
diff --git a/lib/l10n/id.js b/lib/l10n/id.js
index f1d50d6e98d..2f03d5e814f 100644
--- a/lib/l10n/id.js
+++ b/lib/l10n/id.js
@@ -16,6 +16,7 @@ OC.L10N.register(
"Authentication" : "Otentikasi",
"Unknown filetype" : "Tipe berkas tak dikenal",
"Invalid image" : "Gambar tidak sah",
+ "View profile" : "Tampilkan profil",
"today" : "hari ini",
"yesterday" : "kemarin",
"_%n day ago_::_%n days ago_" : ["%n hari yang lalu"],
@@ -36,7 +37,7 @@ OC.L10N.register(
"App \"%s\" cannot be installed because it is not compatible with this version of the server." : "Aplikasi \"%s\" tidak dapat dipasang karena tidak kompatibel dengan versi server ini",
"__language_name__" : "Bahasa Indonesia",
"Help" : "Bantuan",
- "Apps" : "aplikasi",
+ "Apps" : "Aplikasi",
"Settings" : "Setelan",
"Log out" : "Keluar",
"Users" : "Pengguna",
diff --git a/lib/l10n/id.json b/lib/l10n/id.json
index 71ec29b073d..1cae9975fe3 100644
--- a/lib/l10n/id.json
+++ b/lib/l10n/id.json
@@ -14,6 +14,7 @@
"Authentication" : "Otentikasi",
"Unknown filetype" : "Tipe berkas tak dikenal",
"Invalid image" : "Gambar tidak sah",
+ "View profile" : "Tampilkan profil",
"today" : "hari ini",
"yesterday" : "kemarin",
"_%n day ago_::_%n days ago_" : ["%n hari yang lalu"],
@@ -34,7 +35,7 @@
"App \"%s\" cannot be installed because it is not compatible with this version of the server." : "Aplikasi \"%s\" tidak dapat dipasang karena tidak kompatibel dengan versi server ini",
"__language_name__" : "Bahasa Indonesia",
"Help" : "Bantuan",
- "Apps" : "aplikasi",
+ "Apps" : "Aplikasi",
"Settings" : "Setelan",
"Log out" : "Keluar",
"Users" : "Pengguna",
diff --git a/lib/l10n/is.js b/lib/l10n/is.js
index 0e189143ad0..09f31373d69 100644
--- a/lib/l10n/is.js
+++ b/lib/l10n/is.js
@@ -62,6 +62,7 @@ OC.L10N.register(
"seconds ago" : "sekúndum síðan",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Eining með auðkenni: %s er ekki til. Virkjaðu hana í forritastillingum eða hafðu samband við kerfisstjóra.",
"File already exists" : "Skrá er þegar til",
+ "Templates" : "Sniðmát",
"File name is a reserved word" : "Skráarheiti er þegar frátekið orð",
"File name contains at least one invalid character" : "Skráarheitið inniheldur að minnsta kosti einn ógildan staf",
"File name is too long" : "Skráarheiti er of langt",
diff --git a/lib/l10n/is.json b/lib/l10n/is.json
index 503adc055e0..c1e4211b173 100644
--- a/lib/l10n/is.json
+++ b/lib/l10n/is.json
@@ -60,6 +60,7 @@
"seconds ago" : "sekúndum síðan",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Eining með auðkenni: %s er ekki til. Virkjaðu hana í forritastillingum eða hafðu samband við kerfisstjóra.",
"File already exists" : "Skrá er þegar til",
+ "Templates" : "Sniðmát",
"File name is a reserved word" : "Skráarheiti er þegar frátekið orð",
"File name contains at least one invalid character" : "Skráarheitið inniheldur að minnsta kosti einn ógildan staf",
"File name is too long" : "Skráarheiti er of langt",
diff --git a/lib/l10n/it.js b/lib/l10n/it.js
index 2b3f83e215c..9c5ff2d767b 100644
--- a/lib/l10n/it.js
+++ b/lib/l10n/it.js
@@ -58,20 +58,20 @@ OC.L10N.register(
"today" : "oggi",
"tomorrow" : "domani",
"yesterday" : "ieri",
- "_in %n day_::_in %n days_" : ["tra %n giorno","tra %n giorni"],
- "_%n day ago_::_%n days ago_" : ["%d giorno fa","%n giorni fa"],
+ "_in %n day_::_in %n days_" : ["tra %n giorno","tra %n giorni","tra %n giorni"],
+ "_%n day ago_::_%n days ago_" : ["%d giorno fa","%n giorni fa","%n giorni fa"],
"next month" : "il prossimo mese",
"last month" : "mese scorso",
- "_in %n month_::_in %n months_" : ["tra %n mese","tra %n mesi"],
- "_%n month ago_::_%n months ago_" : ["%n mese fa","%n mesi fa"],
+ "_in %n month_::_in %n months_" : ["tra %n mese","tra %n mesi","tra %n mesi"],
+ "_%n month ago_::_%n months ago_" : ["%n mese fa","%n mesi fa","%n mesi fa"],
"next year" : "il prossimo anno",
"last year" : "anno scorso",
- "_in %n year_::_in %n years_" : ["tra %n anno","tra %n anni"],
- "_%n year ago_::_%n years ago_" : ["%n anno fa","%n anni fa"],
- "_in %n hour_::_in %n hours_" : ["tra %n ora","tra %n ore"],
- "_%n hour ago_::_%n hours ago_" : ["%n ora fa","%n ore fa"],
- "_in %n minute_::_in %n minutes_" : ["tra %n minuto","tra %n minuti"],
- "_%n minute ago_::_%n minutes ago_" : ["%n minuto fa","%n minuti fa"],
+ "_in %n year_::_in %n years_" : ["tra %n anno","tra %n anni","tra %n anni"],
+ "_%n year ago_::_%n years ago_" : ["%n anno fa","%n anni fa","%n anni fa"],
+ "_in %n hour_::_in %n hours_" : ["tra %n ora","tra %n ore","tra %n ore"],
+ "_%n hour ago_::_%n hours ago_" : ["%n ora fa","%n ore fa","%n ore fa"],
+ "_in %n minute_::_in %n minutes_" : ["tra %n minuto","tra %n minuti","tra %n minuti"],
+ "_%n minute ago_::_%n minutes ago_" : ["%n minuto fa","%n minuti fa","%n minuti fa"],
"in a few seconds" : "tra pochi secondi",
"seconds ago" : "secondi fa",
"Empty file" : "File vuoto",
@@ -140,7 +140,7 @@ OC.L10N.register(
"Files cannot be shared with delete permissions" : "I file non possono essere condivisi con permessi di eliminazione",
"Files cannot be shared with create permissions" : "I file non possono essere condivisi con permessi di creazione",
"Expiration date is in the past" : "La data di scadenza è nel passato",
- "_Cannot set expiration date more than %n day in the future_::_Cannot set expiration date more than %n days in the future_" : ["Impossibile impostare la data di scadenza a più di %n giorni nel futuro","Impossibile impostare la data di scadenza a più di %n giorni nel futuro"],
+ "_Cannot set expiration date more than %n day in the future_::_Cannot set expiration date more than %n days in the future_" : ["Impossibile impostare la data di scadenza a più di %n giorni nel futuro","Impossibile impostare la data di scadenza a più di %n giorni nel futuro","Impossibile impostare la data di scadenza a più di %n giorni nel futuro"],
"Sharing is only allowed with group members" : "La condivisione è consentita solo con i membri del gruppo",
"Sharing %s failed, because this item is already shared with user %s" : "Condivisione di %s non riuscita, poiché l'oggetto è già condiviso con l'utente %s",
"%1$s shared »%2$s« with you" : "%1$s ha condiviso «%2$s» con te",
@@ -207,7 +207,6 @@ OC.L10N.register(
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "L'applicazione \"%1$s\" non può essere installata poiché le seguenti dipendenze non sono soddisfatte: %2$s",
"a safe home for all your data" : "un posto sicuro per tutti i tuoi dati",
"File is currently busy, please try again later" : "Il file è attualmente occupato, riprova più tardi",
- "Cannot read file" : "Impossibile leggere il file",
"Application is not enabled" : "L'applicazione non è abilitata",
"Authentication error" : "Errore di autenticazione",
"Token expired. Please reload page." : "Token scaduto. Ricarica la pagina.",
@@ -255,6 +254,7 @@ OC.L10N.register(
"Storage is temporarily not available" : "L'archiviazione è temporaneamente non disponibile",
"Storage connection timeout. %s" : "Timeout di connessione all'archiviazione. %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Ciò può essere normalmente corretto fornendo al server web accesso in scrittura alla cartella \"config\".",
+ "Cannot read file" : "Impossibile leggere il file",
"Cannot write into \"config\" directory" : "Impossibile scrivere nella cartella \"config\"",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Ciò può essere normalmente corretto fornendo al server web accesso in scrittura alla cartella di configurazione. Vedi %s",
"Cannot write into \"apps\" directory" : "Impossibile scrivere nella cartella \"apps\"",
@@ -273,4 +273,4 @@ OC.L10N.register(
"Check the value of \"datadirectory\" in your configuration" : "Controlla il valore di \"datadirectory\" nella tua configurazione",
"Your data directory is invalid" : "La cartella dei dati non è valida"
},
-"nplurals=2; plural=(n != 1);");
+"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/lib/l10n/it.json b/lib/l10n/it.json
index f0552dedebb..2f664a14281 100644
--- a/lib/l10n/it.json
+++ b/lib/l10n/it.json
@@ -56,20 +56,20 @@
"today" : "oggi",
"tomorrow" : "domani",
"yesterday" : "ieri",
- "_in %n day_::_in %n days_" : ["tra %n giorno","tra %n giorni"],
- "_%n day ago_::_%n days ago_" : ["%d giorno fa","%n giorni fa"],
+ "_in %n day_::_in %n days_" : ["tra %n giorno","tra %n giorni","tra %n giorni"],
+ "_%n day ago_::_%n days ago_" : ["%d giorno fa","%n giorni fa","%n giorni fa"],
"next month" : "il prossimo mese",
"last month" : "mese scorso",
- "_in %n month_::_in %n months_" : ["tra %n mese","tra %n mesi"],
- "_%n month ago_::_%n months ago_" : ["%n mese fa","%n mesi fa"],
+ "_in %n month_::_in %n months_" : ["tra %n mese","tra %n mesi","tra %n mesi"],
+ "_%n month ago_::_%n months ago_" : ["%n mese fa","%n mesi fa","%n mesi fa"],
"next year" : "il prossimo anno",
"last year" : "anno scorso",
- "_in %n year_::_in %n years_" : ["tra %n anno","tra %n anni"],
- "_%n year ago_::_%n years ago_" : ["%n anno fa","%n anni fa"],
- "_in %n hour_::_in %n hours_" : ["tra %n ora","tra %n ore"],
- "_%n hour ago_::_%n hours ago_" : ["%n ora fa","%n ore fa"],
- "_in %n minute_::_in %n minutes_" : ["tra %n minuto","tra %n minuti"],
- "_%n minute ago_::_%n minutes ago_" : ["%n minuto fa","%n minuti fa"],
+ "_in %n year_::_in %n years_" : ["tra %n anno","tra %n anni","tra %n anni"],
+ "_%n year ago_::_%n years ago_" : ["%n anno fa","%n anni fa","%n anni fa"],
+ "_in %n hour_::_in %n hours_" : ["tra %n ora","tra %n ore","tra %n ore"],
+ "_%n hour ago_::_%n hours ago_" : ["%n ora fa","%n ore fa","%n ore fa"],
+ "_in %n minute_::_in %n minutes_" : ["tra %n minuto","tra %n minuti","tra %n minuti"],
+ "_%n minute ago_::_%n minutes ago_" : ["%n minuto fa","%n minuti fa","%n minuti fa"],
"in a few seconds" : "tra pochi secondi",
"seconds ago" : "secondi fa",
"Empty file" : "File vuoto",
@@ -138,7 +138,7 @@
"Files cannot be shared with delete permissions" : "I file non possono essere condivisi con permessi di eliminazione",
"Files cannot be shared with create permissions" : "I file non possono essere condivisi con permessi di creazione",
"Expiration date is in the past" : "La data di scadenza è nel passato",
- "_Cannot set expiration date more than %n day in the future_::_Cannot set expiration date more than %n days in the future_" : ["Impossibile impostare la data di scadenza a più di %n giorni nel futuro","Impossibile impostare la data di scadenza a più di %n giorni nel futuro"],
+ "_Cannot set expiration date more than %n day in the future_::_Cannot set expiration date more than %n days in the future_" : ["Impossibile impostare la data di scadenza a più di %n giorni nel futuro","Impossibile impostare la data di scadenza a più di %n giorni nel futuro","Impossibile impostare la data di scadenza a più di %n giorni nel futuro"],
"Sharing is only allowed with group members" : "La condivisione è consentita solo con i membri del gruppo",
"Sharing %s failed, because this item is already shared with user %s" : "Condivisione di %s non riuscita, poiché l'oggetto è già condiviso con l'utente %s",
"%1$s shared »%2$s« with you" : "%1$s ha condiviso «%2$s» con te",
@@ -205,7 +205,6 @@
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "L'applicazione \"%1$s\" non può essere installata poiché le seguenti dipendenze non sono soddisfatte: %2$s",
"a safe home for all your data" : "un posto sicuro per tutti i tuoi dati",
"File is currently busy, please try again later" : "Il file è attualmente occupato, riprova più tardi",
- "Cannot read file" : "Impossibile leggere il file",
"Application is not enabled" : "L'applicazione non è abilitata",
"Authentication error" : "Errore di autenticazione",
"Token expired. Please reload page." : "Token scaduto. Ricarica la pagina.",
@@ -253,6 +252,7 @@
"Storage is temporarily not available" : "L'archiviazione è temporaneamente non disponibile",
"Storage connection timeout. %s" : "Timeout di connessione all'archiviazione. %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Ciò può essere normalmente corretto fornendo al server web accesso in scrittura alla cartella \"config\".",
+ "Cannot read file" : "Impossibile leggere il file",
"Cannot write into \"config\" directory" : "Impossibile scrivere nella cartella \"config\"",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Ciò può essere normalmente corretto fornendo al server web accesso in scrittura alla cartella di configurazione. Vedi %s",
"Cannot write into \"apps\" directory" : "Impossibile scrivere nella cartella \"apps\"",
@@ -270,5 +270,5 @@
"Your data directory must be an absolute path" : "La cartella dei dati deve essere un percorso assoluto",
"Check the value of \"datadirectory\" in your configuration" : "Controlla il valore di \"datadirectory\" nella tua configurazione",
"Your data directory is invalid" : "La cartella dei dati non è valida"
-},"pluralForm" :"nplurals=2; plural=(n != 1);"
+},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
} \ No newline at end of file
diff --git a/lib/l10n/ja.js b/lib/l10n/ja.js
index fbe047df50d..5b4164dba39 100644
--- a/lib/l10n/ja.js
+++ b/lib/l10n/ja.js
@@ -207,7 +207,7 @@ OC.L10N.register(
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "次の依存関係を満たしていないため、アプリ \"%1$s\" をインストールできません: %2$s",
"a safe home for all your data" : "すべてのデータを安全に保管します",
"File is currently busy, please try again later" : "現在ファイルはビジーです。後でもう一度試してください。",
- "Cannot read file" : "ファイルを読み込めません",
+ "Cannot download file" : "ファイルをダウンロードできません",
"Application is not enabled" : "アプリケーションは無効です",
"Authentication error" : "認証エラー",
"Token expired. Please reload page." : "トークンが無効になりました。ページを再読込してください。",
@@ -238,6 +238,7 @@ OC.L10N.register(
"The required %s config variable is not configured in the config.php file." : "必要な %s 構成変数が config.php ファイルに設定されていません。",
"Please ask your server administrator to check the Nextcloud configuration." : "サーバー管理者にNextcloud構成を確認するように依頼してください。",
"PostgreSQL >= 9 required." : "PostgreSQL 9以上が必要です",
+ "Please upgrade your database version." : "新しいバージョンのデータベースにアップグレードしてください。",
"Your data directory is readable by other users." : "データディレクトリは他のユーザーからも読み取ることができます",
"Please change the permissions to 0770 so that the directory cannot be listed by other users." : "ディレクトリが他のユーザーから見えないように、パーミッションを 0770 に変更してください。",
"Your data directory must be an absolute path." : "データディレクトリは、絶対パスにする必要があります",
@@ -256,6 +257,7 @@ OC.L10N.register(
"Storage is temporarily not available" : "ストレージは一時的に利用できません",
"Storage connection timeout. %s" : "ストレージへの接続がタイムアウト。 %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Webサーバーにconfigディレクトリへの書き込み権限を与えることで解決する可能性があります。",
+ "Cannot read file" : "ファイルを読み込めません",
"Cannot write into \"config\" directory" : "\"config\" ディレクトリに書き込みができません",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "多くの場合、Webサーバーの configディレクトリ に書き込み権限を与えることで直ります。%s を見てください",
"Cannot write into \"apps\" directory" : "\"apps\" ディレクトリに書き込みができません",
diff --git a/lib/l10n/ja.json b/lib/l10n/ja.json
index 5d53a53454e..ccfdd45c430 100644
--- a/lib/l10n/ja.json
+++ b/lib/l10n/ja.json
@@ -205,7 +205,7 @@
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "次の依存関係を満たしていないため、アプリ \"%1$s\" をインストールできません: %2$s",
"a safe home for all your data" : "すべてのデータを安全に保管します",
"File is currently busy, please try again later" : "現在ファイルはビジーです。後でもう一度試してください。",
- "Cannot read file" : "ファイルを読み込めません",
+ "Cannot download file" : "ファイルをダウンロードできません",
"Application is not enabled" : "アプリケーションは無効です",
"Authentication error" : "認証エラー",
"Token expired. Please reload page." : "トークンが無効になりました。ページを再読込してください。",
@@ -236,6 +236,7 @@
"The required %s config variable is not configured in the config.php file." : "必要な %s 構成変数が config.php ファイルに設定されていません。",
"Please ask your server administrator to check the Nextcloud configuration." : "サーバー管理者にNextcloud構成を確認するように依頼してください。",
"PostgreSQL >= 9 required." : "PostgreSQL 9以上が必要です",
+ "Please upgrade your database version." : "新しいバージョンのデータベースにアップグレードしてください。",
"Your data directory is readable by other users." : "データディレクトリは他のユーザーからも読み取ることができます",
"Please change the permissions to 0770 so that the directory cannot be listed by other users." : "ディレクトリが他のユーザーから見えないように、パーミッションを 0770 に変更してください。",
"Your data directory must be an absolute path." : "データディレクトリは、絶対パスにする必要があります",
@@ -254,6 +255,7 @@
"Storage is temporarily not available" : "ストレージは一時的に利用できません",
"Storage connection timeout. %s" : "ストレージへの接続がタイムアウト。 %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Webサーバーにconfigディレクトリへの書き込み権限を与えることで解決する可能性があります。",
+ "Cannot read file" : "ファイルを読み込めません",
"Cannot write into \"config\" directory" : "\"config\" ディレクトリに書き込みができません",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "多くの場合、Webサーバーの configディレクトリ に書き込み権限を与えることで直ります。%s を見てください",
"Cannot write into \"apps\" directory" : "\"apps\" ディレクトリに書き込みができません",
diff --git a/lib/l10n/ko.js b/lib/l10n/ko.js
index a2329ec6590..039f738edd9 100644
--- a/lib/l10n/ko.js
+++ b/lib/l10n/ko.js
@@ -79,6 +79,7 @@ OC.L10N.register(
"About" : "정보",
"Full name" : "전체 이름",
"Headline" : "표제",
+ "Organisation" : "조직",
"Role" : "직책",
"Unknown user" : "알려지지 않은 사용자",
"Additional settings" : "고급 설정",
diff --git a/lib/l10n/ko.json b/lib/l10n/ko.json
index c431ac654b9..81f384b4c3a 100644
--- a/lib/l10n/ko.json
+++ b/lib/l10n/ko.json
@@ -77,6 +77,7 @@
"About" : "정보",
"Full name" : "전체 이름",
"Headline" : "표제",
+ "Organisation" : "조직",
"Role" : "직책",
"Unknown user" : "알려지지 않은 사용자",
"Additional settings" : "고급 설정",
diff --git a/lib/l10n/mk.js b/lib/l10n/mk.js
index 6a94177fd32..bffc6debf01 100644
--- a/lib/l10n/mk.js
+++ b/lib/l10n/mk.js
@@ -206,7 +206,6 @@ OC.L10N.register(
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "Апликацијата \"%1$s\" не може да се инсталира затоа што следниве зависности не се исполнети: %2$s",
"a safe home for all your data" : "безбеден дом за сите ваши податоци",
"File is currently busy, please try again later" : "Датотеката моментално е зафатена, обидете се повторно",
- "Cannot read file" : "Неможе да се прочита датотеката",
"Application is not enabled" : "Апликацијата не е овозможена",
"Authentication error" : "Грешка во автентикација",
"Token expired. Please reload page." : "Жетонот е истечен. Ве молам превчитајте ја страницата.",
@@ -236,6 +235,7 @@ OC.L10N.register(
"Storage is temporarily not available" : "Складиштето моментално не е достапно",
"Storage connection timeout. %s" : "Поврзувањето со складиштето не успеа. %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Ова најчесто се поправа со давање на дозвола веб серверот за запишување во config папката.",
+ "Cannot read file" : "Неможе да се прочита датотеката",
"Cannot write into \"config\" directory" : "Не може да зе запишува во \"config\" директориумот",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Ова најчесто се поправа со давање дозвола на веб серверот за запишување во config папката. Видете %s",
"Cannot write into \"apps\" directory" : "Не може да зе запишува во \"apps\" директориумот",
diff --git a/lib/l10n/mk.json b/lib/l10n/mk.json
index 6fe249d9c17..f86bfb14998 100644
--- a/lib/l10n/mk.json
+++ b/lib/l10n/mk.json
@@ -204,7 +204,6 @@
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "Апликацијата \"%1$s\" не може да се инсталира затоа што следниве зависности не се исполнети: %2$s",
"a safe home for all your data" : "безбеден дом за сите ваши податоци",
"File is currently busy, please try again later" : "Датотеката моментално е зафатена, обидете се повторно",
- "Cannot read file" : "Неможе да се прочита датотеката",
"Application is not enabled" : "Апликацијата не е овозможена",
"Authentication error" : "Грешка во автентикација",
"Token expired. Please reload page." : "Жетонот е истечен. Ве молам превчитајте ја страницата.",
@@ -234,6 +233,7 @@
"Storage is temporarily not available" : "Складиштето моментално не е достапно",
"Storage connection timeout. %s" : "Поврзувањето со складиштето не успеа. %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Ова најчесто се поправа со давање на дозвола веб серверот за запишување во config папката.",
+ "Cannot read file" : "Неможе да се прочита датотеката",
"Cannot write into \"config\" directory" : "Не може да зе запишува во \"config\" директориумот",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Ова најчесто се поправа со давање дозвола на веб серверот за запишување во config папката. Видете %s",
"Cannot write into \"apps\" directory" : "Не може да зе запишува во \"apps\" директориумот",
diff --git a/lib/l10n/nb.js b/lib/l10n/nb.js
index cd3822384e6..023f43b136c 100644
--- a/lib/l10n/nb.js
+++ b/lib/l10n/nb.js
@@ -5,6 +5,11 @@ OC.L10N.register(
"See %s" : "Se %s",
"Sample configuration detected" : "Eksempeloppsett oppdaget",
"It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Det ble oppdaget at eksempeloppsettet er blitt kopiert. Dette kan ødelegge installasjonen din og støttes ikke. Les dokumentasjonen før du gjør endringer i config.php",
+ "%s email verification" : "%s e-postbekreftelse",
+ "Email verification" : "E-postbekreftelse",
+ "Click the following button to confirm your email." : "Klikk på følgende knapp for å bekrefte e-posten din.",
+ "Click the following link to confirm your email." : "Klikk på følgende lenke for å bekrefte e-posten din.",
+ "Confirm your email" : "Bekreft din e-post",
"%1$s and %2$s" : "%1$s og %2$s",
"%1$s, %2$s and %3$s" : "%1$s, %2$s og %3$s",
"%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s og %4$s",
@@ -45,8 +50,10 @@ OC.L10N.register(
"_%n minute ago_::_%n minutes ago_" : ["for %n minutt siden","for %n minutter siden"],
"in a few seconds" : "om noen sekunder",
"seconds ago" : "for få sekunder siden",
+ "Empty file" : "Tom fil",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Modul med ID: %s finnes ikke. Skru den på i app-innstillingene eller kontakt en administrator.",
"File already exists" : "Filen finnes allerede",
+ "Invalid path" : "Ugyldig filbane",
"File name is a reserved word" : "Filnavnet er et reservert ord",
"File name contains at least one invalid character" : "Filnavnet inneholder minst ett ulovlig tegn",
"File name is too long" : "Filnavnet er for langt",
@@ -62,13 +69,20 @@ OC.L10N.register(
"Log out" : "Logg ut",
"Users" : "Brukere",
"Email" : "E-post",
+ "Mail %s" : "E-post til %s",
"Phone" : "Telefon",
+ "Call %s" : "Ring %s",
"Twitter" : "Twitter",
+ "View %s on Twitter" : "Se %s på Twitter",
"Website" : "Nettsted",
+ "Visit %s" : "Besøk %s",
"Address" : "Adresse",
"Profile picture" : "Profilbilde",
"About" : "Om",
"Full name" : "Fullt nav",
+ "Headline" : "Overskrift",
+ "Organisation" : "Organisasion",
+ "Role" : "Rolle",
"Unknown user" : "Ukjent bruker",
"Additional settings" : "Flere innstillinger",
"%s enter the database username and name." : "%s legg inn database brukernavn og navn.",
@@ -158,6 +172,7 @@ OC.L10N.register(
"Login canceled by app" : "Innlogging avbrutt av app",
"a safe home for all your data" : "et sikkert hjem for alle dine data",
"File is currently busy, please try again later" : "Filen er opptatt for øyeblikket, prøv igjen senere",
+ "Cannot download file" : "Kan ikke laste ned fil",
"Application is not enabled" : "Appen er ikke aktivert",
"Authentication error" : "Autentikasjonsfeil",
"Token expired. Please reload page." : "Symbol utløpt. Last inn siden på nytt.",
@@ -180,6 +195,7 @@ OC.L10N.register(
"Storage connection error. %s" : "Tilkoblingsfeil for lager. %s",
"Storage is temporarily not available" : "Lagring er midlertidig utilgjengelig",
"Storage connection timeout. %s" : "Tidsavbrudd ved tilkobling av lager: %s",
+ "Cannot read file" : "Kan ikke lese fil",
"Cannot write into \"config\" directory" : "Kan ikke skrive i \"config\"-mappen",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Dette kan vanligvis ordnes ved å gi webserveren skrivetilgang til config-mappen. Se %s",
"Cannot write into \"apps\" directory" : "Kan ikke skrive i \"apps\"-mappen",
diff --git a/lib/l10n/nb.json b/lib/l10n/nb.json
index ee405c25723..1f0f6b629c7 100644
--- a/lib/l10n/nb.json
+++ b/lib/l10n/nb.json
@@ -3,6 +3,11 @@
"See %s" : "Se %s",
"Sample configuration detected" : "Eksempeloppsett oppdaget",
"It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Det ble oppdaget at eksempeloppsettet er blitt kopiert. Dette kan ødelegge installasjonen din og støttes ikke. Les dokumentasjonen før du gjør endringer i config.php",
+ "%s email verification" : "%s e-postbekreftelse",
+ "Email verification" : "E-postbekreftelse",
+ "Click the following button to confirm your email." : "Klikk på følgende knapp for å bekrefte e-posten din.",
+ "Click the following link to confirm your email." : "Klikk på følgende lenke for å bekrefte e-posten din.",
+ "Confirm your email" : "Bekreft din e-post",
"%1$s and %2$s" : "%1$s og %2$s",
"%1$s, %2$s and %3$s" : "%1$s, %2$s og %3$s",
"%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s og %4$s",
@@ -43,8 +48,10 @@
"_%n minute ago_::_%n minutes ago_" : ["for %n minutt siden","for %n minutter siden"],
"in a few seconds" : "om noen sekunder",
"seconds ago" : "for få sekunder siden",
+ "Empty file" : "Tom fil",
"Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Modul med ID: %s finnes ikke. Skru den på i app-innstillingene eller kontakt en administrator.",
"File already exists" : "Filen finnes allerede",
+ "Invalid path" : "Ugyldig filbane",
"File name is a reserved word" : "Filnavnet er et reservert ord",
"File name contains at least one invalid character" : "Filnavnet inneholder minst ett ulovlig tegn",
"File name is too long" : "Filnavnet er for langt",
@@ -60,13 +67,20 @@
"Log out" : "Logg ut",
"Users" : "Brukere",
"Email" : "E-post",
+ "Mail %s" : "E-post til %s",
"Phone" : "Telefon",
+ "Call %s" : "Ring %s",
"Twitter" : "Twitter",
+ "View %s on Twitter" : "Se %s på Twitter",
"Website" : "Nettsted",
+ "Visit %s" : "Besøk %s",
"Address" : "Adresse",
"Profile picture" : "Profilbilde",
"About" : "Om",
"Full name" : "Fullt nav",
+ "Headline" : "Overskrift",
+ "Organisation" : "Organisasion",
+ "Role" : "Rolle",
"Unknown user" : "Ukjent bruker",
"Additional settings" : "Flere innstillinger",
"%s enter the database username and name." : "%s legg inn database brukernavn og navn.",
@@ -156,6 +170,7 @@
"Login canceled by app" : "Innlogging avbrutt av app",
"a safe home for all your data" : "et sikkert hjem for alle dine data",
"File is currently busy, please try again later" : "Filen er opptatt for øyeblikket, prøv igjen senere",
+ "Cannot download file" : "Kan ikke laste ned fil",
"Application is not enabled" : "Appen er ikke aktivert",
"Authentication error" : "Autentikasjonsfeil",
"Token expired. Please reload page." : "Symbol utløpt. Last inn siden på nytt.",
@@ -178,6 +193,7 @@
"Storage connection error. %s" : "Tilkoblingsfeil for lager. %s",
"Storage is temporarily not available" : "Lagring er midlertidig utilgjengelig",
"Storage connection timeout. %s" : "Tidsavbrudd ved tilkobling av lager: %s",
+ "Cannot read file" : "Kan ikke lese fil",
"Cannot write into \"config\" directory" : "Kan ikke skrive i \"config\"-mappen",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Dette kan vanligvis ordnes ved å gi webserveren skrivetilgang til config-mappen. Se %s",
"Cannot write into \"apps\" directory" : "Kan ikke skrive i \"apps\"-mappen",
diff --git a/lib/l10n/nl.js b/lib/l10n/nl.js
index 76423ca4486..f1787eff27d 100644
--- a/lib/l10n/nl.js
+++ b/lib/l10n/nl.js
@@ -207,7 +207,6 @@ OC.L10N.register(
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "App \"%1$s\" kan niet worden geïnstalleerd, omdat de volgende afhankelijkheden niet zijn ingevuld: %2$s",
"a safe home for all your data" : "een veilige plek voor al je gegevens",
"File is currently busy, please try again later" : "Bestandsverwerking bezig, probeer het later opnieuw",
- "Cannot read file" : "Kan bestand niet lezen",
"Application is not enabled" : "De applicatie is niet ingeschakeld",
"Authentication error" : "Authenticatiefout",
"Token expired. Please reload page." : "Token verlopen. Herlaad de pagina.",
@@ -253,6 +252,7 @@ OC.L10N.register(
"Storage is temporarily not available" : "Opslag is tijdelijk niet beschikbaar",
"Storage connection timeout. %s" : "Opslag verbinding time-out. %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Dit kan opgelost worden door de config map op de webserver schrijfrechten te geven.",
+ "Cannot read file" : "Kan bestand niet lezen",
"Cannot write into \"config\" directory" : "Kan niet schrijven naar de \"config\" directory",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Dit kan opgelost worden door de config map op de webserver schrijf rechten te geven. See %s",
"Cannot write into \"apps\" directory" : "Kan niet schrijven naar de \"apps\" directory",
diff --git a/lib/l10n/nl.json b/lib/l10n/nl.json
index 79b39acd2c1..5c1eff0b199 100644
--- a/lib/l10n/nl.json
+++ b/lib/l10n/nl.json
@@ -205,7 +205,6 @@
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "App \"%1$s\" kan niet worden geïnstalleerd, omdat de volgende afhankelijkheden niet zijn ingevuld: %2$s",
"a safe home for all your data" : "een veilige plek voor al je gegevens",
"File is currently busy, please try again later" : "Bestandsverwerking bezig, probeer het later opnieuw",
- "Cannot read file" : "Kan bestand niet lezen",
"Application is not enabled" : "De applicatie is niet ingeschakeld",
"Authentication error" : "Authenticatiefout",
"Token expired. Please reload page." : "Token verlopen. Herlaad de pagina.",
@@ -251,6 +250,7 @@
"Storage is temporarily not available" : "Opslag is tijdelijk niet beschikbaar",
"Storage connection timeout. %s" : "Opslag verbinding time-out. %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Dit kan opgelost worden door de config map op de webserver schrijfrechten te geven.",
+ "Cannot read file" : "Kan bestand niet lezen",
"Cannot write into \"config\" directory" : "Kan niet schrijven naar de \"config\" directory",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Dit kan opgelost worden door de config map op de webserver schrijf rechten te geven. See %s",
"Cannot write into \"apps\" directory" : "Kan niet schrijven naar de \"apps\" directory",
diff --git a/lib/l10n/nn_NO.js b/lib/l10n/nn_NO.js
index 33a31973071..924adc60a85 100644
--- a/lib/l10n/nn_NO.js
+++ b/lib/l10n/nn_NO.js
@@ -1,6 +1,7 @@
OC.L10N.register(
"lib",
{
+ "Authentication" : "Godkjenning",
"Unknown filetype" : "Ukjend filtype",
"Invalid image" : "Ugyldig bilete",
"today" : "i dag",
@@ -13,9 +14,10 @@ OC.L10N.register(
"_%n hour ago_::_%n hours ago_" : ["%n time sidan","%n timar sidan"],
"_%n minute ago_::_%n minutes ago_" : ["%n minutt sidan","%n minutt sidan"],
"seconds ago" : "sekund sidan",
+ "File name is too long" : "Filnamnet er for langt",
"__language_name__" : "Nynorsk",
"Help" : "Hjelp",
- "Apps" : "Program",
+ "Apps" : "Appar",
"Settings" : "Instillingar",
"Log out" : "Logg ut",
"Users" : "Brukarar",
@@ -27,6 +29,7 @@ OC.L10N.register(
"Full name" : "Fult namn",
"Unknown user" : "Ukjend brukar",
"Additional settings" : "Tilleggsinnstillingar",
+ "Set an admin password." : "Vel eit admin-passord.",
"Open »%s«" : "Opna »%s«",
"Sunday" : "Søndag",
"Monday" : "Måndag",
@@ -75,6 +78,8 @@ OC.L10N.register(
"Dec." : "Des.",
"A valid username must be provided" : "Du må oppgje eit gyldig brukarnamn",
"A valid password must be provided" : "Du må oppgje eit gyldig passord",
+ "User disabled" : "Brukar deaktivert",
+ "Login canceled by app" : "Innlogging avbroten av app",
"Authentication error" : "Feil i autentisering"
},
"nplurals=2; plural=(n != 1);");
diff --git a/lib/l10n/nn_NO.json b/lib/l10n/nn_NO.json
index 53b50337a17..219148e7b1b 100644
--- a/lib/l10n/nn_NO.json
+++ b/lib/l10n/nn_NO.json
@@ -1,4 +1,5 @@
{ "translations": {
+ "Authentication" : "Godkjenning",
"Unknown filetype" : "Ukjend filtype",
"Invalid image" : "Ugyldig bilete",
"today" : "i dag",
@@ -11,9 +12,10 @@
"_%n hour ago_::_%n hours ago_" : ["%n time sidan","%n timar sidan"],
"_%n minute ago_::_%n minutes ago_" : ["%n minutt sidan","%n minutt sidan"],
"seconds ago" : "sekund sidan",
+ "File name is too long" : "Filnamnet er for langt",
"__language_name__" : "Nynorsk",
"Help" : "Hjelp",
- "Apps" : "Program",
+ "Apps" : "Appar",
"Settings" : "Instillingar",
"Log out" : "Logg ut",
"Users" : "Brukarar",
@@ -25,6 +27,7 @@
"Full name" : "Fult namn",
"Unknown user" : "Ukjend brukar",
"Additional settings" : "Tilleggsinnstillingar",
+ "Set an admin password." : "Vel eit admin-passord.",
"Open »%s«" : "Opna »%s«",
"Sunday" : "Søndag",
"Monday" : "Måndag",
@@ -73,6 +76,8 @@
"Dec." : "Des.",
"A valid username must be provided" : "Du må oppgje eit gyldig brukarnamn",
"A valid password must be provided" : "Du må oppgje eit gyldig passord",
+ "User disabled" : "Brukar deaktivert",
+ "Login canceled by app" : "Innlogging avbroten av app",
"Authentication error" : "Feil i autentisering"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
} \ No newline at end of file
diff --git a/lib/l10n/pl.js b/lib/l10n/pl.js
index 9958aa00fd6..b938b3c39fc 100644
--- a/lib/l10n/pl.js
+++ b/lib/l10n/pl.js
@@ -91,6 +91,8 @@ OC.L10N.register(
"This is an automatically sent email, please do not reply." : "Jest to wiadomość e-mail wysłana automatycznie, prosimy nie odpowiadać na nią.",
"Help" : "Pomoc",
"Apps" : "Aplikacje",
+ "Personal settings" : "Ustawienia osobiste",
+ "Admin settings" : "Ustawienia administratora",
"Settings" : "Ustawienia",
"Log out" : "Wyloguj",
"Users" : "Użytkownicy",
@@ -207,7 +209,7 @@ OC.L10N.register(
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "Nie można zainstalować aplikacji \"%1$s\", ponieważ nie są spełnione następujące zależności: %2$s",
"a safe home for all your data" : "bezpieczny dom dla wszystkich danych",
"File is currently busy, please try again later" : "Plik jest obecnie niedostępny, spróbuj później",
- "Cannot read file" : "Nie można odczytać pliku",
+ "Cannot download file" : "Nie można pobrać pliku",
"Application is not enabled" : "Aplikacja nie jest włączona",
"Authentication error" : "Błąd uwierzytelniania",
"Token expired. Please reload page." : "Token wygasł. Przeładuj stronę.",
@@ -216,7 +218,7 @@ OC.L10N.register(
"This can usually be fixed by giving the web server write access to the config directory. See %s" : "Zwykle można to naprawić, nadając serwerowi WWW dostęp do zapisu do katalogu config. Zobacz %s",
"Or, if you prefer to keep config.php file read only, set the option \"config_is_read_only\" to true in it. See %s" : "Jeśli, albo wolisz zachować plik config.php tylko do odczytu, ustaw w nim opcję \"config_is_read_only\" na true. Zobacz %s",
"Cannot write into \"apps\" directory." : "Nie można zapisać do katalogu \"apps\".",
- "This can usually be fixed by giving the web server write access to the apps directory or disabling the App Store in the config file." : "Zwykle można to naprawić, przyznając serwerowi WWW dostęp do zapisu do katalogu aplikacji lub wyłączając sklep z aplikacjami w pliku konfiguracyjnym.",
+ "This can usually be fixed by giving the web server write access to the apps directory or disabling the App Store in the config file." : "Zwykle można to naprawić, przyznając serwerowi WWW dostęp do zapisu do katalogu aplikacji lub wyłączając Nextcloud App Store w pliku konfiguracyjnym.",
"Cannot create \"data\" directory." : "Nie można utworzyć katalogu \"data\".",
"This can usually be fixed by giving the web server write access to the root directory. See %s" : "Zwykle można to naprawić, nadając serwerowi WWW dostęp do zapisu do katalogu głównego. Zobacz %s",
"Permissions can usually be fixed by giving the web server write access to the root directory. See %s." : "Uprawnienia można zazwyczaj naprawić, nadając serwerowi WWW dostęp do zapisu do katalogu głównego. Zobacz %s.",
@@ -257,6 +259,7 @@ OC.L10N.register(
"Storage is temporarily not available" : "Magazyn jest tymczasowo niedostępny",
"Storage connection timeout. %s" : "Limit czasu połączenia do magazynu. %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Zwykle można to naprawić, nadając serwerowi WWW dostęp do zapisu do katalogu config.",
+ "Cannot read file" : "Nie można odczytać pliku",
"Cannot write into \"config\" directory" : "Nie można zapisać do katalogu \"config\"",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Zwykle można to naprawić, nadając serwerowi WWW dostęp do zapisu do katalogu config. Zobacz %s",
"Cannot write into \"apps\" directory" : "Nie można zapisać do katalogu \"apps\"",
diff --git a/lib/l10n/pl.json b/lib/l10n/pl.json
index de1893fdfe0..2befc2e3f4a 100644
--- a/lib/l10n/pl.json
+++ b/lib/l10n/pl.json
@@ -89,6 +89,8 @@
"This is an automatically sent email, please do not reply." : "Jest to wiadomość e-mail wysłana automatycznie, prosimy nie odpowiadać na nią.",
"Help" : "Pomoc",
"Apps" : "Aplikacje",
+ "Personal settings" : "Ustawienia osobiste",
+ "Admin settings" : "Ustawienia administratora",
"Settings" : "Ustawienia",
"Log out" : "Wyloguj",
"Users" : "Użytkownicy",
@@ -205,7 +207,7 @@
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "Nie można zainstalować aplikacji \"%1$s\", ponieważ nie są spełnione następujące zależności: %2$s",
"a safe home for all your data" : "bezpieczny dom dla wszystkich danych",
"File is currently busy, please try again later" : "Plik jest obecnie niedostępny, spróbuj później",
- "Cannot read file" : "Nie można odczytać pliku",
+ "Cannot download file" : "Nie można pobrać pliku",
"Application is not enabled" : "Aplikacja nie jest włączona",
"Authentication error" : "Błąd uwierzytelniania",
"Token expired. Please reload page." : "Token wygasł. Przeładuj stronę.",
@@ -214,7 +216,7 @@
"This can usually be fixed by giving the web server write access to the config directory. See %s" : "Zwykle można to naprawić, nadając serwerowi WWW dostęp do zapisu do katalogu config. Zobacz %s",
"Or, if you prefer to keep config.php file read only, set the option \"config_is_read_only\" to true in it. See %s" : "Jeśli, albo wolisz zachować plik config.php tylko do odczytu, ustaw w nim opcję \"config_is_read_only\" na true. Zobacz %s",
"Cannot write into \"apps\" directory." : "Nie można zapisać do katalogu \"apps\".",
- "This can usually be fixed by giving the web server write access to the apps directory or disabling the App Store in the config file." : "Zwykle można to naprawić, przyznając serwerowi WWW dostęp do zapisu do katalogu aplikacji lub wyłączając sklep z aplikacjami w pliku konfiguracyjnym.",
+ "This can usually be fixed by giving the web server write access to the apps directory or disabling the App Store in the config file." : "Zwykle można to naprawić, przyznając serwerowi WWW dostęp do zapisu do katalogu aplikacji lub wyłączając Nextcloud App Store w pliku konfiguracyjnym.",
"Cannot create \"data\" directory." : "Nie można utworzyć katalogu \"data\".",
"This can usually be fixed by giving the web server write access to the root directory. See %s" : "Zwykle można to naprawić, nadając serwerowi WWW dostęp do zapisu do katalogu głównego. Zobacz %s",
"Permissions can usually be fixed by giving the web server write access to the root directory. See %s." : "Uprawnienia można zazwyczaj naprawić, nadając serwerowi WWW dostęp do zapisu do katalogu głównego. Zobacz %s.",
@@ -255,6 +257,7 @@
"Storage is temporarily not available" : "Magazyn jest tymczasowo niedostępny",
"Storage connection timeout. %s" : "Limit czasu połączenia do magazynu. %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Zwykle można to naprawić, nadając serwerowi WWW dostęp do zapisu do katalogu config.",
+ "Cannot read file" : "Nie można odczytać pliku",
"Cannot write into \"config\" directory" : "Nie można zapisać do katalogu \"config\"",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Zwykle można to naprawić, nadając serwerowi WWW dostęp do zapisu do katalogu config. Zobacz %s",
"Cannot write into \"apps\" directory" : "Nie można zapisać do katalogu \"apps\"",
diff --git a/lib/l10n/pt_BR.js b/lib/l10n/pt_BR.js
index 59148b038fd..71deeea2397 100644
--- a/lib/l10n/pt_BR.js
+++ b/lib/l10n/pt_BR.js
@@ -58,20 +58,20 @@ OC.L10N.register(
"today" : "hoje",
"tomorrow" : "amanhã",
"yesterday" : "ontem",
- "_in %n day_::_in %n days_" : ["em %n dia","em %n dias"],
- "_%n day ago_::_%n days ago_" : ["%n dia atrás","%n dias atrás"],
+ "_in %n day_::_in %n days_" : ["em %n dia","em %n dias","em %n dias"],
+ "_%n day ago_::_%n days ago_" : ["%n dia atrás","%n dias atrás","%n dias atrás"],
"next month" : "Mês que vem",
"last month" : "último mês",
- "_in %n month_::_in %n months_" : ["em %n mês","em %n meses"],
- "_%n month ago_::_%n months ago_" : ["há %n mês atrás","há %n meses"],
+ "_in %n month_::_in %n months_" : ["em %n mês","em %n meses","em %n meses"],
+ "_%n month ago_::_%n months ago_" : ["há %n mês atrás","há %n meses","há %n meses"],
"next year" : "ano que vem",
"last year" : "último ano",
- "_in %n year_::_in %n years_" : ["em %n ano","em %n anos"],
- "_%n year ago_::_%n years ago_" : ["%n ano atrás","%n anos atrás"],
- "_in %n hour_::_in %n hours_" : ["em %n hora","em %n horas"],
- "_%n hour ago_::_%n hours ago_" : ["há %n hora atrás","há %n horas"],
- "_in %n minute_::_in %n minutes_" : ["em %n minuto","em %n minutos"],
- "_%n minute ago_::_%n minutes ago_" : ["há %n minuto atrás","há %n minutos"],
+ "_in %n year_::_in %n years_" : ["em %n ano","em %n anos","em %n anos"],
+ "_%n year ago_::_%n years ago_" : ["%n ano atrás","%n anos atrás","%n anos atrás"],
+ "_in %n hour_::_in %n hours_" : ["em %n hora","em %n horas","em %n horas"],
+ "_%n hour ago_::_%n hours ago_" : ["há %n hora atrás","há %n horas","há %n horas"],
+ "_in %n minute_::_in %n minutes_" : ["em %n minuto","em %n minutos","em %n minutos"],
+ "_%n minute ago_::_%n minutes ago_" : ["há %n minuto atrás","há %n minutos","há %n minutos"],
"in a few seconds" : "Em alguns segundos",
"seconds ago" : "segundos atrás",
"Empty file" : "Arquivo vazio",
@@ -91,6 +91,7 @@ OC.L10N.register(
"This is an automatically sent email, please do not reply." : "Este e-mail é enviado automaticamente. Por favor, não responda.",
"Help" : "Ajuda",
"Apps" : "Aplicativos",
+ "Personal settings" : "Configurações pessoais",
"Settings" : "Configurações",
"Log out" : "Sair",
"Users" : "Usuários",
@@ -140,7 +141,7 @@ OC.L10N.register(
"Files cannot be shared with delete permissions" : "Arquivos não podem ser compartilhados com permissões de exclusão",
"Files cannot be shared with create permissions" : "Arquivos não podem ser compartilhados com permissões de criação",
"Expiration date is in the past" : "Data de expiração está no passado",
- "_Cannot set expiration date more than %n day in the future_::_Cannot set expiration date more than %n days in the future_" : ["Não foi possível definir a data de expiração superior que %n dias no futuro","Não foi possível definir a data de expiração superior que %n dias no futuro"],
+ "_Cannot set expiration date more than %n day in the future_::_Cannot set expiration date more than %n days in the future_" : ["Não foi possível definir a data de expiração superior que %n dias no futuro","Não foi possível definir a data de expiração superior que %n dias no futuro","Não foi possível definir a data de expiração superior que %n dias no futuro"],
"Sharing is only allowed with group members" : "O compartilhamento só é permitido com membros do grupo ",
"Sharing %s failed, because this item is already shared with user %s" : "Compartilhamento %s falhou, porque este item já está compartilhado com o usuário %s",
"%1$s shared »%2$s« with you" : "%1$s compartilhou »%2$s« com você",
@@ -207,7 +208,7 @@ OC.L10N.register(
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "O aplicativo \"%1$s\" não pode ser instalado devido à estas dependências: %2$s",
"a safe home for all your data" : "Um lar seguro para todos os seus dados",
"File is currently busy, please try again later" : "O arquivo está ocupado, tente novamente mais tarde",
- "Cannot read file" : "Não foi possível ler o arquivo",
+ "Cannot download file" : "Não é possível baixar o arquivo",
"Application is not enabled" : "O aplicativo não está habilitado",
"Authentication error" : "Erro de autenticação",
"Token expired. Please reload page." : "O token expirou. Por favor recarregue a página.",
@@ -257,6 +258,7 @@ OC.L10N.register(
"Storage is temporarily not available" : "Armazenamento temporariamente indisponível",
"Storage connection timeout. %s" : "Atingido o tempo limite de conexão ao armazenamento. %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Geralmente, isso pode ser corrigido concedendo ao servidor web acesso de gravação ao diretório de configuração.",
+ "Cannot read file" : "Não foi possível ler o arquivo",
"Cannot write into \"config\" directory" : "Não foi possível gravar no diretório \"config\"",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Normalmente isso pode ser resolvido dando ao webserver permissão de escritura no diretório config. Veja %s",
"Cannot write into \"apps\" directory" : "Não foi possível gravar no diretório \"apps\"",
@@ -275,4 +277,4 @@ OC.L10N.register(
"Check the value of \"datadirectory\" in your configuration" : "Verifique o valor do \"datadirectory\" na sua configuração",
"Your data directory is invalid" : "Seu diretório de dados é inválido"
},
-"nplurals=2; plural=(n > 1);");
+"nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/lib/l10n/pt_BR.json b/lib/l10n/pt_BR.json
index 2ffa8eea954..80b7285aa26 100644
--- a/lib/l10n/pt_BR.json
+++ b/lib/l10n/pt_BR.json
@@ -56,20 +56,20 @@
"today" : "hoje",
"tomorrow" : "amanhã",
"yesterday" : "ontem",
- "_in %n day_::_in %n days_" : ["em %n dia","em %n dias"],
- "_%n day ago_::_%n days ago_" : ["%n dia atrás","%n dias atrás"],
+ "_in %n day_::_in %n days_" : ["em %n dia","em %n dias","em %n dias"],
+ "_%n day ago_::_%n days ago_" : ["%n dia atrás","%n dias atrás","%n dias atrás"],
"next month" : "Mês que vem",
"last month" : "último mês",
- "_in %n month_::_in %n months_" : ["em %n mês","em %n meses"],
- "_%n month ago_::_%n months ago_" : ["há %n mês atrás","há %n meses"],
+ "_in %n month_::_in %n months_" : ["em %n mês","em %n meses","em %n meses"],
+ "_%n month ago_::_%n months ago_" : ["há %n mês atrás","há %n meses","há %n meses"],
"next year" : "ano que vem",
"last year" : "último ano",
- "_in %n year_::_in %n years_" : ["em %n ano","em %n anos"],
- "_%n year ago_::_%n years ago_" : ["%n ano atrás","%n anos atrás"],
- "_in %n hour_::_in %n hours_" : ["em %n hora","em %n horas"],
- "_%n hour ago_::_%n hours ago_" : ["há %n hora atrás","há %n horas"],
- "_in %n minute_::_in %n minutes_" : ["em %n minuto","em %n minutos"],
- "_%n minute ago_::_%n minutes ago_" : ["há %n minuto atrás","há %n minutos"],
+ "_in %n year_::_in %n years_" : ["em %n ano","em %n anos","em %n anos"],
+ "_%n year ago_::_%n years ago_" : ["%n ano atrás","%n anos atrás","%n anos atrás"],
+ "_in %n hour_::_in %n hours_" : ["em %n hora","em %n horas","em %n horas"],
+ "_%n hour ago_::_%n hours ago_" : ["há %n hora atrás","há %n horas","há %n horas"],
+ "_in %n minute_::_in %n minutes_" : ["em %n minuto","em %n minutos","em %n minutos"],
+ "_%n minute ago_::_%n minutes ago_" : ["há %n minuto atrás","há %n minutos","há %n minutos"],
"in a few seconds" : "Em alguns segundos",
"seconds ago" : "segundos atrás",
"Empty file" : "Arquivo vazio",
@@ -89,6 +89,7 @@
"This is an automatically sent email, please do not reply." : "Este e-mail é enviado automaticamente. Por favor, não responda.",
"Help" : "Ajuda",
"Apps" : "Aplicativos",
+ "Personal settings" : "Configurações pessoais",
"Settings" : "Configurações",
"Log out" : "Sair",
"Users" : "Usuários",
@@ -138,7 +139,7 @@
"Files cannot be shared with delete permissions" : "Arquivos não podem ser compartilhados com permissões de exclusão",
"Files cannot be shared with create permissions" : "Arquivos não podem ser compartilhados com permissões de criação",
"Expiration date is in the past" : "Data de expiração está no passado",
- "_Cannot set expiration date more than %n day in the future_::_Cannot set expiration date more than %n days in the future_" : ["Não foi possível definir a data de expiração superior que %n dias no futuro","Não foi possível definir a data de expiração superior que %n dias no futuro"],
+ "_Cannot set expiration date more than %n day in the future_::_Cannot set expiration date more than %n days in the future_" : ["Não foi possível definir a data de expiração superior que %n dias no futuro","Não foi possível definir a data de expiração superior que %n dias no futuro","Não foi possível definir a data de expiração superior que %n dias no futuro"],
"Sharing is only allowed with group members" : "O compartilhamento só é permitido com membros do grupo ",
"Sharing %s failed, because this item is already shared with user %s" : "Compartilhamento %s falhou, porque este item já está compartilhado com o usuário %s",
"%1$s shared »%2$s« with you" : "%1$s compartilhou »%2$s« com você",
@@ -205,7 +206,7 @@
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "O aplicativo \"%1$s\" não pode ser instalado devido à estas dependências: %2$s",
"a safe home for all your data" : "Um lar seguro para todos os seus dados",
"File is currently busy, please try again later" : "O arquivo está ocupado, tente novamente mais tarde",
- "Cannot read file" : "Não foi possível ler o arquivo",
+ "Cannot download file" : "Não é possível baixar o arquivo",
"Application is not enabled" : "O aplicativo não está habilitado",
"Authentication error" : "Erro de autenticação",
"Token expired. Please reload page." : "O token expirou. Por favor recarregue a página.",
@@ -255,6 +256,7 @@
"Storage is temporarily not available" : "Armazenamento temporariamente indisponível",
"Storage connection timeout. %s" : "Atingido o tempo limite de conexão ao armazenamento. %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Geralmente, isso pode ser corrigido concedendo ao servidor web acesso de gravação ao diretório de configuração.",
+ "Cannot read file" : "Não foi possível ler o arquivo",
"Cannot write into \"config\" directory" : "Não foi possível gravar no diretório \"config\"",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Normalmente isso pode ser resolvido dando ao webserver permissão de escritura no diretório config. Veja %s",
"Cannot write into \"apps\" directory" : "Não foi possível gravar no diretório \"apps\"",
@@ -272,5 +274,5 @@
"Your data directory must be an absolute path" : "O diretório de dados deve ser um caminho absoluto",
"Check the value of \"datadirectory\" in your configuration" : "Verifique o valor do \"datadirectory\" na sua configuração",
"Your data directory is invalid" : "Seu diretório de dados é inválido"
-},"pluralForm" :"nplurals=2; plural=(n > 1);"
+},"pluralForm" :"nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
} \ No newline at end of file
diff --git a/lib/l10n/pt_PT.js b/lib/l10n/pt_PT.js
index 35f3917cf43..81bc932c91a 100644
--- a/lib/l10n/pt_PT.js
+++ b/lib/l10n/pt_PT.js
@@ -38,20 +38,20 @@ OC.L10N.register(
"today" : "hoje",
"tomorrow" : "Amanhã",
"yesterday" : "ontem",
- "_in %n day_::_in %n days_" : ["em %n dia","em %n dias"],
- "_%n day ago_::_%n days ago_" : ["%n dia atrás","%n dias atrás"],
+ "_in %n day_::_in %n days_" : ["em %n dia","em %n dias","em %n dias"],
+ "_%n day ago_::_%n days ago_" : ["%n dia atrás","%n dias atrás","%n dias atrás"],
"next month" : "Próximo mês",
"last month" : "ultimo mês",
- "_in %n month_::_in %n months_" : ["em %n mês","em %n meses"],
- "_%n month ago_::_%n months ago_" : ["%n mês atrás","%n meses atrás"],
+ "_in %n month_::_in %n months_" : ["em %n mês","em %n meses","em %n meses"],
+ "_%n month ago_::_%n months ago_" : ["%n mês atrás","%n meses atrás","%n meses atrás"],
"next year" : "Próximo ano",
"last year" : "ano passado",
- "_in %n year_::_in %n years_" : ["dentro de%n ano","dentro de %n anos"],
- "_%n year ago_::_%n years ago_" : ["%n ano atrás","%n anos atrás"],
- "_in %n hour_::_in %n hours_" : ["dentro de %n hora","dentro de %n horas"],
- "_%n hour ago_::_%n hours ago_" : ["%n hora atrás","%n horas atrás"],
- "_in %n minute_::_in %n minutes_" : ["dentro de %n minuto","dentro de %n minutos"],
- "_%n minute ago_::_%n minutes ago_" : ["%n minuto atrás","%n minutos atrás"],
+ "_in %n year_::_in %n years_" : ["dentro de%n ano","dentro de %n anos","dentro de %n anos"],
+ "_%n year ago_::_%n years ago_" : ["%n ano atrás","%n anos atrás","%n anos atrás"],
+ "_in %n hour_::_in %n hours_" : ["dentro de %n hora","dentro de %n horas","dentro de %n horas"],
+ "_%n hour ago_::_%n hours ago_" : ["%n hora atrás","%n horas atrás","%n horas atrás"],
+ "_in %n minute_::_in %n minutes_" : ["dentro de %n minuto","dentro de %n minutos","dentro de %n minutos"],
+ "_%n minute ago_::_%n minutes ago_" : ["%n minuto atrás","%n minutos atrás","%n minutos atrás"],
"in a few seconds" : "em breves segundos",
"seconds ago" : "Minutos atrás",
"Empty file" : "Ficheiro vazio",
@@ -178,7 +178,6 @@ OC.L10N.register(
"Login canceled by app" : "Sessão cancelada pela app",
"a safe home for all your data" : "Um lugar seguro para todos os seus dados",
"File is currently busy, please try again later" : "O ficheiro está ocupado, por favor, tente mais tarde",
- "Cannot read file" : "Não é possível ler o ficheiro",
"Application is not enabled" : "A aplicação não está activada",
"Authentication error" : "Erro na autenticação",
"Token expired. Please reload page." : "O token expirou. Por favor recarregue a página.",
@@ -207,6 +206,7 @@ OC.L10N.register(
"Storage connection error. %s" : "Erro de ligação ao armazenamento. %s",
"Storage is temporarily not available" : "Armazenamento temporariamente indisponível",
"Storage connection timeout. %s" : "Tempo de ligação ao armazenamento expirou. %s",
+ "Cannot read file" : "Não é possível ler o ficheiro",
"Cannot write into \"config\" directory" : "Não é possível escrever na directoria \"configurar\"",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Isto pode geralmente ser corrigido ao adicionar permissões de escrita à pasta de configuração ao servidor web. Ver %s.",
"Cannot write into \"apps\" directory" : "Não é possivel escrever na directoria \"aplicações\"",
@@ -224,4 +224,4 @@ OC.L10N.register(
"Check the value of \"datadirectory\" in your configuration" : "Verifique o valor de \"datadirectory\" na sua configuração",
"Your data directory is invalid" : "O seu directório de dados é inválido"
},
-"nplurals=2; plural=(n != 1);");
+"nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/lib/l10n/pt_PT.json b/lib/l10n/pt_PT.json
index 9ca3e09f10e..e3fc3a345ef 100644
--- a/lib/l10n/pt_PT.json
+++ b/lib/l10n/pt_PT.json
@@ -36,20 +36,20 @@
"today" : "hoje",
"tomorrow" : "Amanhã",
"yesterday" : "ontem",
- "_in %n day_::_in %n days_" : ["em %n dia","em %n dias"],
- "_%n day ago_::_%n days ago_" : ["%n dia atrás","%n dias atrás"],
+ "_in %n day_::_in %n days_" : ["em %n dia","em %n dias","em %n dias"],
+ "_%n day ago_::_%n days ago_" : ["%n dia atrás","%n dias atrás","%n dias atrás"],
"next month" : "Próximo mês",
"last month" : "ultimo mês",
- "_in %n month_::_in %n months_" : ["em %n mês","em %n meses"],
- "_%n month ago_::_%n months ago_" : ["%n mês atrás","%n meses atrás"],
+ "_in %n month_::_in %n months_" : ["em %n mês","em %n meses","em %n meses"],
+ "_%n month ago_::_%n months ago_" : ["%n mês atrás","%n meses atrás","%n meses atrás"],
"next year" : "Próximo ano",
"last year" : "ano passado",
- "_in %n year_::_in %n years_" : ["dentro de%n ano","dentro de %n anos"],
- "_%n year ago_::_%n years ago_" : ["%n ano atrás","%n anos atrás"],
- "_in %n hour_::_in %n hours_" : ["dentro de %n hora","dentro de %n horas"],
- "_%n hour ago_::_%n hours ago_" : ["%n hora atrás","%n horas atrás"],
- "_in %n minute_::_in %n minutes_" : ["dentro de %n minuto","dentro de %n minutos"],
- "_%n minute ago_::_%n minutes ago_" : ["%n minuto atrás","%n minutos atrás"],
+ "_in %n year_::_in %n years_" : ["dentro de%n ano","dentro de %n anos","dentro de %n anos"],
+ "_%n year ago_::_%n years ago_" : ["%n ano atrás","%n anos atrás","%n anos atrás"],
+ "_in %n hour_::_in %n hours_" : ["dentro de %n hora","dentro de %n horas","dentro de %n horas"],
+ "_%n hour ago_::_%n hours ago_" : ["%n hora atrás","%n horas atrás","%n horas atrás"],
+ "_in %n minute_::_in %n minutes_" : ["dentro de %n minuto","dentro de %n minutos","dentro de %n minutos"],
+ "_%n minute ago_::_%n minutes ago_" : ["%n minuto atrás","%n minutos atrás","%n minutos atrás"],
"in a few seconds" : "em breves segundos",
"seconds ago" : "Minutos atrás",
"Empty file" : "Ficheiro vazio",
@@ -176,7 +176,6 @@
"Login canceled by app" : "Sessão cancelada pela app",
"a safe home for all your data" : "Um lugar seguro para todos os seus dados",
"File is currently busy, please try again later" : "O ficheiro está ocupado, por favor, tente mais tarde",
- "Cannot read file" : "Não é possível ler o ficheiro",
"Application is not enabled" : "A aplicação não está activada",
"Authentication error" : "Erro na autenticação",
"Token expired. Please reload page." : "O token expirou. Por favor recarregue a página.",
@@ -205,6 +204,7 @@
"Storage connection error. %s" : "Erro de ligação ao armazenamento. %s",
"Storage is temporarily not available" : "Armazenamento temporariamente indisponível",
"Storage connection timeout. %s" : "Tempo de ligação ao armazenamento expirou. %s",
+ "Cannot read file" : "Não é possível ler o ficheiro",
"Cannot write into \"config\" directory" : "Não é possível escrever na directoria \"configurar\"",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Isto pode geralmente ser corrigido ao adicionar permissões de escrita à pasta de configuração ao servidor web. Ver %s.",
"Cannot write into \"apps\" directory" : "Não é possivel escrever na directoria \"aplicações\"",
@@ -221,5 +221,5 @@
"Your data directory must be an absolute path" : "O seu directório de dados deve ser um caminho absoluto",
"Check the value of \"datadirectory\" in your configuration" : "Verifique o valor de \"datadirectory\" na sua configuração",
"Your data directory is invalid" : "O seu directório de dados é inválido"
-},"pluralForm" :"nplurals=2; plural=(n != 1);"
+},"pluralForm" :"nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
} \ No newline at end of file
diff --git a/lib/l10n/ro.js b/lib/l10n/ro.js
index 3594dd31251..075e7364752 100644
--- a/lib/l10n/ro.js
+++ b/lib/l10n/ro.js
@@ -5,6 +5,7 @@ OC.L10N.register(
"See %s" : "Vezi %s",
"Sample configuration detected" : "A fost detectată o configurație exemplu",
"It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "S-a detectat copierea configurației exemplu. Acest lucru poate duce la oprirea instanței tale și nu este suportat. Te rugăm să citești documentația înainte de a face modificări în fișierul config.php",
+ "Other activities" : "Alte activități",
"%1$s and %2$s" : "%1$s și %2$s",
"%1$s, %2$s and %3$s" : "%1$s, %2$s și %3$s",
"%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s și %4$s",
@@ -17,6 +18,7 @@ OC.L10N.register(
"Authentication" : "Autentificare",
"Unknown filetype" : "Tip fișier necunoscut",
"Invalid image" : "Imagine invalidă",
+ "View profile" : "Vezi profilul",
"today" : "astăzi",
"tomorrow" : "mâine",
"yesterday" : "ieri",
@@ -30,6 +32,7 @@ OC.L10N.register(
"in a few seconds" : "în câteva secunde",
"seconds ago" : "secunde în urmă",
"File already exists" : "Fișierul există deja",
+ "Templates" : "Șabloane",
"File name is a reserved word" : "Numele fișierului este un cuvânt rezervat",
"File name contains at least one invalid character" : "Numele fișierului conține cel puțin un caracter invalid",
"File name is too long" : "Numele fișierului este prea lung",
diff --git a/lib/l10n/ro.json b/lib/l10n/ro.json
index 8454e885675..72bfb83985b 100644
--- a/lib/l10n/ro.json
+++ b/lib/l10n/ro.json
@@ -3,6 +3,7 @@
"See %s" : "Vezi %s",
"Sample configuration detected" : "A fost detectată o configurație exemplu",
"It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "S-a detectat copierea configurației exemplu. Acest lucru poate duce la oprirea instanței tale și nu este suportat. Te rugăm să citești documentația înainte de a face modificări în fișierul config.php",
+ "Other activities" : "Alte activități",
"%1$s and %2$s" : "%1$s și %2$s",
"%1$s, %2$s and %3$s" : "%1$s, %2$s și %3$s",
"%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s și %4$s",
@@ -15,6 +16,7 @@
"Authentication" : "Autentificare",
"Unknown filetype" : "Tip fișier necunoscut",
"Invalid image" : "Imagine invalidă",
+ "View profile" : "Vezi profilul",
"today" : "astăzi",
"tomorrow" : "mâine",
"yesterday" : "ieri",
@@ -28,6 +30,7 @@
"in a few seconds" : "în câteva secunde",
"seconds ago" : "secunde în urmă",
"File already exists" : "Fișierul există deja",
+ "Templates" : "Șabloane",
"File name is a reserved word" : "Numele fișierului este un cuvânt rezervat",
"File name contains at least one invalid character" : "Numele fișierului conține cel puțin un caracter invalid",
"File name is too long" : "Numele fișierului este prea lung",
diff --git a/lib/l10n/ru.js b/lib/l10n/ru.js
index 2d2cbdb1d8f..85f11c1a5ea 100644
--- a/lib/l10n/ru.js
+++ b/lib/l10n/ru.js
@@ -81,7 +81,7 @@ OC.L10N.register(
"Failed to create file from template" : "Не удалось создать файл на основе шаблона",
"Templates" : "Шаблоны",
"File name is a reserved word" : "Имя файла является зарезервированным словом",
- "File name contains at least one invalid character" : "Имя файла содержит по крайней мере один некорректный символ",
+ "File name contains at least one invalid character" : "Имя файла содержит по крайней мере один недопустимый символ",
"File name is too long" : "Имя файла слишком длинное.",
"Dot files are not allowed" : "Файлы начинающиеся с точки не допускаются",
"Empty filename is not allowed" : "Пустое имя файла не допускается",
@@ -207,7 +207,6 @@ OC.L10N.register(
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "Приложение «%1$s» не может быть установлено, так как не удовлетворены следующие зависимости: %2$s",
"a safe home for all your data" : "надёжный дом для всех ваших данных",
"File is currently busy, please try again later" : "Файл в данный момент используется, повторите попытку позже.",
- "Cannot read file" : "Не удается прочитать файл",
"Application is not enabled" : "Приложение не разрешено",
"Authentication error" : "Ошибка аутентификации",
"Token expired. Please reload page." : "Токен просрочен. Перезагрузите страницу.",
@@ -220,7 +219,7 @@ OC.L10N.register(
"Cannot create \"data\" directory." : "Невозможно создать каталог «data».",
"This can usually be fixed by giving the web server write access to the root directory. See %s" : "Обычно это можно исправить, предоставив веб-серверу права на запись в корневой каталог. Обратитесь к %s",
"Permissions can usually be fixed by giving the web server write access to the root directory. See %s." : "Разрешения обычно можно исправить, предоставив веб-серверу право на запись в корневой каталог. Обратитесь к %s.",
- "Your data directory is not writable." : "Каталог данных не доступен для записи.",
+ "Your data directory is not writable." : "Каталог данных недоступен для записи.",
"Setting locale to %s failed." : "Не удалось настроить локаль %s.",
"Please install one of these locales on your system and restart your web server." : "Установите один из этих языковых пакетов и перезапустите веб-сервер.",
"PHP module %s not installed." : "Не установлен PHP-модуль %s.",
@@ -235,6 +234,8 @@ OC.L10N.register(
"This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Возможно это вызвано кешем/ускорителем вроде Zend OPcache или eAccelerator.",
"PHP modules have been installed, but they are still listed as missing?" : "Модули PHP были установлены, но они все еще перечислены как недостающие?",
"Please ask your server administrator to restart the web server." : "Пожалуйста, попросите вашего администратора перезапустить веб-сервер.",
+ "The required %s config variable is not configured in the config.php file." : "Необходимая переменная %s не настроена в файле config.php.",
+ "Please ask your server administrator to check the Nextcloud configuration." : "Пожалуйста, попросите администратора вашего сервера проверить конфигурацию Nextcloud.",
"PostgreSQL >= 9 required." : "Требуется PostgreSQL версии 9 или более новый.",
"Please upgrade your database version." : "Обновите базу данных.",
"Your data directory is readable by other users." : "Каталог данных доступен для чтения другим пользователям.",
@@ -255,6 +256,7 @@ OC.L10N.register(
"Storage is temporarily not available" : "Хранилище временно недоступно",
"Storage connection timeout. %s" : "Истекло время ожидания подключения к хранилищу. %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Обычно это можно исправить, предоставив веб-серверу права на запись в каталог конфигурации.",
+ "Cannot read file" : "Не удается прочитать файл",
"Cannot write into \"config\" directory" : "Запись в каталог «config» невозможна",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Обычно это можно исправить, предоставив веб-серверу права на запись в каталог конфигурации. Изучите %s.",
"Cannot write into \"apps\" directory" : "Запись в каталог «app» невозможна",
diff --git a/lib/l10n/ru.json b/lib/l10n/ru.json
index 9e6fdb6538e..e5e20754874 100644
--- a/lib/l10n/ru.json
+++ b/lib/l10n/ru.json
@@ -79,7 +79,7 @@
"Failed to create file from template" : "Не удалось создать файл на основе шаблона",
"Templates" : "Шаблоны",
"File name is a reserved word" : "Имя файла является зарезервированным словом",
- "File name contains at least one invalid character" : "Имя файла содержит по крайней мере один некорректный символ",
+ "File name contains at least one invalid character" : "Имя файла содержит по крайней мере один недопустимый символ",
"File name is too long" : "Имя файла слишком длинное.",
"Dot files are not allowed" : "Файлы начинающиеся с точки не допускаются",
"Empty filename is not allowed" : "Пустое имя файла не допускается",
@@ -205,7 +205,6 @@
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "Приложение «%1$s» не может быть установлено, так как не удовлетворены следующие зависимости: %2$s",
"a safe home for all your data" : "надёжный дом для всех ваших данных",
"File is currently busy, please try again later" : "Файл в данный момент используется, повторите попытку позже.",
- "Cannot read file" : "Не удается прочитать файл",
"Application is not enabled" : "Приложение не разрешено",
"Authentication error" : "Ошибка аутентификации",
"Token expired. Please reload page." : "Токен просрочен. Перезагрузите страницу.",
@@ -218,7 +217,7 @@
"Cannot create \"data\" directory." : "Невозможно создать каталог «data».",
"This can usually be fixed by giving the web server write access to the root directory. See %s" : "Обычно это можно исправить, предоставив веб-серверу права на запись в корневой каталог. Обратитесь к %s",
"Permissions can usually be fixed by giving the web server write access to the root directory. See %s." : "Разрешения обычно можно исправить, предоставив веб-серверу право на запись в корневой каталог. Обратитесь к %s.",
- "Your data directory is not writable." : "Каталог данных не доступен для записи.",
+ "Your data directory is not writable." : "Каталог данных недоступен для записи.",
"Setting locale to %s failed." : "Не удалось настроить локаль %s.",
"Please install one of these locales on your system and restart your web server." : "Установите один из этих языковых пакетов и перезапустите веб-сервер.",
"PHP module %s not installed." : "Не установлен PHP-модуль %s.",
@@ -233,6 +232,8 @@
"This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Возможно это вызвано кешем/ускорителем вроде Zend OPcache или eAccelerator.",
"PHP modules have been installed, but they are still listed as missing?" : "Модули PHP были установлены, но они все еще перечислены как недостающие?",
"Please ask your server administrator to restart the web server." : "Пожалуйста, попросите вашего администратора перезапустить веб-сервер.",
+ "The required %s config variable is not configured in the config.php file." : "Необходимая переменная %s не настроена в файле config.php.",
+ "Please ask your server administrator to check the Nextcloud configuration." : "Пожалуйста, попросите администратора вашего сервера проверить конфигурацию Nextcloud.",
"PostgreSQL >= 9 required." : "Требуется PostgreSQL версии 9 или более новый.",
"Please upgrade your database version." : "Обновите базу данных.",
"Your data directory is readable by other users." : "Каталог данных доступен для чтения другим пользователям.",
@@ -253,6 +254,7 @@
"Storage is temporarily not available" : "Хранилище временно недоступно",
"Storage connection timeout. %s" : "Истекло время ожидания подключения к хранилищу. %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Обычно это можно исправить, предоставив веб-серверу права на запись в каталог конфигурации.",
+ "Cannot read file" : "Не удается прочитать файл",
"Cannot write into \"config\" directory" : "Запись в каталог «config» невозможна",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Обычно это можно исправить, предоставив веб-серверу права на запись в каталог конфигурации. Изучите %s.",
"Cannot write into \"apps\" directory" : "Запись в каталог «app» невозможна",
diff --git a/lib/l10n/sc.js b/lib/l10n/sc.js
index c6e13e64a56..2417ed4b2f3 100644
--- a/lib/l10n/sc.js
+++ b/lib/l10n/sc.js
@@ -194,7 +194,6 @@ OC.L10N.register(
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "Non faghet a installare s'aplicatzione %1$s ca is dipendèntzias in fatu non sunt satisfatos:%2$s",
"a safe home for all your data" : "unu logu siguru pro totu is datos tuos",
"File is currently busy, please try again later" : "Pro immoe s'archìviu est impreadu, torra a proare a coa",
- "Cannot read file" : "Impossìbile a lèghere s'archìviu",
"Application is not enabled" : "S'aplicatzione no est ativada",
"Authentication error" : "Errore de autenticatzione",
"Token expired. Please reload page." : "Token iscadidu. Torra a carrigare sa pàgina.",
@@ -224,6 +223,7 @@ OC.L10N.register(
"Storage is temporarily not available" : "S'archiviatzione no est disponìbile pro immoe.",
"Storage connection timeout. %s" : "Tempus de connessione a s'archiviatzione iscadidu. %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Custu in generale si podet assentare dende a su serbidore atzessu a s'iscritura in sa cartella config.",
+ "Cannot read file" : "Impossìbile a lèghere s'archìviu",
"Cannot write into \"config\" directory" : "No faghet a iscriere in sa cartella \"config\"",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Custu in generale si podet assentare dende a su serbidore atzessu a s'iscritura in sa cartella config. Càstia %s",
"Cannot write into \"apps\" directory" : "No faghet a iscriere in sa cartella \"apps\"",
diff --git a/lib/l10n/sc.json b/lib/l10n/sc.json
index 7f3eabbd6fd..482152a4e87 100644
--- a/lib/l10n/sc.json
+++ b/lib/l10n/sc.json
@@ -192,7 +192,6 @@
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "Non faghet a installare s'aplicatzione %1$s ca is dipendèntzias in fatu non sunt satisfatos:%2$s",
"a safe home for all your data" : "unu logu siguru pro totu is datos tuos",
"File is currently busy, please try again later" : "Pro immoe s'archìviu est impreadu, torra a proare a coa",
- "Cannot read file" : "Impossìbile a lèghere s'archìviu",
"Application is not enabled" : "S'aplicatzione no est ativada",
"Authentication error" : "Errore de autenticatzione",
"Token expired. Please reload page." : "Token iscadidu. Torra a carrigare sa pàgina.",
@@ -222,6 +221,7 @@
"Storage is temporarily not available" : "S'archiviatzione no est disponìbile pro immoe.",
"Storage connection timeout. %s" : "Tempus de connessione a s'archiviatzione iscadidu. %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Custu in generale si podet assentare dende a su serbidore atzessu a s'iscritura in sa cartella config.",
+ "Cannot read file" : "Impossìbile a lèghere s'archìviu",
"Cannot write into \"config\" directory" : "No faghet a iscriere in sa cartella \"config\"",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Custu in generale si podet assentare dende a su serbidore atzessu a s'iscritura in sa cartella config. Càstia %s",
"Cannot write into \"apps\" directory" : "No faghet a iscriere in sa cartella \"apps\"",
diff --git a/lib/l10n/sk.js b/lib/l10n/sk.js
index 92995334418..c9bbfd28aa9 100644
--- a/lib/l10n/sk.js
+++ b/lib/l10n/sk.js
@@ -207,7 +207,6 @@ OC.L10N.register(
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "Aplikáciu \"%1$s\" nie je možné inštalovať, pretože nie sú splnené nasledovné závislosti: %2$s",
"a safe home for all your data" : "bezpečný domov pre všetky vaše dáta",
"File is currently busy, please try again later" : "Súbor sa práve používa, skúste prosím neskôr",
- "Cannot read file" : "Nemožno čítať súbor.",
"Application is not enabled" : "Aplikácia nie je zapnutá",
"Authentication error" : "Chyba autentifikácie",
"Token expired. Please reload page." : "Token vypršal. Obnovte, prosím, stránku.",
@@ -235,6 +234,8 @@ OC.L10N.register(
"This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "To je pravdepodobne spôsobené cache/akcelerátorom ako napr. Zend OPcache alebo eAccelerator.",
"PHP modules have been installed, but they are still listed as missing?" : "PHP moduly boli nainštalované, ale stále sa tvária, že chýbajú?",
"Please ask your server administrator to restart the web server." : "Prosím, požiadajte administrátora vášho servera o reštartovanie webového servera.",
+ "The required %s config variable is not configured in the config.php file." : "Požadovaná konfiguračná premenná %s nie je nakonfigurovaná v súbore config.php.",
+ "Please ask your server administrator to check the Nextcloud configuration." : "Požiadajte správcu servera, aby skontroloval konfiguráciu Nextcloud.",
"PostgreSQL >= 9 required." : "Vyžadované PostgreSQL >= 9.",
"Please upgrade your database version." : "Prosím, aktualizujte verziu svojej databázy.",
"Your data directory is readable by other users." : "Váš priečinok s dátami je prístupný na čítanie ostatným užívateľom.",
@@ -255,6 +256,7 @@ OC.L10N.register(
"Storage is temporarily not available" : "Úložisko je dočasne nedostupné",
"Storage connection timeout. %s" : "Vypršanie pripojenia k úložisku. %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Toto sa zvyčajne dá opraviť poskytnutím práva webového servera na zápis do priečinka s nastaveniami.",
+ "Cannot read file" : "Nemožno čítať súbor.",
"Cannot write into \"config\" directory" : "Nie je možné zapisovať do priečinka \"config\"",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "To je zvyčajne možné opraviť tým, že udelíte webovému serveru oprávnenie na zápis do adresára s konfiguráciou. Viď %s",
"Cannot write into \"apps\" directory" : "Nie je možné zapisovať do priečinka \"apps\"",
diff --git a/lib/l10n/sk.json b/lib/l10n/sk.json
index 0638b1e34d7..bdc26a5cef4 100644
--- a/lib/l10n/sk.json
+++ b/lib/l10n/sk.json
@@ -205,7 +205,6 @@
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "Aplikáciu \"%1$s\" nie je možné inštalovať, pretože nie sú splnené nasledovné závislosti: %2$s",
"a safe home for all your data" : "bezpečný domov pre všetky vaše dáta",
"File is currently busy, please try again later" : "Súbor sa práve používa, skúste prosím neskôr",
- "Cannot read file" : "Nemožno čítať súbor.",
"Application is not enabled" : "Aplikácia nie je zapnutá",
"Authentication error" : "Chyba autentifikácie",
"Token expired. Please reload page." : "Token vypršal. Obnovte, prosím, stránku.",
@@ -233,6 +232,8 @@
"This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "To je pravdepodobne spôsobené cache/akcelerátorom ako napr. Zend OPcache alebo eAccelerator.",
"PHP modules have been installed, but they are still listed as missing?" : "PHP moduly boli nainštalované, ale stále sa tvária, že chýbajú?",
"Please ask your server administrator to restart the web server." : "Prosím, požiadajte administrátora vášho servera o reštartovanie webového servera.",
+ "The required %s config variable is not configured in the config.php file." : "Požadovaná konfiguračná premenná %s nie je nakonfigurovaná v súbore config.php.",
+ "Please ask your server administrator to check the Nextcloud configuration." : "Požiadajte správcu servera, aby skontroloval konfiguráciu Nextcloud.",
"PostgreSQL >= 9 required." : "Vyžadované PostgreSQL >= 9.",
"Please upgrade your database version." : "Prosím, aktualizujte verziu svojej databázy.",
"Your data directory is readable by other users." : "Váš priečinok s dátami je prístupný na čítanie ostatným užívateľom.",
@@ -253,6 +254,7 @@
"Storage is temporarily not available" : "Úložisko je dočasne nedostupné",
"Storage connection timeout. %s" : "Vypršanie pripojenia k úložisku. %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Toto sa zvyčajne dá opraviť poskytnutím práva webového servera na zápis do priečinka s nastaveniami.",
+ "Cannot read file" : "Nemožno čítať súbor.",
"Cannot write into \"config\" directory" : "Nie je možné zapisovať do priečinka \"config\"",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "To je zvyčajne možné opraviť tým, že udelíte webovému serveru oprávnenie na zápis do adresára s konfiguráciou. Viď %s",
"Cannot write into \"apps\" directory" : "Nie je možné zapisovať do priečinka \"apps\"",
diff --git a/lib/l10n/sl.js b/lib/l10n/sl.js
index 6bf62591ffd..cf0a09ce781 100644
--- a/lib/l10n/sl.js
+++ b/lib/l10n/sl.js
@@ -85,6 +85,7 @@ OC.L10N.register(
"This is an automatically sent email, please do not reply." : "To sporočilo je samodejno poslano, nanj se nima smisla odzvati.",
"Help" : "Pomoč",
"Apps" : "Programi",
+ "Personal settings" : "Osebne nastavitve",
"Settings" : "Nastavitve",
"Log out" : "Odjava",
"Users" : "Uporabniki",
@@ -200,7 +201,6 @@ OC.L10N.register(
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "Programa »%1$s« ni mogoče namestiti zaradi nerešenih odvisnosti: %2$s",
"a safe home for all your data" : "Varno okolje za vaše podatke!",
"File is currently busy, please try again later" : "Datoteka je trenutno v uporabi. Poskusite znova kasneje.",
- "Cannot read file" : "Datoteke ni mogoče prebrati.",
"Application is not enabled" : "Program ni omogočen",
"Authentication error" : "Napaka overjanja",
"Token expired. Please reload page." : "Žeton je pretekel. Stran je treba ponovno naložiti.",
@@ -230,6 +230,7 @@ OC.L10N.register(
"Storage is temporarily not available" : "Shramba trenutno ni na voljo",
"Storage connection timeout. %s" : "Povezava do shrambe je časovno potekla. %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Napako je mogoče odpraviti z dodelitvijo dovoljenja spletnemu strežniku za pisanje v nastavitveno mapo.",
+ "Cannot read file" : "Datoteke ni mogoče prebrati.",
"Cannot write into \"config\" directory" : "Mapa »config« nima nastavljenih ustreznih dovoljenj za pisanje!",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Napako je mogoče odpraviti z dodelitvijo dovoljenja spletnemu strežniku za pisanje v nastavitveno mapo. Poglejte %s",
"Cannot write into \"apps\" directory" : "V mapo »apps« ni mogoče zapisovati!",
diff --git a/lib/l10n/sl.json b/lib/l10n/sl.json
index ecafd64721c..8b3d781934d 100644
--- a/lib/l10n/sl.json
+++ b/lib/l10n/sl.json
@@ -83,6 +83,7 @@
"This is an automatically sent email, please do not reply." : "To sporočilo je samodejno poslano, nanj se nima smisla odzvati.",
"Help" : "Pomoč",
"Apps" : "Programi",
+ "Personal settings" : "Osebne nastavitve",
"Settings" : "Nastavitve",
"Log out" : "Odjava",
"Users" : "Uporabniki",
@@ -198,7 +199,6 @@
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "Programa »%1$s« ni mogoče namestiti zaradi nerešenih odvisnosti: %2$s",
"a safe home for all your data" : "Varno okolje za vaše podatke!",
"File is currently busy, please try again later" : "Datoteka je trenutno v uporabi. Poskusite znova kasneje.",
- "Cannot read file" : "Datoteke ni mogoče prebrati.",
"Application is not enabled" : "Program ni omogočen",
"Authentication error" : "Napaka overjanja",
"Token expired. Please reload page." : "Žeton je pretekel. Stran je treba ponovno naložiti.",
@@ -228,6 +228,7 @@
"Storage is temporarily not available" : "Shramba trenutno ni na voljo",
"Storage connection timeout. %s" : "Povezava do shrambe je časovno potekla. %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Napako je mogoče odpraviti z dodelitvijo dovoljenja spletnemu strežniku za pisanje v nastavitveno mapo.",
+ "Cannot read file" : "Datoteke ni mogoče prebrati.",
"Cannot write into \"config\" directory" : "Mapa »config« nima nastavljenih ustreznih dovoljenj za pisanje!",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Napako je mogoče odpraviti z dodelitvijo dovoljenja spletnemu strežniku za pisanje v nastavitveno mapo. Poglejte %s",
"Cannot write into \"apps\" directory" : "V mapo »apps« ni mogoče zapisovati!",
diff --git a/lib/l10n/sv.js b/lib/l10n/sv.js
index 901bf146efe..cf59cbb3954 100644
--- a/lib/l10n/sv.js
+++ b/lib/l10n/sv.js
@@ -206,7 +206,6 @@ OC.L10N.register(
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "Appen \"%1$s\" kan inte installeras eftersom följande beroenden inte är uppfyllda: %2$s",
"a safe home for all your data" : "ett säkert hem för all din data",
"File is currently busy, please try again later" : "Filen är för tillfället upptagen, försök igen senare",
- "Cannot read file" : "Kan inte läsa fil",
"Application is not enabled" : "Applikationen är inte aktiverad",
"Authentication error" : "Fel vid autentisering",
"Token expired. Please reload page." : "Token har löpt ut. Uppdatera sidan.",
@@ -236,6 +235,7 @@ OC.L10N.register(
"Storage is temporarily not available" : "Lagringsutrymme är för tillfället inte tillgängligt",
"Storage connection timeout. %s" : "Lagringsutrymme lyckas inte ansluta \"timeout\". %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Detta kan vanligtvis åtgärdas genom att ge webbservern skrivåtkomst till config-katalogen.",
+ "Cannot read file" : "Kan inte läsa fil",
"Cannot write into \"config\" directory" : "Kan inte skriva till \"config\" katalogen",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Detta kan vanligtvis åtgärda genom att ge webbservern skrivåtkomst till konfigureringsmappen. Se %s",
"Cannot write into \"apps\" directory" : "Kan inte skriva till \"apps\" katalogen!",
diff --git a/lib/l10n/sv.json b/lib/l10n/sv.json
index 8bce5388bc6..4181ff7620b 100644
--- a/lib/l10n/sv.json
+++ b/lib/l10n/sv.json
@@ -204,7 +204,6 @@
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "Appen \"%1$s\" kan inte installeras eftersom följande beroenden inte är uppfyllda: %2$s",
"a safe home for all your data" : "ett säkert hem för all din data",
"File is currently busy, please try again later" : "Filen är för tillfället upptagen, försök igen senare",
- "Cannot read file" : "Kan inte läsa fil",
"Application is not enabled" : "Applikationen är inte aktiverad",
"Authentication error" : "Fel vid autentisering",
"Token expired. Please reload page." : "Token har löpt ut. Uppdatera sidan.",
@@ -234,6 +233,7 @@
"Storage is temporarily not available" : "Lagringsutrymme är för tillfället inte tillgängligt",
"Storage connection timeout. %s" : "Lagringsutrymme lyckas inte ansluta \"timeout\". %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Detta kan vanligtvis åtgärdas genom att ge webbservern skrivåtkomst till config-katalogen.",
+ "Cannot read file" : "Kan inte läsa fil",
"Cannot write into \"config\" directory" : "Kan inte skriva till \"config\" katalogen",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Detta kan vanligtvis åtgärda genom att ge webbservern skrivåtkomst till konfigureringsmappen. Se %s",
"Cannot write into \"apps\" directory" : "Kan inte skriva till \"apps\" katalogen!",
diff --git a/lib/l10n/th.js b/lib/l10n/th.js
index 687a78ecf07..c0d7cad0c25 100644
--- a/lib/l10n/th.js
+++ b/lib/l10n/th.js
@@ -127,7 +127,6 @@ OC.L10N.register(
"User disabled" : "ผู้ใช้ถูกปิดใช้งาน",
"a safe home for all your data" : "บ้านที่ปลอดภัยสำหรับข้อมูลของคุณ",
"File is currently busy, please try again later" : "ขณะนี้ไฟล์กำลังใช้งานอยู่ โปรดลองอีกครั้งในภายหลัง",
- "Cannot read file" : "ไม่สามารถอ่านไฟล์",
"Application is not enabled" : "แอพพลิเคชั่นดังกล่าวยังไม่ได้เปิดใช้งาน",
"Authentication error" : "เกิดข้อผิดพลาดในสิทธิ์การเข้าใช้งาน",
"Token expired. Please reload page." : "รหัสยืนยันความถูกต้องหมดอายุ กรุณาโหลดหน้าเว็บใหม่",
@@ -147,6 +146,7 @@ OC.L10N.register(
"Storage connection error. %s" : "ข้อผิดพลาดการเชื่อมต่อพื้นที่จัดเก็บข้อมูล %s",
"Storage is temporarily not available" : "พื้นที่จัดเก็บข้อมูลไม่สามารถใช้งานได้ชั่วคราว",
"Storage connection timeout. %s" : "หมดเวลาการเชื่อมต่อพื้นที่จัดเก็บข้อมูล %s",
+ "Cannot read file" : "ไม่สามารถอ่านไฟล์",
"Cannot write into \"config\" directory" : "ไม่สามารถเขียนลงในไดเรกทอรี \"config\"",
"Cannot write into \"apps\" directory" : "ไม่สามารถเขียนลงในไดเรกทอรี \"apps\"",
"Setting locale to %s failed" : "ตั้งค่าตำแหน่งที่ตั้งเป็น %s ล้มเหลว",
diff --git a/lib/l10n/th.json b/lib/l10n/th.json
index 70eb589442c..7c08e27d33d 100644
--- a/lib/l10n/th.json
+++ b/lib/l10n/th.json
@@ -125,7 +125,6 @@
"User disabled" : "ผู้ใช้ถูกปิดใช้งาน",
"a safe home for all your data" : "บ้านที่ปลอดภัยสำหรับข้อมูลของคุณ",
"File is currently busy, please try again later" : "ขณะนี้ไฟล์กำลังใช้งานอยู่ โปรดลองอีกครั้งในภายหลัง",
- "Cannot read file" : "ไม่สามารถอ่านไฟล์",
"Application is not enabled" : "แอพพลิเคชั่นดังกล่าวยังไม่ได้เปิดใช้งาน",
"Authentication error" : "เกิดข้อผิดพลาดในสิทธิ์การเข้าใช้งาน",
"Token expired. Please reload page." : "รหัสยืนยันความถูกต้องหมดอายุ กรุณาโหลดหน้าเว็บใหม่",
@@ -145,6 +144,7 @@
"Storage connection error. %s" : "ข้อผิดพลาดการเชื่อมต่อพื้นที่จัดเก็บข้อมูล %s",
"Storage is temporarily not available" : "พื้นที่จัดเก็บข้อมูลไม่สามารถใช้งานได้ชั่วคราว",
"Storage connection timeout. %s" : "หมดเวลาการเชื่อมต่อพื้นที่จัดเก็บข้อมูล %s",
+ "Cannot read file" : "ไม่สามารถอ่านไฟล์",
"Cannot write into \"config\" directory" : "ไม่สามารถเขียนลงในไดเรกทอรี \"config\"",
"Cannot write into \"apps\" directory" : "ไม่สามารถเขียนลงในไดเรกทอรี \"apps\"",
"Setting locale to %s failed" : "ตั้งค่าตำแหน่งที่ตั้งเป็น %s ล้มเหลว",
diff --git a/lib/l10n/tr.js b/lib/l10n/tr.js
index e03728a80cb..78a0c3fa913 100644
--- a/lib/l10n/tr.js
+++ b/lib/l10n/tr.js
@@ -91,13 +91,15 @@ OC.L10N.register(
"This is an automatically sent email, please do not reply." : "Bu ileti otomatik olarak gönderildiğinden lütfen yanıtlamayın.",
"Help" : "Yardım",
"Apps" : "Uygulamalar",
+ "Personal settings" : "Kişisel ayarlar",
+ "Admin settings" : "Yönetici ayarları",
"Settings" : "Ayarlar",
"Log out" : "Oturumu kapat",
"Users" : "Kullanıcılar",
"Email" : "E-posta",
"Mail %s" : "E-posta gönder: %s",
"Phone" : "Telefon",
- "Call %s" : "Ara: %s",
+ "Call %s" : "Çağrı gönder: %s",
"Twitter" : "Twitter",
"View %s on Twitter" : "Twitter üzerinde bak: %s",
"Website" : "Web sitesi",
@@ -206,11 +208,11 @@ OC.L10N.register(
"Login canceled by app" : "Oturum açma uygulama tarafından iptal edildi",
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "\"%1$s\" uygulaması, şu gereklilikler sağlanmadığı için kurulamıyor: %2$s",
"a safe home for all your data" : "verileriniz için güvenli bir barınak",
- "File is currently busy, please try again later" : "Dosya şu anda meşgul, lütfen daha sonra deneyin",
- "Cannot read file" : "Dosya okunamadı",
+ "File is currently busy, please try again later" : "Dosya şu anda meşgul. Lütfen bir süre sonra yeniden deneyin",
+ "Cannot download file" : "Dosya indirilemedi",
"Application is not enabled" : "Uygulama etkinleştirilmemiş",
"Authentication error" : "Kimlik doğrulama sorunu",
- "Token expired. Please reload page." : "Kodun süresi dolmuş. Lütfen sayfayı yenileyin.",
+ "Token expired. Please reload page." : "Kodun geçerlilik süresi dolmuş. Lütfen sayfayı yenileyin.",
"No database drivers (sqlite, mysql, or postgresql) installed." : "Herhangi bir veritabanı sürücüsü (sqlite, mysql ya da postgresql) kurulmamış.",
"Cannot write into \"config\" directory." : "\"config\" klasörüne yazılamadı.",
"This can usually be fixed by giving the web server write access to the config directory. See %s" : "Bu sorun genellikle, web sunucusuna config klasörüne yazma izni verilerek çözülebilir. %s bölümüne bakın",
@@ -257,6 +259,7 @@ OC.L10N.register(
"Storage is temporarily not available" : "Depolama geçici olarak kullanılamıyor",
"Storage connection timeout. %s" : "Depolama bağlantısı zaman aşımı. %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Bu sorun genellikle, web sunucusuna config klasörüne yazma izni verilerek çözülebilir.",
+ "Cannot read file" : "Dosya okunamadı",
"Cannot write into \"config\" directory" : "\"config\" klasörüne yazılamıyor",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Bu sorun genellikle, web sunucusuna config klasörüne yazma izni verilerek çözülebilir. %s bölümüne bakın",
"Cannot write into \"apps\" directory" : "\"apps\" klasörüne yazılamıyor",
diff --git a/lib/l10n/tr.json b/lib/l10n/tr.json
index 84fb4177d06..17892cac083 100644
--- a/lib/l10n/tr.json
+++ b/lib/l10n/tr.json
@@ -89,13 +89,15 @@
"This is an automatically sent email, please do not reply." : "Bu ileti otomatik olarak gönderildiğinden lütfen yanıtlamayın.",
"Help" : "Yardım",
"Apps" : "Uygulamalar",
+ "Personal settings" : "Kişisel ayarlar",
+ "Admin settings" : "Yönetici ayarları",
"Settings" : "Ayarlar",
"Log out" : "Oturumu kapat",
"Users" : "Kullanıcılar",
"Email" : "E-posta",
"Mail %s" : "E-posta gönder: %s",
"Phone" : "Telefon",
- "Call %s" : "Ara: %s",
+ "Call %s" : "Çağrı gönder: %s",
"Twitter" : "Twitter",
"View %s on Twitter" : "Twitter üzerinde bak: %s",
"Website" : "Web sitesi",
@@ -204,11 +206,11 @@
"Login canceled by app" : "Oturum açma uygulama tarafından iptal edildi",
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "\"%1$s\" uygulaması, şu gereklilikler sağlanmadığı için kurulamıyor: %2$s",
"a safe home for all your data" : "verileriniz için güvenli bir barınak",
- "File is currently busy, please try again later" : "Dosya şu anda meşgul, lütfen daha sonra deneyin",
- "Cannot read file" : "Dosya okunamadı",
+ "File is currently busy, please try again later" : "Dosya şu anda meşgul. Lütfen bir süre sonra yeniden deneyin",
+ "Cannot download file" : "Dosya indirilemedi",
"Application is not enabled" : "Uygulama etkinleştirilmemiş",
"Authentication error" : "Kimlik doğrulama sorunu",
- "Token expired. Please reload page." : "Kodun süresi dolmuş. Lütfen sayfayı yenileyin.",
+ "Token expired. Please reload page." : "Kodun geçerlilik süresi dolmuş. Lütfen sayfayı yenileyin.",
"No database drivers (sqlite, mysql, or postgresql) installed." : "Herhangi bir veritabanı sürücüsü (sqlite, mysql ya da postgresql) kurulmamış.",
"Cannot write into \"config\" directory." : "\"config\" klasörüne yazılamadı.",
"This can usually be fixed by giving the web server write access to the config directory. See %s" : "Bu sorun genellikle, web sunucusuna config klasörüne yazma izni verilerek çözülebilir. %s bölümüne bakın",
@@ -255,6 +257,7 @@
"Storage is temporarily not available" : "Depolama geçici olarak kullanılamıyor",
"Storage connection timeout. %s" : "Depolama bağlantısı zaman aşımı. %s",
"This can usually be fixed by giving the webserver write access to the config directory." : "Bu sorun genellikle, web sunucusuna config klasörüne yazma izni verilerek çözülebilir.",
+ "Cannot read file" : "Dosya okunamadı",
"Cannot write into \"config\" directory" : "\"config\" klasörüne yazılamıyor",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "Bu sorun genellikle, web sunucusuna config klasörüne yazma izni verilerek çözülebilir. %s bölümüne bakın",
"Cannot write into \"apps\" directory" : "\"apps\" klasörüne yazılamıyor",
diff --git a/lib/l10n/uk.js b/lib/l10n/uk.js
index cea70576e73..8f204020c65 100644
--- a/lib/l10n/uk.js
+++ b/lib/l10n/uk.js
@@ -5,14 +5,17 @@ OC.L10N.register(
"See %s" : "Переглянути %s",
"Sample configuration detected" : "Виявлено приклад конфігурації",
"It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Було виявлено, що приклад конфігурації було скопійовано. Це може нашкодити вашій системі та не підтримується. Будь ласка, зверніться до документації перед внесенням змін в файл config.php",
+ "Other activities" : "Інша активність",
"%1$s and %2$s" : "%1$s та %2$s",
"%1$s, %2$s and %3$s" : "%1$s, %2$s та %3$s",
"%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s та %4$s",
"%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s та %5$s",
"PHP %s or higher is required." : "Необхідно PHP %s або вище",
"PHP with a version lower than %s is required." : "Потрібна версія PHP нижче %s ",
+ "%sbit or higher PHP required." : "%sнеобхідний PHP біт або вище.",
"The command line tool %s could not be found" : "Утиліту командного рядка %s не знайдено",
"The library %s is not available." : "Бібліотека %s недоступна.",
+ "Server version %s or higher is required." : "Потрібна версія сервера %s або вище.",
"Remote wipe started" : "Розпочато віддалене стирання",
"Remote wipe finished" : "Віддалене стирання завершено",
"Authentication" : "Автентифікація",
@@ -26,9 +29,11 @@ OC.L10N.register(
"_%n day ago_::_%n days ago_" : ["%n день тому","%n днів тому","%n днів тому","%n днів тому"],
"next month" : "наступноого місяця",
"last month" : "минулого місяця",
+ "_%n month ago_::_%n months ago_" : ["%n місяць тому","%n місяць тому","%n місяців тому","%n місяців тому"],
"next year" : "наступного року",
"last year" : "минулого року",
"_%n year ago_::_%n years ago_" : ["%n рік тому","%n років тому","%n років тому","%n років тому"],
+ "_%n hour ago_::_%n hours ago_" : ["%nгодину тому","%n годин тому","%n годин тому","%n годин тому"],
"in a few seconds" : "через кілька секунд",
"seconds ago" : "секунди тому",
"Empty file" : "Порожній файл",
@@ -145,6 +150,7 @@ OC.L10N.register(
"PHP module %s not installed." : "%s модуль PHP не встановлено.",
"Please ask your server administrator to install the module." : "Будь ласка, зверніться до адміністратора, щоб встановити модуль.",
"PHP setting \"%s\" is not set to \"%s\"." : "Параметр PHP \"%s\" не встановлено в \"%s\".",
+ "Adjusting this setting in php.ini will make Nextcloud run again" : "Налаштування цього параметра у php.ini змусить Nextcloud запуститися знову",
"libxml2 2.7.0 is at least required. Currently %s is installed." : "Необхідно libxml2 версії принаймні 2.7.0. На разі встановлена %s.",
"To fix this issue update your libxml2 version and restart your web server." : "Що виправити це оновіть версію libxml2 та перезапустіть веб-сервер.",
"PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "Схоже, що PHP налаштовано на вичищення блоків вбудованої документації. Це зробить кілька основних додатків недоступними.",
diff --git a/lib/l10n/uk.json b/lib/l10n/uk.json
index 39dac8b57e5..24ae8d496ba 100644
--- a/lib/l10n/uk.json
+++ b/lib/l10n/uk.json
@@ -3,14 +3,17 @@
"See %s" : "Переглянути %s",
"Sample configuration detected" : "Виявлено приклад конфігурації",
"It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php" : "Було виявлено, що приклад конфігурації було скопійовано. Це може нашкодити вашій системі та не підтримується. Будь ласка, зверніться до документації перед внесенням змін в файл config.php",
+ "Other activities" : "Інша активність",
"%1$s and %2$s" : "%1$s та %2$s",
"%1$s, %2$s and %3$s" : "%1$s, %2$s та %3$s",
"%1$s, %2$s, %3$s and %4$s" : "%1$s, %2$s, %3$s та %4$s",
"%1$s, %2$s, %3$s, %4$s and %5$s" : "%1$s, %2$s, %3$s, %4$s та %5$s",
"PHP %s or higher is required." : "Необхідно PHP %s або вище",
"PHP with a version lower than %s is required." : "Потрібна версія PHP нижче %s ",
+ "%sbit or higher PHP required." : "%sнеобхідний PHP біт або вище.",
"The command line tool %s could not be found" : "Утиліту командного рядка %s не знайдено",
"The library %s is not available." : "Бібліотека %s недоступна.",
+ "Server version %s or higher is required." : "Потрібна версія сервера %s або вище.",
"Remote wipe started" : "Розпочато віддалене стирання",
"Remote wipe finished" : "Віддалене стирання завершено",
"Authentication" : "Автентифікація",
@@ -24,9 +27,11 @@
"_%n day ago_::_%n days ago_" : ["%n день тому","%n днів тому","%n днів тому","%n днів тому"],
"next month" : "наступноого місяця",
"last month" : "минулого місяця",
+ "_%n month ago_::_%n months ago_" : ["%n місяць тому","%n місяць тому","%n місяців тому","%n місяців тому"],
"next year" : "наступного року",
"last year" : "минулого року",
"_%n year ago_::_%n years ago_" : ["%n рік тому","%n років тому","%n років тому","%n років тому"],
+ "_%n hour ago_::_%n hours ago_" : ["%nгодину тому","%n годин тому","%n годин тому","%n годин тому"],
"in a few seconds" : "через кілька секунд",
"seconds ago" : "секунди тому",
"Empty file" : "Порожній файл",
@@ -143,6 +148,7 @@
"PHP module %s not installed." : "%s модуль PHP не встановлено.",
"Please ask your server administrator to install the module." : "Будь ласка, зверніться до адміністратора, щоб встановити модуль.",
"PHP setting \"%s\" is not set to \"%s\"." : "Параметр PHP \"%s\" не встановлено в \"%s\".",
+ "Adjusting this setting in php.ini will make Nextcloud run again" : "Налаштування цього параметра у php.ini змусить Nextcloud запуститися знову",
"libxml2 2.7.0 is at least required. Currently %s is installed." : "Необхідно libxml2 версії принаймні 2.7.0. На разі встановлена %s.",
"To fix this issue update your libxml2 version and restart your web server." : "Що виправити це оновіть версію libxml2 та перезапустіть веб-сервер.",
"PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible." : "Схоже, що PHP налаштовано на вичищення блоків вбудованої документації. Це зробить кілька основних додатків недоступними.",
diff --git a/lib/l10n/zh_CN.js b/lib/l10n/zh_CN.js
index 45137513e09..ba27df9184b 100644
--- a/lib/l10n/zh_CN.js
+++ b/lib/l10n/zh_CN.js
@@ -206,7 +206,6 @@ OC.L10N.register(
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "应用 \"%1$s\" 无法安装,因为不能满足以下依赖: %2$s",
"a safe home for all your data" : "给您所有数据一个安全的家",
"File is currently busy, please try again later" : "文件当前正忙,请稍后再试",
- "Cannot read file" : "无法读取文件",
"Application is not enabled" : "应用程序未启用",
"Authentication error" : "认证出错",
"Token expired. Please reload page." : "Token 过期,请刷新页面。",
@@ -237,6 +236,7 @@ OC.L10N.register(
"Storage is temporarily not available" : "存储暂时不可用",
"Storage connection timeout. %s" : "存储连接超时。%s",
"This can usually be fixed by giving the webserver write access to the config directory." : "通常可以通过授予 Web 服务器对 config 目录的写访问权限来解决此问题。",
+ "Cannot read file" : "无法读取文件",
"Cannot write into \"config\" directory" : "无法写入“config”目录",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "这个通常可以通过赋予写入权限到 config 目录来修复。查看:%s",
"Cannot write into \"apps\" directory" : "无法写入“apps”目录",
diff --git a/lib/l10n/zh_CN.json b/lib/l10n/zh_CN.json
index afd4a0e70dc..f05d7446af2 100644
--- a/lib/l10n/zh_CN.json
+++ b/lib/l10n/zh_CN.json
@@ -204,7 +204,6 @@
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "应用 \"%1$s\" 无法安装,因为不能满足以下依赖: %2$s",
"a safe home for all your data" : "给您所有数据一个安全的家",
"File is currently busy, please try again later" : "文件当前正忙,请稍后再试",
- "Cannot read file" : "无法读取文件",
"Application is not enabled" : "应用程序未启用",
"Authentication error" : "认证出错",
"Token expired. Please reload page." : "Token 过期,请刷新页面。",
@@ -235,6 +234,7 @@
"Storage is temporarily not available" : "存储暂时不可用",
"Storage connection timeout. %s" : "存储连接超时。%s",
"This can usually be fixed by giving the webserver write access to the config directory." : "通常可以通过授予 Web 服务器对 config 目录的写访问权限来解决此问题。",
+ "Cannot read file" : "无法读取文件",
"Cannot write into \"config\" directory" : "无法写入“config”目录",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "这个通常可以通过赋予写入权限到 config 目录来修复。查看:%s",
"Cannot write into \"apps\" directory" : "无法写入“apps”目录",
diff --git a/lib/l10n/zh_HK.js b/lib/l10n/zh_HK.js
index cbc133fbe3e..d614e447f47 100644
--- a/lib/l10n/zh_HK.js
+++ b/lib/l10n/zh_HK.js
@@ -91,6 +91,8 @@ OC.L10N.register(
"This is an automatically sent email, please do not reply." : "此為自動寄送的電子郵件,請勿回覆。",
"Help" : "說明",
"Apps" : "應用程式",
+ "Personal settings" : "個人設定",
+ "Admin settings" : "管理員設定",
"Settings" : "設定",
"Log out" : "登出",
"Users" : "用戶",
@@ -207,7 +209,7 @@ OC.L10N.register(
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "應用程式 \"%1$s\" 無法被安裝,缺少下列所需元件: %2$s",
"a safe home for all your data" : "您資料的安全屋",
"File is currently busy, please try again later" : "檔案目前忙碌中,請稍候再試",
- "Cannot read file" : "無法讀取檔案",
+ "Cannot download file" : "無法下載檔案",
"Application is not enabled" : "應用程式未啟用",
"Authentication error" : "認證錯誤",
"Token expired. Please reload page." : "Token 過期,請重新整理頁面。",
@@ -257,6 +259,7 @@ OC.L10N.register(
"Storage is temporarily not available" : "儲存空間暫時無法使用",
"Storage connection timeout. %s" : "儲存空間連線逾時。%s",
"This can usually be fixed by giving the webserver write access to the config directory." : "允許網頁伺服器寫入 \"config\" 目錄通常可以解決這個問題",
+ "Cannot read file" : "無法讀取檔案",
"Cannot write into \"config\" directory" : "無法寫入 config 目錄",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "允許網頁伺服器寫入 \"config\" 目錄通常可以解決這個問題,詳見 %s",
"Cannot write into \"apps\" directory" : "無法寫入 apps 目錄",
diff --git a/lib/l10n/zh_HK.json b/lib/l10n/zh_HK.json
index 36fdc2cbaf7..5183bcb89c5 100644
--- a/lib/l10n/zh_HK.json
+++ b/lib/l10n/zh_HK.json
@@ -89,6 +89,8 @@
"This is an automatically sent email, please do not reply." : "此為自動寄送的電子郵件,請勿回覆。",
"Help" : "說明",
"Apps" : "應用程式",
+ "Personal settings" : "個人設定",
+ "Admin settings" : "管理員設定",
"Settings" : "設定",
"Log out" : "登出",
"Users" : "用戶",
@@ -205,7 +207,7 @@
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "應用程式 \"%1$s\" 無法被安裝,缺少下列所需元件: %2$s",
"a safe home for all your data" : "您資料的安全屋",
"File is currently busy, please try again later" : "檔案目前忙碌中,請稍候再試",
- "Cannot read file" : "無法讀取檔案",
+ "Cannot download file" : "無法下載檔案",
"Application is not enabled" : "應用程式未啟用",
"Authentication error" : "認證錯誤",
"Token expired. Please reload page." : "Token 過期,請重新整理頁面。",
@@ -255,6 +257,7 @@
"Storage is temporarily not available" : "儲存空間暫時無法使用",
"Storage connection timeout. %s" : "儲存空間連線逾時。%s",
"This can usually be fixed by giving the webserver write access to the config directory." : "允許網頁伺服器寫入 \"config\" 目錄通常可以解決這個問題",
+ "Cannot read file" : "無法讀取檔案",
"Cannot write into \"config\" directory" : "無法寫入 config 目錄",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "允許網頁伺服器寫入 \"config\" 目錄通常可以解決這個問題,詳見 %s",
"Cannot write into \"apps\" directory" : "無法寫入 apps 目錄",
diff --git a/lib/l10n/zh_TW.js b/lib/l10n/zh_TW.js
index d8c668d8f41..5d37d7e2949 100644
--- a/lib/l10n/zh_TW.js
+++ b/lib/l10n/zh_TW.js
@@ -91,6 +91,8 @@ OC.L10N.register(
"This is an automatically sent email, please do not reply." : "此為自動寄送的電子郵件,請不要回覆。",
"Help" : "說明",
"Apps" : "應用程式",
+ "Personal settings" : "個人設定",
+ "Admin settings" : "管理員設定",
"Settings" : "設定",
"Log out" : "登出",
"Users" : "使用者",
@@ -207,7 +209,7 @@ OC.L10N.register(
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "應用程式 \"%1$s\" 無法被安裝,缺少下列所需元件: %2$s",
"a safe home for all your data" : "您資料的安全屋",
"File is currently busy, please try again later" : "檔案目前忙碌中,請稍候再試",
- "Cannot read file" : "無法讀取檔案",
+ "Cannot download file" : "無法下載檔案",
"Application is not enabled" : "應用程式未啟用",
"Authentication error" : "認證錯誤",
"Token expired. Please reload page." : "Token 過期,請重新整理頁面。",
@@ -257,6 +259,7 @@ OC.L10N.register(
"Storage is temporarily not available" : "儲存空間暫時無法使用",
"Storage connection timeout. %s" : "儲存空間連線逾時。%s",
"This can usually be fixed by giving the webserver write access to the config directory." : "允許網頁伺服器寫入 \"config\" 目錄通常可以解決這個問題",
+ "Cannot read file" : "無法讀取檔案",
"Cannot write into \"config\" directory" : "無法寫入 config 目錄",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "允許網頁伺服器寫入 \"config\" 目錄通常可以解決這個問題,詳見 %s",
"Cannot write into \"apps\" directory" : "無法寫入 apps 目錄",
diff --git a/lib/l10n/zh_TW.json b/lib/l10n/zh_TW.json
index feac90fc2d9..6dc0f6814fc 100644
--- a/lib/l10n/zh_TW.json
+++ b/lib/l10n/zh_TW.json
@@ -89,6 +89,8 @@
"This is an automatically sent email, please do not reply." : "此為自動寄送的電子郵件,請不要回覆。",
"Help" : "說明",
"Apps" : "應用程式",
+ "Personal settings" : "個人設定",
+ "Admin settings" : "管理員設定",
"Settings" : "設定",
"Log out" : "登出",
"Users" : "使用者",
@@ -205,7 +207,7 @@
"App \"%1$s\" cannot be installed because the following dependencies are not fulfilled: %2$s" : "應用程式 \"%1$s\" 無法被安裝,缺少下列所需元件: %2$s",
"a safe home for all your data" : "您資料的安全屋",
"File is currently busy, please try again later" : "檔案目前忙碌中,請稍候再試",
- "Cannot read file" : "無法讀取檔案",
+ "Cannot download file" : "無法下載檔案",
"Application is not enabled" : "應用程式未啟用",
"Authentication error" : "認證錯誤",
"Token expired. Please reload page." : "Token 過期,請重新整理頁面。",
@@ -255,6 +257,7 @@
"Storage is temporarily not available" : "儲存空間暫時無法使用",
"Storage connection timeout. %s" : "儲存空間連線逾時。%s",
"This can usually be fixed by giving the webserver write access to the config directory." : "允許網頁伺服器寫入 \"config\" 目錄通常可以解決這個問題",
+ "Cannot read file" : "無法讀取檔案",
"Cannot write into \"config\" directory" : "無法寫入 config 目錄",
"This can usually be fixed by giving the webserver write access to the config directory. See %s" : "允許網頁伺服器寫入 \"config\" 目錄通常可以解決這個問題,詳見 %s",
"Cannot write into \"apps\" directory" : "無法寫入 apps 目錄",
diff --git a/lib/private/Accounts/AccountManager.php b/lib/private/Accounts/AccountManager.php
index 7f79ab46c37..20e0add1ccb 100644
--- a/lib/private/Accounts/AccountManager.php
+++ b/lib/private/Accounts/AccountManager.php
@@ -14,6 +14,7 @@
* @author Lukas Reschke <lukas@statuscode.ch>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Roeland Jago Douma <roeland@famdouma.nl>
+ * @author Thomas Citharel <nextcloud@tcit.fr>
* @author Vincent Petry <vincent@nextcloud.com>
*
* @license AGPL-3.0
@@ -41,7 +42,7 @@ use libphonenumber\PhoneNumber;
use libphonenumber\PhoneNumberFormat;
use libphonenumber\PhoneNumberUtil;
use OC\Profile\TProfileHelper;
-use OC\Cache\CappedMemoryCache;
+use OCP\Cache\CappedMemoryCache;
use OCA\Settings\BackgroundJobs\VerifyUserData;
use OCP\Accounts\IAccount;
use OCP\Accounts\IAccountManager;
@@ -119,6 +120,23 @@ class AccountManager implements IAccountManager {
private $l10nfactory;
private CappedMemoryCache $internalCache;
+ /**
+ * The list of default scopes for each property.
+ */
+ public const DEFAULT_SCOPES = [
+ self::PROPERTY_DISPLAYNAME => self::SCOPE_FEDERATED,
+ self::PROPERTY_ADDRESS => self::SCOPE_LOCAL,
+ self::PROPERTY_WEBSITE => self::SCOPE_LOCAL,
+ self::PROPERTY_EMAIL => self::SCOPE_FEDERATED,
+ self::PROPERTY_AVATAR => self::SCOPE_FEDERATED,
+ self::PROPERTY_PHONE => self::SCOPE_LOCAL,
+ self::PROPERTY_TWITTER => self::SCOPE_LOCAL,
+ self::PROPERTY_ORGANISATION => self::SCOPE_LOCAL,
+ self::PROPERTY_ROLE => self::SCOPE_LOCAL,
+ self::PROPERTY_HEADLINE => self::SCOPE_LOCAL,
+ self::PROPERTY_BIOGRAPHY => self::SCOPE_LOCAL,
+ ];
+
public function __construct(
IDBConnection $connection,
IConfig $config,
@@ -202,7 +220,7 @@ class AccountManager implements IAccountManager {
foreach ($properties as $property) {
if (strlen($property->getValue()) > 2048) {
if ($throwOnData) {
- throw new InvalidArgumentException();
+ throw new InvalidArgumentException($property->getName());
} else {
$property->setValue('');
}
@@ -649,81 +667,84 @@ class AccountManager implements IAccountManager {
/**
* build default user record in case not data set exists yet
- *
- * @param IUser $user
- * @return array
*/
- protected function buildDefaultUserRecord(IUser $user) {
+ protected function buildDefaultUserRecord(IUser $user): array {
+ $scopes = array_merge(self::DEFAULT_SCOPES, array_filter($this->config->getSystemValue('account_manager.default_property_scope', []), static function (string $scope, string $property) {
+ return in_array($property, self::ALLOWED_PROPERTIES, true) && in_array($scope, self::ALLOWED_SCOPES, true);
+ }, ARRAY_FILTER_USE_BOTH));
+
return [
[
'name' => self::PROPERTY_DISPLAYNAME,
'value' => $user->getDisplayName(),
- 'scope' => self::SCOPE_FEDERATED,
+ // Display name must be at least SCOPE_LOCAL
+ 'scope' => $scopes[self::PROPERTY_DISPLAYNAME] === self::SCOPE_PRIVATE ? self::SCOPE_LOCAL : $scopes[self::PROPERTY_DISPLAYNAME],
'verified' => self::NOT_VERIFIED,
],
[
'name' => self::PROPERTY_ADDRESS,
'value' => '',
- 'scope' => self::SCOPE_LOCAL,
+ 'scope' => $scopes[self::PROPERTY_ADDRESS],
'verified' => self::NOT_VERIFIED,
],
[
'name' => self::PROPERTY_WEBSITE,
'value' => '',
- 'scope' => self::SCOPE_LOCAL,
+ 'scope' => $scopes[self::PROPERTY_WEBSITE],
'verified' => self::NOT_VERIFIED,
],
[
'name' => self::PROPERTY_EMAIL,
'value' => $user->getEMailAddress(),
- 'scope' => self::SCOPE_FEDERATED,
+ // Email must be at least SCOPE_LOCAL
+ 'scope' => $scopes[self::PROPERTY_EMAIL] === self::SCOPE_PRIVATE ? self::SCOPE_LOCAL : $scopes[self::PROPERTY_EMAIL],
'verified' => self::NOT_VERIFIED,
],
[
'name' => self::PROPERTY_AVATAR,
- 'scope' => self::SCOPE_FEDERATED
+ 'scope' => $scopes[self::PROPERTY_AVATAR],
],
[
'name' => self::PROPERTY_PHONE,
'value' => '',
- 'scope' => self::SCOPE_LOCAL,
+ 'scope' => $scopes[self::PROPERTY_PHONE],
'verified' => self::NOT_VERIFIED,
],
[
'name' => self::PROPERTY_TWITTER,
'value' => '',
- 'scope' => self::SCOPE_LOCAL,
+ 'scope' => $scopes[self::PROPERTY_TWITTER],
'verified' => self::NOT_VERIFIED,
],
[
'name' => self::PROPERTY_ORGANISATION,
'value' => '',
- 'scope' => self::SCOPE_LOCAL,
+ 'scope' => $scopes[self::PROPERTY_ORGANISATION],
],
[
'name' => self::PROPERTY_ROLE,
'value' => '',
- 'scope' => self::SCOPE_LOCAL,
+ 'scope' => $scopes[self::PROPERTY_ROLE],
],
[
'name' => self::PROPERTY_HEADLINE,
'value' => '',
- 'scope' => self::SCOPE_LOCAL,
+ 'scope' => $scopes[self::PROPERTY_HEADLINE],
],
[
'name' => self::PROPERTY_BIOGRAPHY,
'value' => '',
- 'scope' => self::SCOPE_LOCAL,
+ 'scope' => $scopes[self::PROPERTY_BIOGRAPHY],
],
[
@@ -790,17 +811,8 @@ class AccountManager implements IAccountManager {
// valid case, nothing to do
}
- static $allowedScopes = [
- self::SCOPE_PRIVATE,
- self::SCOPE_LOCAL,
- self::SCOPE_FEDERATED,
- self::SCOPE_PUBLISHED,
- self::VISIBILITY_PRIVATE,
- self::VISIBILITY_CONTACTS_ONLY,
- self::VISIBILITY_PUBLIC,
- ];
foreach ($account->getAllProperties() as $property) {
- $this->testPropertyScope($property, $allowedScopes, true);
+ $this->testPropertyScope($property, self::ALLOWED_SCOPES, true);
}
$oldData = $this->getUser($account->getUser(), false);
diff --git a/lib/private/AllConfig.php b/lib/private/AllConfig.php
index f282baee146..2a0e8f53b14 100644
--- a/lib/private/AllConfig.php
+++ b/lib/private/AllConfig.php
@@ -32,7 +32,7 @@
*/
namespace OC;
-use OC\Cache\CappedMemoryCache;
+use OCP\Cache\CappedMemoryCache;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IConfig;
use OCP\IDBConnection;
@@ -353,8 +353,8 @@ class AllConfig implements IConfig {
$qb = $this->connection->getQueryBuilder();
$qb->delete('preferences')
->where($qb->expr()->eq('userid', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)))
- ->where($qb->expr()->eq('appid', $qb->createNamedParameter($appName, IQueryBuilder::PARAM_STR)))
- ->where($qb->expr()->eq('configkey', $qb->createNamedParameter($key, IQueryBuilder::PARAM_STR)))
+ ->andWhere($qb->expr()->eq('appid', $qb->createNamedParameter($appName, IQueryBuilder::PARAM_STR)))
+ ->andWhere($qb->expr()->eq('configkey', $qb->createNamedParameter($key, IQueryBuilder::PARAM_STR)))
->executeStatement();
if (isset($this->userCache[$userId][$appName])) {
diff --git a/lib/private/App/AppStore/Bundles/HubBundle.php b/lib/private/App/AppStore/Bundles/HubBundle.php
index a52de1dfbd4..d5d236a1855 100644
--- a/lib/private/App/AppStore/Bundles/HubBundle.php
+++ b/lib/private/App/AppStore/Bundles/HubBundle.php
@@ -39,8 +39,8 @@ class HubBundle extends Bundle {
'mail',
];
- $architecture = php_uname('m');
- if (PHP_OS_FAMILY === 'Linux' && in_array($architecture, ['x86_64', 'aarch64'])) {
+ $architecture = function_exists('php_uname') ? php_uname('m') : null;
+ if (isset($architecture) && PHP_OS_FAMILY === 'Linux' && in_array($architecture, ['x86_64', 'aarch64'])) {
$hubApps[] = 'richdocuments';
$hubApps[] = 'richdocumentscode' . ($architecture === 'aarch64' ? '_arm64' : '');
}
diff --git a/lib/private/App/CompareVersion.php b/lib/private/App/CompareVersion.php
index d155945fff1..a349c7aa6f2 100644
--- a/lib/private/App/CompareVersion.php
+++ b/lib/private/App/CompareVersion.php
@@ -41,7 +41,7 @@ class CompareVersion {
* so '13.0.1', '13.0' and '13' are valid.
*
* @param string $actual version as major.minor.patch notation
- * @param string $required version where major is requried and minor and patch are optional
+ * @param string $required version where major is required and minor and patch are optional
* @param string $comparator passed to `version_compare`
* @return bool whether the requirement is fulfilled
* @throws InvalidArgumentException if versions specified in an invalid format
diff --git a/lib/private/App/Platform.php b/lib/private/App/Platform.php
index 12097abbc78..01bf6748654 100644
--- a/lib/private/App/Platform.php
+++ b/lib/private/App/Platform.php
@@ -26,6 +26,7 @@
namespace OC\App;
use OCP\IConfig;
+use OCP\IBinaryFinder;
/**
* Class Platform
@@ -35,40 +36,26 @@ use OCP\IConfig;
* @package OC\App
*/
class Platform {
+ private IConfig $config;
- /**
- * @param IConfig $config
- */
public function __construct(IConfig $config) {
$this->config = $config;
}
- /**
- * @return string
- */
- public function getPhpVersion() {
+ public function getPhpVersion(): string {
return phpversion();
}
- /**
- * @return int
- */
- public function getIntSize() {
+ public function getIntSize(): int {
return PHP_INT_SIZE;
}
- /**
- * @return string
- */
- public function getOcVersion() {
+ public function getOcVersion(): string {
$v = \OCP\Util::getVersion();
return implode('.', $v);
}
- /**
- * @return string
- */
- public function getDatabase() {
+ public function getDatabase(): string {
$dbType = $this->config->getSystemValue('dbtype', 'sqlite');
if ($dbType === 'sqlite3') {
$dbType = 'sqlite';
@@ -77,23 +64,18 @@ class Platform {
return $dbType;
}
- /**
- * @return string
- */
- public function getOS() {
+ public function getOS(): string {
return php_uname('s');
}
/**
* @param $command
- * @return bool
*/
- public function isCommandKnown($command) {
- $path = \OC_Helper::findBinaryPath($command);
- return ($path !== null);
+ public function isCommandKnown(string $command): bool {
+ return \OCP\Server::get(IBinaryFinder::class)->findBinaryPath($command) !== false;
}
- public function getLibraryVersion($name) {
+ public function getLibraryVersion(string $name): ?string {
$repo = new PlatformRepository();
return $repo->findLibrary($name);
}
diff --git a/lib/private/App/PlatformRepository.php b/lib/private/App/PlatformRepository.php
index 94fac5260e1..4166c2ead03 100644
--- a/lib/private/App/PlatformRepository.php
+++ b/lib/private/App/PlatformRepository.php
@@ -31,11 +31,13 @@ namespace OC\App;
* @package OC\App
*/
class PlatformRepository {
+ private array $packages;
+
public function __construct() {
$this->packages = $this->initialize();
}
- protected function initialize() {
+ protected function initialize(): array {
$loadedExtensions = get_loaded_extensions();
$packages = [];
@@ -121,15 +123,11 @@ class PlatformRepository {
return $packages;
}
- private function buildPackageName($name) {
+ private function buildPackageName(string $name): string {
return str_replace(' ', '-', $name);
}
- /**
- * @param $name
- * @return string
- */
- public function findLibrary($name) {
+ public function findLibrary(string $name): ?string {
$extName = $this->buildPackageName($name);
if (isset($this->packages[$extName])) {
return $this->packages[$extName];
@@ -137,19 +135,17 @@ class PlatformRepository {
return null;
}
- private static $modifierRegex = '[._-]?(?:(stable|beta|b|RC|alpha|a|patch|pl|p)(?:[.-]?(\d+))?)?([.-]?dev)?';
+ private static string $modifierRegex = '[._-]?(?:(stable|beta|b|RC|alpha|a|patch|pl|p)(?:[.-]?(\d+))?)?([.-]?dev)?';
/**
* Normalizes a version string to be able to perform comparisons on it
*
* https://github.com/composer/composer/blob/master/src/Composer/Package/Version/VersionParser.php#L94
*
- * @param string $version
* @param string $fullVersion optional complete version string to give more context
* @throws \UnexpectedValueException
- * @return string
*/
- public function normalizeVersion($version, $fullVersion = null) {
+ public function normalizeVersion(string $version, ?string $fullVersion = null): string {
$version = trim($version);
if (null === $fullVersion) {
$fullVersion = $version;
@@ -204,10 +200,7 @@ class PlatformRepository {
throw new \UnexpectedValueException('Invalid version string "' . $version . '"' . $extraMessage);
}
- /**
- * @param string $stability
- */
- private function expandStability($stability) {
+ private function expandStability(string $stability): string {
$stability = strtolower($stability);
switch ($stability) {
case 'a':
diff --git a/lib/private/AppFramework/App.php b/lib/private/AppFramework/App.php
index feebb32d5bc..170acba0689 100644
--- a/lib/private/AppFramework/App.php
+++ b/lib/private/AppFramework/App.php
@@ -237,8 +237,6 @@ class App {
/**
* Shortcut for calling a controller method and printing the result.
* Similar to App:main except that no headers will be sent.
- * This should be used for example when registering sections via
- * \OC\AppFramework\Core\API::registerAdmin()
*
* @param string $controllerName the name of the controller under which it is
* stored in the DI container
diff --git a/lib/private/AppFramework/Bootstrap/Coordinator.php b/lib/private/AppFramework/Bootstrap/Coordinator.php
index b6378830732..3ab6ac4c8b0 100644
--- a/lib/private/AppFramework/Bootstrap/Coordinator.php
+++ b/lib/private/AppFramework/Bootstrap/Coordinator.php
@@ -151,7 +151,7 @@ class Coordinator {
*/
$this->registrationContext->delegateCapabilityRegistrations($apps);
$this->registrationContext->delegateCrashReporterRegistrations($apps, $this->registry);
- $this->registrationContext->delegateDashboardPanelRegistrations($apps, $this->dashboardManager);
+ $this->registrationContext->delegateDashboardPanelRegistrations($this->dashboardManager);
$this->registrationContext->delegateEventListenerRegistrations($this->eventDispatcher);
$this->registrationContext->delegateContainerRegistrations($apps);
$this->registrationContext->delegateMiddlewareRegistrations($apps);
diff --git a/lib/private/AppFramework/Bootstrap/RegistrationContext.php b/lib/private/AppFramework/Bootstrap/RegistrationContext.php
index 7b4d24036e8..3ade98e334f 100644
--- a/lib/private/AppFramework/Bootstrap/RegistrationContext.php
+++ b/lib/private/AppFramework/Bootstrap/RegistrationContext.php
@@ -32,6 +32,7 @@ namespace OC\AppFramework\Bootstrap;
use Closure;
use OCP\Calendar\Resource\IBackend as IResourceBackend;
use OCP\Calendar\Room\IBackend as IRoomBackend;
+use OCP\Collaboration\Reference\IReferenceProvider;
use OCP\Talk\ITalkBackend;
use RuntimeException;
use function array_shift;
@@ -121,6 +122,12 @@ class RegistrationContext {
/** @var ServiceRegistration<ICalendarProvider>[] */
private $calendarProviders = [];
+ /** @var ServiceRegistration<IReferenceProvider>[] */
+ private array $referenceProviders = [];
+
+ /** @var ParameterRegistration[] */
+ private $sensitiveMethods = [];
+
/** @var LoggerInterface */
private $logger;
@@ -270,6 +277,13 @@ class RegistrationContext {
);
}
+ public function registerReferenceProvider(string $class): void {
+ $this->context->registerReferenceProvider(
+ $this->appId,
+ $class
+ );
+ }
+
public function registerProfileLinkAction(string $actionClass): void {
$this->context->registerProfileLinkAction(
$this->appId,
@@ -304,6 +318,14 @@ class RegistrationContext {
$migratorClass
);
}
+
+ public function registerSensitiveMethods(string $class, array $methods): void {
+ $this->context->registerSensitiveMethods(
+ $this->appId,
+ $class,
+ $methods
+ );
+ }
};
}
@@ -387,6 +409,10 @@ class RegistrationContext {
$this->calendarProviders[] = new ServiceRegistration($appId, $class);
}
+ public function registerReferenceProvider(string $appId, string $class): void {
+ $this->referenceProviders[] = new ServiceRegistration($appId, $class);
+ }
+
/**
* @psalm-param class-string<ILinkAction> $actionClass
*/
@@ -430,6 +456,11 @@ class RegistrationContext {
$this->userMigrators[] = new ServiceRegistration($appId, $migratorClass);
}
+ public function registerSensitiveMethods(string $appId, string $class, array $methods): void {
+ $methods = array_filter($methods, 'is_string');
+ $this->sensitiveMethods[] = new ParameterRegistration($appId, $class, $methods);
+ }
+
/**
* @param App[] $apps
*/
@@ -475,10 +506,10 @@ class RegistrationContext {
/**
* @param App[] $apps
*/
- public function delegateDashboardPanelRegistrations(array $apps, IManager $dashboardManager): void {
+ public function delegateDashboardPanelRegistrations(IManager $dashboardManager): void {
while (($panel = array_shift($this->dashboardPanels)) !== null) {
try {
- $dashboardManager->lazyRegisterWidget($panel->getService());
+ $dashboardManager->lazyRegisterWidget($panel->getService(), $panel->getAppId());
} catch (Throwable $e) {
$appId = $panel->getAppId();
$this->logger->error("Error during dashboard registration of $appId: " . $e->getMessage(), [
@@ -676,6 +707,13 @@ class RegistrationContext {
}
/**
+ * @return ServiceRegistration<IReferenceProvider>[]
+ */
+ public function getReferenceProviders(): array {
+ return $this->referenceProviders;
+ }
+
+ /**
* @return ServiceRegistration<ILinkAction>[]
*/
public function getProfileLinkActions(): array {
@@ -712,4 +750,11 @@ class RegistrationContext {
public function getUserMigrators(): array {
return $this->userMigrators;
}
+
+ /**
+ * @return ParameterRegistration[]
+ */
+ public function getSensitiveMethods(): array {
+ return $this->sensitiveMethods;
+ }
}
diff --git a/lib/private/AppFramework/Http/Dispatcher.php b/lib/private/AppFramework/Http/Dispatcher.php
index 21d61bc95aa..c1a203a7165 100644
--- a/lib/private/AppFramework/Http/Dispatcher.php
+++ b/lib/private/AppFramework/Http/Dispatcher.php
@@ -118,7 +118,7 @@ class Dispatcher {
$out = [null, [], null];
try {
- // prefill reflector with everything thats needed for the
+ // prefill reflector with everything that's needed for the
// middlewares
$this->reflector->reflect($controller, $methodName);
@@ -156,7 +156,7 @@ class Dispatcher {
// if an exception appears, the middleware checks if it can handle the
// exception and creates a response. If no response is created, it is
- // assumed that theres no middleware who can handle it and the error is
+ // assumed that there's no middleware who can handle it and the error is
// thrown again
} catch (\Exception $exception) {
$response = $this->middlewareDispatcher->afterException(
diff --git a/lib/private/AppFramework/Http/Request.php b/lib/private/AppFramework/Http/Request.php
index 010d889070e..496a845dd4a 100644
--- a/lib/private/AppFramework/Http/Request.php
+++ b/lib/private/AppFramework/Http/Request.php
@@ -25,6 +25,7 @@ declare(strict_types=1);
* @author Thomas Müller <thomas.mueller@tmit.eu>
* @author Thomas Tanghus <thomas@tanghus.net>
* @author Vincent Petry <vincent@nextcloud.com>
+ * @author Simon Leiner <simon@leiner.me>
*
* @license AGPL-3.0
*
@@ -50,6 +51,7 @@ use OCP\IConfig;
use OCP\IRequest;
use OCP\IRequestId;
use OCP\Security\ICrypto;
+use Symfony\Component\HttpFoundation\IpUtils;
/**
* Class for accessing variables in the request.
@@ -342,7 +344,7 @@ class Request implements \ArrayAccess, \Countable, IRequest {
/**
* Returns all params that were received, be it from the request
- * (as GET or POST) or throuh the URL by the route
+ * (as GET or POST) or through the URL by the route
* @return array the array with all parameters
*/
public function getParams(): array {
@@ -573,41 +575,12 @@ class Request implements \ArrayAccess, \Countable, IRequest {
}
/**
- * Checks if given $remoteAddress matches given $trustedProxy.
- * If $trustedProxy is an IPv4 IP range given in CIDR notation, true will be returned if
- * $remoteAddress is an IPv4 address within that IP range.
- * Otherwise $remoteAddress will be compared to $trustedProxy literally and the result
- * will be returned.
- * @return boolean true if $remoteAddress matches $trustedProxy, false otherwise
- */
- protected function matchesTrustedProxy($trustedProxy, $remoteAddress) {
- $cidrre = '/^([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})\/([0-9]{1,2})$/';
-
- if (preg_match($cidrre, $trustedProxy, $match)) {
- $net = $match[1];
- $shiftbits = min(32, max(0, 32 - intval($match[2])));
- $netnum = ip2long($net) >> $shiftbits;
- $ipnum = ip2long($remoteAddress) >> $shiftbits;
-
- return $ipnum === $netnum;
- }
-
- return $trustedProxy === $remoteAddress;
- }
-
- /**
* Checks if given $remoteAddress matches any entry in the given array $trustedProxies.
* For details regarding what "match" means, refer to `matchesTrustedProxy`.
* @return boolean true if $remoteAddress matches any entry in $trustedProxies, false otherwise
*/
protected function isTrustedProxy($trustedProxies, $remoteAddress) {
- foreach ($trustedProxies as $tp) {
- if ($this->matchesTrustedProxy($tp, $remoteAddress)) {
- return true;
- }
- }
-
- return false;
+ return IpUtils::checkIp($remoteAddress, $trustedProxies);
}
/**
diff --git a/lib/private/AppFramework/Middleware/MiddlewareDispatcher.php b/lib/private/AppFramework/Middleware/MiddlewareDispatcher.php
index 950ef8a13a3..adf17e53caa 100644
--- a/lib/private/AppFramework/Middleware/MiddlewareDispatcher.php
+++ b/lib/private/AppFramework/Middleware/MiddlewareDispatcher.php
@@ -46,7 +46,7 @@ class MiddlewareDispatcher {
private $middlewares;
/**
- * @var int counter which tells us what middlware was executed once an
+ * @var int counter which tells us what middleware was executed once an
* exception occurs
*/
private $middlewareCounter;
diff --git a/lib/private/AppFramework/Middleware/Security/BruteForceMiddleware.php b/lib/private/AppFramework/Middleware/Security/BruteForceMiddleware.php
index 26f4b9ef46f..069d04a9e75 100644
--- a/lib/private/AppFramework/Middleware/Security/BruteForceMiddleware.php
+++ b/lib/private/AppFramework/Middleware/Security/BruteForceMiddleware.php
@@ -47,18 +47,10 @@ use OCP\Security\Bruteforce\MaxDelayReached;
* @package OC\AppFramework\Middleware\Security
*/
class BruteForceMiddleware extends Middleware {
- /** @var ControllerMethodReflector */
- private $reflector;
- /** @var Throttler */
- private $throttler;
- /** @var IRequest */
- private $request;
+ private ControllerMethodReflector $reflector;
+ private Throttler $throttler;
+ private IRequest $request;
- /**
- * @param ControllerMethodReflector $controllerMethodReflector
- * @param Throttler $throttler
- * @param IRequest $request
- */
public function __construct(ControllerMethodReflector $controllerMethodReflector,
Throttler $throttler,
IRequest $request) {
diff --git a/lib/private/AppFramework/Middleware/SessionMiddleware.php b/lib/private/AppFramework/Middleware/SessionMiddleware.php
index f3fd2c99173..32ac2b17ae5 100644
--- a/lib/private/AppFramework/Middleware/SessionMiddleware.php
+++ b/lib/private/AppFramework/Middleware/SessionMiddleware.php
@@ -51,8 +51,8 @@ class SessionMiddleware extends Middleware {
*/
public function beforeController($controller, $methodName) {
$useSession = $this->reflector->hasAnnotation('UseSession');
- if (!$useSession) {
- $this->session->close();
+ if ($useSession) {
+ $this->session->reopen();
}
}
diff --git a/lib/private/Authentication/Listeners/UserDeletedFilesCleanupListener.php b/lib/private/Authentication/Listeners/UserDeletedFilesCleanupListener.php
index 2fb05159d09..5e657be0763 100644
--- a/lib/private/Authentication/Listeners/UserDeletedFilesCleanupListener.php
+++ b/lib/private/Authentication/Listeners/UserDeletedFilesCleanupListener.php
@@ -72,12 +72,12 @@ class UserDeletedFilesCleanupListener implements IEventListener {
}
$storage = $this->homeStorageCache[$event->getUser()->getUID()];
$cache = $storage->getCache();
+ $storage->rmdir('');
if ($cache instanceof Cache) {
$cache->clear();
} else {
throw new \Exception("Home storage has invalid cache");
}
- $storage->rmdir('');
}
}
}
diff --git a/lib/private/Authentication/LoginCredentials/Store.php b/lib/private/Authentication/LoginCredentials/Store.php
index 0ab4c9a37cc..d3db0444664 100644
--- a/lib/private/Authentication/LoginCredentials/Store.php
+++ b/lib/private/Authentication/LoginCredentials/Store.php
@@ -100,7 +100,7 @@ class Store implements IStore {
} catch (SessionNotAvailableException $ex) {
$this->logger->debug('could not get login credentials because session is unavailable', ['app' => 'core', 'exception' => $ex]);
} catch (InvalidTokenException $ex) {
- $this->logger->debug('could not get login credentials because the token is invalid: ' . $ex->getMessage(), ['app' => 'core', 'exception' => $ex]);
+ $this->logger->debug('could not get login credentials because the token is invalid: ' . $ex->getMessage(), ['app' => 'core']);
$trySession = true;
} catch (PasswordlessTokenException $ex) {
$this->logger->debug('could not get login credentials because the token has no password', ['app' => 'core', 'exception' => $ex]);
diff --git a/lib/private/Authentication/Token/IProvider.php b/lib/private/Authentication/Token/IProvider.php
index 0a145bfd7e6..33e0ad46263 100644
--- a/lib/private/Authentication/Token/IProvider.php
+++ b/lib/private/Authentication/Token/IProvider.php
@@ -158,7 +158,7 @@ interface IProvider {
public function setPassword(IToken $token, string $tokenId, string $password);
/**
- * Rotate the token. Usefull for for example oauth tokens
+ * Rotate the token. Useful for for example oauth tokens
*
* @param IToken $token
* @param string $oldTokenId
diff --git a/lib/private/Authentication/Token/PublicKeyTokenProvider.php b/lib/private/Authentication/Token/PublicKeyTokenProvider.php
index a1d75828e27..0f1767e845b 100644
--- a/lib/private/Authentication/Token/PublicKeyTokenProvider.php
+++ b/lib/private/Authentication/Token/PublicKeyTokenProvider.php
@@ -34,7 +34,7 @@ use OC\Authentication\Exceptions\InvalidTokenException;
use OC\Authentication\Exceptions\TokenPasswordExpiredException;
use OC\Authentication\Exceptions\PasswordlessTokenException;
use OC\Authentication\Exceptions\WipeTokenException;
-use OC\Cache\CappedMemoryCache;
+use OCP\Cache\CappedMemoryCache;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\IConfig;
@@ -346,7 +346,7 @@ class PublicKeyTokenProvider implements IProvider {
$config = array_merge([
'digest_alg' => 'sha512',
- 'private_key_bits' => 2048,
+ 'private_key_bits' => $password !== null && strlen($password) > 250 ? 4096 : 2048,
], $this->config->getSystemValue('openssl', []));
// Generate new key
@@ -368,7 +368,10 @@ class PublicKeyTokenProvider implements IProvider {
$dbToken->setPublicKey($publicKey);
$dbToken->setPrivateKey($this->encrypt($privateKey, $token));
- if (!is_null($password)) {
+ if (!is_null($password) && $this->config->getSystemValueBool('auth.storeCryptedPassword', true)) {
+ if (strlen($password) > 469) {
+ throw new \RuntimeException('Trying to save a password with more than 469 characters is not supported. If you want to use big passwords, disable the auth.storeCryptedPassword option in config.php');
+ }
$dbToken->setPassword($this->encryptPassword($password, $publicKey));
}
@@ -398,7 +401,7 @@ class PublicKeyTokenProvider implements IProvider {
$this->cache->clear();
// prevent setting an empty pw as result of pw-less-login
- if ($password === '') {
+ if ($password === '' || !$this->config->getSystemValueBool('auth.storeCryptedPassword', true)) {
return;
}
@@ -406,9 +409,12 @@ class PublicKeyTokenProvider implements IProvider {
$tokens = $this->mapper->getTokenByUser($uid);
foreach ($tokens as $t) {
$publicKey = $t->getPublicKey();
- $t->setPassword($this->encryptPassword($password, $publicKey));
- $t->setPasswordInvalid(false);
- $this->updateToken($t);
+ $encryptedPassword = $this->encryptPassword($password, $publicKey);
+ if ($t->getPassword() !== $encryptedPassword) {
+ $t->setPassword($encryptedPassword);
+ $t->setPasswordInvalid(false);
+ $this->updateToken($t);
+ }
}
}
diff --git a/lib/private/Authentication/Token/TokenCleanupJob.php b/lib/private/Authentication/Token/TokenCleanupJob.php
new file mode 100644
index 00000000000..292f8f310e8
--- /dev/null
+++ b/lib/private/Authentication/Token/TokenCleanupJob.php
@@ -0,0 +1,41 @@
+<?php
+/**
+ * @copyright 2022 Thomas Citharel <nextcloud@tcit.fr>
+ *
+ * @author Thomas Citharel <nextcloud@tcit.fr>
+ *
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+namespace OC\Authentication\Token;
+
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\BackgroundJob\TimedJob;
+
+class TokenCleanupJob extends TimedJob {
+ private IProvider $provider;
+
+ public function __construct(ITimeFactory $time, IProvider $provider) {
+ parent::__construct($time);
+ $this->provider = $provider;
+ // Run once a day at off-peak time
+ $this->setInterval(24 * 60 * 60);
+ $this->setTimeSensitivity(self::TIME_INSENSITIVE);
+ }
+
+ protected function run($argument) {
+ $this->provider->invalidateOldTokens();
+ }
+}
diff --git a/lib/private/Avatar/Avatar.php b/lib/private/Avatar/Avatar.php
index 3bd58bb7681..0eb8f8816d8 100644
--- a/lib/private/Avatar/Avatar.php
+++ b/lib/private/Avatar/Avatar.php
@@ -37,8 +37,7 @@ declare(strict_types=1);
namespace OC\Avatar;
use Imagick;
-use OC\Color;
-use OC_Image;
+use OCP\Color;
use OCP\Files\NotFoundException;
use OCP\IAvatar;
use Psr\Log\LoggerInterface;
@@ -47,9 +46,7 @@ use Psr\Log\LoggerInterface;
* This class gets and sets users avatars.
*/
abstract class Avatar implements IAvatar {
-
- /** @var LoggerInterface */
- protected $logger;
+ protected LoggerInterface $logger;
/**
* https://github.com/sebdesign/cap-height -- for 500px height
@@ -58,10 +55,8 @@ abstract class Avatar implements IAvatar {
* (0.4 letter-to-total-height ratio, 500*0.4=200), so: 200/0.715 = 280px.
* Since we start from the baseline (text-anchor) we need to
* shift the y axis by 100px (half the caps height): 500/2+100=350
- *
- * @var string
*/
- private $svgTemplate = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+ private string $svgTemplate = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="{size}" height="{size}" version="1.1" viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg">
<rect width="100%" height="100%" fill="#{fill}"></rect>
<text x="50%" y="350" style="font-weight:normal;font-size:280px;font-family:\'Noto Sans\';text-anchor:middle;fill:#fff">{letter}</text>
@@ -73,15 +68,11 @@ abstract class Avatar implements IAvatar {
/**
* Returns the user display name.
- *
- * @return string
*/
abstract public function getDisplayName(): string;
/**
* Returns the first letter of the display name, or "?" if no name given.
- *
- * @return string
*/
private function getAvatarText(): string {
$displayName = $this->getDisplayName();
@@ -97,16 +88,14 @@ abstract class Avatar implements IAvatar {
/**
* @inheritdoc
*/
- public function get($size = 64) {
- $size = (int) $size;
-
+ public function get(int $size = 64) {
try {
$file = $this->getFile($size);
} catch (NotFoundException $e) {
return false;
}
- $avatar = new OC_Image();
+ $avatar = new \OCP\Image();
$avatar->loadFromData($file->getContent());
return $avatar;
}
@@ -125,7 +114,7 @@ abstract class Avatar implements IAvatar {
protected function getAvatarVector(int $size): string {
$userDisplayName = $this->getDisplayName();
$bgRGB = $this->avatarBackgroundColor($userDisplayName);
- $bgHEX = sprintf("%02x%02x%02x", $bgRGB->r, $bgRGB->g, $bgRGB->b);
+ $bgHEX = sprintf("%02x%02x%02x", $bgRGB->red(), $bgRGB->green(), $bgRGB->blue());
$text = $this->getAvatarText();
$toReplace = ['{size}', '{fill}', '{letter}'];
return str_replace($toReplace, [$size, $bgHEX, $text], $this->svgTemplate);
@@ -133,13 +122,10 @@ abstract class Avatar implements IAvatar {
/**
* Generate png avatar from svg with Imagick
- *
- * @param int $size
- * @return string|boolean
*/
- protected function generateAvatarFromSvg(int $size) {
+ protected function generateAvatarFromSvg(int $size): ?string {
if (!extension_loaded('imagick')) {
- return false;
+ return null;
}
try {
$font = __DIR__ . '/../../core/fonts/NotoSans-Regular.ttf';
@@ -148,32 +134,27 @@ abstract class Avatar implements IAvatar {
$avatar->setFont($font);
$avatar->readImageBlob($svg);
$avatar->setImageFormat('png');
- $image = new OC_Image();
+ $image = new \OCP\Image();
$image->loadFromData((string)$avatar);
- $data = $image->data();
- return $data === null ? false : $data;
+ return $image->data();
} catch (\Exception $e) {
- return false;
+ return null;
}
}
/**
* Generate png avatar with GD
- *
- * @param string $userDisplayName
- * @param int $size
- * @return string
*/
- protected function generateAvatar($userDisplayName, $size) {
+ protected function generateAvatar(string $userDisplayName, int $size): string {
$text = $this->getAvatarText();
$backgroundColor = $this->avatarBackgroundColor($userDisplayName);
$im = imagecreatetruecolor($size, $size);
$background = imagecolorallocate(
$im,
- $backgroundColor->r,
- $backgroundColor->g,
- $backgroundColor->b
+ $backgroundColor->red(),
+ $backgroundColor->green(),
+ $backgroundColor->blue()
);
$white = imagecolorallocate($im, 255, 255, 255);
imagefilledrectangle($im, 0, 0, $size, $size, $background);
@@ -210,7 +191,7 @@ abstract class Avatar implements IAvatar {
string $text,
string $font,
int $size,
- $angle = 0
+ int $angle = 0
): array {
// Image width & height
$xi = imagesx($image);
@@ -230,37 +211,6 @@ abstract class Avatar implements IAvatar {
return [$x, $y];
}
- /**
- * Calculate steps between two Colors
- * @param object Color $steps start color
- * @param object Color $ends end color
- * @return array [r,g,b] steps for each color to go from $steps to $ends
- */
- private function stepCalc($steps, $ends) {
- $step = [];
- $step[0] = ($ends[1]->r - $ends[0]->r) / $steps;
- $step[1] = ($ends[1]->g - $ends[0]->g) / $steps;
- $step[2] = ($ends[1]->b - $ends[0]->b) / $steps;
- return $step;
- }
-
- /**
- * Convert a string to an integer evenly
- * @param string $hash the text to parse
- * @param int $maximum the maximum range
- * @return int[] between 0 and $maximum
- */
- private function mixPalette($steps, $color1, $color2) {
- $palette = [$color1];
- $step = $this->stepCalc($steps, [$color1, $color2]);
- for ($i = 1; $i < $steps; $i++) {
- $r = intval($color1->r + ($step[0] * $i));
- $g = intval($color1->g + ($step[1] * $i));
- $b = intval($color1->b + ($step[2] * $i));
- $palette[] = new Color($r, $g, $b);
- }
- return $palette;
- }
/**
* Convert a string to an integer evenly
@@ -268,7 +218,7 @@ abstract class Avatar implements IAvatar {
* @param int $maximum the maximum range
* @return int between 0 and $maximum
*/
- private function hashToInt($hash, $maximum) {
+ private function hashToInt(string $hash, int $maximum): int {
$final = 0;
$result = [];
@@ -286,10 +236,9 @@ abstract class Avatar implements IAvatar {
}
/**
- * @param string $hash
- * @return Color Object containting r g b int in the range [0, 255]
+ * @return Color Object containing r g b int in the range [0, 255]
*/
- public function avatarBackgroundColor(string $hash) {
+ public function avatarBackgroundColor(string $hash): Color {
// Normalize hash
$hash = strtolower($hash);
@@ -309,9 +258,9 @@ abstract class Avatar implements IAvatar {
// 3 colors * 6 will result in 18 generated colors
$steps = 6;
- $palette1 = $this->mixPalette($steps, $red, $yellow);
- $palette2 = $this->mixPalette($steps, $yellow, $blue);
- $palette3 = $this->mixPalette($steps, $blue, $red);
+ $palette1 = Color::mixPalette($steps, $red, $yellow);
+ $palette2 = Color::mixPalette($steps, $yellow, $blue);
+ $palette3 = Color::mixPalette($steps, $blue, $red);
$finalPalette = array_merge($palette1, $palette2, $palette3);
diff --git a/lib/private/Avatar/AvatarManager.php b/lib/private/Avatar/AvatarManager.php
index 77138085dc9..ec9bed40850 100644
--- a/lib/private/Avatar/AvatarManager.php
+++ b/lib/private/Avatar/AvatarManager.php
@@ -136,20 +136,23 @@ class AvatarManager implements IAvatarManager {
$avatarScope = '';
}
- if (
+ switch ($avatarScope) {
// v2-private scope hides the avatar from public access and from unknown users
- $avatarScope === IAccountManager::SCOPE_PRIVATE
- && (
- // accessing from public link
- $requestingUser === null
- // logged in, but unknown to user
- || !$this->knownUserService->isKnownToUser($requestingUser->getUID(), $userId)
- )) {
- // use a placeholder avatar which caches the generated images
- return new PlaceholderAvatar($folder, $user, $this->logger);
+ case IAccountManager::SCOPE_PRIVATE:
+ if ($requestingUser !== null && $this->knownUserService->isKnownToUser($requestingUser->getUID(), $userId)) {
+ return new UserAvatar($folder, $this->l, $user, $this->logger, $this->config);
+ }
+ break;
+ case IAccountManager::SCOPE_LOCAL:
+ case IAccountManager::SCOPE_FEDERATED:
+ case IAccountManager::SCOPE_PUBLISHED:
+ return new UserAvatar($folder, $this->l, $user, $this->logger, $this->config);
+ default:
+ // use a placeholder avatar which caches the generated images
+ return new PlaceholderAvatar($folder, $user, $this->logger);
}
- return new UserAvatar($folder, $this->l, $user, $this->logger, $this->config);
+ return new PlaceholderAvatar($folder, $user, $this->logger);
}
/**
diff --git a/lib/private/Avatar/GuestAvatar.php b/lib/private/Avatar/GuestAvatar.php
index f0297b3a93c..79d7e6ee094 100644
--- a/lib/private/Avatar/GuestAvatar.php
+++ b/lib/private/Avatar/GuestAvatar.php
@@ -26,6 +26,7 @@ declare(strict_types=1);
*/
namespace OC\Avatar;
+use OCP\Files\SimpleFS\ISimpleFile;
use OCP\Files\SimpleFS\InMemoryFile;
use Psr\Log\LoggerInterface;
@@ -35,16 +36,13 @@ use Psr\Log\LoggerInterface;
class GuestAvatar extends Avatar {
/**
* Holds the guest user display name.
- *
- * @var string
*/
- private $userDisplayName;
+ private string $userDisplayName;
/**
* GuestAvatar constructor.
*
* @param string $userDisplayName The guest user display name
- * @param LoggerInterface $logger The logger
*/
public function __construct(string $userDisplayName, LoggerInterface $logger) {
parent::__construct($logger);
@@ -53,17 +51,14 @@ class GuestAvatar extends Avatar {
/**
* Tests if the user has an avatar.
- *
- * @return true Guests always have an avatar.
*/
- public function exists() {
+ public function exists(): bool {
+ // Guests always have an avatar.
return true;
}
/**
* Returns the guest user display name.
- *
- * @return string
*/
public function getDisplayName(): string {
return $this->userDisplayName;
@@ -75,24 +70,21 @@ class GuestAvatar extends Avatar {
* @param \OCP\IImage|resource|string $data
* @return void
*/
- public function set($data) {
+ public function set($data): void {
// unimplemented for guest user avatars
}
/**
* Removing avatars isn't implemented for guests.
*/
- public function remove() {
+ public function remove(bool $silent = false): void {
// unimplemented for guest user avatars
}
/**
* Generates an avatar for the guest.
- *
- * @param int $size The desired image size.
- * @return InMemoryFile
*/
- public function getFile($size) {
+ public function getFile(int $size): ISimpleFile {
$avatar = $this->generateAvatar($this->userDisplayName, $size);
return new InMemoryFile('avatar.png', $avatar);
}
@@ -103,9 +95,8 @@ class GuestAvatar extends Avatar {
* @param string $feature The changed feature
* @param mixed $oldValue The previous value
* @param mixed $newValue The new value
- * @return void
*/
- public function userChanged($feature, $oldValue, $newValue) {
+ public function userChanged(string $feature, $oldValue, $newValue): void {
if ($feature === 'displayName') {
$this->userDisplayName = $newValue;
}
@@ -113,8 +104,6 @@ class GuestAvatar extends Avatar {
/**
* Guests don't have custom avatars.
- *
- * @return bool
*/
public function isCustomAvatar(): bool {
return false;
diff --git a/lib/private/Avatar/PlaceholderAvatar.php b/lib/private/Avatar/PlaceholderAvatar.php
index df7a490cbe4..504e5c1457d 100644
--- a/lib/private/Avatar/PlaceholderAvatar.php
+++ b/lib/private/Avatar/PlaceholderAvatar.php
@@ -44,11 +44,8 @@ use Psr\Log\LoggerInterface;
* for faster retrieval, unlike the GuestAvatar.
*/
class PlaceholderAvatar extends Avatar {
- /** @var ISimpleFolder */
- private $folder;
-
- /** @var User */
- private $user;
+ private ISimpleFolder $folder;
+ private User $user;
/**
* UserAvatar constructor.
@@ -71,10 +68,8 @@ class PlaceholderAvatar extends Avatar {
/**
* Check if an avatar exists for the user
- *
- * @return bool
*/
- public function exists() {
+ public function exists(): bool {
return true;
}
@@ -87,14 +82,14 @@ class PlaceholderAvatar extends Avatar {
* @throws NotSquareException if the image is not square
* @return void
*/
- public function set($data) {
+ public function set($data): void {
// unimplemented for placeholder avatars
}
/**
* Removes the users avatar.
*/
- public function remove(bool $silent = false) {
+ public function remove(bool $silent = false): void {
$avatars = $this->folder->getDirectoryListing();
foreach ($avatars as $avatar) {
@@ -113,9 +108,7 @@ class PlaceholderAvatar extends Avatar {
* @throws \OCP\Files\NotPermittedException
* @throws \OCP\PreConditionNotMetException
*/
- public function getFile($size) {
- $size = (int) $size;
-
+ public function getFile(int $size): ISimpleFile {
$ext = 'png';
if ($size === -1) {
@@ -149,8 +142,6 @@ class PlaceholderAvatar extends Avatar {
/**
* Returns the user display name.
- *
- * @return string
*/
public function getDisplayName(): string {
return $this->user->getDisplayName();
@@ -165,14 +156,12 @@ class PlaceholderAvatar extends Avatar {
* @throws NotPermittedException
* @throws \OCP\PreConditionNotMetException
*/
- public function userChanged($feature, $oldValue, $newValue) {
+ public function userChanged(string $feature, $oldValue, $newValue): void {
$this->remove();
}
/**
* Check if the avatar of a user is a custom uploaded one
- *
- * @return bool
*/
public function isCustomAvatar(): bool {
return false;
diff --git a/lib/private/Avatar/UserAvatar.php b/lib/private/Avatar/UserAvatar.php
index b46e4816fa2..f5a1d7e77b1 100644
--- a/lib/private/Avatar/UserAvatar.php
+++ b/lib/private/Avatar/UserAvatar.php
@@ -31,7 +31,6 @@ namespace OC\Avatar;
use OC\NotSquareException;
use OC\User\User;
-use OC_Image;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
use OCP\Files\SimpleFS\ISimpleFile;
@@ -45,17 +44,10 @@ use Psr\Log\LoggerInterface;
* This class represents a registered user's avatar.
*/
class UserAvatar extends Avatar {
- /** @var IConfig */
- private $config;
-
- /** @var ISimpleFolder */
- private $folder;
-
- /** @var IL10N */
- private $l;
-
- /** @var User */
- private $user;
+ private IConfig $config;
+ private ISimpleFolder $folder;
+ private IL10N $l;
+ private User $user;
/**
* UserAvatar constructor.
@@ -69,7 +61,7 @@ class UserAvatar extends Avatar {
public function __construct(
ISimpleFolder $folder,
IL10N $l,
- $user,
+ User $user,
LoggerInterface $logger,
IConfig $config) {
parent::__construct($logger);
@@ -81,10 +73,8 @@ class UserAvatar extends Avatar {
/**
* Check if an avatar exists for the user
- *
- * @return bool
*/
- public function exists() {
+ public function exists(): bool {
return $this->folder->fileExists('avatar.jpg') || $this->folder->fileExists('avatar.png');
}
@@ -97,7 +87,7 @@ class UserAvatar extends Avatar {
* @throws NotSquareException if the image is not square
* @return void
*/
- public function set($data) {
+ public function set($data): void {
$img = $this->getAvatarImage($data);
$data = $img->data();
@@ -125,12 +115,12 @@ class UserAvatar extends Avatar {
* @param IImage|resource|string|\GdImage $data An image object, imagedata or path to the avatar
* @return IImage
*/
- private function getAvatarImage($data) {
+ private function getAvatarImage($data): IImage {
if ($data instanceof IImage) {
return $data;
}
- $img = new OC_Image();
+ $img = new \OCP\Image();
if (
(is_resource($data) && get_resource_type($data) === 'gd') ||
(is_object($data) && get_class($data) === \GdImage::class)
@@ -157,11 +147,8 @@ class UserAvatar extends Avatar {
/**
* Returns the avatar image type.
- *
- * @param IImage $avatar
- * @return string
*/
- private function getAvatarImageType(IImage $avatar) {
+ private function getAvatarImageType(IImage $avatar): string {
$type = substr($avatar->mimeType(), -3);
if ($type === 'peg') {
$type = 'jpg';
@@ -180,7 +167,7 @@ class UserAvatar extends Avatar {
* @throws \Exception if the provided image is not valid
* @throws NotSquareException if the image is not square
*/
- private function validateAvatar(IImage $avatar) {
+ private function validateAvatar(IImage $avatar): void {
$type = $this->getAvatarImageType($avatar);
if ($type !== 'jpg' && $type !== 'png') {
@@ -198,15 +185,14 @@ class UserAvatar extends Avatar {
/**
* Removes the users avatar.
- * @return void
* @throws \OCP\Files\NotPermittedException
* @throws \OCP\PreConditionNotMetException
*/
- public function remove(bool $silent = false) {
+ public function remove(bool $silent = false): void {
$avatars = $this->folder->getDirectoryListing();
$this->config->setUserValue($this->user->getUID(), 'avatar', 'version',
- (int) $this->config->getUserValue($this->user->getUID(), 'avatar', 'version', 0) + 1);
+ (string)((int)$this->config->getUserValue($this->user->getUID(), 'avatar', 'version', '0') + 1));
foreach ($avatars as $avatar) {
$avatar->delete();
@@ -220,10 +206,9 @@ class UserAvatar extends Avatar {
/**
* Get the extension of the avatar. If there is no avatar throw Exception
*
- * @return string
* @throws NotFoundException
*/
- private function getExtension() {
+ private function getExtension(): string {
if ($this->folder->fileExists('avatar.jpg')) {
return 'jpg';
} elseif ($this->folder->fileExists('avatar.png')) {
@@ -243,9 +228,7 @@ class UserAvatar extends Avatar {
* @throws \OCP\Files\NotPermittedException
* @throws \OCP\PreConditionNotMetException
*/
- public function getFile($size) {
- $size = (int) $size;
-
+ public function getFile(int $size): ISimpleFile {
try {
$ext = $this->getExtension();
} catch (NotFoundException $e) {
@@ -279,7 +262,7 @@ class UserAvatar extends Avatar {
$data = $this->generateAvatar($this->getDisplayName(), $size);
}
} else {
- $avatar = new OC_Image();
+ $avatar = new \OCP\Image();
$file = $this->folder->getFile('avatar.' . $ext);
$avatar->loadFromData($file->getContent());
$avatar->resize($size);
@@ -305,8 +288,6 @@ class UserAvatar extends Avatar {
/**
* Returns the user display name.
- *
- * @return string
*/
public function getDisplayName(): string {
return $this->user->getDisplayName();
@@ -321,7 +302,7 @@ class UserAvatar extends Avatar {
* @throws NotPermittedException
* @throws \OCP\PreConditionNotMetException
*/
- public function userChanged($feature, $oldValue, $newValue) {
+ public function userChanged(string $feature, $oldValue, $newValue): void {
// If the avatar is not generated (so an uploaded image) we skip this
if (!$this->folder->fileExists('generated')) {
return;
@@ -332,8 +313,6 @@ class UserAvatar extends Avatar {
/**
* Check if the avatar of a user is a custom uploaded one
- *
- * @return bool
*/
public function isCustomAvatar(): bool {
return $this->config->getUserValue($this->user->getUID(), 'avatar', 'generated', 'false') !== 'true';
diff --git a/lib/private/BackgroundJob/Job.php b/lib/private/BackgroundJob/Job.php
index 399ff05134e..ffcaaf8c36d 100644
--- a/lib/private/BackgroundJob/Job.php
+++ b/lib/private/BackgroundJob/Job.php
@@ -66,6 +66,10 @@ abstract class Job implements IJob {
}
}
+ public function start(IJobList $jobList): void {
+ $this->execute($jobList);
+ }
+
abstract protected function run($argument);
public function setId(int $id) {
diff --git a/lib/private/BackgroundJob/JobList.php b/lib/private/BackgroundJob/JobList.php
index fe65a1879bc..20176e45125 100644
--- a/lib/private/BackgroundJob/JobList.php
+++ b/lib/private/BackgroundJob/JobList.php
@@ -40,21 +40,10 @@ use OCP\IConfig;
use OCP\IDBConnection;
class JobList implements IJobList {
+ protected IDBConnection $connection;
+ protected IConfig $config;
+ protected ITimeFactory $timeFactory;
- /** @var IDBConnection */
- protected $connection;
-
- /**@var IConfig */
- protected $config;
-
- /**@var ITimeFactory */
- protected $timeFactory;
-
- /**
- * @param IDBConnection $connection
- * @param IConfig $config
- * @param ITimeFactory $timeFactory
- */
public function __construct(IDBConnection $connection, IConfig $config, ITimeFactory $timeFactory) {
$this->connection = $connection;
$this->config = $config;
@@ -62,10 +51,10 @@ class JobList implements IJobList {
}
/**
- * @param IJob|string $job
+ * @param IJob|class-string<IJob> $job
* @param mixed $argument
*/
- public function add($job, $argument = null) {
+ public function add($job, $argument = null): void {
if ($job instanceof IJob) {
$class = get_class($job);
} else {
@@ -101,7 +90,7 @@ class JobList implements IJobList {
* @param IJob|string $job
* @param mixed $argument
*/
- public function remove($job, $argument = null) {
+ public function remove($job, $argument = null): void {
if ($job instanceof IJob) {
$class = get_class($job);
} else {
@@ -133,24 +122,20 @@ class JobList implements IJobList {
}
}
- /**
- * @param int $id
- */
- protected function removeById($id) {
+ protected function removeById(int $id): void {
$query = $this->connection->getQueryBuilder();
$query->delete('jobs')
->where($query->expr()->eq('id', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
- $query->execute();
+ $query->executeStatement();
}
/**
* check if a job is in the list
*
- * @param IJob|string $job
+ * @param IJob|class-string<IJob> $job
* @param mixed $argument
- * @return bool
*/
- public function has($job, $argument) {
+ public function has($job, $argument): bool {
if ($job instanceof IJob) {
$class = get_class($job);
} else {
@@ -165,7 +150,7 @@ class JobList implements IJobList {
->andWhere($query->expr()->eq('argument_hash', $query->createNamedParameter(md5($argument))))
->setMaxResults(1);
- $result = $query->execute();
+ $result = $query->executeQuery();
$row = $result->fetch();
$result->closeCursor();
@@ -177,13 +162,33 @@ class JobList implements IJobList {
*
* @return IJob[]
* @deprecated 9.0.0 - This method is dangerous since it can cause load and
- * memory problems when creating too many instances.
+ * memory problems when creating too many instances. Use getJobs instead.
+ */
+ public function getAll(): array {
+ return $this->getJobs(null, null, 0);
+ }
+
+ /**
+ * @param IJob|class-string<IJob>|null $job
+ * @return IJob[]
*/
- public function getAll() {
+ public function getJobs($job, ?int $limit, int $offset): array {
$query = $this->connection->getQueryBuilder();
$query->select('*')
- ->from('jobs');
- $result = $query->execute();
+ ->from('jobs')
+ ->setMaxResults($limit)
+ ->setFirstResult($offset);
+
+ if ($job !== null) {
+ if ($job instanceof IJob) {
+ $class = get_class($job);
+ } else {
+ $class = $job;
+ }
+ $query->where($query->expr()->eq('class', $query->createNamedParameter($class)));
+ }
+
+ $result = $query->executeQuery();
$jobs = [];
while ($row = $result->fetch()) {
@@ -199,9 +204,6 @@ class JobList implements IJobList {
/**
* get the next job in the list
- *
- * @param bool $onlyTimeSensitive
- * @return IJob|null
*/
public function getNext(bool $onlyTimeSensitive = false): ?IJob {
$query = $this->connection->getQueryBuilder();
@@ -224,7 +226,7 @@ class JobList implements IJobList {
->andWhere($update->expr()->eq('reserved_at', $update->createParameter('reserved_at')))
->andWhere($update->expr()->eq('last_checked', $update->createParameter('last_checked')));
- $result = $query->execute();
+ $result = $query->executeQuery();
$row = $result->fetch();
$result->closeCursor();
@@ -232,7 +234,7 @@ class JobList implements IJobList {
$update->setParameter('jobid', $row['id']);
$update->setParameter('reserved_at', $row['reserved_at']);
$update->setParameter('last_checked', $row['last_checked']);
- $count = $update->execute();
+ $count = $update->executeStatement();
if ($count === 0) {
// Background job already executed elsewhere, try again.
@@ -247,7 +249,7 @@ class JobList implements IJobList {
->set('reserved_at', $reset->expr()->literal(0, IQueryBuilder::PARAM_INT))
->set('last_checked', $reset->createNamedParameter($this->timeFactory->getTime() + 12 * 3600, IQueryBuilder::PARAM_INT))
->where($reset->expr()->eq('id', $reset->createNamedParameter($row['id'], IQueryBuilder::PARAM_INT)));
- $reset->execute();
+ $reset->executeStatement();
// Background job from disabled app, try again.
return $this->getNext($onlyTimeSensitive);
@@ -259,11 +261,7 @@ class JobList implements IJobList {
}
}
- /**
- * @param int $id
- * @return IJob|null
- */
- public function getById($id) {
+ public function getById(int $id): ?IJob {
$row = $this->getDetailsById($id);
if ($row) {
@@ -292,15 +290,14 @@ class JobList implements IJobList {
/**
* get the job object from a row in the db
*
- * @param array $row
- * @return IJob|null
+ * @param array{class:class-string<IJob>, id:mixed, last_run:mixed, argument:string} $row
*/
- private function buildJob($row) {
+ private function buildJob(array $row): ?IJob {
try {
try {
// Try to load the job as a service
/** @var IJob $job */
- $job = \OC::$server->query($row['class']);
+ $job = \OCP\Server::get($row['class']);
} catch (QueryException $e) {
if (class_exists($row['class'])) {
$class = $row['class'];
@@ -311,6 +308,10 @@ class JobList implements IJobList {
}
}
+ if (!($job instanceof IJob)) {
+ // This most likely means an invalid job was enqueued. We can ignore it.
+ return null;
+ }
$job->setId((int) $row['id']);
$job->setLastRun((int) $row['last_run']);
$job->setArgument(json_decode($row['argument'], true));
@@ -323,33 +324,27 @@ class JobList implements IJobList {
/**
* set the job that was last ran
- *
- * @param IJob $job
*/
- public function setLastJob(IJob $job) {
+ public function setLastJob(IJob $job): void {
$this->unlockJob($job);
- $this->config->setAppValue('backgroundjob', 'lastjob', $job->getId());
+ $this->config->setAppValue('backgroundjob', 'lastjob', (string)$job->getId());
}
/**
* Remove the reservation for a job
- *
- * @param IJob $job
*/
- public function unlockJob(IJob $job) {
+ public function unlockJob(IJob $job): void {
$query = $this->connection->getQueryBuilder();
$query->update('jobs')
->set('reserved_at', $query->expr()->literal(0, IQueryBuilder::PARAM_INT))
->where($query->expr()->eq('id', $query->createNamedParameter($job->getId(), IQueryBuilder::PARAM_INT)));
- $query->execute();
+ $query->executeStatement();
}
/**
* set the lastRun of $job to now
- *
- * @param IJob $job
*/
- public function setLastRun(IJob $job) {
+ public function setLastRun(IJob $job): void {
$query = $this->connection->getQueryBuilder();
$query->update('jobs')
->set('last_run', $query->createNamedParameter(time(), IQueryBuilder::PARAM_INT))
@@ -360,25 +355,23 @@ class JobList implements IJobList {
$query->set('time_sensitive', $query->createNamedParameter(IJob::TIME_INSENSITIVE));
}
- $query->execute();
+ $query->executeStatement();
}
/**
- * @param IJob $job
- * @param $timeTaken
+ * @param int $timeTaken
*/
- public function setExecutionTime(IJob $job, $timeTaken) {
+ public function setExecutionTime(IJob $job, $timeTaken): void {
$query = $this->connection->getQueryBuilder();
$query->update('jobs')
->set('execution_duration', $query->createNamedParameter($timeTaken, IQueryBuilder::PARAM_INT))
->where($query->expr()->eq('id', $query->createNamedParameter($job->getId(), IQueryBuilder::PARAM_INT)));
- $query->execute();
+ $query->executeStatement();
}
/**
* Reset the $job so it executes on the next trigger
*
- * @param IJob $job
* @since 23.0.0
*/
public function resetBackgroundJob(IJob $job): void {
diff --git a/lib/private/BinaryFinder.php b/lib/private/BinaryFinder.php
new file mode 100644
index 00000000000..a7ef55237db
--- /dev/null
+++ b/lib/private/BinaryFinder.php
@@ -0,0 +1,71 @@
+<?php
+
+declare(strict_types = 1);
+/**
+ * @copyright 2022 Carl Schwan <carl@carlschwan.eu>
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OC;
+
+use OCP\ICache;
+use OCP\ICacheFactory;
+use OCP\IBinaryFinder;
+use Symfony\Component\Process\ExecutableFinder;
+
+/**
+ * Service that find the binary path for a program
+ */
+class BinaryFinder implements IBinaryFinder {
+ private ICache $cache;
+
+ public function __construct(ICacheFactory $cacheFactory) {
+ $this->cache = $cacheFactory->createLocal('findBinaryPath');
+ }
+
+ /**
+ * Try to find a program
+ *
+ * @return false|string
+ */
+ public function findBinaryPath(string $program) {
+ $result = $this->cache->get($program);
+ if ($result !== null) {
+ return $result;
+ }
+ $result = false;
+ if (\OCP\Util::isFunctionEnabled('exec')) {
+ $exeSniffer = new ExecutableFinder();
+ // Returns null if nothing is found
+ $result = $exeSniffer->find($program, null, [
+ '/usr/local/sbin',
+ '/usr/local/bin',
+ '/usr/sbin',
+ '/usr/bin',
+ '/sbin',
+ '/bin',
+ '/opt/bin',
+ ]);
+ if ($result === null) {
+ $result = false;
+ }
+ }
+ // store the value for 5 minutes
+ $this->cache->set($program, $result, 300);
+ return $result;
+ }
+}
diff --git a/lib/private/Cache/CappedMemoryCache.php b/lib/private/Cache/CappedMemoryCache.php
index 6063b5e7110..31e8ef3e720 100644
--- a/lib/private/Cache/CappedMemoryCache.php
+++ b/lib/private/Cache/CappedMemoryCache.php
@@ -28,6 +28,7 @@ use OCP\ICache;
*
* Uses a simple FIFO expiry mechanism
* @template T
+ * @deprecated use OCP\Cache\CappedMemoryCache instead
*/
class CappedMemoryCache implements ICache, \ArrayAccess {
private $capacity;
diff --git a/lib/private/Calendar/Manager.php b/lib/private/Calendar/Manager.php
index 16e142264aa..f0b8e9fd50d 100644
--- a/lib/private/Calendar/Manager.php
+++ b/lib/private/Calendar/Manager.php
@@ -27,12 +27,19 @@ declare(strict_types=1);
namespace OC\Calendar;
use OC\AppFramework\Bootstrap\Coordinator;
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\Calendar\Exceptions\CalendarException;
use OCP\Calendar\ICalendar;
use OCP\Calendar\ICalendarProvider;
use OCP\Calendar\ICalendarQuery;
+use OCP\Calendar\ICreateFromString;
use OCP\Calendar\IManager;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
+use Sabre\VObject\Component\VCalendar;
+use Sabre\VObject\Component\VEvent;
+use Sabre\VObject\Property\VCard\DateTime;
+use Sabre\VObject\Reader;
use Throwable;
use function array_map;
use function array_merge;
@@ -58,12 +65,17 @@ class Manager implements IManager {
/** @var LoggerInterface */
private $logger;
+ private ITimeFactory $timeFactory;
+
+
public function __construct(Coordinator $coordinator,
ContainerInterface $container,
- LoggerInterface $logger) {
+ LoggerInterface $logger,
+ ITimeFactory $timeFactory) {
$this->coordinator = $coordinator;
$this->container = $container;
$this->logger = $logger;
+ $this->timeFactory = $timeFactory;
}
/**
@@ -167,6 +179,11 @@ class Manager implements IManager {
$this->calendarLoaders = [];
}
+ /**
+ * @param string $principalUri
+ * @param array $calendarUris
+ * @return ICreateFromString[]
+ */
public function getCalendarsForPrincipal(string $principalUri, array $calendarUris = []): array {
$context = $this->coordinator->getRegistrationContext();
if ($context === null) {
@@ -198,7 +215,6 @@ class Manager implements IManager {
);
$results = [];
- /** @var ICalendar $calendar */
foreach ($calendars as $calendar) {
$r = $calendar->search(
$query->getSearchPattern() ?? '',
@@ -219,4 +235,137 @@ class Manager implements IManager {
public function newQuery(string $principalUri): ICalendarQuery {
return new CalendarQuery($principalUri);
}
+
+ /**
+ * @throws \OCP\DB\Exception
+ */
+ public function handleIMipReply(string $principalUri, string $sender, string $recipient, string $calendarData): bool {
+ /** @var VCalendar $vObject */
+ $vObject = Reader::read($calendarData);
+ /** @var VEvent $vEvent */
+ $vEvent = $vObject->{'VEVENT'};
+
+ // First, we check if the correct method is passed to us
+ if (strcasecmp('REPLY', $vObject->{'METHOD'}->getValue()) !== 0) {
+ $this->logger->warning('Wrong method provided for processing');
+ return false;
+ }
+
+ // check if mail recipient and organizer are one and the same
+ $organizer = substr($vEvent->{'ORGANIZER'}->getValue(), 7);
+
+ if (strcasecmp($recipient, $organizer) !== 0) {
+ $this->logger->warning('Recipient and ORGANIZER must be identical');
+ return false;
+ }
+
+ //check if the event is in the future
+ /** @var DateTime $eventTime */
+ $eventTime = $vEvent->{'DTSTART'};
+ if ($eventTime->getDateTime()->getTimeStamp() < $this->timeFactory->getTime()) { // this might cause issues with recurrences
+ $this->logger->warning('Only events in the future are processed');
+ return false;
+ }
+
+ $calendars = $this->getCalendarsForPrincipal($principalUri);
+ if (empty($calendars)) {
+ $this->logger->warning('Could not find any calendars for principal ' . $principalUri);
+ return false;
+ }
+
+ $found = null;
+ // if the attendee has been found in at least one calendar event with the UID of the iMIP event
+ // we process it.
+ // Benefit: no attendee lost
+ // Drawback: attendees that have been deleted will still be able to update their partstat
+ foreach ($calendars as $calendar) {
+ // We should not search in writable calendars
+ if ($calendar instanceof ICreateFromString) {
+ $o = $calendar->search($sender, ['ATTENDEE'], ['uid' => $vEvent->{'UID'}->getValue()]);
+ if (!empty($o)) {
+ $found = $calendar;
+ $name = $o[0]['uri'];
+ break;
+ }
+ }
+ }
+
+ if (empty($found)) {
+ $this->logger->info('Event not found in any calendar for principal ' . $principalUri . 'and UID' . $vEvent->{'UID'}->getValue());
+ return false;
+ }
+
+ try {
+ $found->handleIMipMessage($name, $calendarData); // sabre will handle the scheduling behind the scenes
+ } catch (CalendarException $e) {
+ $this->logger->error('Could not update calendar for iMIP processing', ['exception' => $e]);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * @since 25.0.0
+ * @throws \OCP\DB\Exception
+ */
+ public function handleIMipCancel(string $principalUri, string $sender, ?string $replyTo, string $recipient, string $calendarData): bool {
+ $vObject = Reader::read($calendarData);
+ /** @var VEvent $vEvent */
+ $vEvent = $vObject->{'VEVENT'};
+
+ // First, we check if the correct method is passed to us
+ if (strcasecmp('CANCEL', $vObject->{'METHOD'}->getValue()) !== 0) {
+ $this->logger->warning('Wrong method provided for processing');
+ return false;
+ }
+
+ $attendee = substr($vEvent->{'ATTENDEE'}->getValue(), 7);
+ if (strcasecmp($recipient, $attendee) !== 0) {
+ $this->logger->warning('Recipient must be an ATTENDEE of this event');
+ return false;
+ }
+
+ // Thirdly, we need to compare the email address the CANCEL is coming from (in Mail)
+ // or the Reply- To Address submitted with the CANCEL email
+ // to the email address in the ORGANIZER.
+ // We don't want to accept a CANCEL request from just anyone
+ $organizer = substr($vEvent->{'ORGANIZER'}->getValue(), 7);
+ if (strcasecmp($sender, $organizer) !== 0 && strcasecmp($replyTo, $organizer) !== 0) {
+ $this->logger->warning('Sender must be the ORGANIZER of this event');
+ return false;
+ }
+
+ $calendars = $this->getCalendarsForPrincipal($principalUri);
+ $found = null;
+ // if the attendee has been found in at least one calendar event with the UID of the iMIP event
+ // we process it.
+ // Benefit: no attendee lost
+ // Drawback: attendees that have been deleted will still be able to update their partstat
+ foreach ($calendars as $calendar) {
+ // We should not search in writable calendars
+ if ($calendar instanceof ICreateFromString) {
+ $o = $calendar->search($recipient, ['ATTENDEE'], ['uid' => $vEvent->{'UID'}->getValue()]);
+ if (!empty($o)) {
+ $found = $calendar;
+ $name = $o[0]['uri'];
+ break;
+ }
+ }
+ }
+
+ if (empty($found)) {
+ $this->logger->info('Event not found in any calendar for principal ' . $principalUri . 'and UID' . $vEvent->{'UID'}->getValue());
+ // this is a safe operation
+ // we can ignore events that have been cancelled but were not in the calendar anyway
+ return true;
+ }
+
+ try {
+ $found->handleIMipMessage($name, $calendarData); // sabre will handle the scheduling behind the scenes
+ return true;
+ } catch (CalendarException $e) {
+ $this->logger->error('Could not update calendar for iMIP processing', ['exception' => $e]);
+ return false;
+ }
+ }
}
diff --git a/lib/private/Collaboration/Collaborators/UserPlugin.php b/lib/private/Collaboration/Collaborators/UserPlugin.php
index 12304a66ce9..9beecdaa6cb 100644
--- a/lib/private/Collaboration/Collaborators/UserPlugin.php
+++ b/lib/private/Collaboration/Collaborators/UserPlugin.php
@@ -95,7 +95,7 @@ class UserPlugin implements ISearchPlugin {
$this->shareeEnumerationFullMatch = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match', 'yes') === 'yes';
$this->shareeEnumerationFullMatchUserId = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_userid', 'yes') === 'yes';
$this->shareeEnumerationFullMatchEmail = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_email', 'yes') === 'yes';
- $this->shareeEnumerationFullMatchIgnoreSecondDisplayName = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_ignore_second_display_name', 'no') === 'yes';
+ $this->shareeEnumerationFullMatchIgnoreSecondDisplayName = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_ignore_second_dn', 'no') === 'yes';
}
public function search($search, $limit, $offset, ISearchResult $searchResult) {
diff --git a/lib/private/Collaboration/Reference/File/FileReferenceEventListener.php b/lib/private/Collaboration/Reference/File/FileReferenceEventListener.php
new file mode 100644
index 00000000000..6ccae9903dc
--- /dev/null
+++ b/lib/private/Collaboration/Reference/File/FileReferenceEventListener.php
@@ -0,0 +1,61 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2022 Julius Härtl <jus@bitgrid.net>
+ *
+ * @author Julius Härtl <jus@bitgrid.net>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace OC\Collaboration\Reference\File;
+
+use OCP\Collaboration\Reference\IReferenceManager;
+use OCP\EventDispatcher\Event;
+use OCP\EventDispatcher\IEventDispatcher;
+use OCP\Files\Events\Node\NodeDeletedEvent;
+use OCP\Share\Events\ShareCreatedEvent;
+use OCP\Share\Events\ShareDeletedEvent;
+
+class FileReferenceEventListener implements \OCP\EventDispatcher\IEventListener {
+ private IReferenceManager $manager;
+
+ public function __construct(IReferenceManager $manager) {
+ $this->manager = $manager;
+ }
+
+ public static function register(IEventDispatcher $eventDispatcher): void {
+ $eventDispatcher->addServiceListener(NodeDeletedEvent::class, FileReferenceEventListener::class);
+ $eventDispatcher->addServiceListener(ShareDeletedEvent::class, FileReferenceEventListener::class);
+ $eventDispatcher->addServiceListener(ShareCreatedEvent::class, FileReferenceEventListener::class);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function handle(Event $event): void {
+ if ($event instanceof NodeDeletedEvent) {
+ $this->manager->invalidateCache((string)$event->getNode()->getId());
+ }
+ if ($event instanceof ShareDeletedEvent) {
+ $this->manager->invalidateCache((string)$event->getShare()->getNodeId());
+ }
+ if ($event instanceof ShareCreatedEvent) {
+ $this->manager->invalidateCache((string)$event->getShare()->getNodeId());
+ }
+ }
+}
diff --git a/lib/private/Collaboration/Reference/File/FileReferenceProvider.php b/lib/private/Collaboration/Reference/File/FileReferenceProvider.php
new file mode 100644
index 00000000000..39cdb62b09a
--- /dev/null
+++ b/lib/private/Collaboration/Reference/File/FileReferenceProvider.php
@@ -0,0 +1,153 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2022 Julius Härtl <jus@bitgrid.net>
+ *
+ * @author Julius Härtl <jus@bitgrid.net>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace OC\Collaboration\Reference\File;
+
+use OC\Collaboration\Reference\Reference;
+use OC\User\NoUserException;
+use OCP\Collaboration\Reference\IReference;
+use OCP\Collaboration\Reference\IReferenceProvider;
+use OCP\Files\InvalidPathException;
+use OCP\Files\IRootFolder;
+use OCP\Files\Node;
+use OCP\Files\NotFoundException;
+use OCP\Files\NotPermittedException;
+use OCP\IPreview;
+use OCP\IURLGenerator;
+use OCP\IUserSession;
+
+class FileReferenceProvider implements IReferenceProvider {
+ private IURLGenerator $urlGenerator;
+ private IRootFolder $rootFolder;
+ private ?string $userId;
+ private IPreview $previewManager;
+
+ public function __construct(IURLGenerator $urlGenerator, IRootFolder $rootFolder, IUserSession $userSession, IPreview $previewManager) {
+ $this->urlGenerator = $urlGenerator;
+ $this->rootFolder = $rootFolder;
+ $this->userId = $userSession->getUser() ? $userSession->getUser()->getUID() : null;
+ $this->previewManager = $previewManager;
+ }
+
+ public function matchReference(string $referenceText): bool {
+ return $this->getFilesAppLinkId($referenceText) !== null;
+ }
+
+ private function getFilesAppLinkId(string $referenceText): ?int {
+ $start = $this->urlGenerator->getAbsoluteURL('/apps/files');
+ $startIndex = $this->urlGenerator->getAbsoluteURL('/index.php/apps/files');
+
+ $fileId = null;
+
+ if (mb_strpos($referenceText, $start) === 0) {
+ $parts = parse_url($referenceText);
+ parse_str($parts['query'], $query);
+ $fileId = isset($query['fileid']) ? (int)$query['fileid'] : $fileId;
+ $fileId = isset($query['openfile']) ? (int)$query['openfile'] : $fileId;
+ }
+
+ if (mb_strpos($referenceText, $startIndex) === 0) {
+ $parts = parse_url($referenceText);
+ parse_str($parts['query'], $query);
+ $fileId = isset($query['fileid']) ? (int)$query['fileid'] : $fileId;
+ $fileId = isset($query['openfile']) ? (int)$query['openfile'] : $fileId;
+ }
+
+ if (mb_strpos($referenceText, $this->urlGenerator->getAbsoluteURL('/index.php/f/')) === 0) {
+ $fileId = str_replace($this->urlGenerator->getAbsoluteURL('/index.php/f/'), '', $referenceText);
+ }
+
+ if (mb_strpos($referenceText, $this->urlGenerator->getAbsoluteURL('/f/')) === 0) {
+ $fileId = str_replace($this->urlGenerator->getAbsoluteURL('/f/'), '', $referenceText);
+ }
+
+ return $fileId !== null ? (int)$fileId : null;
+ }
+
+ public function resolveReference(string $referenceText): ?IReference {
+ if ($this->matchReference($referenceText)) {
+ $reference = new Reference($referenceText);
+ try {
+ $this->fetchReference($reference);
+ } catch (NotFoundException $e) {
+ $reference->setRichObject('file', null);
+ $reference->setAccessible(false);
+ }
+ return $reference;
+ }
+
+ return null;
+ }
+
+ /**
+ * @throws NotFoundException
+ */
+ private function fetchReference(Reference $reference): void {
+ if ($this->userId === null) {
+ throw new NotFoundException();
+ }
+
+ $fileId = $this->getFilesAppLinkId($reference->getId());
+ if ($fileId === null) {
+ throw new NotFoundException();
+ }
+
+ try {
+ $userFolder = $this->rootFolder->getUserFolder($this->userId);
+ $files = $userFolder->getById($fileId);
+
+ if (empty($files)) {
+ throw new NotFoundException();
+ }
+
+ /** @var Node $file */
+ $file = array_shift($files);
+
+ $reference->setTitle($file->getName());
+ $reference->setDescription($file->getMimetype());
+ $reference->setUrl($this->urlGenerator->getAbsoluteURL('/index.php/f/' . $fileId));
+ $reference->setImageUrl($this->urlGenerator->linkToRouteAbsolute('core.Preview.getPreviewByFileId', ['x' => 1600, 'y' => 630, 'fileId' => $fileId]));
+
+ $reference->setRichObject('file', [
+ 'id' => $file->getId(),
+ 'name' => $file->getName(),
+ 'size' => $file->getSize(),
+ 'path' => $file->getPath(),
+ 'link' => $reference->getUrl(),
+ 'mimetype' => $file->getMimetype(),
+ 'preview-available' => $this->previewManager->isAvailable($file)
+ ]);
+ } catch (InvalidPathException|NotFoundException|NotPermittedException|NoUserException $e) {
+ throw new NotFoundException();
+ }
+ }
+
+ public function getCachePrefix(string $referenceId): string {
+ return (string)$this->getFilesAppLinkId($referenceId);
+ }
+
+ public function getCacheKey(string $referenceId): ?string {
+ return $this->userId ?? '';
+ }
+}
diff --git a/lib/private/Collaboration/Reference/LinkReferenceProvider.php b/lib/private/Collaboration/Reference/LinkReferenceProvider.php
new file mode 100644
index 00000000000..36fbdd0b168
--- /dev/null
+++ b/lib/private/Collaboration/Reference/LinkReferenceProvider.php
@@ -0,0 +1,162 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2022 Julius Härtl <jus@bitgrid.net>
+ *
+ * @author Julius Härtl <jus@bitgrid.net>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace OC\Collaboration\Reference;
+
+use Fusonic\OpenGraph\Consumer;
+use GuzzleHttp\Psr7\LimitStream;
+use GuzzleHttp\Psr7\Utils;
+use OC\Security\RateLimiting\Exception\RateLimitExceededException;
+use OC\Security\RateLimiting\Limiter;
+use OC\SystemConfig;
+use OCP\Collaboration\Reference\IReference;
+use OCP\Collaboration\Reference\IReferenceProvider;
+use OCP\Files\AppData\IAppDataFactory;
+use OCP\Files\NotFoundException;
+use OCP\Http\Client\IClientService;
+use OCP\IRequest;
+use OCP\IURLGenerator;
+use OCP\IUserSession;
+use Psr\Log\LoggerInterface;
+
+class LinkReferenceProvider implements IReferenceProvider {
+ public const MAX_PREVIEW_SIZE = 1024 * 1024;
+
+ public const ALLOWED_CONTENT_TYPES = [
+ 'image/png',
+ 'image/jpg',
+ 'image/jpeg',
+ 'image/gif',
+ 'image/svg+xml',
+ 'image/webp'
+ ];
+
+ private IClientService $clientService;
+ private LoggerInterface $logger;
+ private SystemConfig $systemConfig;
+ private IAppDataFactory $appDataFactory;
+ private IURLGenerator $urlGenerator;
+ private Limiter $limiter;
+ private IUserSession $userSession;
+ private IRequest $request;
+
+ public function __construct(IClientService $clientService, LoggerInterface $logger, SystemConfig $systemConfig, IAppDataFactory $appDataFactory, IURLGenerator $urlGenerator, Limiter $limiter, IUserSession $userSession, IRequest $request) {
+ $this->clientService = $clientService;
+ $this->logger = $logger;
+ $this->systemConfig = $systemConfig;
+ $this->appDataFactory = $appDataFactory;
+ $this->urlGenerator = $urlGenerator;
+ $this->limiter = $limiter;
+ $this->userSession = $userSession;
+ $this->request = $request;
+ }
+
+ public function matchReference(string $referenceText): bool {
+ if ($this->systemConfig->getValue('reference_opengraph', true) !== true) {
+ return false;
+ }
+
+ return (bool)preg_match(IURLGenerator::URL_REGEX, $referenceText);
+ }
+
+ public function resolveReference(string $referenceText): ?IReference {
+ if ($this->matchReference($referenceText)) {
+ $reference = new Reference($referenceText);
+ $this->fetchReference($reference);
+ return $reference;
+ }
+
+ return null;
+ }
+
+ private function fetchReference(Reference $reference): void {
+ try {
+ $user = $this->userSession->getUser();
+ if ($user) {
+ $this->limiter->registerUserRequest('opengraph', 10, 120, $user);
+ } else {
+ $this->limiter->registerAnonRequest('opengraph', 10, 120, $this->request->getRemoteAddress());
+ }
+ } catch (RateLimitExceededException $e) {
+ return;
+ }
+
+ $client = $this->clientService->newClient();
+ try {
+ $response = $client->get($reference->getId(), [ 'timeout' => 10 ]);
+ } catch (\Exception $e) {
+ $this->logger->debug('Failed to fetch link for obtaining open graph data', ['exception' => $e]);
+ return;
+ }
+
+ $responseBody = (string)$response->getBody();
+
+ // OpenGraph handling
+ $consumer = new Consumer();
+ $consumer->useFallbackMode = true;
+ $object = $consumer->loadHtml($responseBody);
+
+ $reference->setUrl($reference->getId());
+
+ if ($object->title) {
+ $reference->setTitle($object->title);
+ }
+
+ if ($object->description) {
+ $reference->setDescription($object->description);
+ }
+
+ if ($object->images) {
+ try {
+ $appData = $this->appDataFactory->get('core');
+ try {
+ $folder = $appData->getFolder('opengraph');
+ } catch (NotFoundException $e) {
+ $folder = $appData->newFolder('opengraph');
+ }
+ $response = $client->get($object->images[0]->url, [ 'timeout' => 10 ]);
+ $contentType = $response->getHeader('Content-Type');
+ $contentLength = $response->getHeader('Content-Length');
+
+ if (in_array($contentType, self::ALLOWED_CONTENT_TYPES, true) && $contentLength < self::MAX_PREVIEW_SIZE) {
+ $stream = Utils::streamFor($response->getBody());
+ $bodyStream = new LimitStream($stream, self::MAX_PREVIEW_SIZE, 0);
+ $reference->setImageContentType($contentType);
+ $folder->newFile(md5($reference->getId()), $bodyStream->getContents());
+ $reference->setImageUrl($this->urlGenerator->linkToRouteAbsolute('core.Reference.preview', ['referenceId' => md5($reference->getId())]));
+ }
+ } catch (\Throwable $e) {
+ $this->logger->error('Failed to fetch and store the open graph image for ' . $reference->getId(), ['exception' => $e]);
+ }
+ }
+ }
+
+ public function getCachePrefix(string $referenceId): string {
+ return $referenceId;
+ }
+
+ public function getCacheKey(string $referenceId): ?string {
+ return null;
+ }
+}
diff --git a/lib/private/Collaboration/Reference/Reference.php b/lib/private/Collaboration/Reference/Reference.php
new file mode 100644
index 00000000000..22dc57782d8
--- /dev/null
+++ b/lib/private/Collaboration/Reference/Reference.php
@@ -0,0 +1,163 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright Copyright (c) 2022 Julius Härtl <jus@bitgrid.net>
+ *
+ * @author Julius Härtl <jus@bitgrid.net>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace OC\Collaboration\Reference;
+
+use OCP\Collaboration\Reference\IReference;
+
+class Reference implements IReference {
+ private string $reference;
+
+ private bool $accessible = true;
+
+ private ?string $title = null;
+ private ?string $description = null;
+ private ?string $imageUrl = null;
+ private ?string $contentType = null;
+ private ?string $url = null;
+
+ private ?string $richObjectType = null;
+ private ?array $richObject = null;
+
+ public function __construct(string $reference) {
+ $this->reference = $reference;
+ }
+
+ public function getId(): string {
+ return $this->reference;
+ }
+
+ public function setAccessible(bool $accessible): void {
+ $this->accessible = $accessible;
+ }
+
+ public function getAccessible(): bool {
+ return $this->accessible;
+ }
+
+ public function setTitle(string $title): void {
+ $this->title = $title;
+ }
+
+ public function getTitle(): string {
+ return $this->title ?? $this->reference;
+ }
+
+ public function setDescription(?string $description): void {
+ $this->description = $description;
+ }
+
+ public function getDescription(): ?string {
+ return $this->description;
+ }
+
+ public function setImageUrl(?string $imageUrl): void {
+ $this->imageUrl = $imageUrl;
+ }
+
+ public function getImageUrl(): ?string {
+ return $this->imageUrl;
+ }
+
+ public function setImageContentType(?string $contentType): void {
+ $this->contentType = $contentType;
+ }
+
+ public function getImageContentType(): ?string {
+ return $this->contentType;
+ }
+
+ public function setUrl(?string $url): void {
+ $this->url = $url;
+ }
+
+ public function getUrl(): ?string {
+ return $this->url;
+ }
+
+ public function setRichObject(string $type, ?array $richObject): void {
+ $this->richObjectType = $type;
+ $this->richObject = $richObject;
+ }
+
+ public function getRichObjectType(): string {
+ if ($this->richObjectType === null) {
+ return 'open-graph';
+ }
+ return $this->richObjectType;
+ }
+
+ public function getRichObject(): array {
+ if ($this->richObject === null) {
+ return $this->getOpenGraphObject();
+ }
+ return $this->richObject;
+ }
+
+ public function getOpenGraphObject(): array {
+ return [
+ 'id' => $this->getId(),
+ 'name' => $this->getTitle(),
+ 'description' => $this->getDescription(),
+ 'thumb' => $this->getImageUrl(),
+ 'link' => $this->getUrl()
+ ];
+ }
+
+ public static function toCache(IReference $reference): array {
+ return [
+ 'id' => $reference->getId(),
+ 'title' => $reference->getTitle(),
+ 'imageUrl' => $reference->getImageUrl(),
+ 'imageContentType' => $reference->getImageContentType(),
+ 'description' => $reference->getDescription(),
+ 'link' => $reference->getUrl(),
+ 'accessible' => $reference->getAccessible(),
+ 'richObjectType' => $reference->getRichObjectType(),
+ 'richObject' => $reference->getRichObject(),
+ ];
+ }
+
+ public static function fromCache(array $cache): IReference {
+ $reference = new Reference($cache['id']);
+ $reference->setTitle($cache['title']);
+ $reference->setDescription($cache['description']);
+ $reference->setImageUrl($cache['imageUrl']);
+ $reference->setImageContentType($cache['imageContentType']);
+ $reference->setUrl($cache['link']);
+ $reference->setRichObject($cache['richObjectType'], $cache['richObject']);
+ $reference->setAccessible($cache['accessible']);
+ return $reference;
+ }
+
+ public function jsonSerialize() {
+ return [
+ 'richObjectType' => $this->getRichObjectType(),
+ 'richObject' => $this->getRichObject(),
+ 'openGraphObject' => $this->getOpenGraphObject(),
+ 'accessible' => $this->accessible
+ ];
+ }
+}
diff --git a/lib/private/Collaboration/Reference/ReferenceManager.php b/lib/private/Collaboration/Reference/ReferenceManager.php
new file mode 100644
index 00000000000..304d693804f
--- /dev/null
+++ b/lib/private/Collaboration/Reference/ReferenceManager.php
@@ -0,0 +1,169 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2022 Julius Härtl <jus@bitgrid.net>
+ *
+ * @author Julius Härtl <jus@bitgrid.net>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace OC\Collaboration\Reference;
+
+use OC\AppFramework\Bootstrap\Coordinator;
+use OC\Collaboration\Reference\File\FileReferenceProvider;
+use OCP\Collaboration\Reference\IReference;
+use OCP\Collaboration\Reference\IReferenceManager;
+use OCP\Collaboration\Reference\IReferenceProvider;
+use OCP\ICache;
+use OCP\ICacheFactory;
+use OCP\IURLGenerator;
+use Psr\Container\ContainerInterface;
+use Psr\Log\LoggerInterface;
+use Throwable;
+
+class ReferenceManager implements IReferenceManager {
+ public const CACHE_TTL = 3600;
+
+ /** @var IReferenceProvider[]|null */
+ private ?array $providers = null;
+ private ICache $cache;
+ private Coordinator $coordinator;
+ private ContainerInterface $container;
+ private LinkReferenceProvider $linkReferenceProvider;
+ private LoggerInterface $logger;
+
+ public function __construct(LinkReferenceProvider $linkReferenceProvider, ICacheFactory $cacheFactory, Coordinator $coordinator, ContainerInterface $container, LoggerInterface $logger) {
+ $this->linkReferenceProvider = $linkReferenceProvider;
+ $this->cache = $cacheFactory->createDistributed('reference');
+ $this->coordinator = $coordinator;
+ $this->container = $container;
+ $this->logger = $logger;
+ }
+
+ public function extractReferences(string $text): array {
+ preg_match_all(IURLGenerator::URL_REGEX, $text, $matches);
+ $references = $matches[0] ?? [];
+ return array_map(function ($reference) {
+ return trim($reference);
+ }, $references);
+ }
+
+ public function getReferenceFromCache(string $referenceId): ?IReference {
+ $matchedProvider = $this->getMatchedProvider($referenceId);
+
+ if ($matchedProvider === null) {
+ return null;
+ }
+
+ $cacheKey = $this->getFullCacheKey($matchedProvider, $referenceId);
+ return $this->getReferenceByCacheKey($cacheKey);
+ }
+
+ public function getReferenceByCacheKey(string $cacheKey): ?IReference {
+ $cached = $this->cache->get($cacheKey);
+ if ($cached) {
+ return Reference::fromCache($cached);
+ }
+
+ return null;
+ }
+
+ public function resolveReference(string $referenceId): ?IReference {
+ $matchedProvider = $this->getMatchedProvider($referenceId);
+
+ if ($matchedProvider === null) {
+ return null;
+ }
+
+ $cacheKey = $this->getFullCacheKey($matchedProvider, $referenceId);
+ $cached = $this->cache->get($cacheKey);
+ if ($cached) {
+ return Reference::fromCache($cached);
+ }
+
+ $reference = $matchedProvider->resolveReference($referenceId);
+ if ($reference) {
+ $this->cache->set($cacheKey, Reference::toCache($reference), self::CACHE_TTL);
+ return $reference;
+ }
+
+ return null;
+ }
+
+ private function getMatchedProvider(string $referenceId): ?IReferenceProvider {
+ $matchedProvider = null;
+ foreach ($this->getProviders() as $provider) {
+ $matchedProvider = $provider->matchReference($referenceId) ? $provider : null;
+ if ($matchedProvider !== null) {
+ break;
+ }
+ }
+
+ if ($matchedProvider === null && $this->linkReferenceProvider->matchReference($referenceId)) {
+ $matchedProvider = $this->linkReferenceProvider;
+ }
+
+ return $matchedProvider;
+ }
+
+ private function getFullCacheKey(IReferenceProvider $provider, string $referenceId): string {
+ $cacheKey = $provider->getCacheKey($referenceId);
+ return md5($provider->getCachePrefix($referenceId)) . (
+ $cacheKey !== null ? ('-' . md5($cacheKey)) : ''
+ );
+ }
+
+ public function invalidateCache(string $cachePrefix, ?string $cacheKey = null): void {
+ if ($cacheKey === null) {
+ $this->cache->clear(md5($cachePrefix));
+ return;
+ }
+
+ $this->cache->remove(md5($cachePrefix) . '-' . md5($cacheKey));
+ }
+
+ /**
+ * @return IReferenceProvider[]
+ */
+ public function getProviders(): array {
+ if ($this->providers === null) {
+ $context = $this->coordinator->getRegistrationContext();
+ if ($context === null) {
+ return [];
+ }
+
+ $this->providers = array_filter(array_map(function ($registration): ?IReferenceProvider {
+ try {
+ /** @var IReferenceProvider $provider */
+ $provider = $this->container->get($registration->getService());
+ } catch (Throwable $e) {
+ $this->logger->error('Could not load reference provider ' . $registration->getService() . ': ' . $e->getMessage(), [
+ 'exception' => $e,
+ ]);
+ return null;
+ }
+
+ return $provider;
+ }, $context->getReferenceProviders()));
+
+ $this->providers[] = $this->container->get(FileReferenceProvider::class);
+ }
+
+ return $this->providers;
+ }
+}
diff --git a/lib/private/Collaboration/Resources/Listener.php b/lib/private/Collaboration/Resources/Listener.php
index ba012b4ca44..b9f1e72cbfa 100644
--- a/lib/private/Collaboration/Resources/Listener.php
+++ b/lib/private/Collaboration/Resources/Listener.php
@@ -26,14 +26,16 @@ declare(strict_types=1);
*/
namespace OC\Collaboration\Resources;
+use OC\EventDispatcher\SymfonyAdapter;
use OCP\Collaboration\Resources\IManager;
+use OCP\Collaboration\Resources\LoadAdditionalScriptsEvent;
+use OCP\EventDispatcher\IEventDispatcher;
use OCP\IGroup;
use OCP\IUser;
-use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\GenericEvent;
class Listener {
- public static function register(EventDispatcherInterface $dispatcher): void {
+ public static function register(SymfonyAdapter $symfonyDispatcher, IEventDispatcher $eventDispatcher): void {
$listener = function (GenericEvent $event) {
/** @var IUser $user */
$user = $event->getArgument('user');
@@ -42,10 +44,10 @@ class Listener {
$resourceManager->invalidateAccessCacheForUser($user);
};
- $dispatcher->addListener(IGroup::class . '::postAddUser', $listener);
- $dispatcher->addListener(IGroup::class . '::postRemoveUser', $listener);
+ $symfonyDispatcher->addListener(IGroup::class . '::postAddUser', $listener);
+ $symfonyDispatcher->addListener(IGroup::class . '::postRemoveUser', $listener);
- $dispatcher->addListener(IUser::class . '::postDelete', function (GenericEvent $event) {
+ $symfonyDispatcher->addListener(IUser::class . '::postDelete', function (GenericEvent $event) {
/** @var IUser $user */
$user = $event->getSubject();
/** @var IManager $resourceManager */
@@ -54,7 +56,7 @@ class Listener {
$resourceManager->invalidateAccessCacheForUser($user);
});
- $dispatcher->addListener(IGroup::class . '::preDelete', function (GenericEvent $event) {
+ $symfonyDispatcher->addListener(IGroup::class . '::preDelete', function (GenericEvent $event) {
/** @var IGroup $group */
$group = $event->getSubject();
/** @var IManager $resourceManager */
@@ -64,5 +66,24 @@ class Listener {
$resourceManager->invalidateAccessCacheForUser($user);
}
});
+
+ // Stay backward compatible with the legacy event for now
+ $fallbackEventRunning = false;
+ $symfonyDispatcher->addListener('\OCP\Collaboration\Resources::loadAdditionalScripts', function () use ($eventDispatcher, &$fallbackEventRunning) {
+ if ($fallbackEventRunning) {
+ return;
+ }
+ $fallbackEventRunning = true;
+ $eventDispatcher->dispatchTyped(new LoadAdditionalScriptsEvent());
+ $fallbackEventRunning = false;
+ });
+ $eventDispatcher->addListener(LoadAdditionalScriptsEvent::class, static function () use ($symfonyDispatcher, &$fallbackEventRunning) {
+ if ($fallbackEventRunning) {
+ return;
+ }
+ $fallbackEventRunning = true;
+ $symfonyDispatcher->dispatch('\OCP\Collaboration\Resources::loadAdditionalScripts');
+ $fallbackEventRunning = false;
+ });
}
}
diff --git a/lib/private/Command/ClosureJob.php b/lib/private/Command/ClosureJob.php
index 498fe6d1d96..5639852e4db 100644
--- a/lib/private/Command/ClosureJob.php
+++ b/lib/private/Command/ClosureJob.php
@@ -23,10 +23,13 @@
namespace OC\Command;
use OC\BackgroundJob\QueuedJob;
+use Laravel\SerializableClosure\SerializableClosure as LaravelClosure;
+use Opis\Closure\SerializableClosure as OpisClosure;
class ClosureJob extends QueuedJob {
protected function run($serializedCallable) {
- $callable = \Opis\Closure\unserialize($serializedCallable);
+ $callable = unserialize($serializedCallable, [LaravelClosure::class, OpisClosure::class]);
+ $callable = $callable->getClosure();
if (is_callable($callable)) {
$callable();
} else {
diff --git a/lib/private/Command/CommandJob.php b/lib/private/Command/CommandJob.php
index 6fa0c6d7ceb..5b267162c81 100644
--- a/lib/private/Command/CommandJob.php
+++ b/lib/private/Command/CommandJob.php
@@ -30,7 +30,7 @@ use OCP\Command\ICommand;
*/
class CommandJob extends QueuedJob {
protected function run($serializedCommand) {
- $command = \Opis\Closure\unserialize($serializedCommand);
+ $command = unserialize($serializedCommand);
if ($command instanceof ICommand) {
$command->handle();
} else {
diff --git a/lib/private/Command/CronBus.php b/lib/private/Command/CronBus.php
index 89a739617d0..8749ad0bff5 100644
--- a/lib/private/Command/CronBus.php
+++ b/lib/private/Command/CronBus.php
@@ -26,6 +26,7 @@
namespace OC\Command;
use OCP\Command\ICommand;
+use Laravel\SerializableClosure\SerializableClosure;
class CronBus extends AsyncBus {
/**
@@ -67,9 +68,9 @@ class CronBus extends AsyncBus {
*/
private function serializeCommand($command) {
if ($command instanceof \Closure) {
- return \Opis\Closure\serialize($command);
+ return serialize(new SerializableClosure($command));
} elseif (is_callable($command) or $command instanceof ICommand) {
- return \Opis\Closure\serialize($command);
+ return serialize($command);
} else {
throw new \InvalidArgumentException('Invalid command');
}
diff --git a/lib/private/Comments/Comment.php b/lib/private/Comments/Comment.php
index 2b338efc75f..c481e36f95b 100644
--- a/lib/private/Comments/Comment.php
+++ b/lib/private/Comments/Comment.php
@@ -45,6 +45,7 @@ class Comment implements IComment {
'creationDT' => null,
'latestChildDT' => null,
'reactions' => null,
+ 'expire_date' => null,
];
/**
@@ -350,13 +351,9 @@ class Comment implements IComment {
}
/**
- * sets the date of the most recent child
- *
- * @param \DateTime $dateTime
- * @return IComment
- * @since 9.0.0
+ * @inheritDoc
*/
- public function setLatestChildDateTime(\DateTime $dateTime = null) {
+ public function setLatestChildDateTime(?\DateTime $dateTime = null) {
$this->data['latestChildDT'] = $dateTime;
return $this;
}
@@ -447,6 +444,21 @@ class Comment implements IComment {
}
/**
+ * @inheritDoc
+ */
+ public function setExpireDate(?\DateTime $dateTime): IComment {
+ $this->data['expire_date'] = $dateTime;
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getExpireDate(): ?\DateTime {
+ return $this->data['expire_date'];
+ }
+
+ /**
* sets the comment data based on an array with keys as taken from the
* database.
*
diff --git a/lib/private/Comments/Manager.php b/lib/private/Comments/Manager.php
index 4a06ac62f1e..53603e51e56 100644
--- a/lib/private/Comments/Manager.php
+++ b/lib/private/Comments/Manager.php
@@ -107,6 +107,9 @@ class Manager implements ICommentsManager {
if (!is_null($data['latest_child_timestamp'])) {
$data['latest_child_timestamp'] = new \DateTime($data['latest_child_timestamp']);
}
+ if (!is_null($data['expire_date'])) {
+ $data['expire_date'] = new \DateTime($data['expire_date']);
+ }
$data['children_count'] = (int)$data['children_count'];
$data['reference_id'] = $data['reference_id'] ?? null;
if ($this->supportReactions()) {
@@ -167,7 +170,6 @@ class Manager implements ICommentsManager {
if ($comment->getId() === '') {
$comment->setChildrenCount(0);
- $comment->setLatestChildDateTime(new \DateTime('0000-00-00 00:00:00', new \DateTimeZone('UTC')));
$comment->setLatestChildDateTime(null);
}
@@ -1203,6 +1205,7 @@ class Manager implements ICommentsManager {
'latest_child_timestamp' => $qb->createNamedParameter($comment->getLatestChildDateTime(), 'datetime'),
'object_type' => $qb->createNamedParameter($comment->getObjectType()),
'object_id' => $qb->createNamedParameter($comment->getObjectId()),
+ 'expire_date' => $qb->createNamedParameter($comment->getExpireDate(), 'datetime'),
];
if ($tryWritingReferenceId) {
@@ -1258,8 +1261,6 @@ class Manager implements ICommentsManager {
}
private function sumReactions(string $parentId): void {
- $qb = $this->dbConn->getQueryBuilder();
-
$totalQuery = $this->dbConn->getQueryBuilder();
$totalQuery
->selectAlias(
@@ -1273,7 +1274,7 @@ class Manager implements ICommentsManager {
)
->selectAlias($totalQuery->func()->count('id'), 'total')
->from('reactions', 'r')
- ->where($totalQuery->expr()->eq('r.parent_id', $qb->createNamedParameter($parentId)))
+ ->where($totalQuery->expr()->eq('r.parent_id', $totalQuery->createNamedParameter($parentId)))
->groupBy('r.reaction')
->orderBy('total', 'DESC')
->addOrderBy('r.reaction', 'ASC')
@@ -1291,9 +1292,10 @@ class Manager implements ICommentsManager {
)
->from($jsonQuery->createFunction('(' . $totalQuery->getSQL() . ')'), 'json');
+ $qb = $this->dbConn->getQueryBuilder();
$qb
->update('comments')
- ->set('reactions', $jsonQuery->createFunction('(' . $jsonQuery->getSQL() . ')'))
+ ->set('reactions', $qb->createFunction('(' . $jsonQuery->getSQL() . ')'))
->where($qb->expr()->eq('id', $qb->createNamedParameter($parentId)))
->executeStatement();
}
@@ -1643,4 +1645,25 @@ class Manager implements ICommentsManager {
$this->initialStateService->provideInitialState('comments', 'max-message-length', IComment::MAX_MESSAGE_LENGTH);
Util::addScript('comments', 'comments-app');
}
+
+ /**
+ * @inheritDoc
+ */
+ public function deleteCommentsExpiredAtObject(string $objectType, string $objectId = ''): bool {
+ $qb = $this->dbConn->getQueryBuilder();
+ $qb->delete('comments')
+ ->where($qb->expr()->lte('expire_date',
+ $qb->createNamedParameter($this->timeFactory->getDateTime(), IQueryBuilder::PARAM_DATE)))
+ ->andWhere($qb->expr()->eq('object_type', $qb->createNamedParameter($objectType)));
+
+ if ($objectId !== '') {
+ $qb->andWhere($qb->expr()->eq('object_id', $qb->createNamedParameter($objectId)));
+ }
+
+ $affectedRows = $qb->executeStatement();
+
+ $this->commentsCache = [];
+
+ return $affectedRows > 0;
+ }
}
diff --git a/lib/private/Config.php b/lib/private/Config.php
index b044d0731a3..37708357339 100644
--- a/lib/private/Config.php
+++ b/lib/private/Config.php
@@ -231,6 +231,14 @@ class Config {
unset($CONFIG);
include $file;
+ if (!defined('PHPUNIT_RUN') && headers_sent()) {
+ // syntax issues in the config file like leading spaces causing PHP to send output
+ $errorMessage = sprintf('Config file has leading content, please remove everything before "<?php" in %s', basename($file));
+ if (!defined('OC_CONSOLE')) {
+ print(\OCP\Util::sanitizeHTML($errorMessage));
+ }
+ throw new \Exception($errorMessage);
+ }
if (isset($CONFIG) && is_array($CONFIG)) {
$this->cache = array_merge($this->cache, $CONFIG);
}
diff --git a/lib/private/Console/Application.php b/lib/private/Console/Application.php
index 12d54b48fa9..fc48f57e499 100644
--- a/lib/private/Console/Application.php
+++ b/lib/private/Console/Application.php
@@ -34,6 +34,7 @@ use OC\MemoryInfo;
use OC\NeedsUpdateException;
use OC_App;
use OCP\AppFramework\QueryException;
+use OCP\App\IAppManager;
use OCP\Console\ConsoleEvent;
use OCP\IConfig;
use OCP\IRequest;
@@ -117,13 +118,14 @@ class Application {
$this->writeMaintenanceModeInfo($input, $output);
} else {
OC_App::loadApps();
- foreach (\OC::$server->getAppManager()->getInstalledApps() as $app) {
+ $appManager = \OCP\Server::get(IAppManager::class);
+ foreach ($appManager->getInstalledApps() as $app) {
$appPath = \OC_App::getAppPath($app);
if ($appPath === false) {
continue;
}
// load commands using info.xml
- $info = \OC_App::getAppInfo($app);
+ $info = $appManager->getAppInfo($app);
if (isset($info['commands'])) {
$this->loadCommandsFromInfoXml($info['commands']);
}
diff --git a/lib/private/Contacts/ContactsMenu/ContactsStore.php b/lib/private/Contacts/ContactsMenu/ContactsStore.php
index 020e8604910..dd4bd973fa9 100644
--- a/lib/private/Contacts/ContactsMenu/ContactsStore.php
+++ b/lib/private/Contacts/ContactsMenu/ContactsStore.php
@@ -123,7 +123,7 @@ class ContactsStore implements IContactsStore {
* 2. if the `shareapi_exclude_groups` config option is enabled and the
* current user is in an excluded group it will filter all local users.
* 3. if the `shareapi_only_share_with_group_members` config option is
- * enabled it will filter all users which doens't have a common group
+ * enabled it will filter all users which doesn't have a common group
* with the current user.
*
* @param IUser $self
@@ -150,7 +150,7 @@ class ContactsStore implements IContactsStore {
$selfGroups = $this->groupManager->getUserGroupIds($self);
if ($excludedGroups) {
- $excludedGroups = $this->config->getAppValue('core', 'shareapi_exclude_groups_list');
+ $excludedGroups = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
$decodedExcludeGroups = json_decode($excludedGroups, true);
$excludeGroupsList = $decodedExcludeGroups ?? [];
diff --git a/lib/private/ContactsManager.php b/lib/private/ContactsManager.php
index 937fb94a09a..68783e3f79b 100644
--- a/lib/private/ContactsManager.php
+++ b/lib/private/ContactsManager.php
@@ -6,6 +6,7 @@
* @author Joas Schilling <coding@schilljs.com>
* @author John Molakvoæ <skjnldsv@protonmail.com>
* @author Morris Jobke <hey@morrisjobke.de>
+ * @author Thomas Citharel <nextcloud@tcit.fr>
* @author Thomas Müller <thomas.mueller@tmit.eu>
* @author Tobia De Koninck <tobia@ledfan.be>
*
@@ -85,7 +86,7 @@ class ContactsManager implements IManager {
/**
* This function can be used to delete the contact identified by the given id
*
- * @param object $id the unique identifier to a contact
+ * @param int $id the unique identifier to a contact
* @param string $address_book_key identifier of the address book in which the contact shall be deleted
* @return bool successful or not
*/
diff --git a/lib/private/DB/Connection.php b/lib/private/DB/Connection.php
index 22c2bbbb793..73e0f4b4ac2 100644
--- a/lib/private/DB/Connection.php
+++ b/lib/private/DB/Connection.php
@@ -588,7 +588,7 @@ class Connection extends \Doctrine\DBAL\Connection {
$random = \OC::$server->getSecureRandom();
$platform = $this->getDatabasePlatform();
$config = \OC::$server->getConfig();
- $dispatcher = \OC::$server->getEventDispatcher();
+ $dispatcher = \OC::$server->get(\OCP\EventDispatcher\IEventDispatcher::class);
if ($platform instanceof SqlitePlatform) {
return new SQLiteMigrator($this, $config, $dispatcher);
} elseif ($platform instanceof OraclePlatform) {
diff --git a/lib/private/DB/MigrationService.php b/lib/private/DB/MigrationService.php
index 13bbe8dc5d0..4b7e4d3a040 100644
--- a/lib/private/DB/MigrationService.php
+++ b/lib/private/DB/MigrationService.php
@@ -45,35 +45,25 @@ use OCP\Migration\IOutput;
use Psr\Log\LoggerInterface;
class MigrationService {
-
- /** @var boolean */
- private $migrationTableCreated;
- /** @var array */
- private $migrations;
- /** @var IOutput */
- private $output;
- /** @var Connection */
- private $connection;
- /** @var string */
- private $appName;
- /** @var bool */
- private $checkOracle;
+ private bool $migrationTableCreated;
+ private array $migrations;
+ private string $migrationsPath;
+ private string $migrationsNamespace;
+ private IOutput $output;
+ private Connection $connection;
+ private string $appName;
+ private bool $checkOracle;
/**
- * MigrationService constructor.
- *
- * @param $appName
- * @param Connection $connection
- * @param AppLocator $appLocator
- * @param IOutput|null $output
* @throws \Exception
*/
- public function __construct($appName, Connection $connection, IOutput $output = null, AppLocator $appLocator = null) {
+ public function __construct($appName, Connection $connection, ?IOutput $output = null, ?AppLocator $appLocator = null) {
$this->appName = $appName;
$this->connection = $connection;
- $this->output = $output;
- if (null === $this->output) {
+ if ($output === null) {
$this->output = new SimpleOutput(\OC::$server->get(LoggerInterface::class), $appName);
+ } else {
+ $this->output = $output;
}
if ($appName === 'core') {
@@ -104,6 +94,7 @@ class MigrationService {
}
}
}
+ $this->migrationTableCreated = false;
}
/**
diff --git a/lib/private/DB/Migrator.php b/lib/private/DB/Migrator.php
index 9ca37d7180a..5dc07be1d2b 100644
--- a/lib/private/DB/Migrator.php
+++ b/lib/private/DB/Migrator.php
@@ -27,48 +27,41 @@
*/
namespace OC\DB;
+use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Exception;
use Doctrine\DBAL\Platforms\MySQLPlatform;
use Doctrine\DBAL\Schema\AbstractAsset;
use Doctrine\DBAL\Schema\Comparator;
use Doctrine\DBAL\Schema\Schema;
+use Doctrine\DBAL\Schema\SchemaDiff;
use Doctrine\DBAL\Types\StringType;
use Doctrine\DBAL\Types\Type;
use OCP\IConfig;
-use Symfony\Component\EventDispatcher\EventDispatcherInterface;
-use Symfony\Component\EventDispatcher\GenericEvent;
use function preg_match;
+use OCP\EventDispatcher\IEventDispatcher;
class Migrator {
- /** @var \Doctrine\DBAL\Connection */
+ /** @var Connection */
protected $connection;
/** @var IConfig */
protected $config;
- /** @var EventDispatcherInterface */
- private $dispatcher;
+ private ?IEventDispatcher $dispatcher;
/** @var bool */
private $noEmit = false;
- /**
- * @param \Doctrine\DBAL\Connection $connection
- * @param IConfig $config
- * @param EventDispatcherInterface $dispatcher
- */
- public function __construct(\Doctrine\DBAL\Connection $connection,
+ public function __construct(Connection $connection,
IConfig $config,
- EventDispatcherInterface $dispatcher = null) {
+ ?IEventDispatcher $dispatcher = null) {
$this->connection = $connection;
$this->config = $config;
$this->dispatcher = $dispatcher;
}
/**
- * @param \Doctrine\DBAL\Schema\Schema $targetSchema
- *
* @throws Exception
*/
public function migrate(Schema $targetSchema) {
@@ -77,7 +70,6 @@ class Migrator {
}
/**
- * @param \Doctrine\DBAL\Schema\Schema $targetSchema
* @return string
*/
public function generateChangeScript(Schema $targetSchema) {
@@ -108,11 +100,9 @@ class Migrator {
}
/**
- * @param Schema $targetSchema
- * @param \Doctrine\DBAL\Connection $connection
- * @return \Doctrine\DBAL\Schema\SchemaDiff
+ * @return SchemaDiff
*/
- protected function getDiff(Schema $targetSchema, \Doctrine\DBAL\Connection $connection) {
+ protected function getDiff(Schema $targetSchema, Connection $connection) {
// adjust varchar columns with a length higher then getVarcharMaxLength to clob
foreach ($targetSchema->getTables() as $table) {
foreach ($table->getColumns() as $column) {
@@ -153,12 +143,9 @@ class Migrator {
}
/**
- * @param \Doctrine\DBAL\Schema\Schema $targetSchema
- * @param \Doctrine\DBAL\Connection $connection
- *
* @throws Exception
*/
- protected function applySchema(Schema $targetSchema, \Doctrine\DBAL\Connection $connection = null) {
+ protected function applySchema(Schema $targetSchema, Connection $connection = null) {
if (is_null($connection)) {
$connection = $this->connection;
}
@@ -194,13 +181,13 @@ class Migrator {
return '/^' . preg_quote($this->config->getSystemValue('dbtableprefix', 'oc_')) . '/';
}
- protected function emit($sql, $step, $max) {
+ protected function emit(string $sql, int $step, int $max): void {
if ($this->noEmit) {
return;
}
if (is_null($this->dispatcher)) {
return;
}
- $this->dispatcher->dispatch('\OC\DB\Migrator::executeSql', new GenericEvent($sql, [$step + 1, $max]));
+ $this->dispatcher->dispatchTyped(new MigratorExecuteSqlEvent($sql, $step, $max));
}
}
diff --git a/lib/private/DB/MigratorExecuteSqlEvent.php b/lib/private/DB/MigratorExecuteSqlEvent.php
new file mode 100644
index 00000000000..997a4eee53a
--- /dev/null
+++ b/lib/private/DB/MigratorExecuteSqlEvent.php
@@ -0,0 +1,55 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright Copyright (c) 2022 Côme Chilliet <come.chilliet@nextcloud.com>
+ *
+ * @author Côme Chilliet <come.chilliet@nextcloud.com>
+ *
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+namespace OC\DB;
+
+use OCP\EventDispatcher\Event;
+
+class MigratorExecuteSqlEvent extends Event {
+ private string $sql;
+ private int $current;
+ private int $max;
+
+ public function __construct(
+ string $sql,
+ int $current,
+ int $max
+ ) {
+ $this->sql = $sql;
+ $this->current = $current;
+ $this->max = $max;
+ }
+
+ public function getSql(): string {
+ return $this->sql;
+ }
+
+ public function getCurrentStep(): int {
+ return $this->current;
+ }
+
+ public function getMaxStep(): int {
+ return $this->max;
+ }
+}
diff --git a/lib/private/DB/QueryBuilder/QueryBuilder.php b/lib/private/DB/QueryBuilder/QueryBuilder.php
index fc436383b04..e81ba61b3a7 100644
--- a/lib/private/DB/QueryBuilder/QueryBuilder.php
+++ b/lib/private/DB/QueryBuilder/QueryBuilder.php
@@ -848,7 +848,7 @@ class QueryBuilder implements IQueryBuilder {
* ->from('users', 'u')
* ->where('u.id = ?');
*
- * // You can optionally programatically build and/or expressions
+ * // You can optionally programmatically build and/or expressions
* $qb = $conn->getQueryBuilder();
*
* $or = $qb->expr()->orx();
@@ -1096,7 +1096,7 @@ class QueryBuilder implements IQueryBuilder {
* Specifies an ordering for the query results.
* Replaces any previously specified orderings, if any.
*
- * @param string $sort The ordering expression.
+ * @param string|IQueryFunction|ILiteral|IParameter $sort The ordering expression.
* @param string $order The ordering direction.
*
* @return $this This QueryBuilder instance.
diff --git a/lib/private/Dashboard/Manager.php b/lib/private/Dashboard/Manager.php
index 09525693b4f..2aeedf3174e 100644
--- a/lib/private/Dashboard/Manager.php
+++ b/lib/private/Dashboard/Manager.php
@@ -27,10 +27,11 @@ declare(strict_types=1);
namespace OC\Dashboard;
use InvalidArgumentException;
-use OCP\AppFramework\QueryException;
+use OCP\App\IAppManager;
use OCP\Dashboard\IManager;
use OCP\Dashboard\IWidget;
-use OCP\IServerContainer;
+use Psr\Container\ContainerExceptionInterface;
+use Psr\Container\ContainerInterface;
use Throwable;
use Psr\Log\LoggerInterface;
@@ -42,10 +43,10 @@ class Manager implements IManager {
/** @var IWidget[] */
private $widgets = [];
- /** @var IServerContainer */
- private $serverContainer;
+ private ContainerInterface $serverContainer;
+ private ?IAppManager $appManager = null;
- public function __construct(IServerContainer $serverContainer) {
+ public function __construct(ContainerInterface $serverContainer) {
$this->serverContainer = $serverContainer;
}
@@ -57,17 +58,25 @@ class Manager implements IManager {
$this->widgets[$widget->getId()] = $widget;
}
- public function lazyRegisterWidget(string $widgetClass): void {
- $this->lazyWidgets[] = $widgetClass;
+ public function lazyRegisterWidget(string $widgetClass, string $appId): void {
+ $this->lazyWidgets[] = ['class' => $widgetClass, 'appId' => $appId];
}
public function loadLazyPanels(): void {
- $classes = $this->lazyWidgets;
- foreach ($classes as $class) {
+ if ($this->appManager === null) {
+ $this->appManager = $this->serverContainer->get(IAppManager::class);
+ }
+ $services = $this->lazyWidgets;
+ foreach ($services as $service) {
+ /** @psalm-suppress InvalidCatch */
try {
+ if (!$this->appManager->isEnabledForUser($service['appId'])) {
+ // all apps are registered, but some may not be enabled for the user
+ continue;
+ }
/** @var IWidget $widget */
- $widget = $this->serverContainer->query($class);
- } catch (QueryException $e) {
+ $widget = $this->serverContainer->get($service['class']);
+ } catch (ContainerExceptionInterface $e) {
/*
* There is a circular dependency between the logger and the registry, so
* we can not inject it. Thus the static call.
@@ -90,7 +99,7 @@ class Manager implements IManager {
*/
\OC::$server->get(LoggerInterface::class)->critical(
'Could not register lazy dashboard widget: ' . $e->getMessage(),
- ['excepiton' => $e]
+ ['exception' => $e]
);
}
@@ -111,7 +120,7 @@ class Manager implements IManager {
} catch (Throwable $e) {
\OC::$server->get(LoggerInterface::class)->critical(
'Error during dashboard widget loading: ' . $e->getMessage(),
- ['excepiton' => $e]
+ ['exception' => $e]
);
}
}
diff --git a/lib/private/Diagnostics/QueryLogger.php b/lib/private/Diagnostics/QueryLogger.php
index 40d68d94ae3..5f401751077 100644
--- a/lib/private/Diagnostics/QueryLogger.php
+++ b/lib/private/Diagnostics/QueryLogger.php
@@ -24,7 +24,7 @@
*/
namespace OC\Diagnostics;
-use OC\Cache\CappedMemoryCache;
+use OCP\Cache\CappedMemoryCache;
use OCP\Diagnostics\IQueryLogger;
class QueryLogger implements IQueryLogger {
diff --git a/lib/private/Encryption/File.php b/lib/private/Encryption/File.php
index 2d7e23a8883..87bc35bc159 100644
--- a/lib/private/Encryption/File.php
+++ b/lib/private/Encryption/File.php
@@ -27,29 +27,24 @@
*/
namespace OC\Encryption;
-use OC\Cache\CappedMemoryCache;
+use OCP\Cache\CappedMemoryCache;
use OCA\Files_External\Service\GlobalStoragesService;
+use OCP\App\IAppManager;
use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;
use OCP\Share\IManager;
class File implements \OCP\Encryption\IFile {
-
- /** @var Util */
- protected $util;
-
- /** @var IRootFolder */
- private $rootFolder;
-
- /** @var IManager */
- private $shareManager;
+ protected Util $util;
+ private IRootFolder $rootFolder;
+ private IManager $shareManager;
/**
- * cache results of already checked folders
- *
+ * Cache results of already checked folders
* @var CappedMemoryCache<array>
*/
protected CappedMemoryCache $cache;
+ private ?IAppManager $appManager = null;
public function __construct(Util $util,
IRootFolder $rootFolder,
@@ -60,6 +55,14 @@ class File implements \OCP\Encryption\IFile {
$this->shareManager = $shareManager;
}
+ public function getAppManager(): IAppManager {
+ // Lazy evaluate app manager as it initialize the db too early otherwise
+ if ($this->appManager) {
+ return $this->appManager;
+ }
+ $this->appManager = \OCP\Server::get(IAppManager::class);
+ return $this->appManager;
+ }
/**
* Get list of users with access to the file
@@ -110,7 +113,7 @@ class File implements \OCP\Encryption\IFile {
}
// check if it is a group mount
- if (\OCP\App::isEnabled("files_external")) {
+ if ($this->getAppManager()->isEnabledForUser("files_external")) {
/** @var GlobalStoragesService $storageService */
$storageService = \OC::$server->get(GlobalStoragesService::class);
$storages = $storageService->getAllStorages();
diff --git a/lib/private/Encryption/HookManager.php b/lib/private/Encryption/HookManager.php
index a2d6b990a88..5081bcccf94 100644
--- a/lib/private/Encryption/HookManager.php
+++ b/lib/private/Encryption/HookManager.php
@@ -25,39 +25,51 @@ namespace OC\Encryption;
use OC\Files\Filesystem;
use OC\Files\View;
+use OC\Files\SetupManager;
use Psr\Log\LoggerInterface;
class HookManager {
- /**
- * @var Update
- */
- private static $updater;
+ private static ?Update $updater = null;
- public static function postShared($params) {
+ public static function postShared($params): void {
self::getUpdate()->postShared($params);
}
- public static function postUnshared($params) {
- self::getUpdate()->postUnshared($params);
+ public static function postUnshared($params): void {
+ // In case the unsharing happens in a background job, we don't have
+ // a session and we load instead the user from the UserManager
+ $path = Filesystem::getPath($params['fileSource']);
+ $owner = Filesystem::getOwner($path);
+ self::getUpdate($owner)->postUnshared($params);
}
- public static function postRename($params) {
+ public static function postRename($params): void {
self::getUpdate()->postRename($params);
}
- public static function postRestore($params) {
+ public static function postRestore($params): void {
self::getUpdate()->postRestore($params);
}
- /**
- * @return Update
- */
- private static function getUpdate() {
+ private static function getUpdate(?string $owner = null): Update {
if (is_null(self::$updater)) {
$user = \OC::$server->getUserSession()->getUser();
+ if (!$user && $owner) {
+ $user = \OC::$server->getUserManager()->get($owner);
+ }
+ if (!$user) {
+ throw new \Exception("Inconsistent data, File unshared, but owner not found. Should not happen");
+ }
+
$uid = '';
if ($user) {
$uid = $user->getUID();
}
+
+ $setupManager = \OC::$server->get(SetupManager::class);
+ if (!$setupManager->isSetupComplete($user)) {
+ $setupManager->setupForUser($user);
+ }
+
self::$updater = new Update(
new View(),
new Util(
diff --git a/lib/private/Encryption/Util.php b/lib/private/Encryption/Util.php
index 693e24c4721..bf7bbce8ad7 100644
--- a/lib/private/Encryption/Util.php
+++ b/lib/private/Encryption/Util.php
@@ -32,11 +32,12 @@ use OC\Encryption\Exceptions\EncryptionHeaderToLargeException;
use OC\Encryption\Exceptions\ModuleDoesNotExistsException;
use OC\Files\Filesystem;
use OC\Files\View;
-use OCA\Files_External\Lib\StorageConfig;
-use OCA\Files_External\Service\GlobalStoragesService;
use OCP\Encryption\IEncryptionModule;
+use OCP\Files\Mount\ISystemMountPoint;
use OCP\IConfig;
+use OCP\IGroupManager;
use OCP\IUser;
+use OCP\IUserManager;
class Util {
public const HEADER_START = 'HBEGIN';
@@ -65,29 +66,23 @@ class Util {
/** @var array */
protected $ocHeaderKeys;
- /** @var \OC\User\Manager */
- protected $userManager;
-
/** @var IConfig */
protected $config;
/** @var array paths excluded from encryption */
protected $excludedPaths;
-
- /** @var \OC\Group\Manager $manager */
- protected $groupManager;
+ protected IGroupManager $groupManager;
+ protected IUserManager $userManager;
/**
*
* @param View $rootView
- * @param \OC\User\Manager $userManager
- * @param \OC\Group\Manager $groupManager
* @param IConfig $config
*/
public function __construct(
View $rootView,
- \OC\User\Manager $userManager,
- \OC\Group\Manager $groupManager,
+ IUserManager $userManager,
+ IGroupManager $groupManager,
IConfig $config) {
$this->ocHeaderKeys = [
self::HEADER_ENCRYPTION_MODULE_KEY
@@ -275,7 +270,7 @@ class Util {
} else {
$result = array_merge($result, $users);
- $groupManager = \OC::$server->getGroupManager();
+ $groupManager = $this->groupManager;
foreach ($groups as $group) {
$groupObject = $groupManager->get($group);
if ($groupObject) {
@@ -298,45 +293,9 @@ class Util {
* @param string $uid
* @return boolean
*/
- public function isSystemWideMountPoint($path, $uid) {
- if (\OCP\App::isEnabled("files_external")) {
- /** @var GlobalStoragesService $storageService */
- $storageService = \OC::$server->get(GlobalStoragesService::class);
- $storages = $storageService->getAllStorages();
- foreach ($storages as $storage) {
- if (strpos($path, '/files/' . $storage->getMountPoint()) === 0) {
- if ($this->isMountPointApplicableToUser($storage, $uid)) {
- return true;
- }
- }
- }
- }
- return false;
- }
-
- /**
- * check if mount point is applicable to user
- *
- * @param StorageConfig $mount
- * @param string $uid
- * @return boolean
- */
- private function isMountPointApplicableToUser(StorageConfig $mount, string $uid) {
- if ($mount->getApplicableUsers() === [] && $mount->getApplicableGroups() === []) {
- // applicable for everyone
- return true;
- }
- // check if mount point is applicable for the user
- if (array_search($uid, $mount->getApplicableUsers()) !== false) {
- return true;
- }
- // check if mount point is applicable for group where the user is a member
- foreach ($mount->getApplicableGroups() as $gid) {
- if ($this->groupManager->isInGroup($uid, $gid)) {
- return true;
- }
- }
- return false;
+ public function isSystemWideMountPoint(string $path, string $uid) {
+ $mount = Filesystem::getMountManager()->find('/' . $uid . $path);
+ return $mount instanceof ISystemMountPoint;
}
/**
@@ -377,32 +336,29 @@ class Util {
}
/**
- * check if recovery key is enabled for user
- *
- * @param string $uid
- * @return boolean
+ * Check if recovery key is enabled for user
*/
- public function recoveryEnabled($uid) {
+ public function recoveryEnabled(string $uid): bool {
$enabled = $this->config->getUserValue($uid, 'encryption', 'recovery_enabled', '0');
return $enabled === '1';
}
/**
- * set new key storage root
+ * Set new key storage root
*
* @param string $root new key store root relative to the data folder
*/
- public function setKeyStorageRoot($root) {
+ public function setKeyStorageRoot(string $root): void {
$this->config->setAppValue('core', 'encryption_key_storage_root', $root);
}
/**
- * get key storage root
+ * Get key storage root
*
* @return string key storage root
*/
- public function getKeyStorageRoot() {
+ public function getKeyStorageRoot(): string {
return $this->config->getAppValue('core', 'encryption_key_storage_root', '');
}
}
diff --git a/lib/private/Federation/CloudFederationProviderManager.php b/lib/private/Federation/CloudFederationProviderManager.php
index c25d4a40363..f077e36d97d 100644
--- a/lib/private/Federation/CloudFederationProviderManager.php
+++ b/lib/private/Federation/CloudFederationProviderManager.php
@@ -150,11 +150,12 @@ class CloudFederationProviderManager implements ICloudFederationProviderManager
return (is_array($result)) ? $result : [];
}
} catch (\Exception $e) {
+ $this->logger->debug($e->getMessage(), ['exception' => $e]);
+
// if flat re-sharing is not supported by the remote server
// we re-throw the exception and fall back to the old behaviour.
// (flat re-shares has been introduced in Nextcloud 9.1)
if ($e->getCode() === Http::STATUS_INTERNAL_SERVER_ERROR) {
- $this->logger->debug($e->getMessage(), ['exception' => $e]);
throw $e;
}
}
diff --git a/lib/private/Federation/CloudIdManager.php b/lib/private/Federation/CloudIdManager.php
index 77bb9437ba2..e4e42cb1293 100644
--- a/lib/private/Federation/CloudIdManager.php
+++ b/lib/private/Federation/CloudIdManager.php
@@ -30,11 +30,17 @@ declare(strict_types=1);
*/
namespace OC\Federation;
+use OCA\DAV\Events\CardUpdatedEvent;
use OCP\Contacts\IManager;
+use OCP\EventDispatcher\Event;
+use OCP\EventDispatcher\IEventDispatcher;
use OCP\Federation\ICloudId;
use OCP\Federation\ICloudIdManager;
+use OCP\ICache;
+use OCP\ICacheFactory;
use OCP\IURLGenerator;
use OCP\IUserManager;
+use OCP\User\Events\UserChangedEvent;
class CloudIdManager implements ICloudIdManager {
/** @var IManager */
@@ -43,11 +49,48 @@ class CloudIdManager implements ICloudIdManager {
private $urlGenerator;
/** @var IUserManager */
private $userManager;
-
- public function __construct(IManager $contactsManager, IURLGenerator $urlGenerator, IUserManager $userManager) {
+ private ICache $memCache;
+ /** @var array[] */
+ private array $cache = [];
+
+ public function __construct(
+ IManager $contactsManager,
+ IURLGenerator $urlGenerator,
+ IUserManager $userManager,
+ ICacheFactory $cacheFactory,
+ IEventDispatcher $eventDispatcher
+ ) {
$this->contactsManager = $contactsManager;
$this->urlGenerator = $urlGenerator;
$this->userManager = $userManager;
+ $this->memCache = $cacheFactory->createDistributed('cloud_id_');
+ $eventDispatcher->addListener(UserChangedEvent::class, [$this, 'handleUserEvent']);
+ $eventDispatcher->addListener(CardUpdatedEvent::class, [$this, 'handleCardEvent']);
+ }
+
+ public function handleUserEvent(Event $event): void {
+ if ($event instanceof UserChangedEvent && $event->getFeature() === 'displayName') {
+ $userId = $event->getUser()->getUID();
+ $key = $userId . '@local';
+ unset($this->cache[$key]);
+ $this->memCache->remove($key);
+ }
+ }
+
+ public function handleCardEvent(Event $event): void {
+ if ($event instanceof CardUpdatedEvent) {
+ $data = $event->getCardData()['carddata'];
+ foreach (explode("\r\n", $data) as $line) {
+ if (strpos($line, "CLOUD;") === 0) {
+ $parts = explode(':', $line, 2);
+ if (isset($parts[1])) {
+ $key = $parts[1];
+ unset($this->cache[$key]);
+ $this->memCache->remove($key);
+ }
+ }
+ }
+ }
}
/**
@@ -120,18 +163,42 @@ class CloudIdManager implements ICloudIdManager {
* @return CloudId
*/
public function getCloudId(string $user, ?string $remote): ICloudId {
- if ($remote === null) {
+ $isLocal = $remote === null;
+ if ($isLocal) {
$remote = rtrim($this->removeProtocolFromUrl($this->urlGenerator->getAbsoluteURL('/')), '/');
$fixedRemote = $this->fixRemoteURL($remote);
- $localUser = $this->userManager->get($user);
- $displayName = !is_null($localUser) ? $localUser->getDisplayName() : '';
+ $host = $fixedRemote;
} else {
- // TODO check what the correct url is for remote (asking the remote)
+ // note that for remote id's we don't strip the protocol for the remote we use to construct the CloudId
+ // this way if a user has an explicit non-https cloud id this will be preserved
+ // we do still use the version without protocol for looking up the display name
$fixedRemote = $this->fixRemoteURL($remote);
$host = $this->removeProtocolFromUrl($fixedRemote);
+ }
+
+ $key = $user . '@' . ($isLocal ? 'local' : $host);
+ $cached = $this->cache[$key] ?? $this->memCache->get($key);
+ if ($cached) {
+ $this->cache[$key] = $cached; // put items from memcache into local cache
+ return new CloudId($cached['id'], $cached['user'], $cached['remote'], $cached['displayName']);
+ }
+
+ if ($isLocal) {
+ $localUser = $this->userManager->get($user);
+ $displayName = $localUser ? $localUser->getDisplayName() : '';
+ } else {
$displayName = $this->getDisplayNameFromContact($user . '@' . $host);
}
$id = $user . '@' . $remote;
+
+ $data = [
+ 'id' => $id,
+ 'user' => $user,
+ 'remote' => $fixedRemote,
+ 'displayName' => $displayName,
+ ];
+ $this->cache[$key] = $data;
+ $this->memCache->set($key, $data, 15 * 60);
return new CloudId($id, $user, $fixedRemote, $displayName);
}
diff --git a/lib/private/Files/AppData/AppData.php b/lib/private/Files/AppData/AppData.php
index 471de799c2f..237fcb42e03 100644
--- a/lib/private/Files/AppData/AppData.php
+++ b/lib/private/Files/AppData/AppData.php
@@ -26,7 +26,7 @@ declare(strict_types=1);
*/
namespace OC\Files\AppData;
-use OC\Cache\CappedMemoryCache;
+use OCP\Cache\CappedMemoryCache;
use OC\Files\SimpleFS\SimpleFolder;
use OC\SystemConfig;
use OCP\Files\Folder;
diff --git a/lib/private/Files/Cache/Cache.php b/lib/private/Files/Cache/Cache.php
index 949079dfa22..ec284282178 100644
--- a/lib/private/Files/Cache/Cache.php
+++ b/lib/private/Files/Cache/Cache.php
@@ -37,6 +37,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
+
namespace OC\Files\Cache;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
@@ -63,7 +64,7 @@ use Psr\Log\LoggerInterface;
/**
* Metadata cache for a storage
*
- * The cache stores the metadata for all files and folders in a storage and is kept up to date trough the following mechanisms:
+ * The cache stores the metadata for all files and folders in a storage and is kept up to date through the following mechanisms:
*
* - Scanner: scans the storage and updates the cache where needed
* - Watcher: checks for changes made to the filesystem outside of the Nextcloud instance and rescans files and folder when a change is detected
@@ -188,6 +189,7 @@ class Cache implements ICache {
$data['fileid'] = (int)$data['fileid'];
$data['parent'] = (int)$data['parent'];
$data['size'] = 0 + $data['size'];
+ $data['unencrypted_size'] = 0 + ($data['unencrypted_size'] ?? 0);
$data['mtime'] = (int)$data['mtime'];
$data['storage_mtime'] = (int)$data['storage_mtime'];
$data['encryptedVersion'] = (int)$data['encrypted'];
@@ -428,7 +430,7 @@ class Cache implements ICache {
protected function normalizeData(array $data): array {
$fields = [
'path', 'parent', 'name', 'mimetype', 'size', 'mtime', 'storage_mtime', 'encrypted',
- 'etag', 'permissions', 'checksum', 'storage'];
+ 'etag', 'permissions', 'checksum', 'storage', 'unencrypted_size'];
$extensionFields = ['metadata_etag', 'creation_time', 'upload_time'];
$doNotCopyStorageMTime = false;
@@ -538,7 +540,7 @@ class Cache implements ICache {
public function remove($file) {
$entry = $this->get($file);
- if ($entry) {
+ if ($entry instanceof ICacheEntry) {
$query = $this->getQueryBuilder();
$query->delete('filecache')
->whereFileId($entry->getId());
@@ -580,7 +582,7 @@ class Cache implements ICache {
$parentIds = [$entry->getId()];
$queue = [$entry->getId()];
- // we walk depth first trough the file tree, removing all filecache_extended attributes while we walk
+ // we walk depth first through the file tree, removing all filecache_extended attributes while we walk
// and collecting all folder ids to later use to delete the filecache entries
while ($entryId = array_pop($queue)) {
$children = $this->getFolderContentsById($entryId);
@@ -873,18 +875,32 @@ class Cache implements ICache {
$id = $entry['fileid'];
$query = $this->getQueryBuilder();
- $query->selectAlias($query->func()->sum('size'), 'f1')
- ->selectAlias($query->func()->min('size'), 'f2')
+ $query->select('size', 'unencrypted_size')
->from('filecache')
- ->whereStorageId($this->getNumericStorageId())
->whereParent($id);
$result = $query->execute();
- $row = $result->fetch();
+ $rows = $result->fetchAll();
$result->closeCursor();
- if ($row) {
- [$sum, $min] = array_values($row);
+ if ($rows) {
+ $sizes = array_map(function (array $row) {
+ return (int)$row['size'];
+ }, $rows);
+ $unencryptedOnlySizes = array_map(function (array $row) {
+ return (int)$row['unencrypted_size'];
+ }, $rows);
+ $unencryptedSizes = array_map(function (array $row) {
+ return (int)(($row['unencrypted_size'] > 0) ? $row['unencrypted_size'] : $row['size']);
+ }, $rows);
+
+ $sum = array_sum($sizes);
+ $min = min($sizes);
+
+ $unencryptedSum = array_sum($unencryptedSizes);
+ $unencryptedMin = min($unencryptedSizes);
+ $unencryptedMax = max($unencryptedOnlySizes);
+
$sum = 0 + $sum;
$min = 0 + $min;
if ($min === -1) {
@@ -892,8 +908,27 @@ class Cache implements ICache {
} else {
$totalSize = $sum;
}
- if ($entry['size'] !== $totalSize) {
- $this->update($id, ['size' => $totalSize]);
+ if ($unencryptedMin === -1 || $min === -1) {
+ $unencryptedTotal = $unencryptedMin;
+ } else {
+ $unencryptedTotal = $unencryptedSum;
+ }
+ } else {
+ $totalSize = 0;
+ $unencryptedTotal = 0;
+ $unencryptedMax = 0;
+ }
+ if ($entry['size'] !== $totalSize) {
+ // only set unencrypted size for a folder if any child entries have it set, or the folder is empty
+ if ($unencryptedMax > 0 || $totalSize === 0) {
+ $this->update($id, [
+ 'size' => $totalSize,
+ 'unencrypted_size' => $unencryptedTotal,
+ ]);
+ } else {
+ $this->update($id, [
+ 'size' => $totalSize,
+ ]);
}
}
}
@@ -927,7 +962,7 @@ class Cache implements ICache {
* use the one with the highest id gives the best result with the background scanner, since that is most
* likely the folder where we stopped scanning previously
*
- * @return string|bool the path of the folder or false when no folder matched
+ * @return string|false the path of the folder or false when no folder matched
*/
public function getIncomplete() {
$query = $this->getQueryBuilder();
diff --git a/lib/private/Files/Cache/CacheEntry.php b/lib/private/Files/Cache/CacheEntry.php
index 12f0273fb6e..8ac76acf6d1 100644
--- a/lib/private/Files/Cache/CacheEntry.php
+++ b/lib/private/Files/Cache/CacheEntry.php
@@ -132,4 +132,12 @@ class CacheEntry implements ICacheEntry {
public function __clone() {
$this->data = array_merge([], $this->data);
}
+
+ public function getUnencryptedSize(): int {
+ if (isset($this->data['unencrypted_size']) && $this->data['unencrypted_size'] > 0) {
+ return $this->data['unencrypted_size'];
+ } else {
+ return $this->data['size'];
+ }
+ }
}
diff --git a/lib/private/Files/Cache/CacheQueryBuilder.php b/lib/private/Files/Cache/CacheQueryBuilder.php
index b5a9101877c..496a8361d77 100644
--- a/lib/private/Files/Cache/CacheQueryBuilder.php
+++ b/lib/private/Files/Cache/CacheQueryBuilder.php
@@ -41,12 +41,16 @@ class CacheQueryBuilder extends QueryBuilder {
parent::__construct($connection, $systemConfig, $logger);
}
- public function selectFileCache(string $alias = null) {
+ public function selectFileCache(string $alias = null, bool $joinExtendedCache = true) {
$name = $alias ? $alias : 'filecache';
- $this->select("$name.fileid", 'storage', 'path', 'path_hash', "$name.parent", 'name', 'mimetype', 'mimepart', 'size', 'mtime',
- 'storage_mtime', 'encrypted', 'etag', 'permissions', 'checksum', 'metadata_etag', 'creation_time', 'upload_time')
- ->from('filecache', $name)
- ->leftJoin($name, 'filecache_extended', 'fe', $this->expr()->eq("$name.fileid", 'fe.fileid'));
+ $this->select("$name.fileid", 'storage', 'path', 'path_hash', "$name.parent", "$name.name", 'mimetype', 'mimepart', 'size', 'mtime',
+ 'storage_mtime', 'encrypted', 'etag', 'permissions', 'checksum', 'unencrypted_size')
+ ->from('filecache', $name);
+
+ if ($joinExtendedCache) {
+ $this->addSelect('metadata_etag', 'creation_time', 'upload_time');
+ $this->leftJoin($name, 'filecache_extended', 'fe', $this->expr()->eq("$name.fileid", 'fe.fileid'));
+ }
$this->alias = $name;
diff --git a/lib/private/Files/Cache/Propagator.php b/lib/private/Files/Cache/Propagator.php
index 270b2b013f5..2909e998bf9 100644
--- a/lib/private/Files/Cache/Propagator.php
+++ b/lib/private/Files/Cache/Propagator.php
@@ -24,6 +24,7 @@
namespace OC\Files\Cache;
+use OC\Files\Storage\Wrapper\Encryption;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Files\Cache\IPropagator;
use OCP\Files\Storage\IReliableEtagStorage;
@@ -65,7 +66,7 @@ class Propagator implements IPropagator {
* @param int $sizeDifference number of bytes the file has grown
*/
public function propagateChange($internalPath, $time, $sizeDifference = 0) {
- // Do not propogate changes in ignored paths
+ // Do not propagate changes in ignored paths
foreach ($this->ignore as $ignore) {
if (strpos($internalPath, $ignore) === 0) {
return;
@@ -113,6 +114,20 @@ class Propagator implements IPropagator {
->andWhere($builder->expr()->in('path_hash', $hashParams))
->andWhere($builder->expr()->gt('size', $builder->expr()->literal(-1, IQueryBuilder::PARAM_INT)));
+ if ($this->storage->instanceOfStorage(Encryption::class)) {
+ // in case of encryption being enabled after some files are already uploaded, some entries will have an unencrypted_size of 0 and a non-zero size
+ $eq = $builder->expr()->eq('unencrypted_size', $builder->expr()->literal(0, IQueryBuilder::PARAM_INT));
+ $sizeColumn = $builder->getColumnName('size');
+ $unencryptedSizeColumn = $builder->getColumnName('unencrypted_size');
+ $builder->set('unencrypted_size', $builder->func()->greatest(
+ $builder->func()->add(
+ $builder->createFunction("CASE WHEN $eq THEN $unencryptedSizeColumn ELSE $sizeColumn END"),
+ $builder->createNamedParameter($sizeDifference)
+ ),
+ $builder->createNamedParameter(-1, IQueryBuilder::PARAM_INT)
+ ));
+ }
+
$builder->execute();
}
}
diff --git a/lib/private/Files/Cache/QuerySearchHelper.php b/lib/private/Files/Cache/QuerySearchHelper.php
index 3bf9abf3524..3529ede9746 100644
--- a/lib/private/Files/Cache/QuerySearchHelper.php
+++ b/lib/private/Files/Cache/QuerySearchHelper.php
@@ -28,6 +28,7 @@ namespace OC\Files\Cache;
use OC\Files\Search\QueryOptimizer\QueryOptimizer;
use OC\Files\Search\SearchBinaryOperator;
use OC\SystemConfig;
+use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Files\Cache\ICache;
use OCP\Files\Cache\ICacheEntry;
use OCP\Files\IMimeTypeLoader;
@@ -102,7 +103,7 @@ class QuerySearchHelper {
$builder = $this->getQueryBuilder();
- $query = $builder->selectFileCache('file');
+ $query = $builder->selectFileCache('file', false);
if ($this->searchBuilder->shouldJoinTags($searchQuery->getSearchOperation())) {
$user = $searchQuery->getUser();
@@ -110,13 +111,21 @@ class QuerySearchHelper {
throw new \InvalidArgumentException("Searching by tag requires the user to be set in the query");
}
$query
- ->innerJoin('file', 'vcategory_to_object', 'tagmap', $builder->expr()->eq('file.fileid', 'tagmap.objid'))
- ->innerJoin('tagmap', 'vcategory', 'tag', $builder->expr()->andX(
+ ->leftJoin('file', 'vcategory_to_object', 'tagmap', $builder->expr()->eq('file.fileid', 'tagmap.objid'))
+ ->leftJoin('tagmap', 'vcategory', 'tag', $builder->expr()->andX(
$builder->expr()->eq('tagmap.type', 'tag.type'),
- $builder->expr()->eq('tagmap.categoryid', 'tag.id')
+ $builder->expr()->eq('tagmap.categoryid', 'tag.id'),
+ $builder->expr()->eq('tag.type', $builder->createNamedParameter('files')),
+ $builder->expr()->eq('tag.uid', $builder->createNamedParameter($user->getUID()))
))
- ->andWhere($builder->expr()->eq('tag.type', $builder->createNamedParameter('files')))
- ->andWhere($builder->expr()->eq('tag.uid', $builder->createNamedParameter($user->getUID())));
+ ->leftJoin('file', 'systemtag_object_mapping', 'systemtagmap', $builder->expr()->andX(
+ $builder->expr()->eq('file.fileid', $builder->expr()->castColumn('systemtagmap.objectid', IQueryBuilder::PARAM_INT)),
+ $builder->expr()->eq('systemtagmap.objecttype', $builder->createNamedParameter('files'))
+ ))
+ ->leftJoin('systemtagmap', 'systemtag', 'systemtag', $builder->expr()->andX(
+ $builder->expr()->eq('systemtag.id', 'systemtagmap.systemtagid'),
+ $builder->expr()->eq('systemtag.visibility', $builder->createNamedParameter(true))
+ ));
}
$storageFilters = array_values(array_map(function (ICache $cache) {
@@ -149,7 +158,7 @@ class QuerySearchHelper {
$result->closeCursor();
- // loop trough all caches for each result to see if the result matches that storage
+ // loop through all caches for each result to see if the result matches that storage
// results are grouped by the same array keys as the caches argument to allow the caller to distringuish the source of the results
$results = array_fill_keys(array_keys($caches), []);
foreach ($rawEntries as $rawEntry) {
diff --git a/lib/private/Files/Cache/Scanner.php b/lib/private/Files/Cache/Scanner.php
index fb32b64c012..4799c3bff7d 100644
--- a/lib/private/Files/Cache/Scanner.php
+++ b/lib/private/Files/Cache/Scanner.php
@@ -339,7 +339,7 @@ class Scanner extends BasicEmitter implements IScanner {
try {
$data = $this->scanFile($path, $reuse, -1, null, $lock);
if ($data and $data['mimetype'] === 'httpd/unix-directory') {
- $size = $this->scanChildren($path, $recursive, $reuse, $data['fileid'], $lock);
+ $size = $this->scanChildren($path, $recursive, $reuse, $data['fileid'], $lock, $data);
$data['size'] = $size;
}
} finally {
@@ -376,9 +376,10 @@ class Scanner extends BasicEmitter implements IScanner {
* @param int $reuse
* @param int $folderId id for the folder to be scanned
* @param bool $lock set to false to disable getting an additional read lock during scanning
+ * @param array $data the data of the folder before (re)scanning the children
* @return int the size of the scanned folder or -1 if the size is unknown at this stage
*/
- protected function scanChildren($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1, $folderId = null, $lock = true) {
+ protected function scanChildren($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1, $folderId = null, $lock = true, array $data = []) {
if ($reuse === -1) {
$reuse = ($recursive === self::SCAN_SHALLOW) ? self::REUSE_ETAG | self::REUSE_SIZE : self::REUSE_ETAG;
}
@@ -397,7 +398,8 @@ class Scanner extends BasicEmitter implements IScanner {
$size += $childSize;
}
}
- if ($this->cacheActive) {
+ $oldSize = $data['size'] ?? null;
+ if ($this->cacheActive && $oldSize !== $size) {
$this->cache->update($folderId, ['size' => $size]);
}
$this->emit('\OC\Files\Cache\Scanner', 'postScanFolder', [$path, $this->storageId]);
@@ -409,6 +411,11 @@ class Scanner extends BasicEmitter implements IScanner {
$existingChildren = $this->getExistingChildren($folderId);
$newChildren = iterator_to_array($this->storage->getDirectoryContent($path));
+ if (count($existingChildren) === 0 && count($newChildren) === 0) {
+ // no need to do a transaction
+ return [];
+ }
+
if ($this->useTransactions) {
\OC::$server->getDatabaseConnection()->beginTransaction();
}
diff --git a/lib/private/Files/Cache/SearchBuilder.php b/lib/private/Files/Cache/SearchBuilder.php
index c8c442bcb8c..1a8c3637063 100644
--- a/lib/private/Files/Cache/SearchBuilder.php
+++ b/lib/private/Files/Cache/SearchBuilder.php
@@ -54,7 +54,7 @@ class SearchBuilder {
ISearchComparison::COMPARE_GREATER_THAN => 'lte',
ISearchComparison::COMPARE_GREATER_THAN_EQUAL => 'lt',
ISearchComparison::COMPARE_LESS_THAN => 'gte',
- ISearchComparison::COMPARE_LESS_THAN_EQUAL => 'lt',
+ ISearchComparison::COMPARE_LESS_THAN_EQUAL => 'gt',
];
public const TAG_FAVORITE = '_$!<Favorite>!$_';
@@ -80,7 +80,7 @@ class SearchBuilder {
return $shouldJoin || $this->shouldJoinTags($operator);
}, false);
} elseif ($operator instanceof ISearchComparison) {
- return $operator->getField() === 'tagname' || $operator->getField() === 'favorite';
+ return $operator->getField() === 'tagname' || $operator->getField() === 'favorite' || $operator->getField() === 'systemtag';
}
return false;
}
@@ -163,8 +163,12 @@ class SearchBuilder {
} elseif ($field === 'favorite') {
$field = 'tag.category';
$value = self::TAG_FAVORITE;
+ } elseif ($field === 'name') {
+ $field = 'file.name';
} elseif ($field === 'tagname') {
$field = 'tag.category';
+ } elseif ($field === 'systemtag') {
+ $field = 'systemtag.name';
} elseif ($field === 'fileid') {
$field = 'file.fileid';
} elseif ($field === 'path' && $type === ISearchComparison::COMPARE_EQUAL && $operator->getQueryHint(ISearchComparison::HINT_PATH_EQ_HASH, true)) {
@@ -182,6 +186,7 @@ class SearchBuilder {
'path' => 'string',
'size' => 'integer',
'tagname' => 'string',
+ 'systemtag' => 'string',
'favorite' => 'boolean',
'fileid' => 'integer',
'storage' => 'integer',
@@ -193,6 +198,7 @@ class SearchBuilder {
'path' => ['eq', 'like', 'clike'],
'size' => ['eq', 'gt', 'lt', 'gte', 'lte'],
'tagname' => ['eq', 'like'],
+ 'systemtag' => ['eq', 'like'],
'favorite' => ['eq'],
'fileid' => ['eq'],
'storage' => ['eq'],
diff --git a/lib/private/Files/Cache/Storage.php b/lib/private/Files/Cache/Storage.php
index fb9e5500658..f77c9b71dd7 100644
--- a/lib/private/Files/Cache/Storage.php
+++ b/lib/private/Files/Cache/Storage.php
@@ -40,7 +40,7 @@ use Psr\Log\LoggerInterface;
* a string id which is generated by the storage backend and reflects the configuration of the storage (e.g. 'smb://user@host/share')
* and a numeric storage id which is referenced in the file cache
*
- * A mapping between the two storage ids is stored in the database and accessible trough this class
+ * A mapping between the two storage ids is stored in the database and accessible through this class
*
* @package OC\Files\Cache
*/
@@ -135,7 +135,7 @@ class Storage {
* Get the numeric of the storage with the provided string id
*
* @param $storageId
- * @return int|null either the numeric storage id or null if the storage id is not knwon
+ * @return int|null either the numeric storage id or null if the storage id is not known
*/
public static function getNumericStorageId($storageId) {
$storageId = self::adjustStorageId($storageId);
diff --git a/lib/private/Files/Cache/StorageGlobal.php b/lib/private/Files/Cache/StorageGlobal.php
index a898c435415..74cbd5abdb2 100644
--- a/lib/private/Files/Cache/StorageGlobal.php
+++ b/lib/private/Files/Cache/StorageGlobal.php
@@ -33,7 +33,7 @@ use OCP\IDBConnection;
* a string id which is generated by the storage backend and reflects the configuration of the storage (e.g. 'smb://user@host/share')
* and a numeric storage id which is referenced in the file cache
*
- * A mapping between the two storage ids is stored in the database and accessible trough this class
+ * A mapping between the two storage ids is stored in the database and accessible through this class
*
* @package OC\Files\Cache
*/
diff --git a/lib/private/Files/Cache/Updater.php b/lib/private/Files/Cache/Updater.php
index 98fb51fe264..f8c187996e6 100644
--- a/lib/private/Files/Cache/Updater.php
+++ b/lib/private/Files/Cache/Updater.php
@@ -73,14 +73,14 @@ class Updater implements IUpdater {
}
/**
- * Disable updating the cache trough this updater
+ * Disable updating the cache through this updater
*/
public function disable() {
$this->enabled = false;
}
/**
- * Re-enable the updating of the cache trough this updater
+ * Re-enable the updating of the cache through this updater
*/
public function enable() {
$this->enabled = true;
diff --git a/lib/private/Files/Cache/Wrapper/CacheJail.php b/lib/private/Files/Cache/Wrapper/CacheJail.php
index 7183a6c0d2a..996f0c02603 100644
--- a/lib/private/Files/Cache/Wrapper/CacheJail.php
+++ b/lib/private/Files/Cache/Wrapper/CacheJail.php
@@ -267,7 +267,7 @@ class CacheJail extends CacheWrapper {
* use the one with the highest id gives the best result with the background scanner, since that is most
* likely the folder where we stopped scanning previously
*
- * @return string|bool the path of the folder or false when no folder matched
+ * @return string|false the path of the folder or false when no folder matched
*/
public function getIncomplete() {
// not supported
@@ -306,10 +306,14 @@ class CacheJail extends CacheWrapper {
}
public function getQueryFilterForStorage(): ISearchOperator {
+ return $this->addJailFilterQuery($this->getCache()->getQueryFilterForStorage());
+ }
+
+ protected function addJailFilterQuery(ISearchOperator $filter): ISearchOperator {
if ($this->getGetUnjailedRoot() !== '' && $this->getGetUnjailedRoot() !== '/') {
return new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND,
[
- $this->getCache()->getQueryFilterForStorage(),
+ $filter,
new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR,
[
new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', $this->getGetUnjailedRoot()),
@@ -319,16 +323,18 @@ class CacheJail extends CacheWrapper {
]
);
} else {
- return $this->getCache()->getQueryFilterForStorage();
+ return $filter;
}
}
public function getCacheEntryFromSearchResult(ICacheEntry $rawEntry): ?ICacheEntry {
- $rawEntry = $this->getCache()->getCacheEntryFromSearchResult($rawEntry);
- if ($rawEntry) {
- $jailedPath = $this->getJailedPath($rawEntry->getPath());
- if ($jailedPath !== null) {
- return $this->formatCacheEntry(clone $rawEntry) ?: null;
+ if ($this->getGetUnjailedRoot() === '' || strpos($rawEntry->getPath(), $this->getGetUnjailedRoot()) === 0) {
+ $rawEntry = $this->getCache()->getCacheEntryFromSearchResult($rawEntry);
+ if ($rawEntry) {
+ $jailedPath = $this->getJailedPath($rawEntry->getPath());
+ if ($jailedPath !== null) {
+ return $this->formatCacheEntry(clone $rawEntry) ?: null;
+ }
}
}
diff --git a/lib/private/Files/Cache/Wrapper/CacheWrapper.php b/lib/private/Files/Cache/Wrapper/CacheWrapper.php
index e5300dc75f5..66ae83fd144 100644
--- a/lib/private/Files/Cache/Wrapper/CacheWrapper.php
+++ b/lib/private/Files/Cache/Wrapper/CacheWrapper.php
@@ -267,7 +267,7 @@ class CacheWrapper extends Cache {
* use the one with the highest id gives the best result with the background scanner, since that is most
* likely the folder where we stopped scanning previously
*
- * @return string|bool the path of the folder or false when no folder matched
+ * @return string|false the path of the folder or false when no folder matched
*/
public function getIncomplete() {
return $this->getCache()->getIncomplete();
diff --git a/lib/private/Files/Config/UserMountCache.php b/lib/private/Files/Config/UserMountCache.php
index c326eeb0b6c..3540b563742 100644
--- a/lib/private/Files/Config/UserMountCache.php
+++ b/lib/private/Files/Config/UserMountCache.php
@@ -28,7 +28,7 @@
*/
namespace OC\Files\Config;
-use OC\Cache\CappedMemoryCache;
+use OCP\Cache\CappedMemoryCache;
use OCA\Files_Sharing\SharedMount;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Files\Config\ICachedMountFileInfo;
@@ -42,7 +42,7 @@ use OCP\IUserManager;
use Psr\Log\LoggerInterface;
/**
- * Cache mounts points per user in the cache so we can easilly look them up
+ * Cache mounts points per user in the cache so we can easily look them up
*/
class UserMountCache implements IUserMountCache {
private IDBConnection $connection;
@@ -90,7 +90,12 @@ class UserMountCache implements IUserMountCache {
$cachedMounts = $this->getMountsForUser($user);
if (is_array($mountProviderClasses)) {
- $cachedMounts = array_filter($cachedMounts, function (ICachedMountInfo $mountInfo) use ($mountProviderClasses) {
+ $cachedMounts = array_filter($cachedMounts, function (ICachedMountInfo $mountInfo) use ($mountProviderClasses, $newMounts) {
+ // for existing mounts that didn't have a mount provider set
+ // we still want the ones that map to new mounts
+ if ($mountInfo->getMountProvider() === '' && isset($newMounts[$mountInfo->getRootId()])) {
+ return true;
+ }
return in_array($mountInfo->getMountProvider(), $mountProviderClasses);
});
}
diff --git a/lib/private/Files/FileInfo.php b/lib/private/Files/FileInfo.php
index 6389544184f..47c893ebbf1 100644
--- a/lib/private/Files/FileInfo.php
+++ b/lib/private/Files/FileInfo.php
@@ -101,7 +101,11 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess {
$this->data = $data;
$this->mount = $mount;
$this->owner = $owner;
- $this->rawSize = $this->data['size'] ?? 0;
+ if (isset($this->data['unencrypted_size']) && $this->data['unencrypted_size'] !== 0) {
+ $this->rawSize = $this->data['unencrypted_size'];
+ } else {
+ $this->rawSize = $this->data['size'] ?? 0;
+ }
}
public function offsetSet($offset, $value): void {
@@ -208,7 +212,12 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess {
public function getSize($includeMounts = true) {
if ($includeMounts) {
$this->updateEntryfromSubMounts();
- return isset($this->data['size']) ? 0 + $this->data['size'] : 0;
+
+ if (isset($this->data['unencrypted_size']) && $this->data['unencrypted_size'] > 0) {
+ return $this->data['unencrypted_size'];
+ } else {
+ return isset($this->data['size']) ? 0 + $this->data['size'] : 0;
+ }
} else {
return $this->rawSize;
}
@@ -386,14 +395,26 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess {
* @param string $entryPath full path of the child entry
*/
public function addSubEntry($data, $entryPath) {
- $this->data['size'] += isset($data['size']) ? $data['size'] : 0;
+ if (!$data) {
+ return;
+ }
+ $hasUnencryptedSize = isset($data['unencrypted_size']) && $data['unencrypted_size'] > 0;
+ if ($hasUnencryptedSize) {
+ $subSize = $data['unencrypted_size'];
+ } else {
+ $subSize = $data['size'] ?: 0;
+ }
+ $this->data['size'] += $subSize;
+ if ($hasUnencryptedSize) {
+ $this->data['unencrypted_size'] += $subSize;
+ }
if (isset($data['mtime'])) {
$this->data['mtime'] = max($this->data['mtime'], $data['mtime']);
}
if (isset($data['etag'])) {
// prefix the etag with the relative path of the subentry to propagate etag on mount moves
$relativeEntryPath = substr($entryPath, strlen($this->getPath()));
- // attach the permissions to propagate etag on permision changes of submounts
+ // attach the permissions to propagate etag on permission changes of submounts
$permissions = isset($data['permissions']) ? $data['permissions'] : 0;
$this->childEtags[] = $relativeEntryPath . '/' . $data['etag'] . $permissions;
}
diff --git a/lib/private/Files/Filesystem.php b/lib/private/Files/Filesystem.php
index 20b44e2736a..9542666b03c 100644
--- a/lib/private/Files/Filesystem.php
+++ b/lib/private/Files/Filesystem.php
@@ -37,7 +37,7 @@
*/
namespace OC\Files;
-use OC\Cache\CappedMemoryCache;
+use OCP\Cache\CappedMemoryCache;
use OC\Files\Mount\MountPoint;
use OC\User\NoUserException;
use OCP\EventDispatcher\IEventDispatcher;
diff --git a/lib/private/Files/Mount/Manager.php b/lib/private/Files/Mount/Manager.php
index 69285018d17..9ba0e504058 100644
--- a/lib/private/Files/Mount/Manager.php
+++ b/lib/private/Files/Mount/Manager.php
@@ -29,7 +29,7 @@ declare(strict_types=1);
namespace OC\Files\Mount;
-use OC\Cache\CappedMemoryCache;
+use OCP\Cache\CappedMemoryCache;
use OC\Files\Filesystem;
use OC\Files\SetupManager;
use OC\Files\SetupManagerFactory;
diff --git a/lib/private/Files/Mount/MoveableMount.php b/lib/private/Files/Mount/MoveableMount.php
index 7ebb7fa2c87..a7372153d75 100644
--- a/lib/private/Files/Mount/MoveableMount.php
+++ b/lib/private/Files/Mount/MoveableMount.php
@@ -37,7 +37,6 @@ interface MoveableMount {
/**
* Remove the mount points
*
- * @return mixed
* @return bool
*/
public function removeMount();
diff --git a/lib/private/Files/Node/File.php b/lib/private/Files/Node/File.php
index e125715e6a8..d8a6741dc6e 100644
--- a/lib/private/Files/Node/File.php
+++ b/lib/private/Files/Node/File.php
@@ -131,7 +131,6 @@ class File extends Node implements \OCP\Files\File {
$this->view->unlink($this->path);
$nonExisting = new NonExistingFile($this->root, $this->view, $this->path, $fileInfo);
$this->sendHooks(['postDelete'], [$nonExisting]);
- $this->exists = false;
$this->fileInfo = null;
} else {
throw new NotPermittedException();
diff --git a/lib/private/Files/Node/Folder.php b/lib/private/Files/Node/Folder.php
index 9c15f0edf41..fb3c78bb801 100644
--- a/lib/private/Files/Node/Folder.php
+++ b/lib/private/Files/Node/Folder.php
@@ -101,9 +101,9 @@ class Folder extends Node implements \OCP\Files\Folder {
return array_map(function (FileInfo $info) {
if ($info->getMimetype() === FileInfo::MIMETYPE_FOLDER) {
- return new Folder($this->root, $this->view, $info->getPath(), $info);
+ return new Folder($this->root, $this->view, $info->getPath(), $info, $this);
} else {
- return new File($this->root, $this->view, $info->getPath(), $info);
+ return new File($this->root, $this->view, $info->getPath(), $info, $this);
}
}, $folderContent);
}
@@ -119,10 +119,11 @@ class Folder extends Node implements \OCP\Files\Folder {
} else {
$isDir = $info->getType() === FileInfo::TYPE_FOLDER;
}
+ $parent = dirname($path) === $this->getPath() ? $this : null;
if ($isDir) {
- return new Folder($this->root, $this->view, $path, $info);
+ return new Folder($this->root, $this->view, $path, $info, $parent);
} else {
- return new File($this->root, $this->view, $path, $info);
+ return new File($this->root, $this->view, $path, $info, $parent);
}
}
@@ -163,7 +164,8 @@ class Folder extends Node implements \OCP\Files\Folder {
if (!$this->view->mkdir($fullPath)) {
throw new NotPermittedException('Could not create folder');
}
- $node = new Folder($this->root, $this->view, $fullPath);
+ $parent = dirname($fullPath) === $this->getPath() ? $this : null;
+ $node = new Folder($this->root, $this->view, $fullPath, null, $parent);
$this->sendHooks(['postWrite', 'postCreate'], [$node]);
return $node;
} else {
@@ -193,7 +195,7 @@ class Folder extends Node implements \OCP\Files\Folder {
if ($result === false) {
throw new NotPermittedException('Could not create path');
}
- $node = new File($this->root, $this->view, $fullPath);
+ $node = new File($this->root, $this->view, $fullPath, null, $this);
$this->sendHooks(['postWrite', 'postCreate'], [$node]);
return $node;
}
@@ -262,7 +264,7 @@ class Folder extends Node implements \OCP\Files\Folder {
$searchHelper = \OC::$server->get(QuerySearchHelper::class);
$resultsPerCache = $searchHelper->searchInCaches($query, $caches);
- // loop trough all results per-cache, constructing the FileInfo object from the CacheEntry and merge them all
+ // loop through all results per-cache, constructing the FileInfo object from the CacheEntry and merge them all
$files = array_merge(...array_map(function (array $results, $relativeMountPoint) use ($mountByMountPoint) {
$mount = $mountByMountPoint[$relativeMountPoint];
return array_map(function (ICacheEntry $result) use ($relativeMountPoint, $mount) {
@@ -296,7 +298,7 @@ class Folder extends Node implements \OCP\Files\Folder {
private function cacheEntryToFileInfo(IMountPoint $mount, string $appendRoot, ICacheEntry $cacheEntry): FileInfo {
$cacheEntry['internalPath'] = $cacheEntry['path'];
- $cacheEntry['path'] = $appendRoot . $cacheEntry->getPath();
+ $cacheEntry['path'] = rtrim($appendRoot . $cacheEntry->getPath(), '/');
$subPath = $cacheEntry['path'] !== '' ? '/' . $cacheEntry['path'] : '';
return new \OC\Files\FileInfo($this->path . $subPath, $mount->getStorage(), $cacheEntry['internalPath'], $cacheEntry, $mount);
}
@@ -388,7 +390,6 @@ class Folder extends Node implements \OCP\Files\Folder {
$this->view->rmdir($this->path);
$nonExisting = new NonExistingFolder($this->root, $this->view, $this->path, $fileInfo);
$this->sendHooks(['postDelete'], [$nonExisting]);
- $this->exists = false;
} else {
throw new NotPermittedException('No delete permission for path');
}
diff --git a/lib/private/Files/Node/Node.php b/lib/private/Files/Node/Node.php
index c8975154059..bfa4d6861ea 100644
--- a/lib/private/Files/Node/Node.php
+++ b/lib/private/Files/Node/Node.php
@@ -62,16 +62,22 @@ class Node implements \OCP\Files\Node {
protected $fileInfo;
/**
+ * @var Node|null
+ */
+ protected $parent;
+
+ /**
* @param \OC\Files\View $view
* @param \OCP\Files\IRootFolder $root
* @param string $path
* @param FileInfo $fileInfo
*/
- public function __construct($root, $view, $path, $fileInfo = null) {
+ public function __construct($root, $view, $path, $fileInfo = null, ?Node $parent = null) {
$this->view = $view;
$this->root = $root;
$this->path = $path;
$this->fileInfo = $fileInfo;
+ $this->parent = $parent;
}
/**
@@ -278,11 +284,16 @@ class Node implements \OCP\Files\Node {
* @return Node
*/
public function getParent() {
- $newPath = dirname($this->path);
- if ($newPath === '' || $newPath === '.' || $newPath === '/') {
- return $this->root;
+ if ($this->parent === null) {
+ $newPath = dirname($this->path);
+ if ($newPath === '' || $newPath === '.' || $newPath === '/') {
+ return $this->root;
+ }
+
+ $this->parent = $this->root->get($newPath);
}
- return $this->root->get($newPath);
+
+ return $this->parent;
}
/**
diff --git a/lib/private/Files/Node/Root.php b/lib/private/Files/Node/Root.php
index 6dd65a4291d..ca930c1002c 100644
--- a/lib/private/Files/Node/Root.php
+++ b/lib/private/Files/Node/Root.php
@@ -32,7 +32,7 @@
namespace OC\Files\Node;
-use OC\Cache\CappedMemoryCache;
+use OCP\Cache\CappedMemoryCache;
use OC\Files\FileInfo;
use OC\Files\Mount\Manager;
use OC\Files\Mount\MountPoint;
@@ -427,7 +427,7 @@ class Root extends Folder implements IRootFolder {
$mountsContainingFile = $mountCache->getMountsForFileId($id, $user);
}
- // when a user has access trough the same storage trough multiple paths
+ // when a user has access through the same storage through multiple paths
// (such as an external storage that is both mounted for a user and shared to the user)
// the mount cache will only hold a single entry for the storage
// this can lead to issues as the different ways the user has access to a storage can have different permissions
diff --git a/lib/private/Files/ObjectStore/NoopScanner.php b/lib/private/Files/ObjectStore/NoopScanner.php
index 3b8cbdb18bb..bdfc93758d4 100644
--- a/lib/private/Files/ObjectStore/NoopScanner.php
+++ b/lib/private/Files/ObjectStore/NoopScanner.php
@@ -68,7 +68,7 @@ class NoopScanner extends Scanner {
* @param array $folderData existing cache data for the folder to be scanned
* @return int the size of the scanned folder or -1 if the size is unknown at this stage
*/
- protected function scanChildren($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1, $folderId = null, $lock = true) {
+ protected function scanChildren($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1, $folderId = null, $lock = true, array $data = []) {
return 0;
}
diff --git a/lib/private/Files/ObjectStore/ObjectStoreStorage.php b/lib/private/Files/ObjectStore/ObjectStoreStorage.php
index adb3928b28a..898f64d97c2 100644
--- a/lib/private/Files/ObjectStore/ObjectStoreStorage.php
+++ b/lib/private/Files/ObjectStore/ObjectStoreStorage.php
@@ -335,6 +335,7 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common {
$handle = fopen($tmpFile, $mode);
return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
$this->writeBack($tmpFile, $path);
+ unlink($tmpFile);
});
case 'a':
case 'ab':
@@ -352,6 +353,7 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common {
$handle = fopen($tmpFile, $mode);
return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
$this->writeBack($tmpFile, $path);
+ unlink($tmpFile);
});
}
return false;
diff --git a/lib/private/Files/ObjectStore/S3ConnectionTrait.php b/lib/private/Files/ObjectStore/S3ConnectionTrait.php
index c3836749c6d..8286321450d 100644
--- a/lib/private/Files/ObjectStore/S3ConnectionTrait.php
+++ b/lib/private/Files/ObjectStore/S3ConnectionTrait.php
@@ -28,6 +28,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
+
namespace OC\Files\ObjectStore;
use Aws\ClientResolver;
@@ -121,15 +122,6 @@ trait S3ConnectionTrait {
)
);
- // since we store the certificate bundles on the primary storage, we can't get the bundle while setting up the primary storage
- if (!isset($this->params['primary_storage'])) {
- /** @var ICertificateManager $certManager */
- $certManager = \OC::$server->get(ICertificateManager::class);
- $certPath = $certManager->getAbsoluteBundlePath();
- } else {
- $certPath = \OC::$SERVERROOT . '/resources/config/ca-bundle.crt';
- }
-
$options = [
'version' => isset($this->params['version']) ? $this->params['version'] : 'latest',
'credentials' => $provider,
@@ -139,7 +131,7 @@ trait S3ConnectionTrait {
'signature_provider' => \Aws\or_chain([self::class, 'legacySignatureProvider'], ClientResolver::_default_signature_provider()),
'csm' => false,
'use_arn_region' => false,
- 'http' => ['verify' => $certPath],
+ 'http' => ['verify' => $this->getCertificateBundlePath()],
];
if ($this->getProxy()) {
$options['http']['proxy'] = $this->getProxy();
@@ -152,7 +144,7 @@ trait S3ConnectionTrait {
if (!$this->connection::isBucketDnsCompatible($this->bucket)) {
$logger = \OC::$server->get(LoggerInterface::class);
$logger->debug('Bucket "' . $this->bucket . '" This bucket name is not dns compatible, it may contain invalid characters.',
- ['app' => 'objectstore']);
+ ['app' => 'objectstore']);
}
if ($this->params['verify_bucket_exists'] && !$this->connection->doesBucketExist($this->bucket)) {
@@ -203,7 +195,7 @@ trait S3ConnectionTrait {
/**
* This function creates a credential provider based on user parameter file
*/
- protected function paramCredentialProvider() : callable {
+ protected function paramCredentialProvider(): callable {
return function () {
$key = empty($this->params['key']) ? null : $this->params['key'];
$secret = empty($this->params['secret']) ? null : $this->params['secret'];
@@ -218,4 +210,19 @@ trait S3ConnectionTrait {
return new RejectedPromise(new CredentialsException($msg));
};
}
+
+ protected function getCertificateBundlePath(): ?string {
+ if ((int)($this->params['use_nextcloud_bundle'] ?? "0")) {
+ // since we store the certificate bundles on the primary storage, we can't get the bundle while setting up the primary storage
+ if (!isset($this->params['primary_storage'])) {
+ /** @var ICertificateManager $certManager */
+ $certManager = \OC::$server->get(ICertificateManager::class);
+ return $certManager->getAbsoluteBundlePath();
+ } else {
+ return \OC::$SERVERROOT . '/resources/config/ca-bundle.crt';
+ }
+ } else {
+ return null;
+ }
+ }
}
diff --git a/lib/private/Files/ObjectStore/S3ObjectTrait.php b/lib/private/Files/ObjectStore/S3ObjectTrait.php
index 4e54a26e98a..9d692e01a23 100644
--- a/lib/private/Files/ObjectStore/S3ObjectTrait.php
+++ b/lib/private/Files/ObjectStore/S3ObjectTrait.php
@@ -43,6 +43,8 @@ trait S3ObjectTrait {
*/
abstract protected function getConnection();
+ abstract protected function getCertificateBundlePath(): ?string;
+
/**
* @param string $urn the unified resource name used to identify the object
* @return resource stream with the read data
@@ -67,8 +69,14 @@ trait S3ObjectTrait {
'http' => [
'protocol_version' => $request->getProtocolVersion(),
'header' => $headers,
- ],
+ ]
];
+ $bundle = $this->getCertificateBundlePath();
+ if ($bundle) {
+ $opts['ssl'] = [
+ 'cafile' => $bundle
+ ];
+ }
if ($this->getProxy()) {
$opts['http']['proxy'] = $this->getProxy();
diff --git a/lib/private/Files/SetupManager.php b/lib/private/Files/SetupManager.php
index 040ba6b898f..5782a5a72a6 100644
--- a/lib/private/Files/SetupManager.php
+++ b/lib/private/Files/SetupManager.php
@@ -82,6 +82,7 @@ class SetupManager {
private IConfig $config;
private bool $listeningForProviders;
private array $fullSetupRequired = [];
+ private bool $setupBuiltinWrappersDone = false;
public function __construct(
IEventLogger $eventLogger,
@@ -121,6 +122,15 @@ class SetupManager {
}
private function setupBuiltinWrappers() {
+ if ($this->setupBuiltinWrappersDone) {
+ return;
+ }
+ $this->setupBuiltinWrappersDone = true;
+
+ // load all filesystem apps before, so no setup-hook gets lost
+ OC_App::loadApps(['filesystem']);
+ $prevLogging = Filesystem::logWarningWhenAddingStorageWrapper(false);
+
Filesystem::addStorageWrapper('mount_options', function ($mountPoint, IStorage $storage, IMountPoint $mount) {
if ($storage->instanceOfStorage(Common::class)) {
$storage->setMountOptions($mount->getOptions());
@@ -188,6 +198,8 @@ class SetupManager {
}
return $storage;
});
+
+ Filesystem::logWarningWhenAddingStorageWrapper($prevLogging);
}
/**
@@ -223,6 +235,9 @@ class SetupManager {
return;
}
$this->setupUsers[] = $user->getUID();
+
+ $this->setupBuiltinWrappers();
+
$prevLogging = Filesystem::logWarningWhenAddingStorageWrapper(false);
OC_Hook::emit('OC_Filesystem', 'preSetup', ['user' => $user->getUID()]);
@@ -321,14 +336,8 @@ class SetupManager {
$this->eventLogger->start('setup_root_fs', 'Setup root filesystem');
- // load all filesystem apps before, so no setup-hook gets lost
- OC_App::loadApps(['filesystem']);
- $prevLogging = Filesystem::logWarningWhenAddingStorageWrapper(false);
-
$this->setupBuiltinWrappers();
- Filesystem::logWarningWhenAddingStorageWrapper($prevLogging);
-
$rootMounts = $this->mountProviderCollection->getRootMounts();
foreach ($rootMounts as $rootMountProvider) {
$this->mountManager->addMount($rootMountProvider);
@@ -380,13 +389,9 @@ class SetupManager {
return;
}
- // for the user's home folder, it's always the home mount
- if (rtrim($path) === "/" . $user->getUID() . "/files") {
- if ($includeChildren) {
- $this->setupForUser($user);
- } else {
- $this->oneTimeUserSetup($user);
- }
+ // for the user's home folder, and includes children we need everything always
+ if (rtrim($path) === "/" . $user->getUID() . "/files" && $includeChildren) {
+ $this->setupForUser($user);
return;
}
@@ -403,6 +408,10 @@ class SetupManager {
return;
}
+ if (!$this->isSetupStarted($user)) {
+ $this->oneTimeUserSetup($user);
+ }
+
$mounts = [];
if (!in_array($cachedMount->getMountProvider(), $setupProviders)) {
$setupProviders[] = $cachedMount->getMountProvider();
@@ -554,10 +563,10 @@ class SetupManager {
});
$genericEvents = [
- '\OCA\Circles::onCircleCreation',
- '\OCA\Circles::onCircleDestruction',
- '\OCA\Circles::onMemberNew',
- '\OCA\Circles::onMemberLeaving',
+ 'OCA\Circles\Events\CreatingCircleEvent',
+ 'OCA\Circles\Events\DestroyingCircleEvent',
+ 'OCA\Circles\Events\AddingCircleMemberEvent',
+ 'OCA\Circles\Events\RemovingCircleMemberEvent',
];
foreach ($genericEvents as $genericEvent) {
diff --git a/lib/private/Files/SimpleFS/NewSimpleFile.php b/lib/private/Files/SimpleFS/NewSimpleFile.php
index 76fc69ebbe7..b2a183b7d29 100644
--- a/lib/private/Files/SimpleFS/NewSimpleFile.php
+++ b/lib/private/Files/SimpleFS/NewSimpleFile.php
@@ -34,15 +34,12 @@ use OCP\Files\NotPermittedException;
use OCP\Files\SimpleFS\ISimpleFile;
class NewSimpleFile implements ISimpleFile {
- private $parentFolder;
- private $name;
- /** @var File|null */
- private $file = null;
+ private Folder $parentFolder;
+ private string $name;
+ private ?File $file = null;
/**
* File constructor.
- *
- * @param File $file
*/
public function __construct(Folder $parentFolder, string $name) {
$this->parentFolder = $parentFolder;
@@ -51,19 +48,15 @@ class NewSimpleFile implements ISimpleFile {
/**
* Get the name
- *
- * @return string
*/
- public function getName() {
+ public function getName(): string {
return $this->name;
}
/**
* Get the size in bytes
- *
- * @return int
*/
- public function getSize() {
+ public function getSize(): int {
if ($this->file) {
return $this->file->getSize();
} else {
@@ -73,10 +66,8 @@ class NewSimpleFile implements ISimpleFile {
/**
* Get the ETag
- *
- * @return string
*/
- public function getETag() {
+ public function getETag(): string {
if ($this->file) {
return $this->file->getEtag();
} else {
@@ -86,10 +77,8 @@ class NewSimpleFile implements ISimpleFile {
/**
* Get the last modification time
- *
- * @return int
*/
- public function getMTime() {
+ public function getMTime(): int {
if ($this->file) {
return $this->file->getMTime();
} else {
@@ -100,11 +89,10 @@ class NewSimpleFile implements ISimpleFile {
/**
* Get the content
*
- * @return string
* @throws NotFoundException
* @throws NotPermittedException
*/
- public function getContent() {
+ public function getContent(): string {
if ($this->file) {
$result = $this->file->getContent();
@@ -125,7 +113,7 @@ class NewSimpleFile implements ISimpleFile {
* @throws NotPermittedException
* @throws NotFoundException
*/
- public function putContent($data) {
+ public function putContent($data): void {
try {
if ($this->file) {
$this->file->putContent($data);
@@ -139,7 +127,7 @@ class NewSimpleFile implements ISimpleFile {
/**
* Sometimes there are some issues with the AppData. Most of them are from
- * user error. But we should handle them gracefull anyway.
+ * user error. But we should handle them gracefully anyway.
*
* If for some reason the current file can't be found. We remove it.
* Then traverse up and check all folders if they exists. This so that the
@@ -147,7 +135,7 @@ class NewSimpleFile implements ISimpleFile {
*
* @throws NotFoundException
*/
- private function checkFile() {
+ private function checkFile(): void {
$cur = $this->file;
while ($cur->stat() === false) {
@@ -171,7 +159,7 @@ class NewSimpleFile implements ISimpleFile {
*
* @throws NotPermittedException
*/
- public function delete() {
+ public function delete(): void {
if ($this->file) {
$this->file->delete();
}
@@ -182,7 +170,7 @@ class NewSimpleFile implements ISimpleFile {
*
* @return string
*/
- public function getMimeType() {
+ public function getMimeType(): string {
if ($this->file) {
return $this->file->getMimeType();
} else {
diff --git a/lib/private/Files/SimpleFS/SimpleFile.php b/lib/private/Files/SimpleFS/SimpleFile.php
index 21a2fd92dcb..a2571ac50e8 100644
--- a/lib/private/Files/SimpleFS/SimpleFile.php
+++ b/lib/private/Files/SimpleFS/SimpleFile.php
@@ -30,52 +30,37 @@ use OCP\Files\NotPermittedException;
use OCP\Files\SimpleFS\ISimpleFile;
class SimpleFile implements ISimpleFile {
+ private File $file;
- /** @var File $file */
- private $file;
-
- /**
- * File constructor.
- *
- * @param File $file
- */
public function __construct(File $file) {
$this->file = $file;
}
/**
* Get the name
- *
- * @return string
*/
- public function getName() {
+ public function getName(): string {
return $this->file->getName();
}
/**
* Get the size in bytes
- *
- * @return int
*/
- public function getSize() {
+ public function getSize(): int {
return $this->file->getSize();
}
/**
* Get the ETag
- *
- * @return string
*/
- public function getETag() {
+ public function getETag(): string {
return $this->file->getEtag();
}
/**
* Get the last modification time
- *
- * @return int
*/
- public function getMTime() {
+ public function getMTime(): int {
return $this->file->getMTime();
}
@@ -84,9 +69,8 @@ class SimpleFile implements ISimpleFile {
*
* @throws NotPermittedException
* @throws NotFoundException
- * @return string
*/
- public function getContent() {
+ public function getContent(): string {
$result = $this->file->getContent();
if ($result === false) {
@@ -103,9 +87,9 @@ class SimpleFile implements ISimpleFile {
* @throws NotPermittedException
* @throws NotFoundException
*/
- public function putContent($data) {
+ public function putContent($data): void {
try {
- return $this->file->putContent($data);
+ $this->file->putContent($data);
} catch (NotFoundException $e) {
$this->checkFile();
}
@@ -113,7 +97,7 @@ class SimpleFile implements ISimpleFile {
/**
* Sometimes there are some issues with the AppData. Most of them are from
- * user error. But we should handle them gracefull anyway.
+ * user error. But we should handle them gracefully anyway.
*
* If for some reason the current file can't be found. We remove it.
* Then traverse up and check all folders if they exists. This so that the
@@ -121,7 +105,7 @@ class SimpleFile implements ISimpleFile {
*
* @throws NotFoundException
*/
- private function checkFile() {
+ private function checkFile(): void {
$cur = $this->file;
while ($cur->stat() === false) {
@@ -145,16 +129,14 @@ class SimpleFile implements ISimpleFile {
*
* @throws NotPermittedException
*/
- public function delete() {
+ public function delete(): void {
$this->file->delete();
}
/**
* Get the MimeType
- *
- * @return string
*/
- public function getMimeType() {
+ public function getMimeType(): string {
return $this->file->getMimeType();
}
@@ -179,7 +161,7 @@ class SimpleFile implements ISimpleFile {
/**
* Open the file as stream for writing, resulting resource can be operated as stream like the result from php's own fopen
*
- * @return resource
+ * @return resource|false
* @throws \OCP\Files\NotPermittedException
* @since 14.0.0
*/
diff --git a/lib/private/Files/SimpleFS/SimpleFolder.php b/lib/private/Files/SimpleFS/SimpleFolder.php
index cd2a712019e..263c25a8873 100644
--- a/lib/private/Files/SimpleFS/SimpleFolder.php
+++ b/lib/private/Files/SimpleFS/SimpleFolder.php
@@ -29,6 +29,7 @@ use OCP\Files\Folder;
use OCP\Files\Node;
use OCP\Files\NotFoundException;
use OCP\Files\SimpleFS\ISimpleFolder;
+use OCP\Files\SimpleFS\ISimpleFile;
class SimpleFolder implements ISimpleFolder {
@@ -44,11 +45,11 @@ class SimpleFolder implements ISimpleFolder {
$this->folder = $folder;
}
- public function getName() {
+ public function getName(): string {
return $this->folder->getName();
}
- public function getDirectoryListing() {
+ public function getDirectoryListing(): array {
$listing = $this->folder->getDirectoryListing();
$fileListing = array_map(function (Node $file) {
@@ -63,15 +64,15 @@ class SimpleFolder implements ISimpleFolder {
return array_values($fileListing);
}
- public function delete() {
+ public function delete(): void {
$this->folder->delete();
}
- public function fileExists($name) {
+ public function fileExists(string $name): bool {
return $this->folder->nodeExists($name);
}
- public function getFile($name) {
+ public function getFile(string $name): ISimpleFile {
$file = $this->folder->get($name);
if (!($file instanceof File)) {
@@ -81,7 +82,7 @@ class SimpleFolder implements ISimpleFolder {
return new SimpleFile($file);
}
- public function newFile($name, $content = null) {
+ public function newFile(string $name, $content = null): ISimpleFile {
if ($content === null) {
// delay creating the file until it's written to
return new NewSimpleFile($this->folder, $name);
diff --git a/lib/private/Files/Storage/Common.php b/lib/private/Files/Storage/Common.php
index 3c970ee75f5..a7bc44e10e2 100644
--- a/lib/private/Files/Storage/Common.php
+++ b/lib/private/Files/Storage/Common.php
@@ -228,6 +228,7 @@ abstract class Common implements Storage, ILockingStorage, IWriteStreamStorage {
while ($file = readdir($dir)) {
if (!Filesystem::isIgnoredDir($file)) {
if (!$this->copy($path1 . '/' . $file, $path2 . '/' . $file)) {
+ closedir($dir);
return false;
}
}
diff --git a/lib/private/Files/Storage/Local.php b/lib/private/Files/Storage/Local.php
index ee8a8c7d161..4996572a40e 100644
--- a/lib/private/Files/Storage/Local.php
+++ b/lib/private/Files/Storage/Local.php
@@ -15,6 +15,7 @@
* @author Jörn Friedrich Dreyer <jfd@butonic.de>
* @author Klaas Freitag <freitag@owncloud.com>
* @author Lukas Reschke <lukas@statuscode.ch>
+ * @author Martin Brugnara <martin@0x6d62.eu>
* @author Michael Gapczynski <GapczynskiM@gmail.com>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Robin Appelman <robin@icewind.nl>
@@ -66,6 +67,8 @@ class Local extends \OC\Files\Storage\Common {
private IMimeTypeDetector $mimeTypeDetector;
+ private $defUMask;
+
public function __construct($arguments) {
if (!isset($arguments['datadir']) || !is_string($arguments['datadir'])) {
throw new \InvalidArgumentException('No data directory set for local storage');
@@ -84,6 +87,7 @@ class Local extends \OC\Files\Storage\Common {
$this->dataDirLength = strlen($this->realDataDir);
$this->config = \OC::$server->get(IConfig::class);
$this->mimeTypeDetector = \OC::$server->get(IMimeTypeDetector::class);
+ $this->defUMask = $this->config->getSystemValue('localstorage.umask', 0022);
}
public function __destruct() {
@@ -95,7 +99,7 @@ class Local extends \OC\Files\Storage\Common {
public function mkdir($path) {
$sourcePath = $this->getSourcePath($path);
- $oldMask = umask(022);
+ $oldMask = umask($this->defUMask);
$result = @mkdir($sourcePath, 0777, true);
umask($oldMask);
return $result;
@@ -273,7 +277,7 @@ class Local extends \OC\Files\Storage\Common {
if ($this->file_exists($path) and !$this->isUpdatable($path)) {
return false;
}
- $oldMask = umask(022);
+ $oldMask = umask($this->defUMask);
if (!is_null($mtime)) {
$result = @touch($this->getSourcePath($path), $mtime);
} else {
@@ -292,7 +296,7 @@ class Local extends \OC\Files\Storage\Common {
}
public function file_put_contents($path, $data) {
- $oldMask = umask(022);
+ $oldMask = umask($this->defUMask);
$result = file_put_contents($this->getSourcePath($path), $data);
umask($oldMask);
return $result;
@@ -365,7 +369,7 @@ class Local extends \OC\Files\Storage\Common {
if ($this->is_dir($path1)) {
return parent::copy($path1, $path2);
} else {
- $oldMask = umask(022);
+ $oldMask = umask($this->defUMask);
$result = copy($this->getSourcePath($path1), $this->getSourcePath($path2));
umask($oldMask);
return $result;
@@ -373,7 +377,7 @@ class Local extends \OC\Files\Storage\Common {
}
public function fopen($path, $mode) {
- $oldMask = umask(022);
+ $oldMask = umask($this->defUMask);
$result = fopen($this->getSourcePath($path), $mode);
umask($oldMask);
return $result;
diff --git a/lib/private/Files/Storage/Wrapper/Encoding.php b/lib/private/Files/Storage/Wrapper/Encoding.php
index d6201dc8877..ac9cc248ce6 100644
--- a/lib/private/Files/Storage/Wrapper/Encoding.php
+++ b/lib/private/Files/Storage/Wrapper/Encoding.php
@@ -28,7 +28,7 @@
*/
namespace OC\Files\Storage\Wrapper;
-use OC\Cache\CappedMemoryCache;
+use OCP\Cache\CappedMemoryCache;
use OC\Files\Filesystem;
use OCP\Files\Storage\IStorage;
use OCP\ICache;
diff --git a/lib/private/Files/Storage/Wrapper/Encryption.php b/lib/private/Files/Storage/Wrapper/Encryption.php
index 4cfe932cc9f..d5bf929101f 100644
--- a/lib/private/Files/Storage/Wrapper/Encryption.php
+++ b/lib/private/Files/Storage/Wrapper/Encryption.php
@@ -33,6 +33,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
+
namespace OC\Files\Storage\Wrapper;
use OC\Encryption\Exceptions\ModuleDoesNotExistsException;
@@ -41,6 +42,7 @@ use OC\Encryption\Util;
use OC\Files\Cache\CacheEntry;
use OC\Files\Filesystem;
use OC\Files\Mount\Manager;
+use OC\Files\ObjectStore\ObjectStoreStorage;
use OC\Files\Storage\LocalTempFileTrait;
use OC\Memcache\ArrayCache;
use OCP\Encryption\Exceptions\GenericEncryptionException;
@@ -139,28 +141,36 @@ class Encryption extends Wrapper {
$size = $this->unencryptedSize[$fullPath];
// update file cache
if ($info instanceof ICacheEntry) {
- $info = $info->getData();
$info['encrypted'] = $info['encryptedVersion'];
} else {
if (!is_array($info)) {
$info = [];
}
$info['encrypted'] = true;
+ $info = new CacheEntry($info);
}
- $info['size'] = $size;
- $this->getCache()->put($path, $info);
+ if ($size !== $info->getUnencryptedSize()) {
+ $this->getCache()->update($info->getId(), [
+ 'unencrypted_size' => $size
+ ]);
+ }
return $size;
}
if (isset($info['fileid']) && $info['encrypted']) {
- return $this->verifyUnencryptedSize($path, $info['size']);
+ return $this->verifyUnencryptedSize($path, $info->getUnencryptedSize());
}
return $this->storage->filesize($path);
}
+ /**
+ * @param string $path
+ * @param array $data
+ * @return array
+ */
private function modifyMetaData(string $path, array $data): array {
$fullPath = $this->getFullPath($path);
$info = $this->getCache()->get($path);
@@ -170,7 +180,7 @@ class Encryption extends Wrapper {
$data['size'] = $this->unencryptedSize[$fullPath];
} else {
if (isset($info['fileid']) && $info['encrypted']) {
- $data['size'] = $this->verifyUnencryptedSize($path, $info['size']);
+ $data['size'] = $this->verifyUnencryptedSize($path, $info->getUnencryptedSize());
$data['encrypted'] = true;
}
}
@@ -478,7 +488,7 @@ class Encryption extends Wrapper {
*
* @return int unencrypted size
*/
- protected function verifyUnencryptedSize($path, $unencryptedSize) {
+ protected function verifyUnencryptedSize(string $path, int $unencryptedSize): int {
$size = $this->storage->filesize($path);
$result = $unencryptedSize;
@@ -510,7 +520,7 @@ class Encryption extends Wrapper {
*
* @return int calculated unencrypted size
*/
- protected function fixUnencryptedSize($path, $size, $unencryptedSize) {
+ protected function fixUnencryptedSize(string $path, int $size, int $unencryptedSize): int {
$headerSize = $this->getHeaderSize($path);
$header = $this->getHeader($path);
$encryptionModule = $this->getEncryptionModule($path);
@@ -581,7 +591,9 @@ class Encryption extends Wrapper {
$cache = $this->storage->getCache();
if ($cache) {
$entry = $cache->get($path);
- $cache->update($entry['fileid'], ['size' => $newUnencryptedSize]);
+ $cache->update($entry['fileid'], [
+ 'unencrypted_size' => $newUnencryptedSize
+ ]);
}
return $newUnencryptedSize;
@@ -621,7 +633,12 @@ class Encryption extends Wrapper {
* @param bool $preserveMtime
* @return bool
*/
- public function moveFromStorage(Storage\IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = true) {
+ public function moveFromStorage(
+ Storage\IStorage $sourceStorage,
+ $sourceInternalPath,
+ $targetInternalPath,
+ $preserveMtime = true
+ ) {
if ($sourceStorage === $this) {
return $this->rename($sourceInternalPath, $targetInternalPath);
}
@@ -656,7 +673,13 @@ class Encryption extends Wrapper {
* @param bool $isRename
* @return bool
*/
- public function copyFromStorage(Storage\IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = false, $isRename = false) {
+ public function copyFromStorage(
+ Storage\IStorage $sourceStorage,
+ $sourceInternalPath,
+ $targetInternalPath,
+ $preserveMtime = false,
+ $isRename = false
+ ) {
// TODO clean this up once the underlying moveFromStorage in OC\Files\Storage\Wrapper\Common is fixed:
// - call $this->storage->copyFromStorage() instead of $this->copyBetweenStorage
@@ -676,7 +699,13 @@ class Encryption extends Wrapper {
* @param bool $isRename
* @param bool $keepEncryptionVersion
*/
- private function updateEncryptedVersion(Storage\IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath, $isRename, $keepEncryptionVersion) {
+ private function updateEncryptedVersion(
+ Storage\IStorage $sourceStorage,
+ $sourceInternalPath,
+ $targetInternalPath,
+ $isRename,
+ $keepEncryptionVersion
+ ) {
$isEncrypted = $this->encryptionManager->isEnabled() && $this->shouldEncrypt($targetInternalPath);
$cacheInformation = [
'encrypted' => $isEncrypted,
@@ -725,7 +754,13 @@ class Encryption extends Wrapper {
* @return bool
* @throws \Exception
*/
- private function copyBetweenStorage(Storage\IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime, $isRename) {
+ private function copyBetweenStorage(
+ Storage\IStorage $sourceStorage,
+ $sourceInternalPath,
+ $targetInternalPath,
+ $preserveMtime,
+ $isRename
+ ) {
// for versions we have nothing to do, because versions should always use the
// key from the original file. Just create a 1:1 copy and done
@@ -743,7 +778,7 @@ class Encryption extends Wrapper {
if (isset($info['encrypted']) && $info['encrypted'] === true) {
$this->updateUnencryptedSize(
$this->getFullPath($targetInternalPath),
- $info['size']
+ $info->getUnencryptedSize()
);
}
$this->updateEncryptedVersion($sourceStorage, $sourceInternalPath, $targetInternalPath, $isRename, true);
@@ -808,13 +843,6 @@ class Encryption extends Wrapper {
return (bool)$result;
}
- /**
- * get the path to a local version of the file.
- * The local version of the file can be temporary and doesn't have to be persistent across requests
- *
- * @param string $path
- * @return string
- */
public function getLocalFile($path) {
if ($this->encryptionManager->isEnabled()) {
$cachedFile = $this->getCachedFile($path);
@@ -825,11 +853,6 @@ class Encryption extends Wrapper {
return $this->storage->getLocalFile($path);
}
- /**
- * Returns the wrapped storage's value for isLocal()
- *
- * @return bool wrapped storage's isLocal() value
- */
public function isLocal() {
if ($this->encryptionManager->isEnabled()) {
return false;
@@ -837,15 +860,11 @@ class Encryption extends Wrapper {
return $this->storage->isLocal();
}
- /**
- * see https://www.php.net/manual/en/function.stat.php
- * only the following keys are required in the result: size and mtime
- *
- * @param string $path
- * @return array
- */
public function stat($path) {
$stat = $this->storage->stat($path);
+ if (!$stat) {
+ return false;
+ }
$fileSize = $this->filesize($path);
$stat['size'] = $fileSize;
$stat[7] = $fileSize;
@@ -853,14 +872,6 @@ class Encryption extends Wrapper {
return $stat;
}
- /**
- * see https://www.php.net/manual/en/function.hash.php
- *
- * @param string $type
- * @param string $path
- * @param bool $raw
- * @return string
- */
public function hash($type, $path, $raw = false) {
$fh = $this->fopen($path, 'rb');
$ctx = hash_init($type);
@@ -1068,6 +1079,13 @@ class Encryption extends Wrapper {
[$count, $result] = \OC_Helper::streamCopy($stream, $target);
fclose($stream);
fclose($target);
+
+ // object store, stores the size after write and doesn't update this during scan
+ // manually store the unencrypted size
+ if ($result && $this->getWrapperStorage()->instanceOfStorage(ObjectStoreStorage::class)) {
+ $this->getCache()->put($path, ['unencrypted_size' => $count]);
+ }
+
return $count;
}
}
diff --git a/lib/private/Files/Stream/HashWrapper.php b/lib/private/Files/Stream/HashWrapper.php
index fd9bb3cdd0b..b2bfcff68d4 100644
--- a/lib/private/Files/Stream/HashWrapper.php
+++ b/lib/private/Files/Stream/HashWrapper.php
@@ -67,7 +67,11 @@ class HashWrapper extends Wrapper {
public function stream_close() {
if (is_callable($this->callback)) {
- call_user_func($this->callback, hash_final($this->hash));
+ // if the stream is closed as a result of the end-of-request GC, the hash context might be cleaned up before this stream
+ if ($this->hash instanceof \HashContext) {
+ $hash = hash_final($this->hash);
+ call_user_func($this->callback, $hash);
+ }
// prevent further calls by potential PHP 7 GC ghosts
$this->callback = null;
}
diff --git a/lib/private/Files/Stream/SeekableHttpStream.php b/lib/private/Files/Stream/SeekableHttpStream.php
index af797c7720d..820a681bd07 100644
--- a/lib/private/Files/Stream/SeekableHttpStream.php
+++ b/lib/private/Files/Stream/SeekableHttpStream.php
@@ -24,6 +24,7 @@
namespace OC\Files\Stream;
use Icewind\Streams\File;
+use Icewind\Streams\Wrapper;
/**
* A stream wrapper that uses http range requests to provide a seekable stream for http reading
@@ -92,6 +93,18 @@ class SeekableHttpStream implements File {
}
$responseHead = stream_get_meta_data($this->current)['wrapper_data'];
+
+ while ($responseHead instanceof Wrapper) {
+ $wrapperOptions = stream_context_get_options($responseHead->context);
+ foreach ($wrapperOptions as $options) {
+ if (isset($options['source']) && is_resource($options['source'])) {
+ $responseHead = stream_get_meta_data($options['source'])['wrapper_data'];
+ continue 2;
+ }
+ }
+ throw new \Exception("Failed to get source stream from stream wrapper of " . get_class($responseHead));
+ }
+
$rangeHeaders = array_values(array_filter($responseHead, function ($v) {
return preg_match('#^content-range:#i', $v) === 1;
}));
diff --git a/lib/private/Files/View.php b/lib/private/Files/View.php
index 2b6732e2ba0..e5394e72ffe 100644
--- a/lib/private/Files/View.php
+++ b/lib/private/Files/View.php
@@ -49,9 +49,7 @@ namespace OC\Files;
use Icewind\Streams\CallbackWrapper;
use OC\Files\Mount\MoveableMount;
use OC\Files\Storage\Storage;
-use OC\User\DisplayNameCache;
use OC\User\LazyUser;
-use OC\User\User;
use OCA\Files_Sharing\SharedMount;
use OCP\Constants;
use OCP\Files\Cache\ICacheEntry;
@@ -103,8 +101,6 @@ class View {
private LoggerInterface $logger;
- private DisplayNameCache $displayNameCache;
-
/**
* @param string $root
* @throws \Exception If $root contains an invalid path
@@ -121,7 +117,6 @@ class View {
$this->lockingProvider = \OC::$server->getLockingProvider();
$this->lockingEnabled = !($this->lockingProvider instanceof \OC\Lock\NoopLockingProvider);
$this->userManager = \OC::$server->getUserManager();
- $this->displayNameCache = \OC::$server->get(DisplayNameCache::class);
$this->logger = \OC::$server->get(LoggerInterface::class);
}
@@ -1164,7 +1159,7 @@ class View {
try {
$this->changeLock($path, ILockingProvider::LOCK_EXCLUSIVE);
} catch (LockedException $e) {
- // release the shared lock we acquired before quiting
+ // release the shared lock we acquired before quitting
$this->unlockFile($path, ILockingProvider::LOCK_SHARED);
throw $e;
}
@@ -1319,7 +1314,7 @@ class View {
* @return IUser
*/
private function getUserObjectForOwner(string $ownerId) {
- return new LazyUser($ownerId, $this->displayNameCache, $this->userManager);
+ return new LazyUser($ownerId, $this->userManager);
}
/**
@@ -1725,7 +1720,7 @@ class View {
/**
* Get the path of a file by id, relative to the view
*
- * Note that the resulting path is not guarantied to be unique for the id, multiple paths can point to the same file
+ * Note that the resulting path is not guaranteed to be unique for the id, multiple paths can point to the same file
*
* @param int $id
* @param int|null $storageId
diff --git a/lib/private/HintException.php b/lib/private/HintException.php
index 735832266cf..20f7142d1c0 100644
--- a/lib/private/HintException.php
+++ b/lib/private/HintException.php
@@ -31,7 +31,7 @@ namespace OC;
* An Exception class with the intention to be presented to the end user
*
* @package OC
- * @depreacted 23.0.0 Use \OCP\HintException
+ * @deprecated 23.0.0 Use \OCP\HintException
*/
class HintException extends \OCP\HintException {
}
diff --git a/lib/private/Http/Client/Client.php b/lib/private/Http/Client/Client.php
index 3ba85a2dd9f..4bf7fd02400 100644
--- a/lib/private/Http/Client/Client.php
+++ b/lib/private/Http/Client/Client.php
@@ -128,7 +128,7 @@ class Client implements IClient {
}
/**
- * Returns a null or an associative array specifiying the proxy URI for
+ * Returns a null or an associative array specifying the proxy URI for
* 'http' and 'https' schemes, in addition to a 'no' key value pair
* providing a list of host names that should not be proxied to.
*
diff --git a/lib/private/Http/Client/LocalAddressChecker.php b/lib/private/Http/Client/LocalAddressChecker.php
index 2789b1b5935..f4fea503ab9 100644
--- a/lib/private/Http/Client/LocalAddressChecker.php
+++ b/lib/private/Http/Client/LocalAddressChecker.php
@@ -27,6 +27,7 @@ namespace OC\Http\Client;
use OCP\Http\Client\LocalServerException;
use Psr\Log\LoggerInterface;
+use Symfony\Component\HttpFoundation\IpUtils;
class LocalAddressChecker {
private LoggerInterface $logger;
@@ -36,7 +37,16 @@ class LocalAddressChecker {
}
public function ThrowIfLocalIp(string $ip) : void {
- if ((bool)filter_var($ip, FILTER_VALIDATE_IP) && !filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
+ $localRanges = [
+ '100.64.0.0/10', // See RFC 6598
+ '192.0.0.0/24', // See RFC 6890
+ ];
+ if (
+ (bool)filter_var($ip, FILTER_VALIDATE_IP) &&
+ (
+ !filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) ||
+ IpUtils::checkIp($ip, $localRanges)
+ )) {
$this->logger->warning("Host $ip was not connected to because it violates local access rules");
throw new LocalServerException('Host violates local access rules');
}
@@ -46,7 +56,9 @@ class LocalAddressChecker {
$delimiter = strrpos($ip, ':'); // Get last colon
$ipv4Address = substr($ip, $delimiter + 1);
- if (!filter_var($ipv4Address, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
+ if (
+ !filter_var($ipv4Address, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) ||
+ IpUtils::checkIp($ip, $localRanges)) {
$this->logger->warning("Host $ip was not connected to because it violates local access rules");
throw new LocalServerException('Host violates local access rules');
}
@@ -66,8 +78,10 @@ class LocalAddressChecker {
$host = substr($host, 1, -1);
}
- // Disallow localhost and local network
- if ($host === 'localhost' || substr($host, -6) === '.local' || substr($host, -10) === '.localhost') {
+ // Disallow local network top-level domains from RFC 6762
+ $localTopLevelDomains = ['local','localhost','intranet','internal','private','corp','home','lan'];
+ $topLevelDomain = substr((strrchr($host, '.') ?: ''), 1);
+ if (in_array($topLevelDomain, $localTopLevelDomains)) {
$this->logger->warning("Host $host was not connected to because it violates local access rules");
throw new LocalServerException('Host violates local access rules');
}
diff --git a/lib/private/IntegrityCheck/Checker.php b/lib/private/IntegrityCheck/Checker.php
index 273eba35446..ba555cff438 100644
--- a/lib/private/IntegrityCheck/Checker.php
+++ b/lib/private/IntegrityCheck/Checker.php
@@ -201,7 +201,8 @@ class Checker {
if ($filename === $this->environmentHelper->getServerRoot() . '/core/js/mimetypelist.js') {
$oldMimetypeList = new GenerateMimetypeFileBuilder();
$newFile = $oldMimetypeList->generateFile($this->mimeTypeDetector->getAllAliases());
- if ($newFile === file_get_contents($filename)) {
+ $oldFile = $this->fileAccessHelper->file_get_contents($filename);
+ if ($newFile === $oldFile) {
$hashes[$relativeFileName] = hash('sha512', $oldMimetypeList->generateFile($this->mimeTypeDetector->getOnlyDefaultAliases()));
continue;
}
diff --git a/lib/private/L10N/L10N.php b/lib/private/L10N/L10N.php
index 09c0f1cdb35..82ef3350b1f 100644
--- a/lib/private/L10N/L10N.php
+++ b/lib/private/L10N/L10N.php
@@ -122,7 +122,7 @@ class L10N implements IL10N {
*
*/
public function n(string $text_singular, string $text_plural, int $count, array $parameters = []): string {
- $identifier = "_${text_singular}_::_${text_plural}_";
+ $identifier = "_{$text_singular}_::_{$text_plural}_";
if (isset($this->translations[$identifier])) {
return (string) new L10NString($this, $identifier, $parameters, $count);
}
diff --git a/lib/private/LargeFileHelper.php b/lib/private/LargeFileHelper.php
index e2984b4bacf..82b3c5ae760 100755
--- a/lib/private/LargeFileHelper.php
+++ b/lib/private/LargeFileHelper.php
@@ -147,7 +147,7 @@ class LargeFileHelper {
* null on failure.
*/
public function getFileSizeViaExec($filename) {
- if (\OC_Helper::is_function_enabled('exec')) {
+ if (\OCP\Util::isFunctionEnabled('exec')) {
$os = strtolower(php_uname('s'));
$arg = escapeshellarg($filename);
$result = null;
@@ -195,7 +195,7 @@ class LargeFileHelper {
$result = - 1;
}
if ($result < 0) {
- if (\OC_Helper::is_function_enabled('exec')) {
+ if (\OCP\Util::isFunctionEnabled('exec')) {
$os = strtolower(php_uname('s'));
if (strpos($os, 'linux') !== false) {
return $this->exec('stat -c %Y ' . escapeshellarg($fullPath));
diff --git a/lib/private/Log.php b/lib/private/Log.php
index 0415967f0f0..4ab647bc6c1 100644
--- a/lib/private/Log.php
+++ b/lib/private/Log.php
@@ -15,6 +15,7 @@ declare(strict_types=1);
* @author Olivier Paroz <github@oparoz.com>
* @author Robin Appelman <robin@icewind.nl>
* @author Roeland Jago Douma <roeland@famdouma.nl>
+ * @author Thomas Citharel <nextcloud@tcit.fr>
* @author Thomas Müller <thomas.mueller@tmit.eu>
* @author Victor Dubiniuk <dubiniuk@owncloud.com>
*
@@ -35,8 +36,11 @@ declare(strict_types=1);
*/
namespace OC;
+use Exception;
use Nextcloud\LogNormalizer\Normalizer;
+use OC\AppFramework\Bootstrap\Coordinator;
use OCP\Log\IDataLogger;
+use Throwable;
use function array_merge;
use OC\Log\ExceptionSerializer;
use OCP\ILogger;
@@ -207,11 +211,11 @@ class Log implements ILogger, IDataLogger {
array_walk($context, [$this->normalizer, 'format']);
$app = $context['app'] ?? 'no app in context';
- $message = $this->interpolateMessage($context, $message);
+ $entry = $this->interpolateMessage($context, $message);
try {
if ($level >= $minLevel) {
- $this->writeLog($app, $message, $level);
+ $this->writeLog($app, $entry, $level);
if ($this->crashReporters !== null) {
$messageContext = array_merge(
@@ -220,14 +224,14 @@ class Log implements ILogger, IDataLogger {
'level' => $level
]
);
- $this->crashReporters->delegateMessage($message, $messageContext);
+ $this->crashReporters->delegateMessage($entry['message'], $messageContext);
}
} else {
if ($this->crashReporters !== null) {
- $this->crashReporters->delegateBreadcrumb($message, 'log', $context);
+ $this->crashReporters->delegateBreadcrumb($entry['message'], 'log', $context);
}
}
- } catch (\Throwable $e) {
+ } catch (Throwable $e) {
// make sure we dont hard crash if logging fails
}
}
@@ -299,24 +303,27 @@ class Log implements ILogger, IDataLogger {
/**
* Logs an exception very detailed
*
- * @param \Exception|\Throwable $exception
+ * @param Exception|Throwable $exception
* @param array $context
* @return void
* @since 8.2.0
*/
- public function logException(\Throwable $exception, array $context = []) {
+ public function logException(Throwable $exception, array $context = []) {
$app = $context['app'] ?? 'no app in context';
$level = $context['level'] ?? ILogger::ERROR;
// if an error is raised before the autoloader is properly setup, we can't serialize exceptions
try {
- $serializer = new ExceptionSerializer($this->config);
- } catch (\Throwable $e) {
+ $serializer = $this->getSerializer();
+ } catch (Throwable $e) {
$this->error("Failed to load ExceptionSerializer serializer while trying to log " . $exception->getMessage());
return;
}
- $data = $serializer->serializeException($exception);
- $data['CustomMessage'] = $this->interpolateMessage($context, $context['message'] ?? '--');
+ $data = $context;
+ unset($data['app']);
+ unset($data['level']);
+ $data = array_merge($serializer->serializeException($exception), $data);
+ $data = $this->interpolateMessage($data, $context['message'] ?? '--', 'CustomMessage');
$minLevel = $this->getLogLevel($context);
@@ -334,7 +341,7 @@ class Log implements ILogger, IDataLogger {
if (!is_null($this->crashReporters)) {
$this->crashReporters->delegateReport($exception, $context);
}
- } catch (\Throwable $e) {
+ } catch (Throwable $e) {
// make sure we dont hard crash if logging fails
}
}
@@ -357,7 +364,7 @@ class Log implements ILogger, IDataLogger {
}
$context['level'] = $level;
- } catch (\Throwable $e) {
+ } catch (Throwable $e) {
// make sure we dont hard crash if logging fails
}
}
@@ -381,16 +388,42 @@ class Log implements ILogger, IDataLogger {
/**
* Interpolate $message as defined in PSR-3
*
- * @param array $context
- * @param string $message
- *
- * @return string
+ * Returns an array containing the context without the interpolated
+ * parameters placeholders and the message as the 'message' - or
+ * user-defined - key.
*/
- private function interpolateMessage(array $context, string $message): string {
+ private function interpolateMessage(array $context, string $message, string $messageKey = 'message'): array {
$replace = [];
+ $usedContextKeys = [];
foreach ($context as $key => $val) {
- $replace['{' . $key . '}'] = $val;
+ $fullKey = '{' . $key . '}';
+ $replace[$fullKey] = $val;
+ if (strpos($message, $fullKey) !== false) {
+ $usedContextKeys[$key] = true;
+ }
+ }
+ return array_merge(array_diff_key($context, $usedContextKeys), [$messageKey => strtr($message, $replace)]);
+ }
+
+ /**
+ * @throws Throwable
+ */
+ protected function getSerializer(): ExceptionSerializer {
+ $serializer = new ExceptionSerializer($this->config);
+ try {
+ /** @var Coordinator $coordinator */
+ $coordinator = \OCP\Server::get(Coordinator::class);
+ foreach ($coordinator->getRegistrationContext()->getSensitiveMethods() as $registration) {
+ $serializer->enlistSensitiveMethods($registration->getName(), $registration->getValue());
+ }
+ // For not every app might be initialized at this time, we cannot assume that the return value
+ // of getSensitiveMethods() is complete. Running delegates in Coordinator::registerApps() is
+ // not possible due to dependencies on the one hand. On the other it would work only with
+ // adding public methods to the PsrLoggerAdapter and this class.
+ // Thus, serializer cannot be a property.
+ } catch (Throwable $t) {
+ // ignore app-defined sensitive methods in this case - they weren't loaded anyway
}
- return strtr($message, $replace);
+ return $serializer;
}
}
diff --git a/lib/private/Log/ExceptionSerializer.php b/lib/private/Log/ExceptionSerializer.php
index dab134b26a4..aaf6a39235e 100644
--- a/lib/private/Log/ExceptionSerializer.php
+++ b/lib/private/Log/ExceptionSerializer.php
@@ -42,6 +42,8 @@ use OCA\Encryption\Session;
use OCP\HintException;
class ExceptionSerializer {
+ public const SENSITIVE_VALUE_PLACEHOLDER = '*** sensitive parameters replaced ***';
+
public const methodsWithSensitiveParameters = [
// Session/User
'completeLogin',
@@ -107,7 +109,7 @@ class ExceptionSerializer {
$this->systemConfig = $systemConfig;
}
- public const methodsWithSensitiveParametersByClass = [
+ protected array $methodsWithSensitiveParametersByClass = [
SetupController::class => [
'run',
'display',
@@ -180,7 +182,7 @@ class ExceptionSerializer {
if (isset($traceLine['args'])) {
$sensitiveValues = array_merge($sensitiveValues, $traceLine['args']);
}
- $traceLine['args'] = ['*** sensitive parameters replaced ***'];
+ $traceLine['args'] = [self::SENSITIVE_VALUE_PLACEHOLDER];
return $traceLine;
}
@@ -188,8 +190,8 @@ class ExceptionSerializer {
$sensitiveValues = [];
$trace = array_map(function (array $traceLine) use (&$sensitiveValues) {
$className = $traceLine['class'] ?? '';
- if ($className && isset(self::methodsWithSensitiveParametersByClass[$className])
- && in_array($traceLine['function'], self::methodsWithSensitiveParametersByClass[$className], true)) {
+ if ($className && isset($this->methodsWithSensitiveParametersByClass[$className])
+ && in_array($traceLine['function'], $this->methodsWithSensitiveParametersByClass[$className], true)) {
return $this->editTrace($sensitiveValues, $traceLine);
}
foreach (self::methodsWithSensitiveParameters as $sensitiveMethod) {
@@ -208,14 +210,16 @@ class ExceptionSerializer {
}
private function removeValuesFromArgs($args, $values) {
- foreach ($args as &$arg) {
+ $workArgs = [];
+ foreach ($args as $arg) {
if (in_array($arg, $values, true)) {
- $arg = '*** sensitive parameter replaced ***';
+ $arg = self::SENSITIVE_VALUE_PLACEHOLDER;
} elseif (is_array($arg)) {
$arg = $this->removeValuesFromArgs($arg, $values);
}
+ $workArgs[] = $arg;
}
- return $args;
+ return $workArgs;
}
private function encodeTrace($trace) {
@@ -285,4 +289,11 @@ class ExceptionSerializer {
return $data;
}
+
+ public function enlistSensitiveMethods(string $class, array $methods): void {
+ if (!isset($this->methodsWithSensitiveParametersByClass[$class])) {
+ $this->methodsWithSensitiveParametersByClass[$class] = [];
+ }
+ $this->methodsWithSensitiveParametersByClass[$class] = array_merge($this->methodsWithSensitiveParametersByClass[$class], $methods);
+ }
}
diff --git a/lib/private/Log/LogDetails.php b/lib/private/Log/LogDetails.php
index 3353ea3f4cc..b3544572708 100644
--- a/lib/private/Log/LogDetails.php
+++ b/lib/private/Log/LogDetails.php
@@ -5,6 +5,7 @@
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Julius Härtl <jus@bitgrid.net>
+ * @author Thomas Citharel <nextcloud@tcit.fr>
*
* @license GNU AGPL version 3 or any later version
*
@@ -90,8 +91,9 @@ abstract class LogDetails {
$entry['exception'] = $message;
$entry['message'] = $message['CustomMessage'] !== '--' ? $message['CustomMessage'] : $message['Message'];
} else {
- $entry['data'] = $message;
$entry['message'] = $message['message'] ?? '(no message provided)';
+ unset($message['message']);
+ $entry['data'] = $message;
}
}
diff --git a/lib/private/Log/Rotate.php b/lib/private/Log/Rotate.php
index 58b2932b417..20bc3327f92 100644
--- a/lib/private/Log/Rotate.php
+++ b/lib/private/Log/Rotate.php
@@ -32,7 +32,7 @@ use OCP\Log\RotationTrait;
* For more professional log management set the 'logfile' config to a different
* location and manage that with your own tools.
*/
-class Rotate extends \OC\BackgroundJob\Job {
+class Rotate extends \OCP\BackgroundJob\Job {
use RotationTrait;
public function run($dummy) {
diff --git a/lib/private/Mail/EMailTemplate.php b/lib/private/Mail/EMailTemplate.php
index 2c091deb2a1..7ec2b46bad3 100644
--- a/lib/private/Mail/EMailTemplate.php
+++ b/lib/private/Mail/EMailTemplate.php
@@ -497,7 +497,7 @@ EOF;
*/
/** @var string $label */
$label = ($plainMetaInfo !== false)? $plainMetaInfo : '';
- $this->plainBody .= sprintf("%${plainIndent}s %s\n",
+ $this->plainBody .= sprintf("%{$plainIndent}s %s\n",
$label,
str_replace("\n", "\n" . str_repeat(' ', $plainIndent + 1), $plainText));
}
diff --git a/lib/private/Mail/Mailer.php b/lib/private/Mail/Mailer.php
index 2f3480498be..d0c3b04eacb 100644
--- a/lib/private/Mail/Mailer.php
+++ b/lib/private/Mail/Mailer.php
@@ -39,6 +39,7 @@ use Egulias\EmailValidator\EmailValidator;
use Egulias\EmailValidator\Validation\RFCValidation;
use OCP\Defaults;
use OCP\EventDispatcher\IEventDispatcher;
+use OCP\IBinaryFinder;
use OCP\IConfig;
use OCP\IL10N;
use OCP\IURLGenerator;
@@ -71,19 +72,14 @@ use Psr\Log\LoggerInterface;
class Mailer implements IMailer {
/** @var \Swift_Mailer Cached mailer */
private $instance = null;
- /** @var IConfig */
- private $config;
+ private IConfig $config;
private LoggerInterface $logger;
/** @var Defaults */
private $defaults;
- /** @var IURLGenerator */
- private $urlGenerator;
- /** @var IL10N */
- private $l10n;
- /** @var IEventDispatcher */
- private $dispatcher;
- /** @var IFactory */
- private $l10nFactory;
+ private IURLGenerator $urlGenerator;
+ private IL10N $l10n;
+ private IEventDispatcher $dispatcher;
+ private IFactory $l10nFactory;
public function __construct(IConfig $config,
LoggerInterface $logger,
@@ -196,6 +192,9 @@ class Mailer implements IMailer {
// Debugging logging
$logMessage = sprintf('Sent mail to "%s" with subject "%s"', print_r($message->getTo(), true), $message->getSubject());
+ if (!empty($failedRecipients)) {
+ $logMessage .= sprintf(' (failed for "%s")', print_r($failedRecipients, true));
+ }
$this->logger->debug($logMessage, ['app' => 'core']);
if ($debugMode && isset($mailLogger)) {
$this->logger->debug($mailLogger->dump(), ['app' => 'core']);
@@ -306,7 +305,7 @@ class Mailer implements IMailer {
$binaryPath = '/var/qmail/bin/sendmail';
break;
default:
- $sendmail = \OC_Helper::findBinaryPath('sendmail');
+ $sendmail = \OCP\Server::get(IBinaryFinder::class)->findBinaryPath('sendmail');
if ($sendmail === null) {
$sendmail = '/usr/sbin/sendmail';
}
diff --git a/lib/private/Memcache/Memcached.php b/lib/private/Memcache/Memcached.php
index db4aa7ba9cc..7d512d4d1ae 100644
--- a/lib/private/Memcache/Memcached.php
+++ b/lib/private/Memcache/Memcached.php
@@ -63,7 +63,7 @@ class Memcached extends Cache implements IMemcache {
\Memcached::OPT_LIBKETAMA_COMPATIBLE => true,
// Enable Binary Protocol
- //\Memcached::OPT_BINARY_PROTOCOL => true,
+ \Memcached::OPT_BINARY_PROTOCOL => true,
];
/**
* By default enable igbinary serializer if available
@@ -119,10 +119,7 @@ class Memcached extends Cache implements IMemcache {
} else {
$result = self::$cache->set($this->getNameSpace() . $key, $value);
}
- if ($result !== true) {
- $this->verifyReturnCode();
- }
- return $result;
+ return $result || $this->isSuccess();
}
public function hasKey($key) {
@@ -132,10 +129,7 @@ class Memcached extends Cache implements IMemcache {
public function remove($key) {
$result = self::$cache->delete($this->getNameSpace() . $key);
- if (self::$cache->getResultCode() !== \Memcached::RES_NOTFOUND) {
- $this->verifyReturnCode();
- }
- return $result;
+ return $result || $this->isSuccess() || self::$cache->getResultCode() === \Memcached::RES_NOTFOUND;
}
public function clear($prefix = '') {
@@ -151,14 +145,10 @@ class Memcached extends Cache implements IMemcache {
* @param mixed $value
* @param int $ttl Time To Live in seconds. Defaults to 60*60*24
* @return bool
- * @throws \Exception
*/
public function add($key, $value, $ttl = 0) {
$result = self::$cache->add($this->getPrefix() . $key, $value, $ttl);
- if (self::$cache->getResultCode() !== \Memcached::RES_NOTSTORED) {
- $this->verifyReturnCode();
- }
- return $result;
+ return $result || $this->isSuccess();
}
/**
@@ -200,15 +190,7 @@ class Memcached extends Cache implements IMemcache {
return extension_loaded('memcached');
}
- /**
- * @throws \Exception
- */
- private function verifyReturnCode() {
- $code = self::$cache->getResultCode();
- if ($code === \Memcached::RES_SUCCESS) {
- return;
- }
- $message = self::$cache->getResultMessage();
- throw new \Exception("Error $code interacting with memcached : $message");
+ private function isSuccess(): bool {
+ return self::$cache->getResultCode() === \Memcached::RES_SUCCESS;
}
}
diff --git a/lib/private/MemoryInfo.php b/lib/private/MemoryInfo.php
index 074e9f915fe..ed6617d879d 100644
--- a/lib/private/MemoryInfo.php
+++ b/lib/private/MemoryInfo.php
@@ -68,7 +68,7 @@ class MemoryInfo {
$last = strtolower(substr($memoryLimit, -1));
$memoryLimit = (int)substr($memoryLimit, 0, -1);
- // intended fall trough
+ // intended fall through
switch ($last) {
case 'g':
$memoryLimit *= 1024;
diff --git a/lib/private/Metadata/IMetadataManager.php b/lib/private/Metadata/IMetadataManager.php
index d2d37f15c25..fa0bcc22801 100644
--- a/lib/private/Metadata/IMetadataManager.php
+++ b/lib/private/Metadata/IMetadataManager.php
@@ -29,7 +29,7 @@ interface IMetadataManager {
public function fetchMetadataFor(string $group, array $fileIds): array;
/**
- * Get the capabilites as an array of mimetype regex to the type provided
+ * Get the capabilities as an array of mimetype regex to the type provided
*/
public function getCapabilities(): array;
}
diff --git a/lib/private/Metadata/Provider/ExifProvider.php b/lib/private/Metadata/Provider/ExifProvider.php
index 2e1eb1d4208..892671556b3 100644
--- a/lib/private/Metadata/Provider/ExifProvider.php
+++ b/lib/private/Metadata/Provider/ExifProvider.php
@@ -17,7 +17,7 @@ class ExifProvider implements IMetadataProvider {
public function execute(File $file): array {
$fileDescriptor = $file->fopen('rb');
- $data = @exif_read_data($fileDescriptor, 'ANY_TAG', true);
+ $data = exif_read_data($fileDescriptor, 'COMPUTED', true);
$size = new FileMetadata();
$size->setGroupName('size');
diff --git a/lib/private/Migration/BackgroundRepair.php b/lib/private/Migration/BackgroundRepair.php
index 03a3d3f4a7c..579ba494e58 100644
--- a/lib/private/Migration/BackgroundRepair.php
+++ b/lib/private/Migration/BackgroundRepair.php
@@ -26,15 +26,14 @@
*/
namespace OC\Migration;
-use OC\BackgroundJob\JobList;
-use OC\BackgroundJob\TimedJob;
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\BackgroundJob\IJobList;
+use OCP\BackgroundJob\TimedJob;
+use OCP\EventDispatcher\IEventDispatcher;
use OC\NeedsUpdateException;
use OC\Repair;
use OC_App;
-use OCP\BackgroundJob\IJobList;
-use OCP\ILogger;
use Psr\Log\LoggerInterface;
-use Symfony\Component\EventDispatcher\EventDispatcherInterface;
/**
* Class BackgroundRepair
@@ -42,33 +41,16 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface;
* @package OC\Migration
*/
class BackgroundRepair extends TimedJob {
+ private IJobList $jobList;
+ private LoggerInterface $logger;
+ private IEventDispatcher $dispatcher;
- /** @var IJobList */
- private $jobList;
-
- /** @var ILogger */
- private $logger;
-
- /** @var EventDispatcherInterface */
- private $dispatcher;
-
- public function __construct(EventDispatcherInterface $dispatcher) {
+ public function __construct(IEventDispatcher $dispatcher, ITimeFactory $time, LoggerInterface $logger, IJobList $jobList) {
+ parent::__construct($time);
$this->dispatcher = $dispatcher;
- }
-
- /**
- * run the job, then remove it from the job list
- *
- * @param JobList $jobList
- * @param ILogger|null $logger
- */
- public function execute($jobList, ILogger $logger = null) {
- // add an interval of 15 mins
- $this->setInterval(15 * 60);
-
- $this->jobList = $jobList;
$this->logger = $logger;
- parent::execute($jobList, $logger);
+ $this->jobList = $jobList;
+ $this->setInterval(15 * 60);
}
/**
@@ -97,8 +79,9 @@ class BackgroundRepair extends TimedJob {
try {
$repair->addStep($step);
} catch (\Exception $ex) {
- $this->logger->logException($ex, [
- 'app' => 'migration'
+ $this->logger->error($ex->getMessage(), [
+ 'app' => 'migration',
+ 'exception' => $ex,
]);
// remove the job - we can never execute it
diff --git a/lib/private/NavigationManager.php b/lib/private/NavigationManager.php
index 2ffd25df9b8..bbf28d2c142 100644
--- a/lib/private/NavigationManager.php
+++ b/lib/private/NavigationManager.php
@@ -211,17 +211,37 @@ class NavigationManager implements INavigationManager {
'icon' => $this->urlGenerator->imagePath('settings', 'apps.svg'),
'name' => $l->t('Apps'),
]);
- }
- // Personal and (if applicable) admin settings
- $this->add([
- 'type' => 'settings',
- 'id' => 'settings',
- 'order' => 2,
- 'href' => $this->urlGenerator->linkToRoute('settings.PersonalSettings.index'),
- 'name' => $l->t('Settings'),
- 'icon' => $this->urlGenerator->imagePath('settings', 'admin.svg'),
- ]);
+ // Personal settings
+ $this->add([
+ 'type' => 'settings',
+ 'id' => 'settings',
+ 'order' => 2,
+ 'href' => $this->urlGenerator->linkToRoute('settings.PersonalSettings.index'),
+ 'name' => $l->t('Personal settings'),
+ 'icon' => $this->urlGenerator->imagePath('settings', 'personal.svg'),
+ ]);
+
+ // Admin settings
+ $this->add([
+ 'type' => 'settings',
+ 'id' => 'admin_settings',
+ 'order' => 3,
+ 'href' => $this->urlGenerator->linkToRoute('settings.AdminSettings.index', ['section' => 'overview']),
+ 'name' => $l->t('Admin settings'),
+ 'icon' => $this->urlGenerator->imagePath('settings', 'admin.svg'),
+ ]);
+ } else {
+ // Personal settings
+ $this->add([
+ 'type' => 'settings',
+ 'id' => 'settings',
+ 'order' => 2,
+ 'href' => $this->urlGenerator->linkToRoute('settings.PersonalSettings.index'),
+ 'name' => $l->t('Settings'),
+ 'icon' => $this->urlGenerator->imagePath('settings', 'admin.svg'),
+ ]);
+ }
$logoutUrl = \OC_User::getLogoutUrl($this->urlGenerator);
if ($logoutUrl !== '') {
diff --git a/lib/private/Notification/Manager.php b/lib/private/Notification/Manager.php
index c707884355b..d758cae428f 100644
--- a/lib/private/Notification/Manager.php
+++ b/lib/private/Notification/Manager.php
@@ -27,7 +27,6 @@ declare(strict_types=1);
namespace OC\Notification;
use OC\AppFramework\Bootstrap\Coordinator;
-use OCP\AppFramework\Utility\ITimeFactory;
use OCP\ICache;
use OCP\ICacheFactory;
use OCP\IUserManager;
@@ -50,8 +49,6 @@ class Manager implements IManager {
private $userManager;
/** @var ICache */
protected $cache;
- /** @var ITimeFactory */
- protected $timeFactory;
/** @var IRegistry */
protected $subscription;
/** @var LoggerInterface */
@@ -79,14 +76,12 @@ class Manager implements IManager {
public function __construct(IValidator $validator,
IUserManager $userManager,
ICacheFactory $cacheFactory,
- ITimeFactory $timeFactory,
IRegistry $subscription,
LoggerInterface $logger,
Coordinator $coordinator) {
$this->validator = $validator;
$this->userManager = $userManager;
$this->cache = $cacheFactory->createDistributed('notifications');
- $this->timeFactory = $timeFactory;
$this->subscription = $subscription;
$this->logger = $logger;
$this->coordinator = $coordinator;
@@ -310,10 +305,7 @@ class Manager implements IManager {
* users overload our infrastructure. For this reason we have to rate-limit the
* use of push notifications. If you need this feature, consider using Nextcloud Enterprise.
*/
- // TODO Remove time check after 1st March 2022
- $isFairUse = $this->timeFactory->getTime() < 1646089200
- || $this->subscription->delegateHasValidSubscription()
- || $this->userManager->countSeenUsers() < 5000;
+ $isFairUse = $this->subscription->delegateHasValidSubscription() || $this->userManager->countSeenUsers() < 500;
$pushAllowed = $isFairUse ? 'yes' : 'no';
$this->cache->set('push_fair_use', $pushAllowed, 3600);
}
diff --git a/lib/private/OCS/DiscoveryService.php b/lib/private/OCS/DiscoveryService.php
index 1d10bbac870..7ab876811e7 100644
--- a/lib/private/OCS/DiscoveryService.php
+++ b/lib/private/OCS/DiscoveryService.php
@@ -62,7 +62,7 @@ class DiscoveryService implements IDiscoveryService {
*
* @param string $remote
* @param string $service the service you want to discover
- * @param bool $skipCache We won't check if the data is in the cache. This is usefull if a background job is updating the status
+ * @param bool $skipCache We won't check if the data is in the cache. This is useful if a background job is updating the status
* @return array
*/
public function discover(string $remote, string $service, bool $skipCache = false): array {
diff --git a/lib/private/Preview/Bitmap.php b/lib/private/Preview/Bitmap.php
index f7c8cf3543b..ffe5bc88856 100644
--- a/lib/private/Preview/Bitmap.php
+++ b/lib/private/Preview/Bitmap.php
@@ -68,7 +68,7 @@ abstract class Bitmap extends ProviderV2 {
$this->cleanTmpFiles();
//new bitmap image object
- $image = new \OC_Image();
+ $image = new \OCP\Image();
$image->loadFromData((string) $bp);
//check if image object is valid
return $image->valid() ? $image : null;
diff --git a/lib/private/Preview/Bundled.php b/lib/private/Preview/Bundled.php
index 063c69ba5dd..df7f630dff7 100644
--- a/lib/private/Preview/Bundled.php
+++ b/lib/private/Preview/Bundled.php
@@ -43,7 +43,7 @@ abstract class Bundled extends ProviderV2 {
$zip = new ZIP($sourceTmp);
$zip->extractFile($path, $targetTmp);
- $image = new \OC_Image();
+ $image = new \OCP\Image();
$image->loadFromFile($targetTmp);
$image->fixOrientation();
diff --git a/lib/private/Preview/Generator.php b/lib/private/Preview/Generator.php
index e058a15bfa5..ef44188da74 100644
--- a/lib/private/Preview/Generator.php
+++ b/lib/private/Preview/Generator.php
@@ -115,7 +115,7 @@ class Generator {
* Generates previews of a file
*
* @param File $file
- * @param array $specifications
+ * @param non-empty-array $specifications
* @param string $mimeType
* @return ISimpleFile the last preview that was generated
* @throws NotFoundException
@@ -213,10 +213,11 @@ class Generator {
throw new NotFoundException('Cached preview size 0, invalid!');
}
}
+ assert($preview !== null);
// Free memory being used by the embedded image resource. Without this the image is kept in memory indefinitely.
// Garbage Collection does NOT free this memory. We have to do it ourselves.
- if ($maxPreviewImage instanceof \OC_Image) {
+ if ($maxPreviewImage instanceof \OCP\Image) {
$maxPreviewImage->destroy();
}
@@ -226,15 +227,25 @@ class Generator {
/**
* Generate a small image straight away without generating a max preview first
* Preview generated is 256x256
+ *
+ * @throws NotFoundException
*/
- private function getSmallImagePreview(ISimpleFolder $previewFolder, File $file, string $mimeType, string $prefix, bool $crop) {
+ private function getSmallImagePreview(ISimpleFolder $previewFolder, File $file, string $mimeType, string $prefix, bool $crop): ISimpleFile {
$nodes = $previewFolder->getDirectoryListing();
foreach ($nodes as $node) {
$name = $node->getName();
- if (($prefix === '' || strpos($name, $prefix) === 0)
- && (str_starts_with($name, '256-256-crop') && $crop || str_starts_with($name, '256-256') && !$crop)) {
- return $node;
+ if (($prefix === '' || str_starts_with($name, $prefix))) {
+ // Prefix match
+ if (str_starts_with($name, $prefix . '256-256-crop') && $crop) {
+ // Cropped image
+ return $node;
+ }
+
+ if (str_starts_with($name, $prefix . '256-256.') && !$crop) {
+ // Uncropped image
+ return $node;
+ }
}
}
@@ -255,7 +266,7 @@ class Generator {
continue;
}
- $preview = $this->helper->getThumbnail($provider, $file, 256, 256, true);
+ $preview = $this->helper->getThumbnail($provider, $file, 256, 256, $crop);
if (!($preview instanceof IImage)) {
continue;
@@ -284,6 +295,8 @@ class Generator {
return $file;
}
}
+
+ throw new NotFoundException('No provider successfully handled the preview generation');
}
/**
diff --git a/lib/private/Preview/HEIC.php b/lib/private/Preview/HEIC.php
index 7ce6b93ba3b..ec200defce8 100644
--- a/lib/private/Preview/HEIC.php
+++ b/lib/private/Preview/HEIC.php
@@ -89,7 +89,7 @@ class HEIC extends ProviderV2 {
$this->cleanTmpFiles();
//new bitmap image object
- $image = new \OC_Image();
+ $image = new \OCP\Image();
$image->loadFromData((string) $bp);
//check if image object is valid
return $image->valid() ? $image : null;
diff --git a/lib/private/Preview/Image.php b/lib/private/Preview/Image.php
index 4bf0cb7a3d4..55e1220b56a 100644
--- a/lib/private/Preview/Image.php
+++ b/lib/private/Preview/Image.php
@@ -45,7 +45,7 @@ abstract class Image extends ProviderV2 {
return null;
}
- $image = new \OC_Image();
+ $image = new \OCP\Image();
$fileName = $this->getLocalFile($file);
diff --git a/lib/private/Preview/MP3.php b/lib/private/Preview/MP3.php
index dec838b6e5a..47d752af477 100644
--- a/lib/private/Preview/MP3.php
+++ b/lib/private/Preview/MP3.php
@@ -56,7 +56,7 @@ class MP3 extends ProviderV2 {
}
if (!is_null($picture)) {
- $image = new \OC_Image();
+ $image = new \OCP\Image();
$image->loadFromData($picture);
if ($image->valid()) {
diff --git a/lib/private/Preview/MarkDown.php b/lib/private/Preview/MarkDown.php
index 929f319f57d..d3806ce6c0e 100644
--- a/lib/private/Preview/MarkDown.php
+++ b/lib/private/Preview/MarkDown.php
@@ -137,7 +137,7 @@ class MarkDown extends TXT {
}
}
- $imageObject = new \OC_Image();
+ $imageObject = new \OCP\Image();
$imageObject->setResource($image);
return $imageObject->valid() ? $imageObject : null;
diff --git a/lib/private/Preview/Movie.php b/lib/private/Preview/Movie.php
index 781cbad1954..5cb66a00881 100644
--- a/lib/private/Preview/Movie.php
+++ b/lib/private/Preview/Movie.php
@@ -145,7 +145,7 @@ class Movie extends ProviderV2 {
exec($cmd, $output, $returnCode);
if ($returnCode === 0) {
- $image = new \OC_Image();
+ $image = new \OCP\Image();
$image->loadFromFile($tmpPath);
if ($image->valid()) {
unlink($tmpPath);
diff --git a/lib/private/Preview/Office.php b/lib/private/Preview/Office.php
index 570988aa684..6717584e887 100644
--- a/lib/private/Preview/Office.php
+++ b/lib/private/Preview/Office.php
@@ -83,7 +83,7 @@ abstract class Office extends ProviderV2 {
return null;
}
- $image = new \OC_Image();
+ $image = new \OCP\Image();
$image->loadFromData((string) $png);
$this->cleanTmpFiles();
diff --git a/lib/private/Preview/ProviderV1Adapter.php b/lib/private/Preview/ProviderV1Adapter.php
index 22db380d30d..5e50db9d8da 100644
--- a/lib/private/Preview/ProviderV1Adapter.php
+++ b/lib/private/Preview/ProviderV1Adapter.php
@@ -55,7 +55,7 @@ class ProviderV1Adapter implements IProviderV2 {
}
private function getViewAndPath(File $file) {
- $view = new View($file->getParent()->getPath());
+ $view = new View(dirname($file->getPath()));
$path = $file->getName();
return [$view, $path];
diff --git a/lib/private/Preview/SVG.php b/lib/private/Preview/SVG.php
index a4ce4899c43..fd472083533 100644
--- a/lib/private/Preview/SVG.php
+++ b/lib/private/Preview/SVG.php
@@ -68,7 +68,7 @@ class SVG extends ProviderV2 {
}
//new image object
- $image = new \OC_Image();
+ $image = new \OCP\Image();
$image->loadFromData((string) $svg);
//check if image object is valid
if ($image->valid()) {
diff --git a/lib/private/Preview/TXT.php b/lib/private/Preview/TXT.php
index bb3af6b90de..f5a68f92be4 100644
--- a/lib/private/Preview/TXT.php
+++ b/lib/private/Preview/TXT.php
@@ -103,7 +103,7 @@ class TXT extends ProviderV2 {
}
}
- $imageObject = new \OC_Image();
+ $imageObject = new \OCP\Image();
$imageObject->setResource($image);
return $imageObject->valid() ? $imageObject : null;
diff --git a/lib/private/PreviewManager.php b/lib/private/PreviewManager.php
index 6c17dd58b4b..e78bdefda8d 100644
--- a/lib/private/PreviewManager.php
+++ b/lib/private/PreviewManager.php
@@ -39,6 +39,7 @@ use OCP\Files\IAppData;
use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;
use OCP\Files\SimpleFS\ISimpleFile;
+use OCP\IBinaryFinder;
use OCP\IConfig;
use OCP\IPreview;
use OCP\IServerContainer;
@@ -47,73 +48,41 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use function array_key_exists;
class PreviewManager implements IPreview {
- /** @var IConfig */
- protected $config;
-
- /** @var IRootFolder */
- protected $rootFolder;
-
- /** @var IAppData */
- protected $appData;
-
- /** @var EventDispatcherInterface */
- protected $eventDispatcher;
-
- /** @var Generator */
- private $generator;
-
- /** @var GeneratorHelper */
- private $helper;
-
- /** @var bool */
- protected $providerListDirty = false;
-
- /** @var bool */
- protected $registeredCoreProviders = false;
-
- /** @var array */
- protected $providers = [];
+ protected IConfig $config;
+ protected IRootFolder $rootFolder;
+ protected IAppData $appData;
+ protected EventDispatcherInterface $eventDispatcher;
+ private ?Generator $generator = null;
+ private GeneratorHelper $helper;
+ protected bool $providerListDirty = false;
+ protected bool $registeredCoreProviders = false;
+ protected array $providers = [];
/** @var array mime type => support status */
- protected $mimeTypeSupportMap = [];
-
- /** @var array */
- protected $defaultProviders;
-
- /** @var string */
- protected $userId;
-
- /** @var Coordinator */
- private $bootstrapCoordinator;
+ protected array $mimeTypeSupportMap = [];
+ protected ?array $defaultProviders = null;
+ protected ?string $userId;
+ private Coordinator $bootstrapCoordinator;
/**
* Hash map (without value) of loaded bootstrap providers
- *
- * @var null[]
* @psalm-var array<string, null>
*/
- private $loadedBootstrapProviders = [];
-
- /** @var IServerContainer */
- private $container;
-
- /**
- * PreviewManager constructor.
- *
- * @param IConfig $config
- * @param IRootFolder $rootFolder
- * @param IAppData $appData
- * @param EventDispatcherInterface $eventDispatcher
- * @param string $userId
- */
- public function __construct(IConfig $config,
- IRootFolder $rootFolder,
- IAppData $appData,
- EventDispatcherInterface $eventDispatcher,
- GeneratorHelper $helper,
- $userId,
- Coordinator $bootstrapCoordinator,
- IServerContainer $container) {
+ private array $loadedBootstrapProviders = [];
+ private IServerContainer $container;
+ private IBinaryFinder $binaryFinder;
+
+ public function __construct(
+ IConfig $config,
+ IRootFolder $rootFolder,
+ IAppData $appData,
+ EventDispatcherInterface $eventDispatcher,
+ GeneratorHelper $helper,
+ ?string $userId,
+ Coordinator $bootstrapCoordinator,
+ IServerContainer $container,
+ IBinaryFinder $binaryFinder
+ ) {
$this->config = $config;
$this->rootFolder = $rootFolder;
$this->appData = $appData;
@@ -122,6 +91,7 @@ class PreviewManager implements IPreview {
$this->userId = $userId;
$this->bootstrapCoordinator = $bootstrapCoordinator;
$this->container = $container;
+ $this->binaryFinder = $binaryFinder;
}
/**
@@ -134,7 +104,7 @@ class PreviewManager implements IPreview {
* @param \Closure $callable
* @return void
*/
- public function registerProvider($mimeTypeRegex, \Closure $callable) {
+ public function registerProvider($mimeTypeRegex, \Closure $callable): void {
if (!$this->config->getSystemValue('enable_previews', true)) {
return;
}
@@ -148,9 +118,8 @@ class PreviewManager implements IPreview {
/**
* Get all providers
- * @return array
*/
- public function getProviders() {
+ public function getProviders(): array {
if (!$this->config->getSystemValue('enable_previews', true)) {
return [];
}
@@ -168,9 +137,8 @@ class PreviewManager implements IPreview {
/**
* Does the manager have any providers
- * @return bool
*/
- public function hasProviders() {
+ public function hasProviders(): bool {
$this->registerCoreProviders();
return !empty($this->providers);
}
@@ -257,11 +225,8 @@ class PreviewManager implements IPreview {
/**
* Check if a preview can be generated for a file
- *
- * @param \OCP\Files\FileInfo $file
- * @return bool
*/
- public function isAvailable(\OCP\Files\FileInfo $file) {
+ public function isAvailable(\OCP\Files\FileInfo $file): bool {
if (!$this->config->getSystemValue('enable_previews', true)) {
return false;
}
@@ -421,10 +386,10 @@ class PreviewManager implements IPreview {
// Office requires openoffice or libreoffice
$officeBinary = $this->config->getSystemValue('preview_libreoffice_path', null);
if (is_null($officeBinary)) {
- $officeBinary = \OC_Helper::findBinaryPath('libreoffice');
+ $officeBinary = $this->binaryFinder->findBinaryPath('libreoffice');
}
if (is_null($officeBinary)) {
- $officeBinary = \OC_Helper::findBinaryPath('openoffice');
+ $officeBinary = $this->binaryFinder->findBinaryPath('openoffice');
}
if (is_string($officeBinary)) {
@@ -439,9 +404,9 @@ class PreviewManager implements IPreview {
// Video requires avconv or ffmpeg
if (in_array(Preview\Movie::class, $this->getEnabledDefaultProvider())) {
- $movieBinary = \OC_Helper::findBinaryPath('avconv');
+ $movieBinary = $this->binaryFinder->findBinaryPath('avconv');
if (is_null($movieBinary)) {
- $movieBinary = \OC_Helper::findBinaryPath('ffmpeg');
+ $movieBinary = $this->binaryFinder->findBinaryPath('ffmpeg');
}
if (is_string($movieBinary)) {
@@ -469,7 +434,7 @@ class PreviewManager implements IPreview {
$this->registerProvider($provider->getMimeTypeRegex(), function () use ($provider) {
try {
- return $this->container->query($provider->getService());
+ return $this->container->get($provider->getService());
} catch (QueryException $e) {
return null;
}
diff --git a/lib/private/Profile/ProfileManager.php b/lib/private/Profile/ProfileManager.php
index edb51458c66..f038986cf6d 100644
--- a/lib/private/Profile/ProfileManager.php
+++ b/lib/private/Profile/ProfileManager.php
@@ -44,6 +44,7 @@ use OCP\IConfig;
use OCP\IUser;
use OCP\L10N\IFactory;
use OCP\Profile\ILinkAction;
+use OCP\Cache\CappedMemoryCache;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
@@ -81,6 +82,8 @@ class ProfileManager {
/** @var null|ILinkAction[] */
private $sortedActions = null;
+ /** @var CappedMemoryCache<ProfileConfig> */
+ private CappedMemoryCache $configCache;
private const CORE_APP_ID = 'core';
@@ -127,6 +130,7 @@ class ProfileManager {
$this->l10nFactory = $l10nFactory;
$this->logger = $logger;
$this->coordinator = $coordinator;
+ $this->configCache = new CappedMemoryCache();
}
/**
@@ -348,13 +352,13 @@ class ProfileManager {
* Return the default profile config
*/
private function getDefaultProfileConfig(IUser $targetUser, ?IUser $visitingUser): array {
- // Contruct the default config for actions
+ // Construct the default config for actions
$actionsConfig = [];
foreach ($this->getActions($targetUser, $visitingUser) as $action) {
$actionsConfig[$action->getId()] = ['visibility' => ProfileConfig::DEFAULT_VISIBILITY];
}
- // Contruct the default config for account properties
+ // Construct the default config for account properties
$propertiesConfig = [];
foreach (ProfileConfig::DEFAULT_PROPERTY_VISIBILITY as $property => $visibility) {
$propertiesConfig[$property] = ['visibility' => $visibility];
@@ -370,7 +374,10 @@ class ProfileManager {
public function getProfileConfig(IUser $targetUser, ?IUser $visitingUser): array {
$defaultProfileConfig = $this->getDefaultProfileConfig($targetUser, $visitingUser);
try {
- $config = $this->configMapper->get($targetUser->getUID());
+ if (($config = $this->configCache[$targetUser->getUID()]) === null) {
+ $config = $this->configMapper->get($targetUser->getUID());
+ $this->configCache[$targetUser->getUID()] = $config;
+ }
// Merge defaults with the existing config in case the defaults are missing
$config->setConfigArray(array_merge(
$defaultProfileConfig,
diff --git a/lib/private/Repair.php b/lib/private/Repair.php
index fb65789dd8a..e2e5da79216 100644
--- a/lib/private/Repair.php
+++ b/lib/private/Repair.php
@@ -34,6 +34,12 @@
*/
namespace OC;
+use OCP\AppFramework\QueryException;
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\Collaboration\Resources\IManager;
+use OCP\EventDispatcher\IEventDispatcher;
+use OCP\Migration\IOutput;
+use OCP\Migration\IRepairStep;
use OC\App\AppStore\Bundles\BundleFetcher;
use OC\Avatar\AvatarManager;
use OC\DB\Connection;
@@ -44,14 +50,15 @@ use OC\Repair\CleanTags;
use OC\Repair\ClearFrontendCaches;
use OC\Repair\ClearGeneratedAvatarCache;
use OC\Repair\Collation;
+use OC\Repair\Events\RepairAdvanceEvent;
+use OC\Repair\Events\RepairErrorEvent;
+use OC\Repair\Events\RepairFinishEvent;
+use OC\Repair\Events\RepairInfoEvent;
+use OC\Repair\Events\RepairStartEvent;
+use OC\Repair\Events\RepairStepEvent;
+use OC\Repair\Events\RepairWarningEvent;
use OC\Repair\MoveUpdaterStepFile;
-use OC\Repair\NC22\LookupServerSendCheck;
-use OC\Repair\Owncloud\CleanPreviews;
-use OC\Repair\Owncloud\MigrateOauthTables;
use OC\Repair\NC11\FixMountStorages;
-use OC\Repair\Owncloud\MoveAvatars;
-use OC\Repair\Owncloud\InstallCoreBundle;
-use OC\Repair\Owncloud\UpdateLanguageCodes;
use OC\Repair\NC13\AddLogRotateJob;
use OC\Repair\NC14\AddPreviewBackgroundCleanupJob;
use OC\Repair\NC16\AddClenupLoginFlowV2BackgroundJob;
@@ -63,23 +70,23 @@ use OC\Repair\NC20\EncryptionMigration;
use OC\Repair\NC20\ShippedDashboardEnable;
use OC\Repair\NC21\AddCheckForUserCertificatesJob;
use OC\Repair\NC21\ValidatePhoneNumber;
+use OC\Repair\NC22\LookupServerSendCheck;
+use OC\Repair\NC24\AddTokenCleanupJob;
use OC\Repair\OldGroupMembershipShares;
+use OC\Repair\Owncloud\CleanPreviews;
use OC\Repair\Owncloud\DropAccountTermsTable;
+use OC\Repair\Owncloud\InstallCoreBundle;
+use OC\Repair\Owncloud\MigrateOauthTables;
+use OC\Repair\Owncloud\MoveAvatars;
use OC\Repair\Owncloud\SaveAccountsTableData;
+use OC\Repair\Owncloud\UpdateLanguageCodes;
use OC\Repair\RemoveLinkShares;
use OC\Repair\RepairDavShares;
use OC\Repair\RepairInvalidShares;
use OC\Repair\RepairMimeTypes;
use OC\Repair\SqliteAutoincrement;
use OC\Template\JSCombiner;
-use OCP\AppFramework\QueryException;
-use OCP\AppFramework\Utility\ITimeFactory;
-use OCP\Collaboration\Resources\IManager;
-use OCP\Migration\IOutput;
-use OCP\Migration\IRepairStep;
use Psr\Log\LoggerInterface;
-use Symfony\Component\EventDispatcher\EventDispatcherInterface;
-use Symfony\Component\EventDispatcher\GenericEvent;
use Throwable;
class Repair implements IOutput {
@@ -87,8 +94,7 @@ class Repair implements IOutput {
/** @var IRepairStep[] */
private $repairSteps;
- /** @var EventDispatcherInterface */
- private $dispatcher;
+ private IEventDispatcher $dispatcher;
/** @var string */
private $currentStep;
@@ -100,7 +106,7 @@ class Repair implements IOutput {
*
* @param IRepairStep[] $repairSteps array of RepairStep instances
*/
- public function __construct(array $repairSteps, EventDispatcherInterface $dispatcher, LoggerInterface $logger) {
+ public function __construct(array $repairSteps, IEventDispatcher $dispatcher, LoggerInterface $logger) {
$this->repairSteps = $repairSteps;
$this->dispatcher = $dispatcher;
$this->logger = $logger;
@@ -111,19 +117,19 @@ class Repair implements IOutput {
*/
public function run() {
if (count($this->repairSteps) === 0) {
- $this->emit('\OC\Repair', 'info', ['No repair steps available']);
+ $this->dispatcher->dispatchTyped(new RepairInfoEvent('No repair steps available'));
return;
}
// run each repair step
foreach ($this->repairSteps as $step) {
$this->currentStep = $step->getName();
- $this->emit('\OC\Repair', 'step', [$this->currentStep]);
+ $this->dispatcher->dispatchTyped(new RepairStepEvent($this->currentStep));
try {
$step->run($this);
} catch (\Exception $e) {
$this->logger->error("Exception while executing repair step " . $step->getName(), ['exception' => $e]);
- $this->emit('\OC\Repair', 'error', [$e->getMessage()]);
+ $this->dispatcher->dispatchTyped(new RepairErrorEvent($e->getMessage()));
}
}
}
@@ -137,7 +143,7 @@ class Repair implements IOutput {
public function addStep($repairStep) {
if (is_string($repairStep)) {
try {
- $s = \OC::$server->query($repairStep);
+ $s = \OC::$server->get($repairStep);
} catch (QueryException $e) {
if (class_exists($repairStep)) {
try {
@@ -209,6 +215,7 @@ class Repair implements IOutput {
\OC::$server->get(AddCheckForUserCertificatesJob::class),
\OC::$server->get(RepairDavShares::class),
\OC::$server->get(LookupServerSendCheck::class),
+ \OC::$server->get(AddTokenCleanupJob::class),
];
}
@@ -248,20 +255,11 @@ class Repair implements IOutput {
}
/**
- * @param string $scope
- * @param string $method
- * @param array $arguments
+ * @param string $message
*/
- public function emit($scope, $method, array $arguments = []) {
- if (!is_null($this->dispatcher)) {
- $this->dispatcher->dispatch("$scope::$method",
- new GenericEvent("$scope::$method", $arguments));
- }
- }
-
- public function info($string) {
+ public function info($message) {
// for now just emit as we did in the past
- $this->emit('\OC\Repair', 'info', [$string]);
+ $this->dispatcher->dispatchTyped(new RepairInfoEvent($message));
}
/**
@@ -269,7 +267,7 @@ class Repair implements IOutput {
*/
public function warning($message) {
// for now just emit as we did in the past
- $this->emit('\OC\Repair', 'warning', [$message]);
+ $this->dispatcher->dispatchTyped(new RepairWarningEvent($message));
}
/**
@@ -277,16 +275,16 @@ class Repair implements IOutput {
*/
public function startProgress($max = 0) {
// for now just emit as we did in the past
- $this->emit('\OC\Repair', 'startProgress', [$max, $this->currentStep]);
+ $this->dispatcher->dispatchTyped(new RepairStartEvent($max, $this->currentStep));
}
/**
- * @param int $step
+ * @param int $step number of step to advance
* @param string $description
*/
public function advance($step = 1, $description = '') {
// for now just emit as we did in the past
- $this->emit('\OC\Repair', 'advance', [$step, $description]);
+ $this->dispatcher->dispatchTyped(new RepairAdvanceEvent($step, $description));
}
/**
@@ -294,6 +292,6 @@ class Repair implements IOutput {
*/
public function finishProgress() {
// for now just emit as we did in the past
- $this->emit('\OC\Repair', 'finishProgress', []);
+ $this->dispatcher->dispatchTyped(new RepairFinishEvent());
}
}
diff --git a/lib/private/Repair/Events/RepairAdvanceEvent.php b/lib/private/Repair/Events/RepairAdvanceEvent.php
new file mode 100644
index 00000000000..174d4ec48cd
--- /dev/null
+++ b/lib/private/Repair/Events/RepairAdvanceEvent.php
@@ -0,0 +1,48 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright Copyright (c) 2022 Côme Chilliet <come.chilliet@nextcloud.com>
+ *
+ * @author Côme Chilliet <come.chilliet@nextcloud.com>
+ *
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+namespace OC\Repair\Events;
+
+use OCP\EventDispatcher\Event;
+
+class RepairAdvanceEvent extends Event {
+ private int $increment;
+ private string $description;
+
+ public function __construct(
+ int $increment,
+ string $description
+ ) {
+ $this->increment = $increment;
+ $this->description = $description;
+ }
+
+ public function getIncrement(): int {
+ return $this->increment;
+ }
+
+ public function getDescription(): string {
+ return $this->description;
+ }
+}
diff --git a/lib/public/BackgroundJob.php b/lib/private/Repair/Events/RepairErrorEvent.php
index 2be11ab0a6e..7f8a87cfda3 100644
--- a/lib/public/BackgroundJob.php
+++ b/lib/private/Repair/Events/RepairErrorEvent.php
@@ -1,10 +1,11 @@
<?php
+
+declare(strict_types=1);
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @copyright Copyright (c) 2022 Côme Chilliet <come.chilliet@nextcloud.com>
*
- * @author Jakob Sack <mail@jakobsack.de>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Robin Appelman <robin@icewind.nl>
+ * @author Côme Chilliet <come.chilliet@nextcloud.com>
*
* @license AGPL-3.0
*
@@ -21,25 +22,20 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
-namespace OCP;
+namespace OC\Repair\Events;
-/**
- * @since 4.5.0
- * @deprecated 14.0.0
- */
-class BackgroundJob {
- /**
- * @since 5.0.0
- * @deprecated 14.0.0
- */
- public static function getExecutionType() {
- return '';
+use OCP\EventDispatcher\Event;
+
+class RepairErrorEvent extends Event {
+ private string $message;
+
+ public function __construct(
+ string $message
+ ) {
+ $this->message = $message;
}
- /**
- * @since 5.0.0
- * @deprecated 14.0.0
- */
- public static function setExecutionType($type) {
+ public function getMessage(): string {
+ return $this->message;
}
}
diff --git a/lib/private/Repair/Events/RepairFinishEvent.php b/lib/private/Repair/Events/RepairFinishEvent.php
new file mode 100644
index 00000000000..abdc2acd1ce
--- /dev/null
+++ b/lib/private/Repair/Events/RepairFinishEvent.php
@@ -0,0 +1,30 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright Copyright (c) 2022 Côme Chilliet <come.chilliet@nextcloud.com>
+ *
+ * @author Côme Chilliet <come.chilliet@nextcloud.com>
+ *
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+namespace OC\Repair\Events;
+
+use OCP\EventDispatcher\Event;
+
+class RepairFinishEvent extends Event {
+}
diff --git a/lib/private/BackgroundJob/Legacy/RegularJob.php b/lib/private/Repair/Events/RepairInfoEvent.php
index 0f337a35eb0..5c2a53fce4f 100644
--- a/lib/private/BackgroundJob/Legacy/RegularJob.php
+++ b/lib/private/Repair/Events/RepairInfoEvent.php
@@ -1,9 +1,11 @@
<?php
+
+declare(strict_types=1);
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @copyright Copyright (c) 2022 Côme Chilliet <come.chilliet@nextcloud.com>
*
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Robin Appelman <robin@icewind.nl>
+ * @author Côme Chilliet <come.chilliet@nextcloud.com>
*
* @license AGPL-3.0
*
@@ -20,22 +22,20 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
-namespace OC\BackgroundJob\Legacy;
+namespace OC\Repair\Events;
-use OCP\AutoloadNotAllowedException;
+use OCP\EventDispatcher\Event;
-/**
- * @deprecated internal class, use \OCP\BackgroundJob\QueuedJob
- */
-class RegularJob extends \OC\BackgroundJob\Job {
- public function run($argument) {
- try {
- if (is_callable($argument)) {
- call_user_func($argument);
- }
- } catch (AutoloadNotAllowedException $e) {
- // job is from a disabled app, ignore
- return null;
- }
+class RepairInfoEvent extends Event {
+ private string $message;
+
+ public function __construct(
+ string $message
+ ) {
+ $this->message = $message;
+ }
+
+ public function getMessage(): string {
+ return $this->message;
}
}
diff --git a/lib/private/BackgroundJob/Legacy/QueuedJob.php b/lib/private/Repair/Events/RepairStartEvent.php
index fd001738d4f..7bb270595c5 100644
--- a/lib/private/BackgroundJob/Legacy/QueuedJob.php
+++ b/lib/private/Repair/Events/RepairStartEvent.php
@@ -1,10 +1,11 @@
<?php
+
+declare(strict_types=1);
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @copyright Copyright (c) 2022 Côme Chilliet <come.chilliet@nextcloud.com>
*
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Robin Appelman <robin@icewind.nl>
+ * @author Côme Chilliet <come.chilliet@nextcloud.com>
*
* @license AGPL-3.0
*
@@ -21,18 +22,27 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
-namespace OC\BackgroundJob\Legacy;
+namespace OC\Repair\Events;
-/**
- * @deprecated internal class, use \OCP\BackgroundJob\QueuedJob
- */
-class QueuedJob extends \OC\BackgroundJob\QueuedJob {
- public function run($argument) {
- $class = $argument['klass'];
- $method = $argument['method'];
- $parameters = $argument['parameters'];
- if (is_callable([$class, $method])) {
- call_user_func([$class, $method], $parameters);
- }
+use OCP\EventDispatcher\Event;
+
+class RepairStartEvent extends Event {
+ private int $max;
+ private string $current;
+
+ public function __construct(
+ int $max,
+ string $current
+ ) {
+ $this->max = $max;
+ $this->current = $current;
+ }
+
+ public function getMaxStep(): int {
+ return $this->max;
+ }
+
+ public function getCurrentStepName(): string {
+ return $this->current;
}
}
diff --git a/lib/private/Repair/Events/RepairStepEvent.php b/lib/private/Repair/Events/RepairStepEvent.php
new file mode 100644
index 00000000000..82af670b950
--- /dev/null
+++ b/lib/private/Repair/Events/RepairStepEvent.php
@@ -0,0 +1,41 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright Copyright (c) 2022 Côme Chilliet <come.chilliet@nextcloud.com>
+ *
+ * @author Côme Chilliet <come.chilliet@nextcloud.com>
+ *
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+namespace OC\Repair\Events;
+
+use OCP\EventDispatcher\Event;
+
+class RepairStepEvent extends Event {
+ private string $stepName;
+
+ public function __construct(
+ string $stepName
+ ) {
+ $this->stepName = $stepName;
+ }
+
+ public function getStepName(): string {
+ return $this->stepName;
+ }
+}
diff --git a/lib/private/Repair/Events/RepairWarningEvent.php b/lib/private/Repair/Events/RepairWarningEvent.php
new file mode 100644
index 00000000000..2aaa44d4719
--- /dev/null
+++ b/lib/private/Repair/Events/RepairWarningEvent.php
@@ -0,0 +1,41 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright Copyright (c) 2022 Côme Chilliet <come.chilliet@nextcloud.com>
+ *
+ * @author Côme Chilliet <come.chilliet@nextcloud.com>
+ *
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+namespace OC\Repair\Events;
+
+use OCP\EventDispatcher\Event;
+
+class RepairWarningEvent extends Event {
+ private string $message;
+
+ public function __construct(
+ string $message
+ ) {
+ $this->message = $message;
+ }
+
+ public function getMessage(): string {
+ return $this->message;
+ }
+}
diff --git a/lib/private/Repair/NC22/LookupServerSendCheck.php b/lib/private/Repair/NC22/LookupServerSendCheck.php
index c68a608ed25..46029357dbe 100644
--- a/lib/private/Repair/NC22/LookupServerSendCheck.php
+++ b/lib/private/Repair/NC22/LookupServerSendCheck.php
@@ -33,12 +33,8 @@ use OCP\Migration\IOutput;
use OCP\Migration\IRepairStep;
class LookupServerSendCheck implements IRepairStep {
-
- /** @var IJobList */
- private $jobList;
-
- /** @var IConfig */
- private $config;
+ private IJobList $jobList;
+ private IConfig $config;
public function __construct(IJobList $jobList, IConfig $config) {
$this->jobList = $jobList;
diff --git a/lib/private/Repair/NC24/AddTokenCleanupJob.php b/lib/private/Repair/NC24/AddTokenCleanupJob.php
new file mode 100644
index 00000000000..97539c5692f
--- /dev/null
+++ b/lib/private/Repair/NC24/AddTokenCleanupJob.php
@@ -0,0 +1,47 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright 2022 Thomas Citharel <nextcloud@tcit.fr>
+ *
+ * @author Thomas Citharel <nextcloud@tcit.fr>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+namespace OC\Repair\NC24;
+
+use OC\Authentication\Token\TokenCleanupJob;
+use OCP\BackgroundJob\IJobList;
+use OCP\Migration\IOutput;
+use OCP\Migration\IRepairStep;
+
+class AddTokenCleanupJob implements IRepairStep {
+ private IJobList $jobList;
+
+ public function __construct(IJobList $jobList) {
+ $this->jobList = $jobList;
+ }
+
+ public function getName(): string {
+ return 'Add token cleanup job';
+ }
+
+ public function run(IOutput $output) {
+ $this->jobList->add(TokenCleanupJob::class);
+ }
+}
diff --git a/lib/private/Repair/RemoveLinkShares.php b/lib/private/Repair/RemoveLinkShares.php
index 1b0270e928d..e1ce78cdbf3 100644
--- a/lib/private/Repair/RemoveLinkShares.php
+++ b/lib/private/Repair/RemoveLinkShares.php
@@ -217,7 +217,7 @@ class RemoveLinkShares implements IRepairStep {
$output->finishProgress();
$shareResult->closeCursor();
- // Notifiy all admins
+ // Notify all admins
$adminGroup = $this->groupManager->get('admin');
$adminUsers = $adminGroup->getUsers();
foreach ($adminUsers as $user) {
diff --git a/lib/private/Repair/RepairMimeTypes.php b/lib/private/Repair/RepairMimeTypes.php
index 61512627258..5b216331dc7 100644
--- a/lib/private/Repair/RepairMimeTypes.php
+++ b/lib/private/Repair/RepairMimeTypes.php
@@ -211,6 +211,15 @@ class RepairMimeTypes implements IRepairStep {
return $this->updateMimetypes($updatedMimetypes);
}
+ private function introduceOnlyofficeFormType() {
+ $updatedMimetypes = [
+ "oform" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document.oform",
+ "docxf" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document.docxf",
+ ];
+
+ return $this->updateMimetypes($updatedMimetypes);
+ }
+
/**
* Fix mime types
@@ -260,5 +269,9 @@ class RepairMimeTypes implements IRepairStep {
if (version_compare($ocVersionFromBeforeUpdate, '23.0.0.2', '<') && $this->introduceFlatOpenDocumentType()) {
$out->info('Fixed Flat OpenDocument mime types');
}
+
+ if (version_compare($ocVersionFromBeforeUpdate, '25.0.0.2', '<') && $this->introduceOnlyofficeFormType()) {
+ $out->info('Fixed ONLYOFFICE Forms OpenXML mime types');
+ }
}
}
diff --git a/lib/private/Route/Router.php b/lib/private/Route/Router.php
index b957173cacc..7e1acd49800 100644
--- a/lib/private/Route/Router.php
+++ b/lib/private/Route/Router.php
@@ -409,7 +409,7 @@ class Router implements IRouter {
* register the routes for the app. The application class will be chosen by
* camelcasing the appname, e.g.: my_app will be turned into
* \OCA\MyApp\AppInfo\Application. If that class does not exist, a default
- * App will be intialized. This makes it optional to ship an
+ * App will be initialized. This makes it optional to ship an
* appinfo/application.php by using the built in query resolver
*
* @param array $routes the application routes
diff --git a/lib/private/Security/Bruteforce/Throttler.php b/lib/private/Security/Bruteforce/Throttler.php
index c47d102b881..299cab93eb3 100644
--- a/lib/private/Security/Bruteforce/Throttler.php
+++ b/lib/private/Security/Bruteforce/Throttler.php
@@ -36,6 +36,7 @@ use OC\Security\Normalizer\IpAddress;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\IConfig;
use OCP\IDBConnection;
+use OCP\Security\Bruteforce\IThrottler;
use OCP\Security\Bruteforce\MaxDelayReached;
use Psr\Log\LoggerInterface;
@@ -52,11 +53,8 @@ use Psr\Log\LoggerInterface;
*
* @package OC\Security\Bruteforce
*/
-class Throttler {
+class Throttler implements IThrottler {
public const LOGIN_ACTION = 'login';
- public const MAX_DELAY = 25;
- public const MAX_DELAY_MS = 25000; // in milliseconds
- public const MAX_ATTEMPTS = 10;
/** @var IDBConnection */
private $db;
@@ -65,8 +63,8 @@ class Throttler {
private LoggerInterface $logger;
/** @var IConfig */
private $config;
- /** @var bool */
- private $hasAttemptsDeleted = false;
+ /** @var bool[] */
+ private $hasAttemptsDeleted = [];
public function __construct(IDBConnection $db,
ITimeFactory $timeFactory,
@@ -225,7 +223,7 @@ class Throttler {
$maxAgeHours = 48;
}
- if ($ip === '' || $this->hasAttemptsDeleted) {
+ if ($ip === '' || isset($this->hasAttemptsDeleted[$action])) {
return 0;
}
@@ -303,7 +301,7 @@ class Throttler {
$qb->executeStatement();
- $this->hasAttemptsDeleted = true;
+ $this->hasAttemptsDeleted[$action] = true;
}
/**
@@ -311,7 +309,7 @@ class Throttler {
*
* @param string $ip
*/
- public function resetDelayForIP($ip) {
+ public function resetDelayForIP(string $ip): void {
$cutoffTime = $this->getCutoffTimestamp();
$qb = $this->db->getQueryBuilder();
diff --git a/lib/private/Security/CSP/ContentSecurityPolicyNonceManager.php b/lib/private/Security/CSP/ContentSecurityPolicyNonceManager.php
index f3329135727..1167b3358d2 100644
--- a/lib/private/Security/CSP/ContentSecurityPolicyNonceManager.php
+++ b/lib/private/Security/CSP/ContentSecurityPolicyNonceManager.php
@@ -80,10 +80,8 @@ class ContentSecurityPolicyNonceManager {
public function browserSupportsCspV3(): bool {
$browserWhitelist = [
Request::USER_AGENT_CHROME,
- // Firefox 45+
- '/^Mozilla\/5\.0 \([^)]+\) Gecko\/[0-9.]+ Firefox\/(4[5-9]|[5-9][0-9])\.[0-9.]+$/',
- // Safari 12+
- '/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\) Version\/(?:1[2-9]|[2-9][0-9])\.[0-9]+(?:\.[0-9]+)? Safari\/[0-9.A-Z]+$/',
+ Request::USER_AGENT_FIREFOX,
+ Request::USER_AGENT_SAFARI,
];
if ($this->request->isUserAgent($browserWhitelist)) {
diff --git a/lib/private/Security/TrustedDomainHelper.php b/lib/private/Security/TrustedDomainHelper.php
index 0688ebba5b3..1927af9cb1d 100644
--- a/lib/private/Security/TrustedDomainHelper.php
+++ b/lib/private/Security/TrustedDomainHelper.php
@@ -97,7 +97,7 @@ class TrustedDomainHelper implements ITrustedDomainHelper {
if (preg_match(Request::REGEX_LOCALHOST, $domain) === 1) {
return true;
}
- // Reject misformed domains in any case
+ // Reject malformed domains in any case
if (strpos($domain, '-') === 0 || strpos($domain, '..') !== false) {
return false;
}
diff --git a/lib/private/Security/VerificationToken/CleanUpJob.php b/lib/private/Security/VerificationToken/CleanUpJob.php
index be9d24ece45..4510dcffe0a 100644
--- a/lib/private/Security/VerificationToken/CleanUpJob.php
+++ b/lib/private/Security/VerificationToken/CleanUpJob.php
@@ -28,27 +28,20 @@ namespace OC\Security\VerificationToken;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\IConfig;
-use OCP\ILogger;
use OCP\IUserManager;
+use OCP\BackgroundJob\IJobList;
+use OCP\BackgroundJob\Job;
use OCP\Security\VerificationToken\InvalidTokenException;
use OCP\Security\VerificationToken\IVerificationToken;
-class CleanUpJob extends \OCP\BackgroundJob\Job {
-
- /** @var int */
- protected $runNotBefore;
- /** @var string */
- protected $userId;
- /** @var string */
- protected $subject;
- /** @var string */
- protected $pwdPrefix;
- /** @var IConfig */
- private $config;
- /** @var IVerificationToken */
- private $verificationToken;
- /** @var IUserManager */
- private $userManager;
+class CleanUpJob extends Job {
+ protected ?int $runNotBefore = null;
+ protected ?string $userId = null;
+ protected ?string $subject = null;
+ protected ?string $pwdPrefix = null;
+ private IConfig $config;
+ private IVerificationToken $verificationToken;
+ private IUserManager $userManager;
public function __construct(ITimeFactory $time, IConfig $config, IVerificationToken $verificationToken, IUserManager $userManager) {
parent::__construct($time);
@@ -81,10 +74,10 @@ class CleanUpJob extends \OCP\BackgroundJob\Job {
}
}
- public function execute($jobList, ILogger $logger = null) {
+ public function start(IJobList $jobList): void {
if ($this->time->getTime() >= $this->runNotBefore) {
$jobList->remove($this, $this->argument);
- parent::execute($jobList, $logger);
+ parent::start($jobList);
}
}
}
diff --git a/lib/private/Server.php b/lib/private/Server.php
index 6e6fa430489..c1a50cfcaff 100644
--- a/lib/private/Server.php
+++ b/lib/private/Server.php
@@ -73,6 +73,7 @@ use OC\Collaboration\Collaborators\MailPlugin;
use OC\Collaboration\Collaborators\RemoteGroupPlugin;
use OC\Collaboration\Collaborators\RemotePlugin;
use OC\Collaboration\Collaborators\UserPlugin;
+use OC\Collaboration\Reference\ReferenceManager;
use OC\Command\CronBus;
use OC\Comments\ManagerFactory as CommentsManagerFactory;
use OC\Contacts\ContactsMenu\ActionFactory;
@@ -151,6 +152,9 @@ use OC\SystemTag\ManagerFactory as SystemTagManagerFactory;
use OC\Tagging\TagMapper;
use OC\Talk\Broker;
use OC\Template\JSCombiner;
+use OC\User\DisplayNameCache;
+use OC\User\Listeners\UserChangedListener;
+use OC\User\Listeners\UserDeletedListener;
use OCA\Theming\ImageManager;
use OCA\Theming\ThemingDefaults;
use OCA\Theming\Util;
@@ -159,6 +163,7 @@ use OCP\App\IAppManager;
use OCP\Authentication\LoginCredentials\IStore;
use OCP\BackgroundJob\IJobList;
use OCP\Collaboration\AutoComplete\IManager;
+use OCP\Collaboration\Reference\IReferenceManager;
use OCP\Command\IBus;
use OCP\Comments\ICommentsManager;
use OCP\Contacts\ContactsMenu\IActionFactory;
@@ -180,7 +185,6 @@ use OCP\Files\IMimeTypeLoader;
use OCP\Files\IRootFolder;
use OCP\Files\Lock\ILockManager;
use OCP\Files\Mount\IMountManager;
-use OCP\Files\NotFoundException;
use OCP\Files\Storage\IStorageFactory;
use OCP\Files\Template\ITemplateManager;
use OCP\FullTextSearch\IFullTextSearchManager;
@@ -200,6 +204,7 @@ use OCP\IAvatarManager;
use OCP\ICache;
use OCP\ICacheFactory;
use OCP\ICertificateManager;
+use OCP\IBinaryFinder;
use OCP\IDateTimeFormatter;
use OCP\IDateTimeZone;
use OCP\IDBConnection;
@@ -217,7 +222,6 @@ use OCP\ISession;
use OCP\ITagManager;
use OCP\ITempManager;
use OCP\IURLGenerator;
-use OCP\IUser;
use OCP\IUserManager;
use OCP\IUserSession;
use OCP\L10N\IFactory;
@@ -231,6 +235,7 @@ use OCP\Remote\Api\IApiFactory;
use OCP\Remote\IInstanceFactory;
use OCP\RichObjectStrings\IValidator;
use OCP\Route\IRouter;
+use OCP\Security\Bruteforce\IThrottler;
use OCP\Security\IContentSecurityPolicyManager;
use OCP\Security\ICredentialsManager;
use OCP\Security\ICrypto;
@@ -249,6 +254,7 @@ use OCP\User\Events\BeforeUserLoggedOutEvent;
use OCP\User\Events\PasswordUpdatedEvent;
use OCP\User\Events\PostLoginEvent;
use OCP\User\Events\UserChangedEvent;
+use OCP\User\Events\UserDeletedEvent;
use OCP\User\Events\UserLoggedInEvent;
use OCP\User\Events\UserLoggedInWithCookieEvent;
use OCP\User\Events\UserLoggedOutEvent;
@@ -332,7 +338,8 @@ class Server extends ServerContainer implements IServerContainer {
$c->get(GeneratorHelper::class),
$c->get(ISession::class)->get('user_id'),
$c->get(Coordinator::class),
- $c->get(IServerContainer::class)
+ $c->get(IServerContainer::class),
+ $c->get(IBinaryFinder::class)
);
});
/** @deprecated 19.0.0 */
@@ -351,7 +358,7 @@ class Server extends ServerContainer implements IServerContainer {
return new Profiler($c->get(SystemConfig::class));
});
- $this->registerService(\OCP\Encryption\IManager::class, function (Server $c) {
+ $this->registerService(\OCP\Encryption\IManager::class, function (Server $c): Encryption\Manager {
$view = new View();
$util = new Encryption\Util(
$view,
@@ -472,6 +479,10 @@ class Server extends ServerContainer implements IServerContainer {
$this->registerDeprecatedAlias('UserManager', \OC\User\Manager::class);
$this->registerAlias(\OCP\IUserManager::class, \OC\User\Manager::class);
+ $this->registerService(DisplayNameCache::class, function (ContainerInterface $c) {
+ return $c->get(\OC\User\Manager::class)->getDisplayNameCache();
+ });
+
$this->registerService(\OCP\IGroupManager::class, function (ContainerInterface $c) {
$groupManager = new \OC\Group\Manager(
$this->get(IUserManager::class),
@@ -1001,6 +1012,7 @@ class Server extends ServerContainer implements IServerContainer {
$this->registerAlias(ITrustedDomainHelper::class, TrustedDomainHelper::class);
/** @deprecated 19.0.0 */
$this->registerDeprecatedAlias('Throttler', Throttler::class);
+ $this->registerAlias(IThrottler::class, Throttler::class);
$this->registerService('IntegrityCodeChecker', function (ContainerInterface $c) {
// IConfig and IAppManager requires a working database. This code
// might however be called when ownCloud is not yet setup.
@@ -1178,14 +1190,12 @@ class Server extends ServerContainer implements IServerContainer {
$manager->registerDisplayNameResolver('user', function ($id) use ($c) {
$manager = $c->get(IUserManager::class);
- $user = $manager->get($id);
- if (is_null($user)) {
- $l = $c->getL10N('core');
- $displayName = $l->t('Unknown user');
- } else {
- $displayName = $user->getDisplayName();
+ $userDisplayName = $manager->getDisplayName($id);
+ if ($userDisplayName === null) {
+ $l = $c->get(IFactory::class)->get('core');
+ return $l->t('Unknown user');
}
- return $displayName;
+ return $userDisplayName;
});
return $manager;
@@ -1330,6 +1340,8 @@ class Server extends ServerContainer implements IServerContainer {
$this->registerAlias(\OCP\Collaboration\Resources\IProviderManager::class, \OC\Collaboration\Resources\ProviderManager::class);
$this->registerAlias(\OCP\Collaboration\Resources\IManager::class, \OC\Collaboration\Resources\Manager::class);
+ $this->registerAlias(IReferenceManager::class, ReferenceManager::class);
+
$this->registerDeprecatedAlias('SettingsManager', \OC\Settings\Manager::class);
$this->registerAlias(\OCP\Settings\IManager::class, \OC\Settings\Manager::class);
$this->registerService(\OC\Files\AppData\Factory::class, function (ContainerInterface $c) {
@@ -1353,7 +1365,13 @@ class Server extends ServerContainer implements IServerContainer {
});
$this->registerService(ICloudIdManager::class, function (ContainerInterface $c) {
- return new CloudIdManager($c->get(\OCP\Contacts\IManager::class), $c->get(IURLGenerator::class), $c->get(IUserManager::class));
+ return new CloudIdManager(
+ $c->get(\OCP\Contacts\IManager::class),
+ $c->get(IURLGenerator::class),
+ $c->get(IUserManager::class),
+ $c->get(ICacheFactory::class),
+ $c->get(IEventDispatcher::class),
+ );
});
$this->registerAlias(\OCP\GlobalScale\IConfig::class, \OC\GlobalScale\Config::class);
@@ -1440,6 +1458,8 @@ class Server extends ServerContainer implements IServerContainer {
$this->registerAlias(\OCP\Files\AppData\IAppDataFactory::class, \OC\Files\AppData\Factory::class);
+ $this->registerAlias(IBinaryFinder::class, BinaryFinder::class);
+
$this->connectDispatcher();
}
@@ -1473,52 +1493,13 @@ class Server extends ServerContainer implements IServerContainer {
return $this->get(\OC\Calendar\Room\Manager::class);
}
- private function connectDispatcher() {
- $dispatcher = $this->get(SymfonyAdapter::class);
-
- // Delete avatar on user deletion
- $dispatcher->addListener('OCP\IUser::preDelete', function (GenericEvent $e) {
- $logger = $this->get(LoggerInterface::class);
- $manager = $this->getAvatarManager();
- /** @var IUser $user */
- $user = $e->getSubject();
-
- try {
- $avatar = $manager->getAvatar($user->getUID());
- $avatar->remove();
- } catch (NotFoundException $e) {
- // no avatar to remove
- } catch (\Exception $e) {
- // Ignore exceptions
- $logger->info('Could not cleanup avatar of ' . $user->getUID());
- }
- });
-
- $dispatcher->addListener('OCP\IUser::changeUser', function (GenericEvent $e) {
- $manager = $this->getAvatarManager();
- /** @var IUser $user */
- $user = $e->getSubject();
- $feature = $e->getArgument('feature');
- $oldValue = $e->getArgument('oldValue');
- $value = $e->getArgument('value');
-
- // We only change the avatar on display name changes
- if ($feature !== 'displayName') {
- return;
- }
-
- try {
- $avatar = $manager->getAvatar($user->getUID());
- $avatar->userChanged($feature, $oldValue, $value);
- } catch (NotFoundException $e) {
- // no avatar to remove
- }
- });
-
- /** @var IEventDispatcher $eventDispatched */
- $eventDispatched = $this->get(IEventDispatcher::class);
- $eventDispatched->addServiceListener(LoginFailed::class, LoginFailedListener::class);
- $eventDispatched->addServiceListener(PostLoginEvent::class, UserLoggedInListener::class);
+ private function connectDispatcher(): void {
+ /** @var IEventDispatcher $eventDispatcher */
+ $eventDispatcher = $this->get(IEventDispatcher::class);
+ $eventDispatcher->addServiceListener(LoginFailed::class, LoginFailedListener::class);
+ $eventDispatcher->addServiceListener(PostLoginEvent::class, UserLoggedInListener::class);
+ $eventDispatcher->addServiceListener(UserChangedEvent::class, UserChangedListener::class);
+ $eventDispatcher->addServiceListener(UserDeletedEvent::class, UserDeletedListener::class);
}
/**
diff --git a/lib/private/Session/CryptoSessionData.php b/lib/private/Session/CryptoSessionData.php
index 2e3bd46da5b..b01887e39e2 100644
--- a/lib/private/Session/CryptoSessionData.php
+++ b/lib/private/Session/CryptoSessionData.php
@@ -97,8 +97,17 @@ class CryptoSessionData implements \ArrayAccess, ISession {
* @param mixed $value
*/
public function set(string $key, $value) {
+ if ($this->get($key) === $value) {
+ // Do not write the session if the value hasn't changed to avoid reopening
+ return;
+ }
+
+ $reopened = $this->reopen();
$this->sessionValues[$key] = $value;
$this->isModified = true;
+ if ($reopened) {
+ $this->close();
+ }
}
/**
@@ -131,9 +140,13 @@ class CryptoSessionData implements \ArrayAccess, ISession {
* @param string $key
*/
public function remove(string $key) {
+ $reopened = $this->reopen();
$this->isModified = true;
unset($this->sessionValues[$key]);
$this->session->remove(self::encryptedSessionName);
+ if ($reopened) {
+ $this->close();
+ }
}
/**
@@ -149,6 +162,10 @@ class CryptoSessionData implements \ArrayAccess, ISession {
$this->session->clear();
}
+ public function reopen(): bool {
+ return $this->session->reopen();
+ }
+
/**
* Wrapper around session_regenerate_id
*
diff --git a/lib/private/Session/Internal.php b/lib/private/Session/Internal.php
index 285b6fd7960..87dd5ed6014 100644
--- a/lib/private/Session/Internal.php
+++ b/lib/private/Session/Internal.php
@@ -68,8 +68,11 @@ class Internal extends Session {
* @param integer $value
*/
public function set(string $key, $value) {
- $this->validateSession();
+ $reopened = $this->reopen();
$_SESSION[$key] = $value;
+ if ($reopened) {
+ $this->close();
+ }
}
/**
@@ -101,6 +104,7 @@ class Internal extends Session {
}
public function clear() {
+ $this->reopen();
$this->invoke('session_unset');
$this->regenerateId();
$this->startSession(true);
@@ -120,6 +124,7 @@ class Internal extends Session {
* @return void
*/
public function regenerateId(bool $deleteOldSession = true, bool $updateToken = false) {
+ $this->reopen();
$oldId = null;
if ($updateToken) {
@@ -171,8 +176,14 @@ class Internal extends Session {
/**
* @throws \Exception
*/
- public function reopen() {
- throw new \Exception('The session cannot be reopened - reopen() is ony to be used in unit testing.');
+ public function reopen(): bool {
+ if ($this->sessionClosed) {
+ $this->startSession(false, false);
+ $this->sessionClosed = false;
+ return true;
+ }
+
+ return false;
}
/**
@@ -214,7 +225,11 @@ class Internal extends Session {
}
}
- private function startSession(bool $silence = false) {
- $this->invoke('session_start', [['cookie_samesite' => 'Lax']], $silence);
+ private function startSession(bool $silence = false, bool $readAndClose = true) {
+ $sessionParams = ['cookie_samesite' => 'Lax'];
+ if (\OC::hasSessionRelaxedExpiry()) {
+ $sessionParams['read_and_close'] = $readAndClose;
+ }
+ $this->invoke('session_start', [$sessionParams], $silence);
}
}
diff --git a/lib/private/Session/Memory.php b/lib/private/Session/Memory.php
index 0afd3703366..b9b3dba54b7 100644
--- a/lib/private/Session/Memory.php
+++ b/lib/private/Session/Memory.php
@@ -53,7 +53,6 @@ class Memory extends Session {
* @param integer $value
*/
public function set(string $key, $value) {
- $this->validateSession();
$this->data[$key] = $value;
}
@@ -80,7 +79,6 @@ class Memory extends Session {
* @param string $key
*/
public function remove(string $key) {
- $this->validateSession();
unset($this->data[$key]);
}
@@ -110,8 +108,10 @@ class Memory extends Session {
/**
* Helper function for PHPUnit execution - don't use in non-test code
*/
- public function reopen() {
+ public function reopen(): bool {
+ $reopened = $this->sessionClosed;
$this->sessionClosed = false;
+ return $reopened;
}
/**
diff --git a/lib/private/Settings/Manager.php b/lib/private/Settings/Manager.php
index ed331c59725..44f1df09c15 100644
--- a/lib/private/Settings/Manager.php
+++ b/lib/private/Settings/Manager.php
@@ -126,8 +126,13 @@ class Manager implements IManager {
}
foreach (array_unique($this->sectionClasses[$type]) as $index => $class) {
- /** @var IIconSection $section */
- $section = \OC::$server->get($class);
+ try {
+ /** @var IIconSection $section */
+ $section = $this->container->get($class);
+ } catch (QueryException $e) {
+ $this->log->info($e->getMessage(), ['exception' => $e]);
+ continue;
+ }
$sectionID = $section->getID();
diff --git a/lib/private/Setup.php b/lib/private/Setup.php
index 177ede1e292..edbb9b33275 100644
--- a/lib/private/Setup.php
+++ b/lib/private/Setup.php
@@ -53,6 +53,7 @@ use Exception;
use InvalidArgumentException;
use OC\App\AppStore\Bundles\BundleFetcher;
use OC\Authentication\Token\PublicKeyTokenProvider;
+use OC\Authentication\Token\TokenCleanupJob;
use OC\Log\Rotate;
use OC\Preview\BackgroundCleanupJob;
use OCP\AppFramework\Utility\ITimeFactory;
@@ -450,6 +451,7 @@ class Setup {
public static function installBackgroundJobs() {
$jobList = \OC::$server->getJobList();
+ $jobList->add(TokenCleanupJob::class);
$jobList->add(Rotate::class);
$jobList->add(BackgroundCleanupJob::class);
}
diff --git a/lib/private/Setup/AbstractDatabase.php b/lib/private/Setup/AbstractDatabase.php
index 8690e7c1c66..5ee9548564c 100644
--- a/lib/private/Setup/AbstractDatabase.php
+++ b/lib/private/Setup/AbstractDatabase.php
@@ -141,9 +141,9 @@ abstract class AbstractDatabase {
}
/**
- * @param string $userName
+ * @param string $username
*/
- abstract public function setupDatabase($userName);
+ abstract public function setupDatabase($username);
public function runMigrations() {
if (!is_dir(\OC::$SERVERROOT."/core/Migrations")) {
diff --git a/lib/private/Setup/MySQL.php b/lib/private/Setup/MySQL.php
index 920baf3e4ee..e3004c269bc 100644
--- a/lib/private/Setup/MySQL.php
+++ b/lib/private/Setup/MySQL.php
@@ -80,7 +80,7 @@ class MySQL extends AbstractDatabase {
$user = $this->dbUser;
//we can't use OC_DB functions here because we need to connect as the administrative user.
$characterSet = $this->config->getValue('mysql.utf8mb4', false) ? 'utf8mb4' : 'utf8';
- $query = "CREATE DATABASE IF NOT EXISTS `$name` CHARACTER SET $characterSet COLLATE ${characterSet}_bin;";
+ $query = "CREATE DATABASE IF NOT EXISTS `$name` CHARACTER SET $characterSet COLLATE {$characterSet}_bin;";
$connection->executeUpdate($query);
} catch (\Exception $ex) {
$this->logger->error('Database creation failed.', [
@@ -129,15 +129,28 @@ class MySQL extends AbstractDatabase {
'exception' => $ex,
'app' => 'mysql.setup',
]);
+ throw $ex;
}
}
/**
* @param $username
* @param IDBConnection $connection
- * @return array
*/
- private function createSpecificUser($username, $connection) {
+ private function createSpecificUser($username, $connection): void {
+ $rootUser = $this->dbUser;
+ $rootPassword = $this->dbPassword;
+
+ //create a random password so we don't need to store the admin password in the config file
+ $saveSymbols = str_replace(['\"', '\\', '\'', '`'], '', ISecureRandom::CHAR_SYMBOLS);
+ $password = $this->random->generate(22, ISecureRandom::CHAR_ALPHANUMERIC . $saveSymbols)
+ . $this->random->generate(2, ISecureRandom::CHAR_UPPER)
+ . $this->random->generate(2, ISecureRandom::CHAR_LOWER)
+ . $this->random->generate(2, ISecureRandom::CHAR_DIGITS)
+ . $this->random->generate(2, $saveSymbols)
+ ;
+ $this->dbPassword = str_shuffle($password);
+
try {
//user already specified in config
$oldUser = $this->config->getValue('dbuser', false);
@@ -160,10 +173,6 @@ class MySQL extends AbstractDatabase {
if (count($data) === 0) {
//use the admin login data for the new database user
$this->dbUser = $adminUser;
-
- //create a random password so we don't need to store the admin password in the config file
- $this->dbPassword = $this->random->generate(30, ISecureRandom::CHAR_ALPHANUMERIC);
-
$this->createDBUser($connection);
break;
@@ -180,6 +189,9 @@ class MySQL extends AbstractDatabase {
'exception' => $ex,
'app' => 'mysql.setup',
]);
+ // Restore the original credentials
+ $this->dbUser = $rootUser;
+ $this->dbPassword = $rootPassword;
}
$this->config->setValues([
diff --git a/lib/private/Share/Constants.php b/lib/private/Share/Constants.php
index 31c734f94aa..3632a2a26d1 100644
--- a/lib/private/Share/Constants.php
+++ b/lib/private/Share/Constants.php
@@ -79,7 +79,7 @@ class Constants {
public const FORMAT_STATUSES = -2;
public const FORMAT_SOURCES = -3; // ToDo Check if it is still in use otherwise remove it
- public const RESPONSE_FORMAT = 'json'; // default resonse format for ocs calls
+ public const RESPONSE_FORMAT = 'json'; // default response format for ocs calls
public const TOKEN_LENGTH = 15; // old (oc7) length is 32, keep token length in db at least that for compatibility
diff --git a/lib/private/Share/Share.php b/lib/private/Share/Share.php
index 9018f35ac2a..f47c042df29 100644
--- a/lib/private/Share/Share.php
+++ b/lib/private/Share/Share.php
@@ -732,7 +732,7 @@ class Share extends Constants {
foreach ($result as $key => $r) {
// for file/folder shares we need to compare file_source, otherwise we compare item_source
// only group shares if they already point to the same target, otherwise the file where shared
- // before grouping of shares was added. In this case we don't group them toi avoid confusions
+ // before grouping of shares was added. In this case we don't group them to avoid confusions
if (($fileSharing && $item['file_source'] === $r['file_source'] && $item['file_target'] === $r['file_target']) ||
(!$fileSharing && $item['item_source'] === $r['item_source'] && $item['item_target'] === $r['item_target'])) {
// add the first item to the list of grouped shares
@@ -757,7 +757,7 @@ class Share extends Constants {
/**
* construct select statement
* @param int $format
- * @param boolean $fileDependent ist it a file/folder share or a generla share
+ * @param boolean $fileDependent ist it a file/folder share or a general share
* @param string $uidOwner
* @return string select statement
*/
diff --git a/lib/private/Share20/DefaultShareProvider.php b/lib/private/Share20/DefaultShareProvider.php
index 520bd17d3cf..c78cf62e069 100644
--- a/lib/private/Share20/DefaultShareProvider.php
+++ b/lib/private/Share20/DefaultShareProvider.php
@@ -52,6 +52,7 @@ use OCP\IUserManager;
use OCP\L10N\IFactory;
use OCP\Mail\IMailer;
use OCP\Share\Exceptions\ShareNotFound;
+use OCP\Share\IAttributes;
use OCP\Share\IShare;
use OCP\Share\IShareProvider;
@@ -174,6 +175,8 @@ class DefaultShareProvider implements IShareProvider {
if (method_exists($share, 'getParent')) {
$qb->setValue('parent', $qb->createNamedParameter($share->getParent()));
}
+
+ $qb->setValue('hide_download', $qb->createNamedParameter($share->getHideDownload() ? 1 : 0, IQueryBuilder::PARAM_INT));
} else {
throw new \Exception('invalid share type!');
}
@@ -193,6 +196,12 @@ class DefaultShareProvider implements IShareProvider {
// set the permissions
$qb->setValue('permissions', $qb->createNamedParameter($share->getPermissions()));
+ // set share attributes
+ $shareAttributes = $this->formatShareAttributes(
+ $share->getAttributes()
+ );
+ $qb->setValue('attributes', $qb->createNamedParameter($shareAttributes));
+
// Set who created this share
$qb->setValue('uid_initiator', $qb->createNamedParameter($share->getSharedBy()));
@@ -248,6 +257,8 @@ class DefaultShareProvider implements IShareProvider {
public function update(\OCP\Share\IShare $share) {
$originalShare = $this->getShareById($share->getId());
+ $shareAttributes = $this->formatShareAttributes($share->getAttributes());
+
if ($share->getShareType() === IShare::TYPE_USER) {
/*
* We allow updating the recipient on user shares.
@@ -259,6 +270,7 @@ class DefaultShareProvider implements IShareProvider {
->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
->set('permissions', $qb->createNamedParameter($share->getPermissions()))
+ ->set('attributes', $qb->createNamedParameter($shareAttributes))
->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
@@ -272,6 +284,7 @@ class DefaultShareProvider implements IShareProvider {
->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
->set('permissions', $qb->createNamedParameter($share->getPermissions()))
+ ->set('attributes', $qb->createNamedParameter($shareAttributes))
->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
@@ -301,6 +314,7 @@ class DefaultShareProvider implements IShareProvider {
->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
->andWhere($qb->expr()->neq('permissions', $qb->createNamedParameter(0)))
->set('permissions', $qb->createNamedParameter($share->getPermissions()))
+ ->set('attributes', $qb->createNamedParameter($shareAttributes))
->execute();
} elseif ($share->getShareType() === IShare::TYPE_LINK) {
$qb = $this->dbConn->getQueryBuilder();
@@ -311,6 +325,7 @@ class DefaultShareProvider implements IShareProvider {
->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
->set('permissions', $qb->createNamedParameter($share->getPermissions()))
+ ->set('attributes', $qb->createNamedParameter($shareAttributes))
->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
->set('token', $qb->createNamedParameter($share->getToken()))
@@ -611,6 +626,10 @@ class DefaultShareProvider implements IShareProvider {
$data = $stmt->fetch();
$stmt->closeCursor();
+ $shareAttributes = $this->formatShareAttributes(
+ $share->getAttributes()
+ );
+
if ($data === false) {
// No usergroup share yet. Create one.
$qb = $this->dbConn->getQueryBuilder();
@@ -626,6 +645,7 @@ class DefaultShareProvider implements IShareProvider {
'file_source' => $qb->createNamedParameter($share->getNodeId()),
'file_target' => $qb->createNamedParameter($share->getTarget()),
'permissions' => $qb->createNamedParameter($share->getPermissions()),
+ 'attributes' => $qb->createNamedParameter($shareAttributes),
'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()),
])->execute();
} else {
@@ -641,9 +661,12 @@ class DefaultShareProvider implements IShareProvider {
return $share;
}
- public function getSharesInFolder($userId, Folder $node, $reshares) {
+ public function getSharesInFolder($userId, Folder $node, $reshares, $shallow = true) {
$qb = $this->dbConn->getQueryBuilder();
- $qb->select('*')
+ $qb->select('s.*',
+ 'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage', 'f.path_hash',
+ 'f.parent AS f_parent', 'f.name', 'f.mimetype', 'f.mimepart', 'f.size', 'f.mtime', 'f.storage_mtime',
+ 'f.encrypted', 'f.unencrypted_size', 'f.etag', 'f.checksum')
->from('share', 's')
->andWhere($qb->expr()->orX(
$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
@@ -679,12 +702,21 @@ class DefaultShareProvider implements IShareProvider {
}, $childMountNodes);
$qb->innerJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
- $qb->andWhere(
- $qb->expr()->orX(
- $qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())),
- $qb->expr()->in('f.fileid', $qb->createParameter('chunk'))
- )
- );
+ if ($shallow) {
+ $qb->andWhere(
+ $qb->expr()->orX(
+ $qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())),
+ $qb->expr()->in('f.fileid', $qb->createParameter('chunk'))
+ )
+ );
+ } else {
+ $qb->andWhere(
+ $qb->expr()->orX(
+ $qb->expr()->like('f.path', $qb->createNamedParameter($this->dbConn->escapeLikeParameter($node->getInternalPath()) . '/%')),
+ $qb->expr()->in('f.fileid', $qb->createParameter('chunk'))
+ )
+ );
+ }
$qb->orderBy('id');
@@ -922,8 +954,8 @@ class DefaultShareProvider implements IShareProvider {
$start = 0;
while (true) {
- $groups = array_slice($allGroups, $start, 100);
- $start += 100;
+ $groups = array_slice($allGroups, $start, 1000);
+ $start += 1000;
if ($groups === []) {
break;
@@ -1061,6 +1093,8 @@ class DefaultShareProvider implements IShareProvider {
$share->setToken($data['token']);
}
+ $share = $this->updateShareAttributes($share, $data['attributes']);
+
$share->setSharedBy($data['uid_initiator']);
$share->setShareOwner($data['uid_owner']);
@@ -1282,7 +1316,7 @@ class DefaultShareProvider implements IShareProvider {
$chunks = array_chunk($ids, 100);
foreach ($chunks as $chunk) {
/*
- * Delete all special shares wit this users for the found group shares
+ * Delete all special shares with this users for the found group shares
*/
$qb->delete('share')
->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
@@ -1528,4 +1562,48 @@ class DefaultShareProvider implements IShareProvider {
}
$cursor->closeCursor();
}
+
+ /**
+ * Load from database format (JSON string) to IAttributes
+ *
+ * @return IShare the modified share
+ */
+ private function updateShareAttributes(IShare $share, ?string $data): IShare {
+ if ($data !== null && $data !== '') {
+ $attributes = new ShareAttributes();
+ $compressedAttributes = \json_decode($data, true);
+ if ($compressedAttributes === false || $compressedAttributes === null) {
+ return $share;
+ }
+ foreach ($compressedAttributes as $compressedAttribute) {
+ $attributes->setAttribute(
+ $compressedAttribute[0],
+ $compressedAttribute[1],
+ $compressedAttribute[2]
+ );
+ }
+ $share->setAttributes($attributes);
+ }
+
+ return $share;
+ }
+
+ /**
+ * Format IAttributes to database format (JSON string)
+ */
+ private function formatShareAttributes(?IAttributes $attributes): ?string {
+ if ($attributes === null || empty($attributes->toArray())) {
+ return null;
+ }
+
+ $compressedAttributes = [];
+ foreach ($attributes->toArray() as $attribute) {
+ $compressedAttributes[] = [
+ 0 => $attribute['scope'],
+ 1 => $attribute['key'],
+ 2 => $attribute['enabled']
+ ];
+ }
+ return \json_encode($compressedAttributes);
+ }
}
diff --git a/lib/private/Share20/Manager.php b/lib/private/Share20/Manager.php
index eed86bb41c3..2ef61cf3404 100644
--- a/lib/private/Share20/Manager.php
+++ b/lib/private/Share20/Manager.php
@@ -41,7 +41,7 @@
*/
namespace OC\Share20;
-use OC\Cache\CappedMemoryCache;
+use OCP\Cache\CappedMemoryCache;
use OC\Files\Mount\MoveableMount;
use OC\KnownUser\KnownUserService;
use OC\Share20\Exception\ProviderException;
@@ -650,7 +650,7 @@ class Manager implements IManager {
}
// Check if public upload is allowed
- if (!$this->shareApiLinkAllowPublicUpload() &&
+ if ($share->getNodeType() === 'folder' && !$this->shareApiLinkAllowPublicUpload() &&
($share->getPermissions() & (\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE))) {
throw new \InvalidArgumentException('Public upload is not allowed');
}
@@ -1093,6 +1093,7 @@ class Manager implements IManager {
'shareWith' => $share->getSharedWith(),
'uidOwner' => $share->getSharedBy(),
'permissions' => $share->getPermissions(),
+ 'attributes' => $share->getAttributes() !== null ? $share->getAttributes()->toArray() : null,
'path' => $userFolder->getRelativePath($share->getNode()->getPath()),
]);
}
@@ -1303,11 +1304,11 @@ class Manager implements IManager {
return $provider->move($share, $recipientId);
}
- public function getSharesInFolder($userId, Folder $node, $reshares = false) {
+ public function getSharesInFolder($userId, Folder $node, $reshares = false, $shallow = true) {
$providers = $this->factory->getAllProviders();
- return array_reduce($providers, function ($shares, IShareProvider $provider) use ($userId, $node, $reshares) {
- $newShares = $provider->getSharesInFolder($userId, $node, $reshares);
+ return array_reduce($providers, function ($shares, IShareProvider $provider) use ($userId, $node, $reshares, $shallow) {
+ $newShares = $provider->getSharesInFolder($userId, $node, $reshares, $shallow);
foreach ($newShares as $fid => $data) {
if (!isset($shares[$fid])) {
$shares[$fid] = [];
@@ -1543,7 +1544,7 @@ class Manager implements IManager {
* Reduce the permissions for link or email shares if public upload is not enabled
*/
if (($share->getShareType() === IShare::TYPE_LINK || $share->getShareType() === IShare::TYPE_EMAIL)
- && !$this->shareApiLinkAllowPublicUpload()) {
+ && $share->getNodeType() === 'folder' && !$this->shareApiLinkAllowPublicUpload()) {
$share->setPermissions($share->getPermissions() & ~(\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE));
}
@@ -1968,7 +1969,7 @@ class Manager implements IManager {
}
public function ignoreSecondDisplayName(): bool {
- return $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_ignore_second_display_name', 'no') === 'yes';
+ return $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_ignore_second_dn', 'no') === 'yes';
}
public function currentUserCanEnumerateTargetUser(?IUser $currentUser, IUser $targetUser): bool {
@@ -2010,7 +2011,7 @@ class Manager implements IManager {
/**
* Copied from \OC_Util::isSharingDisabledForUser
*
- * TODO: Deprecate fuction from OC_Util
+ * TODO: Deprecate function from OC_Util
*
* @param string $userId
* @return bool
diff --git a/lib/private/Share20/Share.php b/lib/private/Share20/Share.php
index 7ed03832e4c..c2d45503696 100644
--- a/lib/private/Share20/Share.php
+++ b/lib/private/Share20/Share.php
@@ -37,6 +37,7 @@ use OCP\Files\Node;
use OCP\Files\NotFoundException;
use OCP\IUserManager;
use OCP\Share\Exceptions\IllegalIDChangeException;
+use OCP\Share\IAttributes;
use OCP\Share\IShare;
class Share implements IShare {
@@ -65,6 +66,8 @@ class Share implements IShare {
private $shareOwner;
/** @var int */
private $permissions;
+ /** @var IAttributes */
+ private $attributes;
/** @var int */
private $status;
/** @var string */
@@ -319,7 +322,7 @@ class Share implements IShare {
* @inheritdoc
*/
public function setPermissions($permissions) {
- //TODO checkes
+ //TODO checks
$this->permissions = $permissions;
return $this;
@@ -335,6 +338,28 @@ class Share implements IShare {
/**
* @inheritdoc
*/
+ public function newAttributes(): IAttributes {
+ return new ShareAttributes();
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function setAttributes(?IAttributes $attributes) {
+ $this->attributes = $attributes;
+ return $this;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function getAttributes(): ?IAttributes {
+ return $this->attributes;
+ }
+
+ /**
+ * @inheritdoc
+ */
public function setStatus(int $status): IShare {
$this->status = $status;
return $this;
@@ -511,7 +536,7 @@ class Share implements IShare {
* Set the parent of this share
*
* @param int parent
- * @return \OCP\Share\IShare
+ * @return IShare
* @deprecated The new shares do not have parents. This is just here for legacy reasons.
*/
public function setParent($parent) {
diff --git a/lib/private/Share20/ShareAttributes.php b/lib/private/Share20/ShareAttributes.php
new file mode 100644
index 00000000000..92f034e6783
--- /dev/null
+++ b/lib/private/Share20/ShareAttributes.php
@@ -0,0 +1,73 @@
+<?php
+/**
+ * @author Piotr Mrowczynski <piotr@owncloud.com>
+ *
+ * @copyright Copyright (c) 2019, ownCloud GmbH
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+namespace OC\Share20;
+
+use OCP\Share\IAttributes;
+
+class ShareAttributes implements IAttributes {
+
+ /** @var array */
+ private $attributes;
+
+ public function __construct() {
+ $this->attributes = [];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function setAttribute($scope, $key, $enabled) {
+ if (!\array_key_exists($scope, $this->attributes)) {
+ $this->attributes[$scope] = [];
+ }
+ $this->attributes[$scope][$key] = $enabled;
+ return $this;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function getAttribute($scope, $key) {
+ if (\array_key_exists($scope, $this->attributes) &&
+ \array_key_exists($key, $this->attributes[$scope])) {
+ return $this->attributes[$scope][$key];
+ }
+ return null;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function toArray() {
+ $result = [];
+ foreach ($this->attributes as $scope => $keys) {
+ foreach ($keys as $key => $enabled) {
+ $result[] = [
+ "scope" => $scope,
+ "key" => $key,
+ "enabled" => $enabled
+ ];
+ }
+ }
+
+ return $result;
+ }
+}
diff --git a/lib/private/Streamer.php b/lib/private/Streamer.php
index 80ab5a5524c..88204be9805 100644
--- a/lib/private/Streamer.php
+++ b/lib/private/Streamer.php
@@ -148,13 +148,13 @@ class Streamer {
/**
* Add a file to the archive at the specified location and file name.
*
- * @param string $stream Stream to read data from
+ * @param resource $stream Stream to read data from
* @param string $internalName Filepath and name to be used in the archive.
* @param int $size Filesize
* @param int|bool $time File mtime as int, or false
* @return bool $success
*/
- public function addFileFromStream($stream, $internalName, $size, $time) {
+ public function addFileFromStream($stream, string $internalName, int $size, $time): bool {
$options = [];
if ($time) {
$options = [
diff --git a/lib/private/Template/JSConfigHelper.php b/lib/private/Template/JSConfigHelper.php
index 58f3106bafd..61f0c4e39cd 100644
--- a/lib/private/Template/JSConfigHelper.php
+++ b/lib/private/Template/JSConfigHelper.php
@@ -1,4 +1,6 @@
<?php
+
+declare(strict_types=1);
/**
* @copyright Copyright (c) 2016, Roeland Jago Douma <roeland@famdouma.nl>
*
@@ -33,73 +35,44 @@ namespace OC\Template;
use bantu\IniGetWrapper\IniGetWrapper;
use OC\CapabilitiesManager;
+use OC\Share\Share;
+use OCP\App\AppPathNotFoundException;
use OCP\App\IAppManager;
use OCP\Constants;
use OCP\Defaults;
+use OCP\Files\FileInfo;
use OCP\IConfig;
use OCP\IGroupManager;
use OCP\IInitialStateService;
use OCP\IL10N;
use OCP\ISession;
use OCP\IURLGenerator;
+use OCP\ILogger;
use OCP\IUser;
use OCP\User\Backend\IPasswordConfirmationBackend;
+use OCP\Util;
class JSConfigHelper {
-
- /** @var IL10N */
- private $l;
-
- /** @var Defaults */
- private $defaults;
-
- /** @var IAppManager */
- private $appManager;
-
- /** @var ISession */
- private $session;
-
- /** @var IUser|null */
- private $currentUser;
-
- /** @var IConfig */
- private $config;
-
- /** @var IGroupManager */
- private $groupManager;
-
- /** @var IniGetWrapper */
- private $iniWrapper;
-
- /** @var IURLGenerator */
- private $urlGenerator;
-
- /** @var CapabilitiesManager */
- private $capabilitiesManager;
-
- /** @var IInitialStateService */
- private $initialStateService;
+ protected IL10N $l;
+ protected Defaults $defaults;
+ protected IAppManager $appManager;
+ protected ISession $session;
+ protected ?IUser $currentUser;
+ protected IConfig $config;
+ protected IGroupManager $groupManager;
+ protected IniGetWrapper $iniWrapper;
+ protected IURLGenerator $urlGenerator;
+ protected CapabilitiesManager $capabilitiesManager;
+ protected IInitialStateService $initialStateService;
/** @var array user back-ends excluded from password verification */
private $excludedUserBackEnds = ['user_saml' => true, 'user_globalsiteselector' => true];
- /**
- * @param IL10N $l
- * @param Defaults $defaults
- * @param IAppManager $appManager
- * @param ISession $session
- * @param IUser|null $currentUser
- * @param IConfig $config
- * @param IGroupManager $groupManager
- * @param IniGetWrapper $iniWrapper
- * @param IURLGenerator $urlGenerator
- * @param CapabilitiesManager $capabilitiesManager
- */
public function __construct(IL10N $l,
Defaults $defaults,
IAppManager $appManager,
ISession $session,
- $currentUser,
+ ?IUser $currentUser,
IConfig $config,
IGroupManager $groupManager,
IniGetWrapper $iniWrapper,
@@ -119,7 +92,7 @@ class JSConfigHelper {
$this->initialStateService = $initialStateService;
}
- public function getConfig() {
+ public function getConfig(): string {
$userBackendAllowsPasswordConfirmation = true;
if ($this->currentUser !== null) {
$uid = $this->currentUser->getUID();
@@ -144,10 +117,13 @@ class JSConfigHelper {
}
foreach ($apps as $app) {
- $apps_paths[$app] = \OC_App::getAppWebPath($app);
+ try {
+ $apps_paths[$app] = $this->appManager->getAppWebPath($app);
+ } catch (AppPathNotFoundException $e) {
+ $apps_paths[$app] = false;
+ }
}
-
$enableLinkPasswordByDefault = $this->config->getAppValue('core', 'shareapi_enable_link_password_by_default', 'no');
$enableLinkPasswordByDefault = $enableLinkPasswordByDefault === 'yes';
$defaultExpireDateEnabled = $this->config->getAppValue('core', 'shareapi_default_expire_date', 'no') === 'yes';
@@ -193,14 +169,17 @@ class JSConfigHelper {
'session_lifetime' => min($this->config->getSystemValue('session_lifetime', $this->iniWrapper->getNumeric('session.gc_maxlifetime')), $this->iniWrapper->getNumeric('session.gc_maxlifetime')),
'session_keepalive' => $this->config->getSystemValue('session_keepalive', true),
'auto_logout' => $this->config->getSystemValue('auto_logout', false),
- 'version' => implode('.', \OCP\Util::getVersion()),
+ 'version' => implode('.', Util::getVersion()),
'versionstring' => \OC_Util::getVersionString(),
'enable_avatars' => true, // here for legacy reasons - to not crash existing code that relies on this value
'lost_password_link' => $this->config->getSystemValue('lost_password_link', null),
'modRewriteWorking' => $this->config->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true',
'sharing.maxAutocompleteResults' => max(0, $this->config->getSystemValueInt('sharing.maxAutocompleteResults', Constants::SHARING_MAX_AUTOCOMPLETE_RESULTS_DEFAULT)),
'sharing.minSearchStringLength' => $this->config->getSystemValueInt('sharing.minSearchStringLength', 0),
- 'blacklist_files_regex' => \OCP\Files\FileInfo::BLACKLIST_FILES_REGEX,
+ 'blacklist_files_regex' => FileInfo::BLACKLIST_FILES_REGEX,
+ 'loglevel' => $this->config->getSystemValue('loglevel_frontend',
+ $this->config->getSystemValue('loglevel', ILogger::WARN)
+ ),
];
$array = [
@@ -275,10 +254,10 @@ class JSConfigHelper {
'defaultExpireDateEnabled' => $defaultExpireDateEnabled,
'defaultExpireDate' => $defaultExpireDate,
'defaultExpireDateEnforced' => $enforceDefaultExpireDate,
- 'enforcePasswordForPublicLink' => \OCP\Util::isPublicLinkPasswordRequired(),
+ 'enforcePasswordForPublicLink' => Util::isPublicLinkPasswordRequired(),
'enableLinkPasswordByDefault' => $enableLinkPasswordByDefault,
- 'sharingDisabledForUser' => \OCP\Util::isSharingDisabledForUser(),
- 'resharingAllowed' => \OC\Share\Share::isResharingAllowed(),
+ 'sharingDisabledForUser' => Util::isSharingDisabledForUser(),
+ 'resharingAllowed' => Share::isResharingAllowed(),
'remoteShareAllowed' => $outgoingServer2serverShareEnabled,
'federatedCloudShareDoc' => $this->urlGenerator->linkToDocs('user-sharing-federated'),
'allowGroupSharing' => \OC::$server->getShareManager()->allowGroupSharing(),
@@ -314,6 +293,8 @@ class JSConfigHelper {
]);
}
+ $this->initialStateService->provideInitialState('core', 'projects_enabled', $this->config->getSystemValueBool('projects.enabled', false));
+
$this->initialStateService->provideInitialState('core', 'config', $config);
$this->initialStateService->provideInitialState('core', 'capabilities', $capabilities);
diff --git a/lib/private/Template/JSResourceLocator.php b/lib/private/Template/JSResourceLocator.php
index 95ae0d3d832..9e76655aba2 100644
--- a/lib/private/Template/JSResourceLocator.php
+++ b/lib/private/Template/JSResourceLocator.php
@@ -71,12 +71,14 @@ class JSResourceLocator extends ResourceLocator {
} elseif ($this->appendIfExist($this->serverroot, $theme_dir.'apps/'.$script.'.js')
|| $this->appendIfExist($this->serverroot, $theme_dir.$script.'.js')
|| $this->appendIfExist($this->serverroot, $script.'.js')
+ || $this->appendIfExist($this->serverroot, $theme_dir . "dist/$app-$scriptName.js")
|| $this->appendIfExist($this->serverroot, "dist/$app-$scriptName.js")
|| $this->appendIfExist($this->serverroot, 'apps/'.$script.'.js')
|| $this->cacheAndAppendCombineJsonIfExist($this->serverroot, $script.'.json')
|| $this->appendIfExist($this->serverroot, $theme_dir.'core/'.$script.'.js')
|| $this->appendIfExist($this->serverroot, 'core/'.$script.'.js')
- || (strpos($scriptName, '/') === -1 && $this->appendIfExist($this->serverroot, "dist/core-$scriptName.js"))
+ || (strpos($scriptName, '/') === -1 && ($this->appendIfExist($this->serverroot, $theme_dir . "dist/core-$scriptName.js")
+ || $this->appendIfExist($this->serverroot, "dist/core-$scriptName.js")))
|| $this->cacheAndAppendCombineJsonIfExist($this->serverroot, 'core/'.$script.'.json')
) {
return;
diff --git a/lib/private/TemplateLayout.php b/lib/private/TemplateLayout.php
index a5aabc04b61..d041db2a7c2 100644
--- a/lib/private/TemplateLayout.php
+++ b/lib/private/TemplateLayout.php
@@ -100,6 +100,7 @@ class TemplateLayout extends \OC_Template {
}
$this->initialState->provideInitialState('core', 'active-app', $this->navigationManager->getActiveEntry());
+ $this->initialState->provideInitialState('core', 'apps', $this->navigationManager->getAll());
$this->initialState->provideInitialState('unified-search', 'limit-default', (int)$this->config->getAppValue('core', 'unified-search.limit-default', (string)SearchQuery::LIMIT_DEFAULT));
$this->initialState->provideInitialState('unified-search', 'min-search-length', (int)$this->config->getAppValue('core', 'unified-search.min-search-length', (string)2));
$this->initialState->provideInitialState('unified-search', 'live-search', $this->config->getAppValue('core', 'unified-search.live-search', 'yes') === 'yes');
@@ -281,6 +282,9 @@ class TemplateLayout extends \OC_Template {
}
$this->assign('initialStates', $this->initialState->getInitialStates());
+
+ $this->assign('id-app-content', $renderAs === TemplateResponse::RENDER_AS_USER ? '#app-content' : '#content');
+ $this->assign('id-app-navigation', $renderAs === TemplateResponse::RENDER_AS_USER ? '#app-navigation' : null);
}
/**
diff --git a/lib/private/URLGenerator.php b/lib/private/URLGenerator.php
index 753a4a217d1..7be2895a1ef 100644
--- a/lib/private/URLGenerator.php
+++ b/lib/private/URLGenerator.php
@@ -42,6 +42,8 @@ namespace OC;
use OC\Route\Router;
use OCA\Theming\ThemingDefaults;
+use OCP\App\AppPathNotFoundException;
+use OCP\App\IAppManager;
use OCP\ICacheFactory;
use OCP\IConfig;
use OCP\IRequest;
@@ -65,12 +67,14 @@ class URLGenerator implements IURLGenerator {
private $router;
/** @var null|string */
private $baseUrl = null;
+ private ?IAppManager $appManager = null;
public function __construct(IConfig $config,
IUserSession $userSession,
ICacheFactory $cacheFactory,
IRequest $request,
- Router $router) {
+ Router $router
+ ) {
$this->config = $config;
$this->userSession = $userSession;
$this->cacheFactory = $cacheFactory;
@@ -78,6 +82,14 @@ class URLGenerator implements IURLGenerator {
$this->router = $router;
}
+ private function getAppManager(): IAppManager {
+ if ($this->appManager !== null) {
+ return $this->appManager;
+ }
+ $this->appManager = \OCP\Server::get(IAppManager::class);
+ return $this->appManager;
+ }
+
/**
* Creates an url using a defined route
*
@@ -132,9 +144,9 @@ class URLGenerator implements IURLGenerator {
$frontControllerActive = ($this->config->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true');
if ($appName !== '') {
- $app_path = \OC_App::getAppPath($appName);
+ $app_path = $this->getAppManager()->getAppPath($appName);
// Check if the app is in the app folder
- if ($app_path && file_exists($app_path . '/' . $file)) {
+ if (file_exists($app_path . '/' . $file)) {
if (substr($file, -3) === 'php') {
$urlLinkTo = \OC::$WEBROOT . '/index.php/apps/' . $appName;
if ($frontControllerActive) {
@@ -142,7 +154,7 @@ class URLGenerator implements IURLGenerator {
}
$urlLinkTo .= ($file !== 'index.php') ? '/' . $file : '';
} else {
- $urlLinkTo = \OC_App::getAppWebPath($appName) . '/' . $file;
+ $urlLinkTo = $this->getAppManager()->getAppWebPath($appName) . '/' . $file;
}
} else {
$urlLinkTo = \OC::$WEBROOT . '/' . $appName . '/' . $file;
@@ -189,11 +201,20 @@ class URLGenerator implements IURLGenerator {
//if a theme has a png but not an svg always use the png
$basename = substr(basename($file), 0, -4);
- $appPath = \OC_App::getAppPath($appName);
+ try {
+ $appPath = $this->getAppManager()->getAppPath($appName);
+ } catch (AppPathNotFoundException $e) {
+ if ($appName === 'core' || $appName === '') {
+ $appName = 'core';
+ $appPath = false;
+ } else {
+ throw new RuntimeException('image not found: image: ' . $file . ' webroot: ' . \OC::$WEBROOT . ' serverroot: ' . \OC::$SERVERROOT);
+ }
+ }
// Check if the app is in the app folder
$path = '';
- $themingEnabled = $this->config->getSystemValue('installed', false) && \OCP\App::isEnabled('theming') && \OC_App::isAppLoaded('theming');
+ $themingEnabled = $this->config->getSystemValue('installed', false) && $this->getAppManager()->isEnabledForUser('theming');
$themingImagePath = false;
if ($themingEnabled) {
$themingDefaults = \OC::$server->getThemingDefaults();
@@ -220,10 +241,10 @@ class URLGenerator implements IURLGenerator {
} elseif ($themingEnabled && $themingImagePath) {
$path = $themingImagePath;
} elseif ($appPath && file_exists($appPath . "/img/$file")) {
- $path = \OC_App::getAppWebPath($appName) . "/img/$file";
+ $path = $this->getAppManager()->getAppWebPath($appName) . "/img/$file";
} elseif ($appPath && !file_exists($appPath . "/img/$basename.svg")
&& file_exists($appPath . "/img/$basename.png")) {
- $path = \OC_App::getAppWebPath($appName) . "/img/$basename.png";
+ $path = $this->getAppManager()->getAppWebPath($appName) . "/img/$basename.png";
} elseif (!empty($appName) and file_exists(\OC::$SERVERROOT . "/$appName/img/$file")) {
$path = \OC::$WEBROOT . "/$appName/img/$file";
} elseif (!empty($appName) and (!file_exists(\OC::$SERVERROOT . "/$appName/img/$basename.svg")
@@ -320,7 +341,7 @@ class URLGenerator implements IURLGenerator {
* @return string base url of the current request
*/
public function getBaseUrl(): string {
- // BaseUrl can be equal to 'http(s)://' during the first steps of the intial setup.
+ // BaseUrl can be equal to 'http(s)://' during the first steps of the initial setup.
if ($this->baseUrl === null || $this->baseUrl === "http://" || $this->baseUrl === "https://") {
$this->baseUrl = $this->request->getServerProtocol() . '://' . $this->request->getServerHost() . \OC::$WEBROOT;
}
diff --git a/lib/private/Updater.php b/lib/private/Updater.php
index 2c06cffcb19..78613ddbb0c 100644
--- a/lib/private/Updater.php
+++ b/lib/private/Updater.php
@@ -40,19 +40,28 @@ declare(strict_types=1);
*/
namespace OC;
+use OCP\App\IAppManager;
+use OCP\EventDispatcher\Event;
+use OCP\EventDispatcher\IEventDispatcher;
+use OCP\HintException;
+use OCP\IConfig;
+use OCP\ILogger;
+use OCP\Util;
use OC\App\AppManager;
use OC\DB\Connection;
use OC\DB\MigrationService;
+use OC\DB\MigratorExecuteSqlEvent;
use OC\Hooks\BasicEmitter;
use OC\IntegrityCheck\Checker;
+use OC\Repair\Events\RepairAdvanceEvent;
+use OC\Repair\Events\RepairErrorEvent;
+use OC\Repair\Events\RepairFinishEvent;
+use OC\Repair\Events\RepairInfoEvent;
+use OC\Repair\Events\RepairStartEvent;
+use OC\Repair\Events\RepairStepEvent;
+use OC\Repair\Events\RepairWarningEvent;
use OC_App;
-use OCP\App\IAppManager;
-use OCP\HintException;
-use OCP\IConfig;
-use OCP\ILogger;
-use OCP\Util;
use Psr\Log\LoggerInterface;
-use Symfony\Component\EventDispatcher\GenericEvent;
/**
* Class that handles autoupdating of ownCloud
@@ -102,7 +111,6 @@ class Updater extends BasicEmitter {
* @return bool true if the operation succeeded, false otherwise
*/
public function upgrade(): bool {
- $this->emitRepairEvents();
$this->logAllEvents();
$logLevel = $this->config->getSystemValue('loglevel', ILogger::WARN);
@@ -248,7 +256,7 @@ class Updater extends BasicEmitter {
file_put_contents($this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/.ocdata', '');
// pre-upgrade repairs
- $repair = new Repair(Repair::getBeforeUpgradeRepairSteps(), \OC::$server->getEventDispatcher(), \OC::$server->get(LoggerInterface::class));
+ $repair = new Repair(Repair::getBeforeUpgradeRepairSteps(), \OC::$server->get(\OCP\EventDispatcher\IEventDispatcher::class), \OC::$server->get(LoggerInterface::class));
$repair->run();
$this->doCoreUpgrade();
@@ -289,11 +297,11 @@ class Updater extends BasicEmitter {
}
// post-upgrade repairs
- $repair = new Repair(Repair::getRepairSteps(), \OC::$server->getEventDispatcher(), \OC::$server->get(LoggerInterface::class));
+ $repair = new Repair(Repair::getRepairSteps(), \OC::$server->get(\OCP\EventDispatcher\IEventDispatcher::class), \OC::$server->get(LoggerInterface::class));
$repair->run();
//Invalidate update feed
- $this->config->setAppValue('core', 'lastupdatedat', 0);
+ $this->config->setAppValue('core', 'lastupdatedat', '0');
// Check for code integrity if not disabled
if (\OC::$server->getIntegrityCodeChecker()->isCodeCheckEnforced()) {
@@ -378,7 +386,7 @@ class Updater extends BasicEmitter {
$appManager = \OC::$server->getAppManager();
foreach ($apps as $app) {
// check if the app is compatible with this version of Nextcloud
- $info = OC_App::getAppInfo($app);
+ $info = $appManager->getAppInfo($app);
if ($info === null || !OC_App::isAppCompatible($version, $info)) {
if ($appManager->isShipped($app)) {
throw new \UnexpectedValueException('The files of the app "' . $app . '" were not correctly replaced before running the update');
@@ -432,91 +440,47 @@ class Updater extends BasicEmitter {
}
}
- /**
- * Forward messages emitted by the repair routine
- */
- private function emitRepairEvents(): void {
- $dispatcher = \OC::$server->getEventDispatcher();
- $dispatcher->addListener('\OC\Repair::warning', function ($event) {
- if ($event instanceof GenericEvent) {
- $this->emit('\OC\Updater', 'repairWarning', $event->getArguments());
- }
- });
- $dispatcher->addListener('\OC\Repair::error', function ($event) {
- if ($event instanceof GenericEvent) {
- $this->emit('\OC\Updater', 'repairError', $event->getArguments());
- }
- });
- $dispatcher->addListener('\OC\Repair::info', function ($event) {
- if ($event instanceof GenericEvent) {
- $this->emit('\OC\Updater', 'repairInfo', $event->getArguments());
- }
- });
- $dispatcher->addListener('\OC\Repair::step', function ($event) {
- if ($event instanceof GenericEvent) {
- $this->emit('\OC\Updater', 'repairStep', $event->getArguments());
- }
- });
- }
-
private function logAllEvents(): void {
$log = $this->log;
- $dispatcher = \OC::$server->getEventDispatcher();
- $dispatcher->addListener('\OC\DB\Migrator::executeSql', function ($event) use ($log) {
- if (!$event instanceof GenericEvent) {
- return;
- }
- $log->info('\OC\DB\Migrator::executeSql: ' . $event->getSubject() . ' (' . $event->getArgument(0) . ' of ' . $event->getArgument(1) . ')', ['app' => 'updater']);
- });
- $dispatcher->addListener('\OC\DB\Migrator::checkTable', function ($event) use ($log) {
- if (!$event instanceof GenericEvent) {
- return;
+ /** @var IEventDispatcher $dispatcher */
+ $dispatcher = \OC::$server->get(IEventDispatcher::class);
+ $dispatcher->addListener(
+ MigratorExecuteSqlEvent::class,
+ function (MigratorExecuteSqlEvent $event) use ($log): void {
+ $log->info(get_class($event).': ' . $event->getSql() . ' (' . $event->getCurrentStep() . ' of ' . $event->getMaxStep() . ')', ['app' => 'updater']);
}
- $log->info('\OC\DB\Migrator::checkTable: ' . $event->getSubject() . ' (' . $event->getArgument(0) . ' of ' . $event->getArgument(1) . ')', ['app' => 'updater']);
- });
-
- $repairListener = function ($event) use ($log) {
- if (!$event instanceof GenericEvent) {
- return;
- }
- switch ($event->getSubject()) {
- case '\OC\Repair::startProgress':
- $log->info('\OC\Repair::startProgress: Starting ... ' . $event->getArgument(1) . ' (' . $event->getArgument(0) . ')', ['app' => 'updater']);
- break;
- case '\OC\Repair::advance':
- $desc = $event->getArgument(1);
- if (empty($desc)) {
- $desc = '';
- }
- $log->info('\OC\Repair::advance: ' . $desc . ' (' . $event->getArgument(0) . ')', ['app' => 'updater']);
-
- break;
- case '\OC\Repair::finishProgress':
- $log->info('\OC\Repair::finishProgress', ['app' => 'updater']);
- break;
- case '\OC\Repair::step':
- $log->info('\OC\Repair::step: Repair step: ' . $event->getArgument(0), ['app' => 'updater']);
- break;
- case '\OC\Repair::info':
- $log->info('\OC\Repair::info: Repair info: ' . $event->getArgument(0), ['app' => 'updater']);
- break;
- case '\OC\Repair::warning':
- $log->warning('\OC\Repair::warning: Repair warning: ' . $event->getArgument(0), ['app' => 'updater']);
- break;
- case '\OC\Repair::error':
- $log->error('\OC\Repair::error: Repair error: ' . $event->getArgument(0), ['app' => 'updater']);
- break;
+ );
+
+ $repairListener = function (Event $event) use ($log): void {
+ if ($event instanceof RepairStartEvent) {
+ $log->info(get_class($event).': Starting ... ' . $event->getMaxStep() . ' (' . $event->getCurrentStepName() . ')', ['app' => 'updater']);
+ } elseif ($event instanceof RepairAdvanceEvent) {
+ $desc = $event->getDescription();
+ if (empty($desc)) {
+ $desc = '';
+ }
+ $log->info(get_class($event).': ' . $desc . ' (' . $event->getIncrement() . ')', ['app' => 'updater']);
+ } elseif ($event instanceof RepairFinishEvent) {
+ $log->info(get_class($event), ['app' => 'updater']);
+ } elseif ($event instanceof RepairStepEvent) {
+ $log->info(get_class($event).': Repair step: ' . $event->getStepName(), ['app' => 'updater']);
+ } elseif ($event instanceof RepairInfoEvent) {
+ $log->info(get_class($event).': Repair info: ' . $event->getMessage(), ['app' => 'updater']);
+ } elseif ($event instanceof RepairWarningEvent) {
+ $log->warning(get_class($event).': Repair warning: ' . $event->getMessage(), ['app' => 'updater']);
+ } elseif ($event instanceof RepairErrorEvent) {
+ $log->error(get_class($event).': Repair error: ' . $event->getMessage(), ['app' => 'updater']);
}
};
- $dispatcher->addListener('\OC\Repair::startProgress', $repairListener);
- $dispatcher->addListener('\OC\Repair::advance', $repairListener);
- $dispatcher->addListener('\OC\Repair::finishProgress', $repairListener);
- $dispatcher->addListener('\OC\Repair::step', $repairListener);
- $dispatcher->addListener('\OC\Repair::info', $repairListener);
- $dispatcher->addListener('\OC\Repair::warning', $repairListener);
- $dispatcher->addListener('\OC\Repair::error', $repairListener);
+ $dispatcher->addListener(RepairStartEvent::class, $repairListener);
+ $dispatcher->addListener(RepairAdvanceEvent::class, $repairListener);
+ $dispatcher->addListener(RepairFinishEvent::class, $repairListener);
+ $dispatcher->addListener(RepairStepEvent::class, $repairListener);
+ $dispatcher->addListener(RepairInfoEvent::class, $repairListener);
+ $dispatcher->addListener(RepairWarningEvent::class, $repairListener);
+ $dispatcher->addListener(RepairErrorEvent::class, $repairListener);
$this->listen('\OC\Updater', 'maintenanceEnabled', function () use ($log) {
diff --git a/lib/private/User/Database.php b/lib/private/User/Database.php
index a9464c27085..f106c2e8b6d 100644
--- a/lib/private/User/Database.php
+++ b/lib/private/User/Database.php
@@ -45,7 +45,7 @@ declare(strict_types=1);
*/
namespace OC\User;
-use OC\Cache\CappedMemoryCache;
+use OCP\Cache\CappedMemoryCache;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\IDBConnection;
use OCP\Security\Events\ValidatePasswordPolicyEvent;
@@ -212,9 +212,15 @@ class Database extends ABackend implements
* @param string $displayName The new display name
* @return bool
*
+ * @throws \InvalidArgumentException
+ *
* Change the display name of a user
*/
public function setDisplayName(string $uid, string $displayName): bool {
+ if (mb_strlen($displayName) > 64) {
+ throw new \InvalidArgumentException('Invalid displayname');
+ }
+
$this->fixDI();
if ($this->userExists($uid)) {
@@ -275,7 +281,7 @@ class Database extends ABackend implements
->setMaxResults($limit)
->setFirstResult($offset);
- $result = $query->execute();
+ $result = $query->executeQuery();
$displayNames = [];
while ($row = $result->fetch()) {
$displayNames[(string)$row['uid']] = (string)$row['displayname'];
diff --git a/lib/private/User/DisplayNameCache.php b/lib/private/User/DisplayNameCache.php
index 22a79863e49..5d1cc8940d7 100644
--- a/lib/private/User/DisplayNameCache.php
+++ b/lib/private/User/DisplayNameCache.php
@@ -47,7 +47,7 @@ class DisplayNameCache implements IEventListener {
$this->userManager = $userManager;
}
- public function getDisplayName(string $userId) {
+ public function getDisplayName(string $userId): ?string {
if (isset($this->cache[$userId])) {
return $this->cache[$userId];
}
@@ -61,7 +61,7 @@ class DisplayNameCache implements IEventListener {
if ($user) {
$displayName = $user->getDisplayName();
} else {
- $displayName = $userId;
+ $displayName = null;
}
$this->cache[$userId] = $displayName;
$this->memCache->set($userId, $displayName, 60 * 10); // 10 minutes
diff --git a/lib/private/User/LazyUser.php b/lib/private/User/LazyUser.php
index 8b98b112731..096578b8f37 100644
--- a/lib/private/User/LazyUser.php
+++ b/lib/private/User/LazyUser.php
@@ -25,15 +25,14 @@ namespace OC\User;
use OCP\IUser;
use OCP\IUserManager;
+use OCP\UserInterface;
class LazyUser implements IUser {
private ?IUser $user = null;
- private DisplayNameCache $displayNameCache;
private string $uid;
private IUserManager $userManager;
- public function __construct(string $uid, DisplayNameCache $displayNameCache, IUserManager $userManager) {
- $this->displayNameCache = $displayNameCache;
+ public function __construct(string $uid, IUserManager $userManager) {
$this->uid = $uid;
$this->userManager = $userManager;
}
@@ -52,7 +51,7 @@ class LazyUser implements IUser {
}
public function getDisplayName() {
- return $this->displayNameCache->getDisplayName($this->uid);
+ return $this->userManager->getDisplayName($this->uid) ?? $this->uid;
}
public function setDisplayName($displayName) {
@@ -83,7 +82,7 @@ class LazyUser implements IUser {
return $this->getUser()->getBackendClassName();
}
- public function getBackend() {
+ public function getBackend(): ?UserInterface {
return $this->getUser()->getBackend();
}
diff --git a/lib/private/User/Listeners/UserChangedListener.php b/lib/private/User/Listeners/UserChangedListener.php
new file mode 100644
index 00000000000..a561db2423d
--- /dev/null
+++ b/lib/private/User/Listeners/UserChangedListener.php
@@ -0,0 +1,62 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright Copyright (c) 2022 Carl Schwan <carl@carlschwan.eu>
+ *
+ * @license AGPL-3.0-or-later
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+namespace OC\User\Listeners;
+
+use OCP\EventDispatcher\Event;
+use OCP\EventDispatcher\IEventListener;
+use OCP\User\Events\UserChangedEvent;
+use OCP\Files\NotFoundException;
+use OCP\IAvatarManager;
+
+/**
+ * @template-implements IEventListener<UserChangedEvent>
+ */
+class UserChangedListener implements IEventListener {
+ private IAvatarManager $avatarManager;
+
+ public function __construct(IAvatarManager $avatarManager) {
+ $this->avatarManager = $avatarManager;
+ }
+
+ public function handle(Event $event): void {
+ if (!($event instanceof UserChangedEvent)) {
+ return;
+ }
+
+ $user = $event->getUser();
+ $feature = $event->getFeature();
+ $oldValue = $event->getOldValue();
+ $value = $event->getValue();
+
+ // We only change the avatar on display name changes
+ if ($feature === 'displayName') {
+ try {
+ $avatar = $this->avatarManager->getAvatar($user->getUID());
+ $avatar->userChanged($feature, $oldValue, $value);
+ } catch (NotFoundException $e) {
+ // no avatar to remove
+ }
+ }
+ }
+}
diff --git a/lib/private/User/Listeners/UserDeletedListener.php b/lib/private/User/Listeners/UserDeletedListener.php
new file mode 100644
index 00000000000..7c9c46ef371
--- /dev/null
+++ b/lib/private/User/Listeners/UserDeletedListener.php
@@ -0,0 +1,65 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright Copyright (c) 2022 Carl Schwan <carl@carlschwan.eu>
+ *
+ * @license AGPL-3.0-or-later
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+namespace OC\User\Listeners;
+
+use OCP\EventDispatcher\Event;
+use OCP\EventDispatcher\IEventListener;
+use OCP\User\Events\UserDeletedEvent;
+use OCP\Files\NotFoundException;
+use OCP\IAvatarManager;
+use Psr\Log\LoggerInterface;
+
+/**
+ * @template-implements IEventListener<UserDeletedEvent>
+ */
+class UserDeletedListener implements IEventListener {
+ private IAvatarManager $avatarManager;
+ private LoggerInterface $logger;
+
+ public function __construct(LoggerInterface $logger, IAvatarManager $avatarManager) {
+ $this->avatarManager = $avatarManager;
+ $this->logger = $logger;
+ }
+
+ public function handle(Event $event): void {
+ if (!($event instanceof UserDeletedEvent)) {
+ return;
+ }
+
+ $user = $event->getUser();
+
+ // Delete avatar on user deletion
+ try {
+ $avatar = $this->avatarManager->getAvatar($user->getUID());
+ $avatar->remove(true);
+ } catch (NotFoundException $e) {
+ // no avatar to remove
+ } catch (\Exception $e) {
+ // Ignore exceptions
+ $this->logger->info('Could not cleanup avatar of ' . $user->getUID(), [
+ 'exception' => $e,
+ ]);
+ }
+ }
+}
diff --git a/lib/private/User/Manager.php b/lib/private/User/Manager.php
index c59cbaa7b20..be5151313c4 100644
--- a/lib/private/User/Manager.php
+++ b/lib/private/User/Manager.php
@@ -48,6 +48,8 @@ use OCP\Notification\IManager;
use OCP\Support\Subscription\IRegistry;
use OCP\User\Backend\IGetRealUIDBackend;
use OCP\User\Backend\ISearchKnownUsersBackend;
+use OCP\User\Backend\ICheckPasswordBackend;
+use OCP\User\Backend\ICountUsersBackend;
use OCP\User\Events\BeforeUserCreatedEvent;
use OCP\User\Events\UserCreatedEvent;
use OCP\UserInterface;
@@ -93,6 +95,8 @@ class Manager extends PublicEmitter implements IUserManager {
/** @var IEventDispatcher */
private $eventDispatcher;
+ private DisplayNameCache $displayNameCache;
+
public function __construct(IConfig $config,
EventDispatcherInterface $oldDispatcher,
ICacheFactory $cacheFactory,
@@ -106,6 +110,7 @@ class Manager extends PublicEmitter implements IUserManager {
unset($cachedUsers[$user->getUID()]);
});
$this->eventDispatcher = $eventDispatcher;
+ $this->displayNameCache = new DisplayNameCache($cacheFactory, $this);
}
/**
@@ -183,6 +188,10 @@ class Manager extends PublicEmitter implements IUserManager {
return null;
}
+ public function getDisplayName(string $uid): ?string {
+ return $this->displayNameCache->getDisplayName($uid);
+ }
+
/**
* get or construct the user object
*
@@ -223,7 +232,7 @@ class Manager extends PublicEmitter implements IUserManager {
*
* @param string $loginName
* @param string $password
- * @return mixed the User object on success, false otherwise
+ * @return IUser|false the User object on success, false otherwise
*/
public function checkPassword($loginName, $password) {
$result = $this->checkPasswordNoLogging($loginName, $password);
@@ -254,7 +263,8 @@ class Manager extends PublicEmitter implements IUserManager {
$backends = $this->backends;
}
foreach ($backends as $backend) {
- if ($backend->implementsActions(Backend::CHECK_PASSWORD)) {
+ if ($backend instanceof ICheckPasswordBackend || $backend->implementsActions(Backend::CHECK_PASSWORD)) {
+ /** @var ICheckPasswordBackend $backend */
$uid = $backend->checkPassword($loginName, $password);
if ($uid !== false) {
return $this->getUserObject($uid, $backend);
@@ -268,7 +278,8 @@ class Manager extends PublicEmitter implements IUserManager {
$password = urldecode($password);
foreach ($backends as $backend) {
- if ($backend->implementsActions(Backend::CHECK_PASSWORD)) {
+ if ($backend instanceof ICheckPasswordBackend || $backend->implementsActions(Backend::CHECK_PASSWORD)) {
+ /** @var ICheckPasswordBackend|UserInterface $backend */
$uid = $backend->checkPassword($loginName, $password);
if ($uid !== false) {
return $this->getUserObject($uid, $backend);
@@ -376,7 +387,7 @@ class Manager extends PublicEmitter implements IUserManager {
* @param string $uid
* @param string $password
* @throws \InvalidArgumentException
- * @return bool|IUser the created user or false
+ * @return false|IUser the created user or false
*/
public function createUser($uid, $password) {
// DI injection is not used here as IRegistry needs the user manager itself for user count and thus it would create a cyclic dependency
@@ -415,7 +426,7 @@ class Manager extends PublicEmitter implements IUserManager {
* @param string $uid
* @param string $password
* @param UserInterface $backend
- * @return IUser|null
+ * @return IUser|false
* @throws \InvalidArgumentException
*/
public function createUserFromBackend($uid, $password, UserInterface $backend) {
@@ -469,8 +480,9 @@ class Manager extends PublicEmitter implements IUserManager {
/** @deprecated 21.0.0 use UserCreatedEvent event with the IEventDispatcher instead */
$this->emit('\OC\User', 'postCreateUser', [$user, $password]);
$this->eventDispatcher->dispatchTyped(new UserCreatedEvent($user, $password));
+ return $user;
}
- return $user;
+ return false;
}
/**
@@ -478,16 +490,13 @@ class Manager extends PublicEmitter implements IUserManager {
*
* @param boolean $hasLoggedIn when true only users that have a lastLogin
* entry in the preferences table will be affected
- * @return array|int an array of backend class as key and count number as value
- * if $hasLoggedIn is true only an int is returned
+ * @return array<string, int> an array of backend class as key and count number as value
*/
- public function countUsers($hasLoggedIn = false) {
- if ($hasLoggedIn) {
- return $this->countSeenUsers();
- }
+ public function countUsers() {
$userCountStatistics = [];
foreach ($this->backends as $backend) {
- if ($backend->implementsActions(Backend::COUNT_USERS)) {
+ if ($backend instanceof ICountUsersBackend || $backend->implementsActions(Backend::COUNT_USERS)) {
+ /** @var ICountUsersBackend|IUserBackend $backend */
$backendUsers = $backend->countUsers();
if ($backendUsers !== false) {
if ($backend instanceof IUserBackend) {
@@ -528,7 +537,7 @@ class Manager extends PublicEmitter implements IUserManager {
* The callback is executed for each user on each backend.
* If the callback returns false no further users will be retrieved.
*
- * @param \Closure $callback
+ * @psalm-param \Closure(\OCP\IUser):?bool $callback
* @param string $search
* @param boolean $onlySeen when true only users that have a lastLogin entry
* in the preferences table will be affected
@@ -740,4 +749,8 @@ class Manager extends PublicEmitter implements IUserManager {
return !file_exists(rtrim($dataDirectory, '/') . '/' . $uid);
}
+
+ public function getDisplayNameCache(): DisplayNameCache {
+ return $this->displayNameCache;
+ }
}
diff --git a/lib/private/User/Session.php b/lib/private/User/Session.php
index 365a01c4595..65a213d4bf8 100644
--- a/lib/private/User/Session.php
+++ b/lib/private/User/Session.php
@@ -90,7 +90,7 @@ use Symfony\Component\EventDispatcher\GenericEvent;
*/
class Session implements IUserSession, Emitter {
- /** @var Manager|PublicEmitter $manager */
+ /** @var Manager $manager */
private $manager;
/** @var ISession $session */
@@ -288,9 +288,9 @@ class Session implements IUserSession, Emitter {
}
/**
- * get the login name of the current user
+ * Get the login name of the current user
*
- * @return string
+ * @return ?string
*/
public function getLoginName() {
if ($this->activeUser) {
@@ -865,17 +865,25 @@ class Session implements IUserSession, Emitter {
$tokens = $this->config->getUserKeys($uid, 'login_token');
// test cookies token against stored tokens
if (!in_array($currentToken, $tokens, true)) {
+ $this->logger->error('Tried to log in {uid} but could not verify token', [
+ 'app' => 'core',
+ 'uid' => $uid,
+ ]);
return false;
}
// replace successfully used token with a new one
$this->config->deleteUserValue($uid, 'login_token', $currentToken);
$newToken = $this->random->generate(32);
- $this->config->setUserValue($uid, 'login_token', $newToken, $this->timeFactory->getTime());
+ $this->config->setUserValue($uid, 'login_token', $newToken, (string)$this->timeFactory->getTime());
try {
$sessionId = $this->session->getId();
$token = $this->tokenProvider->renewSessionToken($oldSessionId, $sessionId);
} catch (SessionNotAvailableException $ex) {
+ $this->logger->warning('Could not renew session token for {uid} because the session is unavailable', [
+ 'app' => 'core',
+ 'uid' => $uid,
+ ]);
return false;
} catch (InvalidTokenException $ex) {
$this->logger->warning('Renewing session token failed', ['app' => 'core']);
@@ -905,7 +913,7 @@ class Session implements IUserSession, Emitter {
*/
public function createRememberMeToken(IUser $user) {
$token = $this->random->generate(32);
- $this->config->setUserValue($user->getUID(), 'login_token', $token, $this->timeFactory->getTime());
+ $this->config->setUserValue($user->getUID(), 'login_token', $token, (string)$this->timeFactory->getTime());
$this->setMagicInCookie($user->getUID(), $token);
}
diff --git a/lib/private/User/User.php b/lib/private/User/User.php
index e7aa72fafba..72c0d5c1a88 100644
--- a/lib/private/User/User.php
+++ b/lib/private/User/User.php
@@ -52,6 +52,10 @@ use OCP\IUserBackend;
use OCP\User\Events\BeforeUserDeletedEvent;
use OCP\User\Events\UserDeletedEvent;
use OCP\User\GetQuotaEvent;
+use OCP\User\Backend\ISetDisplayNameBackend;
+use OCP\User\Backend\ISetPasswordBackend;
+use OCP\User\Backend\IProvideAvatarBackend;
+use OCP\User\Backend\IGetHomeBackend;
use OCP\UserInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\GenericEvent;
@@ -150,12 +154,17 @@ class User implements IUser {
*
* @param string $displayName
* @return bool
+ *
+ * @since 25.0.0 Throw InvalidArgumentException
+ * @throws \InvalidArgumentException
*/
public function setDisplayName($displayName) {
$displayName = trim($displayName);
$oldDisplayName = $this->getDisplayName();
if ($this->backend->implementsActions(Backend::SET_DISPLAYNAME) && !empty($displayName) && $displayName !== $oldDisplayName) {
- $result = $this->backend->setDisplayName($this->uid, $displayName);
+ /** @var ISetDisplayNameBackend $backend */
+ $backend = $this->backend;
+ $result = $backend->setDisplayName($this->uid, $displayName);
if ($result) {
$this->displayName = $displayName;
$this->triggerChange('displayName', $displayName, $oldDisplayName);
@@ -238,10 +247,15 @@ class User implements IUser {
* updates the timestamp of the most recent login of this user
*/
public function updateLastLoginTimestamp() {
- $firstTimeLogin = ($this->getLastLogin() === 0);
- $this->lastLogin = time();
- $this->config->setUserValue(
- $this->uid, 'login', 'lastLogin', $this->lastLogin);
+ $previousLogin = $this->getLastLogin();
+ $now = time();
+ $firstTimeLogin = $previousLogin === 0;
+
+ if ($now - $previousLogin > 60) {
+ $this->lastLogin = time();
+ $this->config->setUserValue(
+ $this->uid, 'login', 'lastLogin', (string)$this->lastLogin);
+ }
return $firstTimeLogin;
}
@@ -280,7 +294,7 @@ class User implements IUser {
\OC::$server->getCommentsManager()->deleteReferencesOfActor('users', $this->uid);
\OC::$server->getCommentsManager()->deleteReadMarksFromUser($this);
- /** @var IAvatarManager $avatarManager */
+ /** @var AvatarManager $avatarManager */
$avatarManager = \OC::$server->query(AvatarManager::class);
$avatarManager->deleteUserAvatar($this->uid);
@@ -319,7 +333,9 @@ class User implements IUser {
$this->emitter->emit('\OC\User', 'preSetPassword', [$this, $password, $recoveryPassword]);
}
if ($this->backend->implementsActions(Backend::SET_PASSWORD)) {
- $result = $this->backend->setPassword($this->uid, $password);
+ /** @var ISetPasswordBackend $backend */
+ $backend = $this->backend;
+ $result = $backend->setPassword($this->uid, $password);
if ($result !== false) {
$this->legacyDispatcher->dispatch(IUser::class . '::postSetPassword', new GenericEvent($this, [
@@ -344,7 +360,8 @@ class User implements IUser {
*/
public function getHome() {
if (!$this->home) {
- if ($this->backend->implementsActions(Backend::GET_HOME) and $home = $this->backend->getHome($this->uid)) {
+ /** @psalm-suppress UndefinedInterfaceMethod Once we get rid of the legacy implementsActions, psalm won't complain anymore */
+ if (($this->backend instanceof IGetHomeBackend || $this->backend->implementsActions(Backend::GET_HOME)) && $home = $this->backend->getHome($this->uid)) {
$this->home = $home;
} elseif ($this->config) {
$this->home = $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/' . $this->uid;
@@ -367,18 +384,20 @@ class User implements IUser {
return get_class($this->backend);
}
- public function getBackend() {
+ public function getBackend(): ?UserInterface {
return $this->backend;
}
/**
- * check if the backend allows the user to change his avatar on Personal page
+ * Check if the backend allows the user to change his avatar on Personal page
*
* @return bool
*/
public function canChangeAvatar() {
- if ($this->backend->implementsActions(Backend::PROVIDE_AVATAR)) {
- return $this->backend->canChangeAvatar($this->uid);
+ if ($this->backend instanceof IProvideAvatarBackend || $this->backend->implementsActions(Backend::PROVIDE_AVATAR)) {
+ /** @var IProvideAvatarBackend $backend */
+ $backend = $this->backend;
+ return $backend->canChangeAvatar($this->uid);
}
return true;
}
@@ -501,7 +520,7 @@ class User implements IUser {
$oldQuota = $this->config->getUserValue($this->uid, 'files', 'quota', '');
if ($quota !== 'none' and $quota !== 'default') {
$quota = OC_Helper::computerFileSize($quota);
- $quota = OC_Helper::humanFileSize($quota);
+ $quota = OC_Helper::humanFileSize((int)$quota);
}
if ($quota !== $oldQuota) {
$this->config->setUserValue($this->uid, 'files', 'quota', $quota);
@@ -540,19 +559,16 @@ class User implements IUser {
public function getCloudId() {
$uid = $this->getUID();
$server = $this->urlGenerator->getAbsoluteURL('/');
+ if (substr($server, -10) === '/index.php') {
+ $server = substr($server, 0, -10);
+ }
$server = rtrim($this->removeProtocolFromUrl($server), '/');
return $uid . '@' . $server;
}
- /**
- * @param string $url
- * @return string
- */
- private function removeProtocolFromUrl($url) {
+ private function removeProtocolFromUrl(string $url): string {
if (strpos($url, 'https://') === 0) {
return substr($url, strlen('https://'));
- } elseif (strpos($url, 'http://') === 0) {
- return substr($url, strlen('http://'));
}
return $url;
diff --git a/lib/private/legacy/OC_App.php b/lib/private/legacy/OC_App.php
index f290b7a610c..a5eb26d0d4b 100644
--- a/lib/private/legacy/OC_App.php
+++ b/lib/private/legacy/OC_App.php
@@ -50,18 +50,19 @@ declare(strict_types=1);
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
+use OCP\AppFramework\QueryException;
+use OCP\App\ManagerEvent;
+use OCP\Authentication\IAlternativeLogin;
+use OCP\ILogger;
+use OCP\Settings\IManager as ISettingsManager;
+use OC\AppFramework\Bootstrap\Coordinator;
use OC\App\DependencyAnalyzer;
use OC\App\Platform;
-use OC\AppFramework\Bootstrap\Coordinator;
use OC\DB\MigrationService;
use OC\Installer;
use OC\Repair;
+use OC\Repair\Events\RepairErrorEvent;
use OC\ServerNotAvailableException;
-use OCP\App\ManagerEvent;
-use OCP\AppFramework\QueryException;
-use OCP\Authentication\IAlternativeLogin;
-use OCP\ILogger;
-use OCP\Settings\IManager as ISettingsManager;
use Psr\Log\LoggerInterface;
/**
@@ -183,7 +184,7 @@ class OC_App {
'app' => $app,
]);
try {
- self::requireAppFile($app);
+ self::requireAppFile($appPath);
} catch (Throwable $ex) {
if ($ex instanceof ServerNotAvailableException) {
throw $ex;
@@ -679,25 +680,6 @@ class OC_App {
}
/**
- * register an admin form to be shown
- *
- * @param string $app
- * @param string $page
- */
- public static function registerAdmin(string $app, string $page) {
- self::$adminForms[] = $app . '/' . $page . '.php';
- }
-
- /**
- * register a personal form to be shown
- * @param string $app
- * @param string $page
- */
- public static function registerPersonal(string $app, string $page) {
- self::$personalForms[] = $app . '/' . $page . '.php';
- }
-
- /**
* @param array $entry
* @deprecated 20.0.0 Please register your alternative login option using the registerAlternativeLogin() on the RegistrationContext in your Application class implementing the OCP\Authentication\IAlternativeLogin interface
*/
@@ -1066,7 +1048,7 @@ class OC_App {
// load the app
self::loadApp($appId);
- $dispatcher = OC::$server->getEventDispatcher();
+ $dispatcher = \OC::$server->get(\OCP\EventDispatcher\IEventDispatcher::class);
// load the steps
$r = new Repair([], $dispatcher, \OC::$server->get(LoggerInterface::class));
@@ -1074,7 +1056,7 @@ class OC_App {
try {
$r->addStep($step);
} catch (Exception $ex) {
- $r->emit('\OC\Repair', 'error', [$ex->getMessage()]);
+ $dispatcher->dispatchTyped(new RepairErrorEvent($ex->getMessage()));
\OC::$server->getLogger()->logException($ex);
}
}
diff --git a/lib/private/legacy/OC_Files.php b/lib/private/legacy/OC_Files.php
index 02e15fd08d5..6a3a44d6cc0 100644
--- a/lib/private/legacy/OC_Files.php
+++ b/lib/private/legacy/OC_Files.php
@@ -44,10 +44,12 @@ use bantu\IniGetWrapper\IniGetWrapper;
use OC\Files\View;
use OC\Streamer;
use OCP\Lock\ILockingProvider;
+use OCP\Files\Events\BeforeZipCreatedEvent;
+use OCP\Files\Events\BeforeDirectFileDownloadEvent;
+use OCP\EventDispatcher\IEventDispatcher;
/**
* Class for file server access
- *
*/
class OC_Files {
public const FILE = 1;
@@ -167,6 +169,14 @@ class OC_Files {
}
}
+ //Dispatch an event to see if any apps have problem with download
+ $event = new BeforeZipCreatedEvent($dir, is_array($files) ? $files : [$files]);
+ $dispatcher = \OCP\Server::get(IEventDispatcher::class);
+ $dispatcher->dispatchTyped($event);
+ if ((!$event->isSuccessful()) || $event->getErrorMessage() !== null) {
+ throw new \OC\ForbiddenException($event->getErrorMessage());
+ }
+
$streamer = new Streamer(\OC::$server->getRequest(), $fileSize, $numberOfFiles);
OC_Util::obEnd();
@@ -222,13 +232,16 @@ class OC_Files {
self::unlockAllTheFiles($dir, $files, $getType, $view, $filename);
OC::$server->getLogger()->logException($ex);
$l = \OC::$server->getL10N('lib');
- \OC_Template::printErrorPage($l->t('Cannot read file'), $ex->getMessage(), 200);
+ \OC_Template::printErrorPage($l->t('Cannot download file'), $ex->getMessage(), 200);
} catch (\Exception $ex) {
self::unlockAllTheFiles($dir, $files, $getType, $view, $filename);
OC::$server->getLogger()->logException($ex);
$l = \OC::$server->getL10N('lib');
$hint = method_exists($ex, 'getHint') ? $ex->getHint() : '';
- \OC_Template::printErrorPage($l->t('Cannot read file'), $hint, 200);
+ if ($event && $event->getErrorMessage() !== null) {
+ $hint .= ' ' . $event->getErrorMessage();
+ }
+ \OC_Template::printErrorPage($l->t('Cannot download file'), $hint, 200);
}
}
@@ -287,6 +300,7 @@ class OC_Files {
* @param string $name
* @param string $dir
* @param array $params ; 'head' boolean to only send header of the request ; 'range' http range header
+ * @throws \OC\ForbiddenException
*/
private static function getSingleFile($view, $dir, $name, $params) {
$filename = $dir . '/' . $name;
@@ -322,6 +336,19 @@ class OC_Files {
$rangeArray = self::parseHttpRangeHeader(substr($params['range'], 6), $fileSize);
}
+ $dispatcher = \OC::$server->query(IEventDispatcher::class);
+ $event = new BeforeDirectFileDownloadEvent($filename);
+ $dispatcher->dispatchTyped($event);
+
+ if (!\OC\Files\Filesystem::isReadable($filename) || $event->getErrorMessage()) {
+ if ($event->getErrorMessage()) {
+ $msg = $event->getErrorMessage();
+ } else {
+ $msg = 'Access denied';
+ }
+ throw new \OC\ForbiddenException($msg);
+ }
+
self::sendHeaders($filename, $name, $rangeArray);
if (isset($params['head']) && $params['head']) {
diff --git a/lib/private/legacy/OC_Helper.php b/lib/private/legacy/OC_Helper.php
index 226f73a0711..710225c7474 100644
--- a/lib/private/legacy/OC_Helper.php
+++ b/lib/private/legacy/OC_Helper.php
@@ -47,9 +47,9 @@ use bantu\IniGetWrapper\IniGetWrapper;
use OC\Files\Filesystem;
use OCP\Files\Mount\IMountPoint;
use OCP\ICacheFactory;
+use OCP\IBinaryFinder;
use OCP\IUser;
use Psr\Log\LoggerInterface;
-use Symfony\Component\Process\ExecutableFinder;
/**
* Collection of useful functions
@@ -434,47 +434,19 @@ class OC_Helper {
/**
* Checks if a function is available
*
- * @param string $function_name
- * @return bool
+ * @deprecated Since 25.0.0 use \OCP\Util::isFunctionEnabled instead
*/
- public static function is_function_enabled($function_name) {
- if (!function_exists($function_name)) {
- return false;
- }
- $ini = \OC::$server->get(IniGetWrapper::class);
- $disabled = explode(',', $ini->get('disable_functions') ?: '');
- $disabled = array_map('trim', $disabled);
- if (in_array($function_name, $disabled)) {
- return false;
- }
- $disabled = explode(',', $ini->get('suhosin.executor.func.blacklist') ?: '');
- $disabled = array_map('trim', $disabled);
- if (in_array($function_name, $disabled)) {
- return false;
- }
- return true;
+ public static function is_function_enabled(string $function_name): bool {
+ return \OCP\Util::isFunctionEnabled($function_name);
}
/**
* Try to find a program
- *
- * @param string $program
- * @return null|string
+ * @deprecated Since 25.0.0 Use \OC\BinaryFinder directly
*/
- public static function findBinaryPath($program) {
- $memcache = \OC::$server->getMemCacheFactory()->createDistributed('findBinaryPath');
- if ($memcache->hasKey($program)) {
- return $memcache->get($program);
- }
- $result = null;
- if (self::is_function_enabled('exec')) {
- $exeSniffer = new ExecutableFinder();
- // Returns null if nothing is found
- $result = $exeSniffer->find($program, null, ['/usr/local/sbin', '/usr/local/bin', '/usr/sbin', '/usr/bin', '/sbin', '/bin', '/opt/bin']);
- }
- // store the value for 5 minutes
- $memcache->set($program, $result, 300);
- return $result;
+ public static function findBinaryPath(string $program): ?string {
+ $result = \OCP\Server::get(IBinaryFinder::class)->findBinaryPath($program);
+ return $result !== false ? $result : null;
}
/**
diff --git a/lib/private/legacy/OC_Image.php b/lib/private/legacy/OC_Image.php
index 3988eb8eaa5..a212d639084 100644
--- a/lib/private/legacy/OC_Image.php
+++ b/lib/private/legacy/OC_Image.php
@@ -103,10 +103,8 @@ class OC_Image implements \OCP\IImage {
* @return bool
*/
public function valid() {
- if (is_resource($this->resource)) {
- return true;
- }
- if (is_object($this->resource) && get_class($this->resource) === \GdImage::class) {
+ if ((is_resource($this->resource) && get_resource_type($this->resource) === 'gd') ||
+ (is_object($this->resource) && get_class($this->resource) === \GdImage::class)) {
return true;
}
@@ -486,7 +484,7 @@ class OC_Image implements \OCP\IImage {
*/
public function fixOrientation() {
if (!$this->valid()) {
- $this->logger->error(__METHOD__ . '(): No image loaded', ['app' => 'core']);
+ $this->logger->debug(__METHOD__ . '(): No image loaded', ['app' => 'core']);
return false;
}
$o = $this->getOrientation();
@@ -994,7 +992,7 @@ class OC_Image implements \OCP\IImage {
*/
public function resize($maxSize) {
if (!$this->valid()) {
- $this->logger->error(__METHOD__ . '(): No image loaded', ['app' => 'core']);
+ $this->logger->debug(__METHOD__ . '(): No image loaded', ['app' => 'core']);
return false;
}
$result = $this->resizeNew($maxSize);
@@ -1009,7 +1007,7 @@ class OC_Image implements \OCP\IImage {
*/
private function resizeNew($maxSize) {
if (!$this->valid()) {
- $this->logger->error(__METHOD__ . '(): No image loaded', ['app' => 'core']);
+ $this->logger->debug(__METHOD__ . '(): No image loaded', ['app' => 'core']);
return false;
}
$widthOrig = imagesx($this->resource);
@@ -1034,7 +1032,7 @@ class OC_Image implements \OCP\IImage {
*/
public function preciseResize(int $width, int $height): bool {
if (!$this->valid()) {
- $this->logger->error(__METHOD__ . '(): No image loaded', ['app' => 'core']);
+ $this->logger->debug(__METHOD__ . '(): No image loaded', ['app' => 'core']);
return false;
}
$result = $this->preciseResizeNew($width, $height);
@@ -1055,14 +1053,14 @@ class OC_Image implements \OCP\IImage {
return false;
}
if (!$this->valid()) {
- $this->logger->error(__METHOD__ . '(): No image loaded', ['app' => 'core']);
+ $this->logger->debug(__METHOD__ . '(): No image loaded', ['app' => 'core']);
return false;
}
$widthOrig = imagesx($this->resource);
$heightOrig = imagesy($this->resource);
$process = imagecreatetruecolor($width, $height);
if ($process === false) {
- $this->logger->error(__METHOD__ . '(): Error creating true color image', ['app' => 'core']);
+ $this->logger->debug(__METHOD__ . '(): Error creating true color image', ['app' => 'core']);
return false;
}
@@ -1075,7 +1073,7 @@ class OC_Image implements \OCP\IImage {
$res = imagecopyresampled($process, $this->resource, 0, 0, 0, 0, $width, $height, $widthOrig, $heightOrig);
if ($res === false) {
- $this->logger->error(__METHOD__ . '(): Error re-sampling process image', ['app' => 'core']);
+ $this->logger->debug(__METHOD__ . '(): Error re-sampling process image', ['app' => 'core']);
imagedestroy($process);
return false;
}
@@ -1090,7 +1088,7 @@ class OC_Image implements \OCP\IImage {
*/
public function centerCrop($size = 0) {
if (!$this->valid()) {
- $this->logger->error('OC_Image->centerCrop, No image loaded', ['app' => 'core']);
+ $this->logger->debug('OC_Image->centerCrop, No image loaded', ['app' => 'core']);
return false;
}
$widthOrig = imagesx($this->resource);
@@ -1117,7 +1115,7 @@ class OC_Image implements \OCP\IImage {
}
$process = imagecreatetruecolor($targetWidth, $targetHeight);
if ($process === false) {
- $this->logger->error('OC_Image->centerCrop, Error creating true color image', ['app' => 'core']);
+ $this->logger->debug('OC_Image->centerCrop, Error creating true color image', ['app' => 'core']);
return false;
}
@@ -1130,7 +1128,7 @@ class OC_Image implements \OCP\IImage {
imagecopyresampled($process, $this->resource, 0, 0, $x, $y, $targetWidth, $targetHeight, $width, $height);
if ($process === false) {
- $this->logger->error('OC_Image->centerCrop, Error re-sampling process image ' . $width . 'x' . $height, ['app' => 'core']);
+ $this->logger->debug('OC_Image->centerCrop, Error re-sampling process image ' . $width . 'x' . $height, ['app' => 'core']);
return false;
}
imagedestroy($this->resource);
@@ -1149,7 +1147,7 @@ class OC_Image implements \OCP\IImage {
*/
public function crop(int $x, int $y, int $w, int $h): bool {
if (!$this->valid()) {
- $this->logger->error(__METHOD__ . '(): No image loaded', ['app' => 'core']);
+ $this->logger->debug(__METHOD__ . '(): No image loaded', ['app' => 'core']);
return false;
}
$result = $this->cropNew($x, $y, $w, $h);
@@ -1169,12 +1167,12 @@ class OC_Image implements \OCP\IImage {
*/
public function cropNew(int $x, int $y, int $w, int $h) {
if (!$this->valid()) {
- $this->logger->error(__METHOD__ . '(): No image loaded', ['app' => 'core']);
+ $this->logger->debug(__METHOD__ . '(): No image loaded', ['app' => 'core']);
return false;
}
$process = imagecreatetruecolor($w, $h);
if ($process === false) {
- $this->logger->error(__METHOD__ . '(): Error creating true color image', ['app' => 'core']);
+ $this->logger->debug(__METHOD__ . '(): Error creating true color image', ['app' => 'core']);
return false;
}
@@ -1187,7 +1185,7 @@ class OC_Image implements \OCP\IImage {
imagecopyresampled($process, $this->resource, 0, 0, $x, $y, $w, $h, $w, $h);
if ($process === false) {
- $this->logger->error(__METHOD__ . '(): Error re-sampling process image ' . $w . 'x' . $h, ['app' => 'core']);
+ $this->logger->debug(__METHOD__ . '(): Error re-sampling process image ' . $w . 'x' . $h, ['app' => 'core']);
return false;
}
return $process;
@@ -1204,7 +1202,7 @@ class OC_Image implements \OCP\IImage {
*/
public function fitIn($maxWidth, $maxHeight) {
if (!$this->valid()) {
- $this->logger->error(__METHOD__ . '(): No image loaded', ['app' => 'core']);
+ $this->logger->debug(__METHOD__ . '(): No image loaded', ['app' => 'core']);
return false;
}
$widthOrig = imagesx($this->resource);
@@ -1227,7 +1225,7 @@ class OC_Image implements \OCP\IImage {
*/
public function scaleDownToFit($maxWidth, $maxHeight) {
if (!$this->valid()) {
- $this->logger->error(__METHOD__ . '(): No image loaded', ['app' => 'core']);
+ $this->logger->debug(__METHOD__ . '(): No image loaded', ['app' => 'core']);
return false;
}
$widthOrig = imagesx($this->resource);
diff --git a/lib/private/legacy/OC_User.php b/lib/private/legacy/OC_User.php
index bc47359dafc..de066e143b4 100644
--- a/lib/private/legacy/OC_User.php
+++ b/lib/private/legacy/OC_User.php
@@ -178,7 +178,12 @@ class OC_User {
}
$userSession->setLoginName($uid);
$request = OC::$server->getRequest();
- $userSession->createSessionToken($request, $uid, $uid);
+ $password = null;
+ if ($backend instanceof \OCP\Authentication\IProvideUserSecretBackend) {
+ $password = $backend->getCurrentUserSecret();
+ }
+ $userSession->createSessionToken($request, $uid, $uid, $password);
+ $userSession->createRememberMeToken($userSession->getUser());
// setup the filesystem
OC_Util::setupFS($uid);
// first call the post_login hooks, the login-process needs to be
@@ -190,7 +195,7 @@ class OC_User {
'post_login',
[
'uid' => $uid,
- 'password' => null,
+ 'password' => $password,
'isTokenLogin' => false,
]
);
diff --git a/lib/public/Accounts/IAccountManager.php b/lib/public/Accounts/IAccountManager.php
index ae5f6b1e542..e41327171b4 100644
--- a/lib/public/Accounts/IAccountManager.php
+++ b/lib/public/Accounts/IAccountManager.php
@@ -8,6 +8,7 @@ declare(strict_types=1);
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Joas Schilling <coding@schilljs.com>
* @author Julius Härtl <jus@bitgrid.net>
+ * @author Thomas Citharel <nextcloud@tcit.fr>
* @author Vincent Petry <vincent@nextcloud.com>
*
* @license GNU AGPL version 3 or any later version
@@ -89,6 +90,21 @@ interface IAccountManager {
*/
public const VISIBILITY_PUBLIC = 'public';
+ /**
+ * The list of allowed scopes
+ *
+ * @since 25.0.0
+ */
+ public const ALLOWED_SCOPES = [
+ self::SCOPE_PRIVATE,
+ self::SCOPE_LOCAL,
+ self::SCOPE_FEDERATED,
+ self::SCOPE_PUBLISHED,
+ self::VISIBILITY_PRIVATE,
+ self::VISIBILITY_CONTACTS_ONLY,
+ self::VISIBILITY_PUBLIC,
+ ];
+
public const PROPERTY_AVATAR = 'avatar';
public const PROPERTY_DISPLAYNAME = 'displayname';
public const PROPERTY_PHONE = 'phone';
@@ -122,6 +138,26 @@ interface IAccountManager {
*/
public const PROPERTY_PROFILE_ENABLED = 'profile_enabled';
+ /**
+ * The list of allowed properties
+ *
+ * @since 25.0.0
+ */
+ public const ALLOWED_PROPERTIES = [
+ self::PROPERTY_AVATAR,
+ self::PROPERTY_DISPLAYNAME,
+ self::PROPERTY_PHONE,
+ self::PROPERTY_EMAIL,
+ self::PROPERTY_WEBSITE,
+ self::PROPERTY_ADDRESS,
+ self::PROPERTY_TWITTER,
+ self::PROPERTY_ORGANISATION,
+ self::PROPERTY_ROLE,
+ self::PROPERTY_HEADLINE,
+ self::PROPERTY_BIOGRAPHY,
+ self::PROPERTY_PROFILE_ENABLED,
+ ];
+
public const COLLECTION_EMAIL = 'additional_mail';
public const NOT_VERIFIED = '0';
diff --git a/lib/public/App.php b/lib/public/App.php
deleted file mode 100644
index 5103e624316..00000000000
--- a/lib/public/App.php
+++ /dev/null
@@ -1,104 +0,0 @@
-<?php
-/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Bart Visscher <bartv@thisnet.nl>
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Frank Karlitschek <frank@karlitschek.de>
- * @author Georg Ehrke <oc.list@georgehrke.com>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Jörn Friedrich Dreyer <jfd@butonic.de>
- * @author Julius Härtl <jus@bitgrid.net>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Robin McCorkell <robin@mccorkell.me.uk>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- * @author Thomas Müller <thomas.mueller@tmit.eu>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
- */
-// use OCP namespace for all classes that are considered public.
-// This means that they should be used by apps instead of the internal ownCloud classes
-
-namespace OCP;
-
-/**
- * This class provides functions to manage apps in ownCloud
- * @since 4.0.0
- * @deprecated 14.0.0
- */
-class App {
-
-
- /**
- * Register a Configuration Screen that should appear in the personal settings section.
- * @param string $app appid
- * @param string $page page to be included
- * @return void
- * @since 4.0.0
- * @deprecated 14.0.0 Use settings section in appinfo.xml to register personal admin sections
- */
- public static function registerPersonal($app, $page) {
- \OC_App::registerPersonal($app, $page);
- }
-
- /**
- * Register a Configuration Screen that should appear in the Admin section.
- * @param string $app string appid
- * @param string $page string page to be included
- * @return void
- * @since 4.0.0
- * @deprecated 14.0.0 Use settings section in appinfo.xml to register admin sections
- */
- public static function registerAdmin($app, $page) {
- \OC_App::registerAdmin($app, $page);
- }
-
- /**
- * Read app metadata from the info.xml file
- * @param string $app id of the app or the path of the info.xml file
- * @param boolean $path (optional)
- * @return array|null
- * @deprecated 14.0.0 ise \OC::$server->getAppManager()->getAppInfo($appId)
- * @since 4.0.0
- */
- public static function getAppInfo($app, $path = false) {
- return \OC_App::getAppInfo($app, $path);
- }
-
- /**
- * checks whether or not an app is enabled
- * @param string $app
- * @return boolean
- *
- * This function checks whether or not an app is enabled.
- * @since 4.0.0
- * @deprecated 13.0.0 use \OC::$server->getAppManager()->isEnabledForUser($appId)
- */
- public static function isEnabled($app) {
- return \OC::$server->getAppManager()->isEnabledForUser($app);
- }
-
- /**
- * Get the last version of the app from appinfo/info.xml
- * @param string $app
- * @return string
- * @since 4.0.0
- * @deprecated 14.0.0 use \OC::$server->getAppManager()->getAppVersion($appId)
- */
- public static function getAppVersion($app) {
- return \OC::$server->getAppManager()->getAppVersion($app);
- }
-}
diff --git a/lib/public/App/IAppManager.php b/lib/public/App/IAppManager.php
index 7473b229427..e0b5c049290 100644
--- a/lib/public/App/IAppManager.php
+++ b/lib/public/App/IAppManager.php
@@ -36,6 +36,9 @@ use OCP\IUser;
/**
* Interface IAppManager
*
+ * @warning This interface shouldn't be included with dependency injection in
+ * classes used for installing Nextcloud.
+ *
* @since 8.0.0
*/
interface IAppManager {
diff --git a/lib/public/AppFramework/Bootstrap/IRegistrationContext.php b/lib/public/AppFramework/Bootstrap/IRegistrationContext.php
index a5d675f14c7..0f398c13979 100644
--- a/lib/public/AppFramework/Bootstrap/IRegistrationContext.php
+++ b/lib/public/AppFramework/Bootstrap/IRegistrationContext.php
@@ -33,6 +33,7 @@ use OCP\AppFramework\IAppContainer;
use OCP\Authentication\TwoFactorAuth\IProvider;
use OCP\Calendar\ICalendarProvider;
use OCP\Capabilities\ICapability;
+use OCP\Collaboration\Reference\IReferenceProvider;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\Template\ICustomTemplateProvider;
use OCP\IContainer;
@@ -255,6 +256,15 @@ interface IRegistrationContext {
public function registerCalendarProvider(string $class): void;
/**
+ * Register a reference provider
+ *
+ * @param string $class
+ * @psalm-param class-string<IReferenceProvider> $class
+ * @since 25.0.0
+ */
+ public function registerReferenceProvider(string $class): void;
+
+ /**
* Register an implementation of \OCP\Profile\ILinkAction that
* will handle the implementation of a profile link action
*
@@ -306,4 +316,15 @@ interface IRegistrationContext {
* @since 24.0.0
*/
public function registerUserMigrator(string $migratorClass): void;
+
+ /**
+ * Announce methods of classes that may contain sensitive values, which
+ * should be obfuscated before being logged.
+ *
+ * @param string $class
+ * @param string[] $methods
+ * @return void
+ * @since 25.0.0
+ */
+ public function registerSensitiveMethods(string $class, array $methods): void;
}
diff --git a/lib/public/AppFramework/Db/Entity.php b/lib/public/AppFramework/Db/Entity.php
index a059e3a27b0..dcd1c77c042 100644
--- a/lib/public/AppFramework/Db/Entity.php
+++ b/lib/public/AppFramework/Db/Entity.php
@@ -113,6 +113,9 @@ abstract class Entity {
$type = $this->_fieldTypes[$name];
if ($type === 'blob') {
// (B)LOB is treated as string when we read from the DB
+ if (is_resource($args[0])) {
+ $args[0] = stream_get_contents($args[0]);
+ }
$type = 'string';
}
diff --git a/lib/public/AppFramework/Http/JSONResponse.php b/lib/public/AppFramework/Http/JSONResponse.php
index f4b936435c8..4870a64afd7 100644
--- a/lib/public/AppFramework/Http/JSONResponse.php
+++ b/lib/public/AppFramework/Http/JSONResponse.php
@@ -64,13 +64,7 @@ class JSONResponse extends Response {
* @throws \Exception If data could not get encoded
*/
public function render() {
- $response = json_encode($this->data, JSON_HEX_TAG);
- if ($response === false) {
- throw new \Exception(sprintf('Could not json_encode due to invalid ' .
- 'non UTF-8 characters in the array: %s', var_export($this->data, true)));
- }
-
- return $response;
+ return json_encode($this->data, JSON_HEX_TAG | JSON_THROW_ON_ERROR);
}
/**
diff --git a/lib/public/AppFramework/Http/TemplateResponse.php b/lib/public/AppFramework/Http/TemplateResponse.php
index 9b010d38bae..23843cd21d1 100644
--- a/lib/public/AppFramework/Http/TemplateResponse.php
+++ b/lib/public/AppFramework/Http/TemplateResponse.php
@@ -139,6 +139,15 @@ class TemplateResponse extends Response {
/**
+ * @return string the app id of the used template
+ * @since 25.0.0
+ */
+ public function getApp(): string {
+ return $this->appName;
+ }
+
+
+ /**
* Used for accessing the name of the set template
* @return string the name of the used template
* @since 6.0.0
diff --git a/lib/public/AppFramework/Http/ZipResponse.php b/lib/public/AppFramework/Http/ZipResponse.php
index 7495583ae12..23e9f1f7a94 100644
--- a/lib/public/AppFramework/Http/ZipResponse.php
+++ b/lib/public/AppFramework/Http/ZipResponse.php
@@ -37,7 +37,7 @@ use OCP\IRequest;
* @since 15.0.0
*/
class ZipResponse extends Response implements ICallbackResponse {
- /** @var array{internalName: string, resource: string, size: int, time: int}[] Files to be added to the zip response */
+ /** @var array{internalName: string, resource: resource, size: int, time: int}[] Files to be added to the zip response */
private array $resources = [];
/** @var string Filename that the zip file should have */
private string $name;
diff --git a/lib/public/Authentication/IProvideUserSecretBackend.php b/lib/public/Authentication/IProvideUserSecretBackend.php
new file mode 100644
index 00000000000..08f4043d828
--- /dev/null
+++ b/lib/public/Authentication/IProvideUserSecretBackend.php
@@ -0,0 +1,41 @@
+<?php
+/**
+ * @copyright Copyright (c) 2021, MichaIng <micha@dietpi.com>
+ *
+ * @author MichaIng <micha@dietpi.com>
+ *
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+// use OCP namespace for all classes that are considered public.
+// This means that they should be used by apps instead of the internal ownCloud classes
+
+namespace OCP\Authentication;
+
+/**
+ * Interface IProvideUserSecretBackend
+ *
+ * @since 23.0.0
+ */
+interface IProvideUserSecretBackend {
+
+ /**
+ * Optionally returns a stable per-user secret. This secret is for
+ * instance used to secure file encryption keys.
+ * @return string
+ * @since 23.0.0
+ */
+ public function getCurrentUserSecret(): string;
+}
diff --git a/lib/public/BackgroundJob/IJob.php b/lib/public/BackgroundJob/IJob.php
index 3c2da42bf88..24d8e7aad4a 100644
--- a/lib/public/BackgroundJob/IJob.php
+++ b/lib/public/BackgroundJob/IJob.php
@@ -29,7 +29,10 @@ namespace OCP\BackgroundJob;
use OCP\ILogger;
/**
- * Interface IJob
+ * This interface represend a backgroud job run with cron
+ *
+ * To implement a background job, you must extend either \OCP\BackgroundJob\Job,
+ * \OCP\BackgroundJob\TimedJob or \OCP\BackgroundJob\QueuedJob
*
* @since 7.0.0
*/
@@ -49,10 +52,26 @@ interface IJob {
* @param IJobList $jobList The job list that manages the state of this job
* @param ILogger|null $logger
* @since 7.0.0
+ * @deprecated since 25.0.0 Use start() instead. This method will be removed
+ * with the ILogger interface
*/
public function execute(IJobList $jobList, ILogger $logger = null);
/**
+ * Start the background job with the registered argument
+ *
+ * This methods will take care of running the background job, of initializing
+ * the state and cleaning up the job list after running the job.
+ *
+ * For common background job scenario, you will want to use TimedJob or QueuedJob
+ * instead of overwritting this method.
+ *
+ * @param IJobList $jobList The job list that manages the state of this job
+ * @since 25.0.0
+ */
+ public function start(IJobList $jobList): void;
+
+ /**
* @since 7.0.0
*/
public function setId(int $id);
diff --git a/lib/public/BackgroundJob/IJobList.php b/lib/public/BackgroundJob/IJobList.php
index eab37a03f36..8f6449b070b 100644
--- a/lib/public/BackgroundJob/IJobList.php
+++ b/lib/public/BackgroundJob/IJobList.php
@@ -7,6 +7,7 @@
* @author Noveen Sachdeva <noveen.sachdeva@research.iiit.ac.in>
* @author Robin Appelman <robin@icewind.nl>
* @author Robin McCorkell <robin@mccorkell.me.uk>
+ * @author Côme Chilliet <come.chilliet@nextcloud.com>
*
* @license AGPL-3.0
*
@@ -41,66 +42,71 @@ namespace OCP\BackgroundJob;
* be specified in the constructor of the job by calling
* $this->setInterval($interval) with $interval in seconds.
*
+ * This interface should be used directly and not implemented by an application.
+ * The implementation is provided by the server.
+ *
* @since 7.0.0
*/
interface IJobList {
/**
* Add a job to the list
*
- * @param \OCP\BackgroundJob\IJob|string $job
+ * @param IJob|class-string<IJob> $job
* @param mixed $argument The argument to be passed to $job->run() when the job is exectured
* @since 7.0.0
*/
- public function add($job, $argument = null);
+ public function add($job, $argument = null): void;
/**
* Remove a job from the list
*
- * @param \OCP\BackgroundJob\IJob|string $job
+ * @param IJob|class-string<IJob> $job
* @param mixed $argument
* @since 7.0.0
*/
- public function remove($job, $argument = null);
+ public function remove($job, $argument = null): void;
/**
* check if a job is in the list
*
- * @param \OCP\BackgroundJob\IJob|string $job
+ * @param IJob|class-string<IJob> $job
* @param mixed $argument
- * @return bool
* @since 7.0.0
*/
- public function has($job, $argument);
+ public function has($job, $argument): bool;
/**
* get all jobs in the list
*
- * @return \OCP\BackgroundJob\IJob[]
+ * @return IJob[]
* @since 7.0.0
* @deprecated 9.0.0 - This method is dangerous since it can cause load and
- * memory problems when creating too many instances.
+ * memory problems when creating too many instances. Use getJobs instead.
+ */
+ public function getAll(): array;
+
+ /**
+ * Get jobs matching the search
+ *
+ * @param IJob|class-string<IJob>|null $job
+ * @return IJob[]
+ * @since 25.0.0
*/
- public function getAll();
+ public function getJobs($job, ?int $limit, int $offset): array;
/**
* get the next job in the list
*
- * @param bool $onlyTimeSensitive
- * @return \OCP\BackgroundJob\IJob|null
* @since 7.0.0 - In 24.0.0 parameter $onlyTimeSensitive got added
*/
public function getNext(bool $onlyTimeSensitive = false): ?IJob;
/**
- * @param int $id
- * @return \OCP\BackgroundJob\IJob|null
* @since 7.0.0
*/
- public function getById($id);
+ public function getById(int $id): ?IJob;
/**
- * @param int $id
- * @return array|null
* @since 23.0.0
*/
public function getDetailsById(int $id): ?array;
@@ -108,40 +114,34 @@ interface IJobList {
/**
* set the job that was last ran to the current time
*
- * @param \OCP\BackgroundJob\IJob $job
* @since 7.0.0
*/
- public function setLastJob(IJob $job);
+ public function setLastJob(IJob $job): void;
/**
* Remove the reservation for a job
*
- * @param IJob $job
* @since 9.1.0
*/
- public function unlockJob(IJob $job);
+ public function unlockJob(IJob $job): void;
/**
* set the lastRun of $job to now
*
- * @param IJob $job
* @since 7.0.0
*/
- public function setLastRun(IJob $job);
+ public function setLastRun(IJob $job): void;
/**
* set the run duration of $job
*
- * @param IJob $job
- * @param $timeTaken
* @since 12.0.0
*/
- public function setExecutionTime(IJob $job, $timeTaken);
+ public function setExecutionTime(IJob $job, int $timeTaken): void;
/**
* Reset the $job so it executes on the next trigger
*
- * @param IJob $job
* @since 23.0.0
*/
public function resetBackgroundJob(IJob $job): void;
diff --git a/lib/public/BackgroundJob/Job.php b/lib/public/BackgroundJob/Job.php
index 5b20ac82684..d60fb5905c9 100644
--- a/lib/public/BackgroundJob/Job.php
+++ b/lib/public/BackgroundJob/Job.php
@@ -28,6 +28,7 @@ namespace OCP\BackgroundJob;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\ILogger;
+use Psr\Log\LoggerInterface;
/**
* Base class for background jobs
@@ -38,18 +39,10 @@ use OCP\ILogger;
* @since 15.0.0
*/
abstract class Job implements IJob {
-
- /** @var int $id */
- protected $id;
-
- /** @var int $lastRun */
- protected $lastRun;
-
- /** @var mixed $argument */
+ protected int $id = 0;
+ protected int $lastRun = 0;
protected $argument;
-
- /** @var ITimeFactory */
- protected $time;
+ protected ITimeFactory $time;
/**
* @since 15.0.0
@@ -68,10 +61,16 @@ abstract class Job implements IJob {
* @since 15.0.0
*/
public function execute(IJobList $jobList, ILogger $logger = null) {
+ $this->start($jobList);
+ }
+
+ /**
+ * @inheritdoc
+ * @since 25.0.0
+ */
+ public function start(IJobList $jobList): void {
$jobList->setLastRun($this);
- if ($logger === null) {
- $logger = \OC::$server->getLogger();
- }
+ $logger = \OCP\Server::get(LoggerInterface::class);
try {
$jobStartTime = $this->time->getTime();
@@ -83,9 +82,9 @@ abstract class Job implements IJob {
$jobList->setExecutionTime($this, $timeTaken);
} catch (\Exception $e) {
if ($logger) {
- $logger->logException($e, [
+ $logger->error('Error while running background job (class: ' . get_class($this) . ', arguments: ' . print_r($this->argument, true) . ')', [
'app' => 'core',
- 'message' => 'Error while running background job (class: ' . get_class($this) . ', arguments: ' . print_r($this->argument, true) . ')'
+ 'exception' => $e,
]);
}
}
diff --git a/lib/public/BackgroundJob/QueuedJob.php b/lib/public/BackgroundJob/QueuedJob.php
index e7e6e9a2939..b9c4d693b8f 100644
--- a/lib/public/BackgroundJob/QueuedJob.php
+++ b/lib/public/BackgroundJob/QueuedJob.php
@@ -35,15 +35,26 @@ use OCP\ILogger;
abstract class QueuedJob extends Job {
/**
- * run the job, then remove it from the joblist
+ * Run the job, then remove it from the joblist
*
* @param IJobList $jobList
* @param ILogger|null $logger
*
* @since 15.0.0
+ * @deprecated since 25.0.0 Use start() instead. This method will be removed
+ * with the ILogger interface
*/
final public function execute($jobList, ILogger $logger = null) {
+ $this->start($jobList);
+ }
+
+ /**
+ * Run the job, then remove it from the joblist
+ *
+ * @since 25.0.0
+ */
+ final public function start(IJobList $jobList): void {
$jobList->remove($this, $this->argument);
- parent::execute($jobList, $logger);
+ parent::start($jobList);
}
}
diff --git a/lib/public/BackgroundJob/TimedJob.php b/lib/public/BackgroundJob/TimedJob.php
index 579486f6fbf..9d6b599c21b 100644
--- a/lib/public/BackgroundJob/TimedJob.php
+++ b/lib/public/BackgroundJob/TimedJob.php
@@ -36,13 +36,11 @@ use OCP\ILogger;
* @since 15.0.0
*/
abstract class TimedJob extends Job {
- /** @var int */
- protected $interval = 0;
- /** @var int */
- protected $timeSensitivity = IJob::TIME_SENSITIVE;
+ protected int $interval = 0;
+ protected int $timeSensitivity = IJob::TIME_SENSITIVE;
/**
- * set the interval for the job
+ * Set the interval for the job
*
* @param int $seconds the time to pass between two runs of the same job in seconds
*
@@ -89,10 +87,20 @@ abstract class TimedJob extends Job {
* @param ILogger|null $logger
*
* @since 15.0.0
+ * @deprecated since 25.0.0 Use start() instead
*/
final public function execute($jobList, ILogger $logger = null) {
+ $this->start($jobList);
+ }
+
+ /**
+ * Run the job if the last run is is more than the interval ago
+ *
+ * @since 25.0.0
+ */
+ final public function start(IJobList $jobList): void {
if (($this->time->getTime() - $this->lastRun) > $this->interval) {
- parent::execute($jobList, $logger);
+ parent::start($jobList);
}
}
}
diff --git a/lib/public/Cache/CappedMemoryCache.php b/lib/public/Cache/CappedMemoryCache.php
new file mode 100644
index 00000000000..6699600d42c
--- /dev/null
+++ b/lib/public/Cache/CappedMemoryCache.php
@@ -0,0 +1,160 @@
+<?php
+/**
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ *
+ * @author Robin Appelman <robin@icewind.nl>
+ *
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+namespace OCP\Cache;
+
+use OCP\ICache;
+
+/**
+ * In-memory cache with a capacity limit to keep memory usage in check
+ *
+ * Uses a simple FIFO expiry mechanism
+ *
+ * @since 25.0.0
+ * @template T
+ */
+class CappedMemoryCache implements ICache, \ArrayAccess {
+ private int $capacity;
+ /** @var T[] */
+ private array $cache = [];
+
+ /**
+ * @inheritdoc
+ * @since 25.0.0
+ */
+ public function __construct(int $capacity = 512) {
+ $this->capacity = $capacity;
+ }
+
+ /**
+ * @inheritdoc
+ * @since 25.0.0
+ */
+ public function hasKey($key): bool {
+ return isset($this->cache[$key]);
+ }
+
+ /**
+ * @return ?T
+ * @since 25.0.0
+ */
+ public function get($key) {
+ return $this->cache[$key] ?? null;
+ }
+
+ /**
+ * @inheritdoc
+ * @param string $key
+ * @param T $value
+ * @param int $ttl
+ * @since 25.0.0
+ * @return bool
+ */
+ public function set($key, $value, $ttl = 0): bool {
+ if (is_null($key)) {
+ $this->cache[] = $value;
+ } else {
+ $this->cache[$key] = $value;
+ }
+ $this->garbageCollect();
+ return true;
+ }
+
+ /**
+ * @since 25.0.0
+ */
+ public function remove($key): bool {
+ unset($this->cache[$key]);
+ return true;
+ }
+
+ /**
+ * @inheritdoc
+ * @since 25.0.0
+ */
+ public function clear($prefix = ''): bool {
+ $this->cache = [];
+ return true;
+ }
+
+ /**
+ * @since 25.0.0
+ */
+ public function offsetExists($offset): bool {
+ return $this->hasKey($offset);
+ }
+
+ /**
+ * @inheritdoc
+ * @return T
+ * @since 25.0.0
+ */
+ #[\ReturnTypeWillChange]
+ public function &offsetGet($offset) {
+ return $this->cache[$offset];
+ }
+
+ /**
+ * @inheritdoc
+ * @param string $offset
+ * @param T $value
+ * @since 25.0.0
+ */
+ public function offsetSet($offset, $value): void {
+ $this->set($offset, $value);
+ }
+
+ /**
+ * @inheritdoc
+ * @since 25.0.0
+ */
+ public function offsetUnset($offset): void {
+ $this->remove($offset);
+ }
+
+ /**
+ * @return T[]
+ * @since 25.0.0
+ */
+ public function getData(): array {
+ return $this->cache;
+ }
+
+
+ /**
+ * @since 25.0.0
+ */
+ private function garbageCollect(): void {
+ while (count($this->cache) > $this->capacity) {
+ reset($this->cache);
+ $key = key($this->cache);
+ $this->remove($key);
+ }
+ }
+
+ /**
+ * @inheritdoc
+ * @since 25.0.0
+ */
+ public static function isAvailable(): bool {
+ return true;
+ }
+}
diff --git a/lib/public/Calendar/ICreateFromString.php b/lib/public/Calendar/ICreateFromString.php
index 343405e8ab6..8c4bdd44041 100644
--- a/lib/public/Calendar/ICreateFromString.php
+++ b/lib/public/Calendar/ICreateFromString.php
@@ -1,4 +1,6 @@
<?php
+
+declare(strict_types=1);
/**
* @copyright 2021 Anna Larch <anna.larch@gmx.net>
*
@@ -38,4 +40,11 @@ interface ICreateFromString extends ICalendar {
* @throws CalendarException
*/
public function createFromString(string $name, string $calendarData): void;
+
+ /**
+ * @since 25.0.0
+ *
+ * @throws CalendarException
+ */
+ public function handleIMipMessage(string $name, string $calendarData): void;
}
diff --git a/lib/public/Calendar/IManager.php b/lib/public/Calendar/IManager.php
index 7f0eec80910..dd65917d12b 100644
--- a/lib/public/Calendar/IManager.php
+++ b/lib/public/Calendar/IManager.php
@@ -156,4 +156,18 @@ interface IManager {
* @since 23.0.0
*/
public function newQuery(string $principalUri) : ICalendarQuery;
+
+ /**
+ * Handle a iMip REPLY message
+ *
+ * @since 25.0.0
+ */
+ public function handleIMipReply(string $principalUri, string $sender, string $recipient, string $calendarData): bool;
+
+ /**
+ * Handle a iMip CANCEL message
+ *
+ * @since 25.0.0
+ */
+ public function handleIMipCancel(string $principalUri, string $sender, ?string $replyTo, string $recipient, string $calendarData): bool;
}
diff --git a/lib/public/Collaboration/Reference/IReference.php b/lib/public/Collaboration/Reference/IReference.php
new file mode 100644
index 00000000000..0155ae86dd8
--- /dev/null
+++ b/lib/public/Collaboration/Reference/IReference.php
@@ -0,0 +1,130 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2022 Julius Härtl <jus@bitgrid.net>
+ *
+ * @author Julius Härtl <jus@bitgrid.net>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace OCP\Collaboration\Reference;
+
+use JsonSerializable;
+
+/**
+ * @since 25.0.0
+ */
+interface IReference extends JsonSerializable {
+
+ /**
+ * @since 25.0.0
+ */
+ public function getId(): string;
+
+ /**
+ * Accessible flag indicates if the user has access to the provided reference
+ *
+ * @since 25.0.0
+ */
+ public function setAccessible(bool $accessible): void;
+
+ /**
+ * Accessible flag indicates if the user has access to the provided reference
+ *
+ * @since 25.0.0
+ */
+ public function getAccessible(): bool;
+
+ /**
+ * @since 25.0.0
+ */
+ public function setTitle(string $title): void;
+
+ /**
+ * @since 25.0.0
+ */
+ public function getTitle(): string;
+
+ /**
+ * @since 25.0.0
+ */
+ public function setDescription(?string $description): void;
+
+ /**
+ * @since 25.0.0
+ */
+ public function getDescription(): ?string;
+
+ /**
+ * @since 25.0.0
+ */
+ public function setImageUrl(?string $imageUrl): void;
+
+ /**
+ * @since 25.0.0
+ */
+ public function getImageUrl(): ?string;
+
+ /**
+ * @since 25.0.0
+ */
+ public function setImageContentType(?string $contentType): void;
+
+ /**
+ * @since 25.0.0
+ */
+ public function getImageContentType(): ?string;
+
+ /**
+ * @since 25.0.0
+ */
+ public function setUrl(?string $url): void;
+
+ /**
+ * @since 25.0.0
+ */
+ public function getUrl(): ?string;
+
+ /**
+ * Set the reference specific rich object representation
+ *
+ * @since 25.0.0
+ */
+ public function setRichObject(string $type, ?array $richObject): void;
+
+ /**
+ * Returns the type of the reference specific rich object
+ *
+ * @since 25.0.0
+ */
+ public function getRichObjectType(): string;
+
+ /**
+ * Returns the reference specific rich object representation
+ *
+ * @since 25.0.0
+ */
+ public function getRichObject(): array;
+
+ /**
+ * Returns the opengraph rich object representation
+ *
+ * @since 25.0.0
+ */
+ public function getOpenGraphObject(): array;
+}
diff --git a/lib/public/Collaboration/Reference/IReferenceManager.php b/lib/public/Collaboration/Reference/IReferenceManager.php
new file mode 100644
index 00000000000..487e243c7ed
--- /dev/null
+++ b/lib/public/Collaboration/Reference/IReferenceManager.php
@@ -0,0 +1,70 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2022 Julius Härtl <jus@bitgrid.net>
+ *
+ * @author Julius Härtl <jus@bitgrid.net>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace OCP\Collaboration\Reference;
+
+/**
+ * @since 25.0.0
+ */
+interface IReferenceManager {
+ /**
+ * Return all reference identifiers within a string as an array
+ *
+ * @return string[] Array of found references (urls)
+ * @since 25.0.0
+ */
+ public function extractReferences(string $text): array;
+
+ /**
+ * Resolve a given reference id to its metadata with all available providers
+ *
+ * This method has a fallback to always provide the open graph metadata,
+ * but may still return null in case this is disabled or the fetching fails
+ *
+ * @since 25.0.0
+ */
+ public function resolveReference(string $referenceId): ?IReference;
+
+ /**
+ * Get a reference by its cache key
+ *
+ * @since 25.0.0
+ */
+ public function getReferenceByCacheKey(string $cacheKey): ?IReference;
+
+ /**
+ * Explicitly get a reference from the cache to avoid heavy fetches for cases
+ * the cache can then be filled with a separate request from the frontend
+ *
+ * @since 25.0.0
+ */
+ public function getReferenceFromCache(string $referenceId): ?IReference;
+
+ /**
+ * Invalidate all cache entries with a prefix or just one if the cache key is provided
+ *
+ * @since 25.0.0
+ */
+ public function invalidateCache(string $cachePrefix, ?string $cacheKey = null): void;
+}
diff --git a/lib/public/Collaboration/Reference/IReferenceProvider.php b/lib/public/Collaboration/Reference/IReferenceProvider.php
new file mode 100644
index 00000000000..100374b78b3
--- /dev/null
+++ b/lib/public/Collaboration/Reference/IReferenceProvider.php
@@ -0,0 +1,63 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2022 Julius Härtl <jus@bitgrid.net>
+ *
+ * @author Julius Härtl <jus@bitgrid.net>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace OCP\Collaboration\Reference;
+
+/**
+ * @since 25.0.0
+ */
+interface IReferenceProvider {
+ /**
+ * Validate that a given reference identifier matches the current provider
+ *
+ * @since 25.0.0
+ */
+ public function matchReference(string $referenceText): bool;
+
+ /**
+ * Return a reference with its metadata for a given reference identifier
+ *
+ * @since 25.0.0
+ */
+ public function resolveReference(string $referenceText): ?IReference;
+
+ /**
+ * Return true if the reference metadata can be globally cached
+ *
+ * @since 25.0.0
+ */
+ public function getCachePrefix(string $referenceId): string;
+
+ /**
+ * Return a custom cache key to be used for caching the metadata
+ * This could be for example the current user id if the reference
+ * access permissions are different for each user
+ *
+ * Should return null, if the cache is only related to the
+ * reference id and has no further dependency
+ *
+ * @since 25.0.0
+ */
+ public function getCacheKey(string $referenceId): ?string;
+}
diff --git a/lib/public/Collaboration/Reference/RenderReferenceEvent.php b/lib/public/Collaboration/Reference/RenderReferenceEvent.php
new file mode 100644
index 00000000000..b6d312636c4
--- /dev/null
+++ b/lib/public/Collaboration/Reference/RenderReferenceEvent.php
@@ -0,0 +1,36 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2022 Julius Härtl <jus@bitgrid.net>
+ *
+ * @author Julius Härtl <jus@bitgrid.net>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace OCP\Collaboration\Reference;
+
+use OCP\EventDispatcher\Event;
+
+/**
+ * Event that apps can emit on their page rendering to trigger loading of aditional
+ * scripts for reference widget rendering
+ *
+ * @since 25.0.0
+ */
+class RenderReferenceEvent extends Event {
+}
diff --git a/lib/public/Collaboration/Resources/LoadAdditionalScriptsEvent.php b/lib/public/Collaboration/Resources/LoadAdditionalScriptsEvent.php
new file mode 100644
index 00000000000..2f1dc5a0a00
--- /dev/null
+++ b/lib/public/Collaboration/Resources/LoadAdditionalScriptsEvent.php
@@ -0,0 +1,40 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright Copyright (c) 2022 Julius Härtl <jus@bitgrid.net>
+ *
+ * @author Julius Härtl <jus@bitgrid.net>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCP\Collaboration\Resources;
+
+use OCP\EventDispatcher\Event;
+
+/**
+ * This event is used by apps to register their own frontend scripts for integrating
+ * projects in their app. Apps also need to dispatch the event in order to load
+ * scripts during page load
+ *
+ * @see https://docs.nextcloud.com/server/latest/developer_manual/digging_deeper/projects.html
+ * @since 25.0.0
+ */
+class LoadAdditionalScriptsEvent extends Event {
+}
diff --git a/lib/public/Color.php b/lib/public/Color.php
new file mode 100644
index 00000000000..e2cabd9c2fc
--- /dev/null
+++ b/lib/public/Color.php
@@ -0,0 +1,142 @@
+<?php
+/**
+ * @copyright Copyright (c) 2016, John Molakvoæ <skjnldsv@protonmail.com>
+ *
+ * @author Christoph Wurst <christoph@winzerhof-wurst.at>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+namespace OCP;
+
+/**
+ * Simple RGB color container
+ * @since 25.0.0
+ */
+class Color {
+ private int $r;
+ private int $g;
+ private int $b;
+
+ /**
+ * @since 25.0.0
+ */
+ public function __construct($r, $g, $b) {
+ $this->r = $r;
+ $this->g = $g;
+ $this->b = $b;
+ }
+
+ /**
+ * Returns the red color component of this color as an int from 0 to 255
+ *
+ * @since 25.0.0
+ */
+ public function red(): int {
+ return $this->r;
+ }
+
+ /**
+ * Returns the red color component of this color as a float from 0 to 1
+ *
+ * @since 25.0.0
+ */
+ public function redF(): float {
+ return $this->r / 255;
+ }
+
+ /**
+ * Returns the green color component of this color as an int from 0 to 255
+ *
+ * @since 25.0.0
+ */
+ public function green(): int {
+ return $this->g;
+ }
+
+ /**
+ * Returns the green color component of this color as a float from 0 to 1
+ *
+ * @since 25.0.0
+ */
+ public function greenF(): float {
+ return $this->g / 255;
+ }
+
+ /**
+ * Returns the green blue component of this color as an int from 0 to 255
+ *
+ * @since 25.0.0
+ */
+ public function blue(): int {
+ return $this->b;
+ }
+
+ /**
+ * Returns the blue color component of this color as a float from 0 to 1
+ *
+ * @since 25.0.0
+ */
+ public function blueF(): float {
+ return $this->g / 255;
+ }
+
+ /**
+ * Returns the name of the color in the format "#RRGGBB"; i.e. a "#" character followed by three two-digit hexadecimal numbers.
+ *
+ * @since 25.0.0
+ */
+ public function name(): string {
+ return sprintf("#%02x%02x%02x", $this->r, $this->g, $this->b);
+ }
+
+ /**
+ * Mix two colors
+ *
+ * @param int $steps the number of intermediate colors that should be generated for the palette
+ * @param Color $color1 the first color
+ * @param Color $color2 the second color
+ * @return list<Color>
+ * @since 25.0.0
+ */
+ public static function mixPalette(int $steps, Color $color1, Color $color2): array {
+ $palette = [$color1];
+ $step = self::stepCalc($steps, [$color1, $color2]);
+ for ($i = 1; $i < $steps; $i++) {
+ $r = intval($color1->red() + ($step[0] * $i));
+ $g = intval($color1->green() + ($step[1] * $i));
+ $b = intval($color1->blue() + ($step[2] * $i));
+ $palette[] = new Color($r, $g, $b);
+ }
+ return $palette;
+ }
+
+ /**
+ * Calculate steps between two Colors
+ * @param int $steps start color
+ * @param Color[] $ends end color
+ * @return array{0: int, 1: int, 2: int} [r,g,b] steps for each color to go from $steps to $ends
+ * @since 25.0.0
+ */
+ private static function stepCalc(int $steps, array $ends): array {
+ $step = [];
+ $step[0] = ($ends[1]->red() - $ends[0]->red()) / $steps;
+ $step[1] = ($ends[1]->green() - $ends[0]->green()) / $steps;
+ $step[2] = ($ends[1]->blue() - $ends[0]->blue()) / $steps;
+ return $step;
+ }
+}
diff --git a/lib/public/Comments/IComment.php b/lib/public/Comments/IComment.php
index 8465eaf49f4..eb696fa5f06 100644
--- a/lib/public/Comments/IComment.php
+++ b/lib/public/Comments/IComment.php
@@ -230,11 +230,11 @@ interface IComment {
/**
* sets the date of the most recent child
*
- * @param \DateTime $dateTime
+ * @param \DateTime|null $dateTime
* @return IComment
* @since 9.0.0
*/
- public function setLatestChildDateTime(\DateTime $dateTime);
+ public function setLatestChildDateTime(?\DateTime $dateTime = null);
/**
* returns the object type the comment is attached to
@@ -299,4 +299,21 @@ interface IComment {
* @since 24.0.0
*/
public function setReactions(?array $reactions): IComment;
+
+ /**
+ * Set message expire date
+ *
+ * @param \DateTime|null $dateTime
+ * @return IComment
+ * @since 25.0.0
+ */
+ public function setExpireDate(?\DateTime $dateTime): IComment;
+
+ /**
+ * Get message expire date
+ *
+ * @return ?\DateTime
+ * @since 25.0.0
+ */
+ public function getExpireDate(): ?\DateTime;
}
diff --git a/lib/public/Comments/ICommentsManager.php b/lib/public/Comments/ICommentsManager.php
index c34bd4718cc..da9e4d76a38 100644
--- a/lib/public/Comments/ICommentsManager.php
+++ b/lib/public/Comments/ICommentsManager.php
@@ -482,4 +482,15 @@ interface ICommentsManager {
* @since 21.0.0
*/
public function load(): void;
+
+ /**
+ * Delete comments with field expire_date less than current date
+ * Only will delete the message related with the object.
+ *
+ * @param string $objectType the object type (e.g. 'files')
+ * @param string $objectId e.g. the file id, leave empty to expire on all objects of this type
+ * @return boolean true if at least one row was deleted
+ * @since 25.0.0
+ */
+ public function deleteCommentsExpiredAtObject(string $objectType, string $objectId = ''): bool;
}
diff --git a/lib/public/Config/BeforePreferenceDeletedEvent.php b/lib/public/Config/BeforePreferenceDeletedEvent.php
new file mode 100644
index 00000000000..a2d1dad7034
--- /dev/null
+++ b/lib/public/Config/BeforePreferenceDeletedEvent.php
@@ -0,0 +1,83 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2022 Joas Schilling <coding@schilljs.com>
+ *
+ * @author Joas Schilling <coding@schilljs.com>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCP\Config;
+
+use OCP\EventDispatcher\Event;
+
+/**
+ * @since 25.0.0
+ */
+class BeforePreferenceDeletedEvent extends Event {
+ protected string $userId;
+ protected string $appId;
+ protected string $configKey;
+ protected bool $valid = false;
+
+ /**
+ * @since 25.0.0
+ */
+ public function __construct(string $userId, string $appId, string $configKey) {
+ parent::__construct();
+ $this->userId = $userId;
+ $this->appId = $appId;
+ $this->configKey = $configKey;
+ }
+
+ /**
+ * @since 25.0.0
+ */
+ public function getUserId(): string {
+ return $this->userId;
+ }
+
+ /**
+ * @since 25.0.0
+ */
+ public function getAppId(): string {
+ return $this->appId;
+ }
+
+ /**
+ * @since 25.0.0
+ */
+ public function getConfigKey(): string {
+ return $this->configKey;
+ }
+
+ /**
+ * @since 25.0.0
+ */
+ public function isValid(): bool {
+ return $this->valid;
+ }
+
+ /**
+ * @since 25.0.0
+ */
+ public function setValid(bool $valid): void {
+ $this->valid = $valid;
+ }
+}
diff --git a/lib/public/Config/BeforePreferenceSetEvent.php b/lib/public/Config/BeforePreferenceSetEvent.php
new file mode 100644
index 00000000000..31681405a47
--- /dev/null
+++ b/lib/public/Config/BeforePreferenceSetEvent.php
@@ -0,0 +1,92 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2022 Joas Schilling <coding@schilljs.com>
+ *
+ * @author Joas Schilling <coding@schilljs.com>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCP\Config;
+
+use OCP\EventDispatcher\Event;
+
+/**
+ * @since 25.0.0
+ */
+class BeforePreferenceSetEvent extends Event {
+ protected string $userId;
+ protected string $appId;
+ protected string $configKey;
+ protected string $configValue;
+ protected bool $valid = false;
+
+ /**
+ * @since 25.0.0
+ */
+ public function __construct(string $userId, string $appId, string $configKey, string $configValue) {
+ parent::__construct();
+ $this->userId = $userId;
+ $this->appId = $appId;
+ $this->configKey = $configKey;
+ $this->configValue = $configValue;
+ }
+
+ /**
+ * @since 25.0.0
+ */
+ public function getUserId(): string {
+ return $this->userId;
+ }
+
+ /**
+ * @since 25.0.0
+ */
+ public function getAppId(): string {
+ return $this->appId;
+ }
+
+ /**
+ * @since 25.0.0
+ */
+ public function getConfigKey(): string {
+ return $this->configKey;
+ }
+
+ /**
+ * @since 25.0.0
+ */
+ public function getConfigValue(): string {
+ return $this->configValue;
+ }
+
+ /**
+ * @since 25.0.0
+ */
+ public function isValid(): bool {
+ return $this->valid;
+ }
+
+ /**
+ * @since 25.0.0
+ */
+ public function setValid(bool $valid): void {
+ $this->valid = $valid;
+ }
+}
diff --git a/lib/public/Contacts/IManager.php b/lib/public/Contacts/IManager.php
index e9bdc01c060..65be12c4c39 100644
--- a/lib/public/Contacts/IManager.php
+++ b/lib/public/Contacts/IManager.php
@@ -105,7 +105,7 @@ interface IManager {
/**
* This function can be used to delete the contact identified by the given id
*
- * @param object $id the unique identifier to a contact
+ * @param int $id the unique identifier to a contact
* @param string $address_book_key identifier of the address book in which the contact shall be deleted
* @return bool successful or not
* @since 6.0.0
diff --git a/lib/public/DB/QueryBuilder/IQueryBuilder.php b/lib/public/DB/QueryBuilder/IQueryBuilder.php
index e3257e82bca..218b7d8cb2d 100644
--- a/lib/public/DB/QueryBuilder/IQueryBuilder.php
+++ b/lib/public/DB/QueryBuilder/IQueryBuilder.php
@@ -820,7 +820,7 @@ interface IQueryBuilder {
* Specifies an ordering for the query results.
* Replaces any previously specified orderings, if any.
*
- * @param string $sort The ordering expression.
+ * @param string|IQueryFunction|ILiteral|IParameter $sort The ordering expression.
* @param string $order The ordering direction.
*
* @return $this This QueryBuilder instance.
diff --git a/lib/public/Dashboard/IManager.php b/lib/public/Dashboard/IManager.php
index 396624876ef..d9e73c89eb9 100644
--- a/lib/public/Dashboard/IManager.php
+++ b/lib/public/Dashboard/IManager.php
@@ -36,7 +36,7 @@ interface IManager {
* @param string $widgetClass
* @since 20.0.0
*/
- public function lazyRegisterWidget(string $widgetClass): void;
+ public function lazyRegisterWidget(string $widgetClass, string $appId): void;
/**
* @since 20.0.0
diff --git a/lib/public/Federation/Events/TrustedServerRemovedEvent.php b/lib/public/Federation/Events/TrustedServerRemovedEvent.php
new file mode 100644
index 00000000000..785991c1554
--- /dev/null
+++ b/lib/public/Federation/Events/TrustedServerRemovedEvent.php
@@ -0,0 +1,48 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright 2022 Carl Schwan <carl@carlschwan.eu>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+namespace OCP\Federation\Events;
+
+use OCP\EventDispatcher\Event;
+
+/**
+ * @since 25.0.0
+ */
+class TrustedServerRemovedEvent extends Event {
+ private string $urlHash;
+
+ /**
+ * @since 25.0.0
+ */
+ public function __construct(string $urlHash) {
+ parent::__construct();
+ $this->urlHash = $urlHash;
+ }
+
+ /**
+ * @since 25.0.0
+ */
+ public function getUrlHash(): string {
+ return $this->urlHash;
+ }
+}
diff --git a/lib/public/Files/Cache/ICache.php b/lib/public/Files/Cache/ICache.php
index e27f4207f1e..37e71f3ac79 100644
--- a/lib/public/Files/Cache/ICache.php
+++ b/lib/public/Files/Cache/ICache.php
@@ -243,7 +243,7 @@ interface ICache {
* use the one with the highest id gives the best result with the background scanner, since that is most
* likely the folder where we stopped scanning previously
*
- * @return string|bool the path of the folder or false when no folder matched
+ * @return string|false the path of the folder or false when no folder matched
* @since 9.0.0
*/
public function getIncomplete();
diff --git a/lib/public/Files/Cache/ICacheEntry.php b/lib/public/Files/Cache/ICacheEntry.php
index 17eecf89ddb..e1e8129394c 100644
--- a/lib/public/Files/Cache/ICacheEntry.php
+++ b/lib/public/Files/Cache/ICacheEntry.php
@@ -162,4 +162,14 @@ interface ICacheEntry extends ArrayAccess {
* @since 18.0.0
*/
public function getUploadTime(): ?int;
+
+ /**
+ * Get the unencrypted size
+ *
+ * This might be different from the result of getSize
+ *
+ * @return int
+ * @since 25.0.0
+ */
+ public function getUnencryptedSize(): int;
}
diff --git a/lib/public/Files/DavUtil.php b/lib/public/Files/DavUtil.php
new file mode 100644
index 00000000000..343f3c2ac0f
--- /dev/null
+++ b/lib/public/Files/DavUtil.php
@@ -0,0 +1,89 @@
+<?php
+/**
+ * @copyright Copyright (c) 2022 Côme Chilliet <come.chilliet@nextcloud.com>
+ *
+ * @author Arthur Schiwon <blizzz@owncloud.com>
+ * @author Bart Visscher <bartv@thisnet.nl>
+ * @author Jakob Sack <mail@jakobsack.de>
+ * @author Jörn Friedrich Dreyer <jfd@butonic.de>
+ * @author Klaas Freitag <freitag@owncloud.com>
+ * @author Markus Goetz <markus@woboq.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ * @author Côme Chilliet <come.chilliet@nextcloud.com>
+ *
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCP\Files;
+
+/**
+ * This class provides different helper functions related to WebDAV protocol
+ *
+ * @since 25.0.0
+ */
+class DavUtil {
+ /**
+ * Compute the fileId to use for dav responses
+ *
+ * @param int $id Id of the file returned by FileInfo::getId
+ * @since 25.0.0
+ */
+ public static function getDavFileId(int $id): string {
+ $instanceId = \OC_Util::getInstanceId();
+ $id = sprintf('%08d', $id);
+ return $id . $instanceId;
+ }
+
+ /**
+ * Compute the format needed for returning permissions for dav
+ *
+ * @since 25.0.0
+ */
+ public static function getDavPermissions(FileInfo $info): string {
+ $p = '';
+ if ($info->isShared()) {
+ $p .= 'S';
+ }
+ if ($info->isShareable()) {
+ $p .= 'R';
+ }
+ if ($info->isMounted()) {
+ $p .= 'M';
+ }
+ if ($info->isReadable()) {
+ $p .= 'G';
+ }
+ if ($info->isDeletable()) {
+ $p .= 'D';
+ }
+ if ($info->isUpdateable()) {
+ $p .= 'NV'; // Renameable, Moveable
+ }
+ if ($info->getType() === FileInfo::TYPE_FILE) {
+ if ($info->isUpdateable()) {
+ $p .= 'W';
+ }
+ } else {
+ if ($info->isCreatable()) {
+ $p .= 'CK';
+ }
+ }
+ return $p;
+ }
+}
diff --git a/lib/public/Files/Events/BeforeDirectFileDownloadEvent.php b/lib/public/Files/Events/BeforeDirectFileDownloadEvent.php
new file mode 100644
index 00000000000..a32c95c6408
--- /dev/null
+++ b/lib/public/Files/Events/BeforeDirectFileDownloadEvent.php
@@ -0,0 +1,84 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright 2022 Carl Schwan <carl@carlschwan.eu>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+namespace OCP\Files\Events;
+
+use OCP\EventDispatcher\Event;
+
+/**
+ * This event is triggered when a user tries to download a file
+ * directly.
+ *
+ * @since 25.0.0
+ */
+class BeforeDirectFileDownloadEvent extends Event {
+ private string $path;
+ private bool $successful = true;
+ private ?string $errorMessage = null;
+
+ /**
+ * @since 25.0.0
+ */
+ public function __construct(string $path) {
+ parent::__construct();
+ $this->path = $path;
+ }
+
+ /**
+ * @since 25.0.0
+ */
+ public function getPath(): string {
+ return $this->path;
+ }
+
+ /**
+ * @since 25.0.0
+ */
+ public function isSuccessful(): bool {
+ return $this->successful;
+ }
+
+ /**
+ * Set if the event was successful
+ *
+ * @since 25.0.0
+ */
+ public function setSuccessful(bool $successful): void {
+ $this->successful = $successful;
+ }
+
+ /**
+ * Get the error message, if any
+ * @since 25.0.0
+ */
+ public function getErrorMessage(): ?string {
+ return $this->errorMessage;
+ }
+
+ /**
+ * @since 25.0.0
+ */
+ public function setErrorMessage(string $errorMessage): void {
+ $this->errorMessage = $errorMessage;
+ }
+}
diff --git a/lib/public/Files/Events/BeforeZipCreatedEvent.php b/lib/public/Files/Events/BeforeZipCreatedEvent.php
new file mode 100644
index 00000000000..18f41a42899
--- /dev/null
+++ b/lib/public/Files/Events/BeforeZipCreatedEvent.php
@@ -0,0 +1,91 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright 2022 Carl Schwan <carl@carlschwan.eu>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCP\Files\Events;
+
+use OCP\EventDispatcher\Event;
+
+/**
+ * @since 25.0.0
+ */
+class BeforeZipCreatedEvent extends Event {
+ private string $directory;
+ private array $files;
+ private bool $successful = true;
+ private ?string $errorMessage = null;
+
+ /**
+ * @since 25.0.0
+ */
+ public function __construct(string $directory, array $files) {
+ parent::__construct();
+ $this->directory = $directory;
+ $this->files = $files;
+ }
+
+ /**
+ * @since 25.0.0
+ */
+ public function getDirectory(): string {
+ return $this->directory;
+ }
+
+ /**
+ * @since 25.0.0
+ */
+ public function getFiles(): array {
+ return $this->files;
+ }
+
+ /**
+ * @since 25.0.0
+ */
+ public function isSuccessful(): bool {
+ return $this->successful;
+ }
+
+ /**
+ * Set if the event was successful
+ *
+ * @since 25.0.0
+ */
+ public function setSuccessful(bool $successful): void {
+ $this->successful = $successful;
+ }
+
+ /**
+ * Get the error message, if any
+ * @since 25.0.0
+ */
+ public function getErrorMessage(): ?string {
+ return $this->errorMessage;
+ }
+
+ /**
+ * @since 25.0.0
+ */
+ public function setErrorMessage(string $errorMessage): void {
+ $this->errorMessage = $errorMessage;
+ }
+}
diff --git a/lib/public/Files/Mount/ISystemMountPoint.php b/lib/public/Files/Mount/ISystemMountPoint.php
new file mode 100644
index 00000000000..a090a84947b
--- /dev/null
+++ b/lib/public/Files/Mount/ISystemMountPoint.php
@@ -0,0 +1,34 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2022 Robin Appelman <robin@icewind.nl>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCP\Files\Mount;
+
+/**
+ * Mark a mountpoint as containing system data, meaning that the data is not user specific
+ *
+ * Example use case is signaling to the encryption wrapper that system-wide keys should be used for a mountpoint
+ *
+ * @since 25.0.0
+ */
+interface ISystemMountPoint extends IMountPoint {
+}
diff --git a/lib/public/Files/SimpleFS/ISimpleFile.php b/lib/public/Files/SimpleFS/ISimpleFile.php
index 8f1921cb7cb..34cd128d449 100644
--- a/lib/public/Files/SimpleFS/ISimpleFile.php
+++ b/lib/public/Files/SimpleFS/ISimpleFile.php
@@ -41,44 +41,39 @@ interface ISimpleFile {
/**
* Get the name
*
- * @return string
* @since 11.0.0
*/
- public function getName();
+ public function getName(): string;
/**
* Get the size in bytes
*
- * @return int
* @since 11.0.0
*/
- public function getSize();
+ public function getSize(): int;
/**
* Get the ETag
*
- * @return string
* @since 11.0.0
*/
- public function getETag();
+ public function getETag(): string;
/**
* Get the last modification time
*
- * @return int
* @since 11.0.0
*/
- public function getMTime();
+ public function getMTime(): int;
/**
* Get the content
*
* @throws NotPermittedException
* @throws NotFoundException
- * @return string
* @since 11.0.0
*/
- public function getContent();
+ public function getContent(): string;
/**
* Overwrite the file
@@ -88,7 +83,7 @@ interface ISimpleFile {
* @throws NotFoundException
* @since 11.0.0
*/
- public function putContent($data);
+ public function putContent($data): void;
/**
* Delete the file
@@ -96,15 +91,14 @@ interface ISimpleFile {
* @throws NotPermittedException
* @since 11.0.0
*/
- public function delete();
+ public function delete(): void;
/**
* Get the MimeType
*
- * @return string
* @since 11.0.0
*/
- public function getMimeType();
+ public function getMimeType(): string;
/**
* @since 24.0.0
diff --git a/lib/public/Files/SimpleFS/ISimpleFolder.php b/lib/public/Files/SimpleFS/ISimpleFolder.php
index 0159c41760b..3c8e6e88ab3 100644
--- a/lib/public/Files/SimpleFS/ISimpleFolder.php
+++ b/lib/public/Files/SimpleFS/ISimpleFolder.php
@@ -38,7 +38,7 @@ interface ISimpleFolder {
* @return ISimpleFile[]
* @since 11.0.0
*/
- public function getDirectoryListing();
+ public function getDirectoryListing(): array;
/**
* Check if a file with $name exists
@@ -47,28 +47,24 @@ interface ISimpleFolder {
* @return bool
* @since 11.0.0
*/
- public function fileExists($name);
+ public function fileExists(string $name): bool;
/**
* Get the file named $name from the folder
*
- * @param string $name
- * @return ISimpleFile
* @throws NotFoundException
* @since 11.0.0
*/
- public function getFile($name);
+ public function getFile(string $name): ISimpleFile;
/**
* Creates a new file with $name in the folder
*
- * @param string $name
* @param string|resource|null $content @since 19.0.0
- * @return ISimpleFile
* @throws NotPermittedException
* @since 11.0.0
*/
- public function newFile($name, $content = null);
+ public function newFile(string $name, $content = null): ISimpleFile;
/**
* Remove the folder and all the files in it
@@ -76,13 +72,12 @@ interface ISimpleFolder {
* @throws NotPermittedException
* @since 11.0.0
*/
- public function delete();
+ public function delete(): void;
/**
* Get the folder name
*
- * @return string
* @since 11.0.0
*/
- public function getName();
+ public function getName(): string;
}
diff --git a/lib/public/Files/SimpleFS/ISimpleRoot.php b/lib/public/Files/SimpleFS/ISimpleRoot.php
index fd590b66b31..31c3efe76dd 100644
--- a/lib/public/Files/SimpleFS/ISimpleRoot.php
+++ b/lib/public/Files/SimpleFS/ISimpleRoot.php
@@ -35,8 +35,6 @@ interface ISimpleRoot {
/**
* Get the folder with name $name
*
- * @param string $name
- * @return ISimpleFolder
* @throws NotFoundException
* @throws \RuntimeException
* @since 11.0.0
@@ -56,8 +54,6 @@ interface ISimpleRoot {
/**
* Create a new folder named $name
*
- * @param string $name
- * @return ISimpleFolder
* @throws NotPermittedException
* @throws \RuntimeException
* @since 11.0.0
diff --git a/lib/public/Files/SimpleFS/InMemoryFile.php b/lib/public/Files/SimpleFS/InMemoryFile.php
index 590cb43e1d6..393449d4f1f 100644
--- a/lib/public/Files/SimpleFS/InMemoryFile.php
+++ b/lib/public/Files/SimpleFS/InMemoryFile.php
@@ -36,17 +36,13 @@ use OCP\Files\NotPermittedException;
class InMemoryFile implements ISimpleFile {
/**
* Holds the file name.
- *
- * @var string
*/
- private $name;
+ private string $name;
/**
* Holds the file contents.
- *
- * @var string
*/
- private $contents;
+ private string $contents;
/**
* InMemoryFile constructor.
@@ -64,7 +60,7 @@ class InMemoryFile implements ISimpleFile {
* @inheritdoc
* @since 16.0.0
*/
- public function getName() {
+ public function getName(): string {
return $this->name;
}
@@ -72,7 +68,7 @@ class InMemoryFile implements ISimpleFile {
* @inheritdoc
* @since 16.0.0
*/
- public function getSize() {
+ public function getSize(): int {
return strlen($this->contents);
}
@@ -80,7 +76,7 @@ class InMemoryFile implements ISimpleFile {
* @inheritdoc
* @since 16.0.0
*/
- public function getETag() {
+ public function getETag(): string {
return '';
}
@@ -88,7 +84,7 @@ class InMemoryFile implements ISimpleFile {
* @inheritdoc
* @since 16.0.0
*/
- public function getMTime() {
+ public function getMTime(): int {
return time();
}
@@ -96,7 +92,7 @@ class InMemoryFile implements ISimpleFile {
* @inheritdoc
* @since 16.0.0
*/
- public function getContent() {
+ public function getContent(): string {
return $this->contents;
}
@@ -104,7 +100,7 @@ class InMemoryFile implements ISimpleFile {
* @inheritdoc
* @since 16.0.0
*/
- public function putContent($data) {
+ public function putContent($data): void {
$this->contents = $data;
}
@@ -113,7 +109,7 @@ class InMemoryFile implements ISimpleFile {
*
* @since 16.0.0
*/
- public function delete() {
+ public function delete(): void {
// unimplemented for in memory files
}
@@ -121,7 +117,7 @@ class InMemoryFile implements ISimpleFile {
* @inheritdoc
* @since 16.0.0
*/
- public function getMimeType() {
+ public function getMimeType(): string {
$fileInfo = new \finfo(FILEINFO_MIME_TYPE);
return $fileInfo->buffer($this->contents);
}
diff --git a/lib/public/FullTextSearch/Model/ISearchRequest.php b/lib/public/FullTextSearch/Model/ISearchRequest.php
index be93b2d9970..76fc3b18b02 100644
--- a/lib/public/FullTextSearch/Model/ISearchRequest.php
+++ b/lib/public/FullTextSearch/Model/ISearchRequest.php
@@ -193,7 +193,7 @@ interface ISearchRequest {
*
* @return ISearchRequest
*/
- public function setMetaTags(array $tags): IsearchRequest;
+ public function setMetaTags(array $tags): ISearchRequest;
/**
diff --git a/lib/public/IAddressBook.php b/lib/public/IAddressBook.php
index 4bb632ae070..8df9a0c007d 100644
--- a/lib/public/IAddressBook.php
+++ b/lib/public/IAddressBook.php
@@ -10,6 +10,7 @@
* @author Morris Jobke <hey@morrisjobke.de>
* @author Robin McCorkell <robin@mccorkell.me.uk>
* @author Roeland Jago Douma <roeland@famdouma.nl>
+ * @author Thomas Citharel <nextcloud@tcit.fr>
* @author Thomas Müller <thomas.mueller@tmit.eu>
*
* @license AGPL-3.0
@@ -47,7 +48,6 @@ namespace OCP {
/**
* @return string defining the unique uri
* @since 16.0.0
- * @return string
*/
public function getUri(): string;
@@ -98,7 +98,7 @@ namespace OCP {
public function getPermissions();
/**
- * @param object $id the unique identifier to a contact
+ * @param int $id the unique identifier to a contact
* @return bool successful or not
* @since 5.0.0
*/
diff --git a/lib/public/IAvatar.php b/lib/public/IAvatar.php
index 8a1bc792450..d05a12e1dbf 100644
--- a/lib/public/IAvatar.php
+++ b/lib/public/IAvatar.php
@@ -36,66 +36,70 @@ use OCP\Files\SimpleFS\ISimpleFile;
interface IAvatar {
/**
- * get the users avatar
+ * Get the users avatar
+ *
* @param int $size size in px of the avatar, avatars are square, defaults to 64, -1 can be used to not scale the image
- * @return boolean|\OCP\IImage containing the avatar or false if there's no image
+ * @return false|\OCP\IImage containing the avatar or false if there's no image
* @since 6.0.0 - size of -1 was added in 9.0.0
*/
- public function get($size = 64);
+ public function get(int $size = 64);
/**
* Check if an avatar exists for the user
*
- * @return bool
* @since 8.1.0
*/
- public function exists();
+ public function exists(): bool;
/**
* Check if the avatar of a user is a custom uploaded one
*
- * @return bool
* @since 14.0.0
*/
public function isCustomAvatar(): bool;
/**
- * sets the users avatar
+ * Sets the users avatar
+ *
* @param \OCP\IImage|resource|string $data An image object, imagedata or path to set a new avatar
* @throws \Exception if the provided file is not a jpg or png image
* @throws \Exception if the provided image is not valid
* @throws \OC\NotSquareException if the image is not square
- * @return void
* @since 6.0.0
*/
- public function set($data);
+ public function set($data): void;
/**
- * remove the users avatar
- * @return void
+ * Remove the user's avatar
+ *
+ * @param bool $silent Whether removing the avatar should trigger a change
* @since 6.0.0
*/
- public function remove();
+ public function remove(bool $silent = false): void;
/**
* Get the file of the avatar
- * @param int $size -1 can be used to not scale the image
- * @return ISimpleFile
+ *
+ * @param int $size The desired image size. -1 can be used to not scale the image
* @throws NotFoundException
* @since 9.0.0
*/
- public function getFile($size);
+ public function getFile(int $size): ISimpleFile;
/**
- * @param string $text
- * @return Color Object containting r g b int in the range [0, 255]
+ * Get the avatar background color
+ *
* @since 14.0.0
*/
- public function avatarBackgroundColor(string $text);
+ public function avatarBackgroundColor(string $hash): Color;
/**
- * Handle a changed user
+ * Updates the display name if changed.
+ *
+ * @param string $feature The changed feature
+ * @param mixed $oldValue The previous value
+ * @param mixed $newValue The new value
* @since 13.0.0
*/
- public function userChanged($feature, $oldValue, $newValue);
+ public function userChanged(string $feature, $oldValue, $newValue): void;
}
diff --git a/lib/public/IAvatarManager.php b/lib/public/IAvatarManager.php
index 573e109f003..15894550d10 100644
--- a/lib/public/IAvatarManager.php
+++ b/lib/public/IAvatarManager.php
@@ -37,15 +37,14 @@ namespace OCP;
interface IAvatarManager {
/**
- * return a user specific instance of \OCP\IAvatar
+ * Return a user specific instance of \OCP\IAvatar
* @see IAvatar
- * @param string $user the ownCloud user id
- * @return IAvatar
+ * @param string $userId the Nextcloud user id
* @throws \Exception In case the username is potentially dangerous
* @throws \OCP\Files\NotFoundException In case there is no user folder yet
* @since 6.0.0
*/
- public function getAvatar(string $user) : IAvatar;
+ public function getAvatar(string $userId): IAvatar;
/**
* Returns a guest user avatar instance.
diff --git a/lib/public/IBinaryFinder.php b/lib/public/IBinaryFinder.php
new file mode 100644
index 00000000000..028c1f4d0a7
--- /dev/null
+++ b/lib/public/IBinaryFinder.php
@@ -0,0 +1,41 @@
+<?php
+
+declare(strict_types = 1);
+/**
+ * @copyright 2022 Carl Schwan <carl@carlschwan.eu>
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCP;
+
+/**
+ * Service that find the binary path for a program.
+ *
+ * This interface should be injected via depency injection and must
+ * not be implemented in applications.
+ *
+ * @since 25.0.0
+ */
+interface IBinaryFinder {
+ /**
+ * Try to find a program
+ *
+ * @return false|string
+ * @since 25.0.0
+ */
+ public function findBinaryPath(string $program);
+}
diff --git a/lib/public/IDBConnection.php b/lib/public/IDBConnection.php
index d12baa400c0..69ede2c8438 100644
--- a/lib/public/IDBConnection.php
+++ b/lib/public/IDBConnection.php
@@ -199,7 +199,7 @@ interface IDBConnection {
* @param array $updatePreconditionValues ensure values match preconditions (column name => value)
* @return int number of new rows
* @throws Exception used to be the removed dbal exception, since 21.0.0 it's \OCP\DB\Exception
- * @throws PreconditionNotMetException
+ * @throws PreConditionNotMetException
* @since 9.0.0
*/
public function setValues($table, array $keys, array $values, array $updatePreconditionValues = []): int;
diff --git a/lib/public/IEventSource.php b/lib/public/IEventSource.php
index 85f09837125..879b365cc77 100644
--- a/lib/public/IEventSource.php
+++ b/lib/public/IEventSource.php
@@ -35,7 +35,7 @@ interface IEventSource {
/**
* send a message to the client
*
- * @param string $type
+ * @param string $type One of success, notice, error, failure and done. Used in core/js/update.js
* @param mixed $data
*
* if only one parameter is given, a typeless message will be send with that parameter as data
diff --git a/lib/public/ISession.php b/lib/public/ISession.php
index 2709e09d4ca..12cd716ec3f 100644
--- a/lib/public/ISession.php
+++ b/lib/public/ISession.php
@@ -84,6 +84,14 @@ interface ISession {
public function clear();
/**
+ * Reopen a session for writing again
+ *
+ * @return bool true if the session was actually reopened, otherwise false
+ * @since 25.0.0
+ */
+ public function reopen(): bool;
+
+ /**
* Close the session and release the lock
* @since 7.0.0
*/
diff --git a/lib/public/IURLGenerator.php b/lib/public/IURLGenerator.php
index 580536b8b5f..808ba66c862 100644
--- a/lib/public/IURLGenerator.php
+++ b/lib/public/IURLGenerator.php
@@ -35,6 +35,16 @@ namespace OCP;
* @since 6.0.0
*/
interface IURLGenerator {
+
+ /**
+ * Regex for matching http(s) urls
+ *
+ * This is a copy of the frontend regex in core/src/OCP/comments.js, make sure to adjust both when changing
+ *
+ * @since 25.0.0
+ */
+ public const URL_REGEX = '/(\s|\n|^)(https?:\/\/)?((?:[-A-Z0-9+_]+\.)+[-A-Z]+(?:\/[-A-Z0-9+&@#%?=~_|!:,.;()]*)*)(\s|\n|$)/mi';
+
/**
* Returns the URL for a route
* @param string $routeName the name of the route
diff --git a/lib/public/IUser.php b/lib/public/IUser.php
index 1a1d1e44d8a..bb7bdf3304a 100644
--- a/lib/public/IUser.php
+++ b/lib/public/IUser.php
@@ -58,6 +58,9 @@ interface IUser {
* @param string $displayName
* @return bool
* @since 8.0.0
+ *
+ * @since 25.0.0 Throw InvalidArgumentException
+ * @throws \InvalidArgumentException
*/
public function setDisplayName($displayName);
@@ -112,8 +115,7 @@ interface IUser {
/**
* Get the backend for the current user object
- *
- * @return UserInterface
+ * @return ?UserInterface
* @since 15.0.0
*/
public function getBackend();
diff --git a/lib/public/IUserManager.php b/lib/public/IUserManager.php
index e5c220af40c..af0d5f08809 100644
--- a/lib/public/IUserManager.php
+++ b/lib/public/IUserManager.php
@@ -85,6 +85,15 @@ interface IUserManager {
public function get($uid);
/**
+ * Get the display name of a user
+ *
+ * @param string $uid
+ * @return string|null
+ * @since 25.0.0
+ */
+ public function getDisplayName(string $uid): ?string;
+
+ /**
* check if a user exists
*
* @param string $uid
@@ -98,7 +107,7 @@ interface IUserManager {
*
* @param string $loginName
* @param string $password
- * @return mixed the User object on success, false otherwise
+ * @return IUser|false the User object on success, false otherwise
* @since 8.0.0
*/
public function checkPassword($loginName, $password);
@@ -141,7 +150,7 @@ interface IUserManager {
* @param string $uid
* @param string $password
* @throws \InvalidArgumentException
- * @return bool|\OCP\IUser the created user or false
+ * @return false|\OCP\IUser the created user or false
* @since 8.0.0
*/
public function createUser($uid, $password);
@@ -157,9 +166,9 @@ interface IUserManager {
public function createUserFromBackend($uid, $password, UserInterface $backend);
/**
- * returns how many users per backend exist (if supported by backend)
+ * Get how many users per backend exist (if supported by backend)
*
- * @return array an array of backend class as key and count number as value
+ * @return array<string, int> an array of backend class name as key and count number as value
* @since 8.0.0
*/
public function countUsers();
diff --git a/lib/public/Log/functions.php b/lib/public/Log/functions.php
new file mode 100644
index 00000000000..cc6961d8fba
--- /dev/null
+++ b/lib/public/Log/functions.php
@@ -0,0 +1,73 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * @copyright 2022 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2022 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace OCP\Log;
+
+use OC;
+use OCP\AppFramework\QueryException;
+use Psr\Log\LoggerInterface;
+use Psr\Log\NullLogger;
+use function class_exists;
+
+/**
+ * Get a PSR logger
+ *
+ * Whenever possible, inject a logger into your classes instead of relying on
+ * this helper function.
+ *
+ * @warning the returned logger implementation is not guaranteed to be the same
+ * between two function calls. During early stages of the process you
+ * might in fact get a noop implementation when Nextcloud isn't ready
+ * to log. Therefore you MUST NOT cache the result of this function but
+ * fetch a new logger for every log line you want to write.
+ *
+ * @param string|null $appId optional parameter to acquire the app-specific logger
+ *
+ * @return LoggerInterface
+ * @since 24.0.0
+ */
+function logger(string $appId = null): LoggerInterface {
+ if (!class_exists(OC::class) || OC::$server === null) {
+ // If someone calls this log before Nextcloud is initialized, there is
+ // no logging available. In that case we return a noop implementation
+ // TODO: evaluate whether logging to error_log could be an alternative
+ return new NullLogger();
+ }
+
+ if ($appId !== null) {
+ try {
+ $appContainer = OC::$server->getRegisteredAppContainer($appId);
+ return $appContainer->get(LoggerInterface::class);
+ } catch (QueryException $e) {
+ // Ignore and return the server logger below
+ }
+ }
+
+ try {
+ return OC::$server->get(LoggerInterface::class);
+ } catch (QueryException $e) {
+ return new NullLogger();
+ }
+}
diff --git a/lib/public/Migration/IOutput.php b/lib/public/Migration/IOutput.php
index 2dba50ab2c6..212b5d927f8 100644
--- a/lib/public/Migration/IOutput.php
+++ b/lib/public/Migration/IOutput.php
@@ -32,18 +32,21 @@ interface IOutput {
/**
* @param string $message
+ * @return void
* @since 9.1.0
*/
public function info($message);
/**
* @param string $message
+ * @return void
* @since 9.1.0
*/
public function warning($message);
/**
* @param int $max
+ * @return void
* @since 9.1.0
*/
public function startProgress($max = 0);
@@ -51,12 +54,13 @@ interface IOutput {
/**
* @param int $step
* @param string $description
+ * @return void
* @since 9.1.0
*/
public function advance($step = 1, $description = '');
/**
- * @param int $max
+ * @return void
* @since 9.1.0
*/
public function finishProgress();
diff --git a/lib/public/Security/Bruteforce/IThrottler.php b/lib/public/Security/Bruteforce/IThrottler.php
new file mode 100644
index 00000000000..6f492d6c59d
--- /dev/null
+++ b/lib/public/Security/Bruteforce/IThrottler.php
@@ -0,0 +1,126 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2022 Joas Schilling <coding@schilljs.com>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCP\Security\Bruteforce;
+
+/**
+ * Class Throttler implements the bruteforce protection for security actions in
+ * Nextcloud.
+ *
+ * It is working by logging invalid login attempts to the database and slowing
+ * down all login attempts from the same subnet. The max delay is 30 seconds and
+ * the starting delay are 200 milliseconds. (after the first failed login)
+ *
+ * This is based on Paragonie's AirBrake for Airship CMS. You can find the original
+ * code at https://github.com/paragonie/airship/blob/7e5bad7e3c0fbbf324c11f963fd1f80e59762606/src/Engine/Security/AirBrake.php
+ *
+ * @package OC\Security\Bruteforce
+ * @since 25.0.0
+ */
+interface IThrottler {
+ /**
+ * @since 25.0.0
+ */
+ public const MAX_DELAY = 25;
+
+ /**
+ * @since 25.0.0
+ */
+ public const MAX_DELAY_MS = 25000; // in milliseconds
+
+ /**
+ * @since 25.0.0
+ */
+ public const MAX_ATTEMPTS = 10;
+
+ /**
+ * Register a failed attempt to bruteforce a security control
+ *
+ * @param string $action
+ * @param string $ip
+ * @param array $metadata Optional metadata logged to the database
+ * @since 25.0.0
+ */
+ public function registerAttempt(string $action, string $ip, array $metadata = []): void;
+
+ /**
+ * Get the throttling delay (in milliseconds)
+ *
+ * @param string $ip
+ * @param string $action optionally filter by action
+ * @param float $maxAgeHours
+ * @return int
+ * @since 25.0.0
+ */
+ public function getAttempts(string $ip, string $action = '', float $maxAgeHours = 12): int;
+
+ /**
+ * Get the throttling delay (in milliseconds)
+ *
+ * @param string $ip
+ * @param string $action optionally filter by action
+ * @return int
+ * @since 25.0.0
+ */
+ public function getDelay(string $ip, string $action = ''): int;
+
+ /**
+ * Reset the throttling delay for an IP address, action and metadata
+ *
+ * @param string $ip
+ * @param string $action
+ * @param array $metadata
+ * @since 25.0.0
+ */
+ public function resetDelay(string $ip, string $action, array $metadata): void;
+
+ /**
+ * Reset the throttling delay for an IP address
+ *
+ * @param string $ip
+ * @since 25.0.0
+ */
+ public function resetDelayForIP(string $ip): void;
+
+ /**
+ * Will sleep for the defined amount of time
+ *
+ * @param string $ip
+ * @param string $action optionally filter by action
+ * @return int the time spent sleeping
+ * @since 25.0.0
+ */
+ public function sleepDelay(string $ip, string $action = ''): int;
+
+ /**
+ * Will sleep for the defined amount of time unless maximum was reached in the last 30 minutes
+ * In this case a "429 Too Many Request" exception is thrown
+ *
+ * @param string $ip
+ * @param string $action optionally filter by action
+ * @return int the time spent sleeping
+ * @throws MaxDelayReached when reached the maximum
+ * @since 25.0.0
+ */
+ public function sleepDelayOrThrowOnMax(string $ip, string $action = ''): int;
+}
diff --git a/lib/public/Share/IAttributes.php b/lib/public/Share/IAttributes.php
new file mode 100644
index 00000000000..6e4cee08b12
--- /dev/null
+++ b/lib/public/Share/IAttributes.php
@@ -0,0 +1,68 @@
+<?php
+/**
+ * @author Piotr Mrowczynski <piotr@owncloud.com>
+ *
+ * @copyright Copyright (c) 2019, ownCloud GmbH
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+namespace OCP\Share;
+
+/**
+ * Interface IAttributes
+ *
+ * @package OCP\Share
+ * @since 25.0.0
+ */
+interface IAttributes {
+
+ /**
+ * Sets an attribute enabled/disabled. If the key did not exist before it will be created.
+ *
+ * @param string $scope scope
+ * @param string $key key
+ * @param bool $enabled enabled
+ * @return IAttributes The modified object
+ * @since 25.0.0
+ */
+ public function setAttribute($scope, $key, $enabled);
+
+ /**
+ * Returns if attribute is enabled/disabled for given scope id and key.
+ * If attribute does not exist, returns null
+ *
+ * @param string $scope scope
+ * @param string $key key
+ * @return bool|null
+ * @since 25.0.0
+ */
+ public function getAttribute($scope, $key);
+
+ /**
+ * Formats the IAttributes object to array with the following format:
+ * [
+ * 0 => [
+ * "scope" => <string>,
+ * "key" => <string>,
+ * "enabled" => <bool>
+ * ],
+ * ...
+ * ]
+ *
+ * @return array formatted IAttributes
+ * @since 25.0.0
+ */
+ public function toArray();
+}
diff --git a/lib/public/Share/IManager.php b/lib/public/Share/IManager.php
index f207ca87a2c..0810acc673a 100644
--- a/lib/public/Share/IManager.php
+++ b/lib/public/Share/IManager.php
@@ -135,10 +135,11 @@ interface IManager {
* @param string $userId
* @param Folder $node
* @param bool $reshares
+ * @param bool $shallow Whether the method should stop at the first level, or look into sub-folders.
* @return IShare[][] [$fileId => IShare[], ...]
* @since 11.0.0
*/
- public function getSharesInFolder($userId, Folder $node, $reshares = false);
+ public function getSharesInFolder($userId, Folder $node, $reshares = false, $shallow = true);
/**
* Get shares shared by (initiated) by the provided user.
diff --git a/lib/public/Share/IShare.php b/lib/public/Share/IShare.php
index 1d3cf9bbbdf..5a825552e26 100644
--- a/lib/public/Share/IShare.php
+++ b/lib/public/Share/IShare.php
@@ -36,7 +36,9 @@ use OCP\Files\NotFoundException;
use OCP\Share\Exceptions\IllegalIDChangeException;
/**
- * Interface IShare
+ * This interface allows to represent a share object.
+ *
+ * This interface must not be implemented in your application.
*
* @since 9.0.0
*/
@@ -300,7 +302,7 @@ interface IShare {
* See \OCP\Constants::PERMISSION_*
*
* @param int $permissions
- * @return \OCP\Share\IShare The modified object
+ * @return IShare The modified object
* @since 9.0.0
*/
public function setPermissions($permissions);
@@ -315,6 +317,31 @@ interface IShare {
public function getPermissions();
/**
+ * Create share attributes object
+ *
+ * @since 25.0.0
+ * @return IAttributes
+ */
+ public function newAttributes(): IAttributes;
+
+ /**
+ * Set share attributes
+ *
+ * @param ?IAttributes $attributes
+ * @since 25.0.0
+ * @return IShare The modified object
+ */
+ public function setAttributes(?IAttributes $attributes);
+
+ /**
+ * Get share attributes
+ *
+ * @since 25.0.0
+ * @return ?IAttributes
+ */
+ public function getAttributes(): ?IAttributes;
+
+ /**
* Set the accepted status
* See self::STATUS_*
*
diff --git a/lib/public/Share/IShareProvider.php b/lib/public/Share/IShareProvider.php
index 6af513360fe..c549592d6f6 100644
--- a/lib/public/Share/IShareProvider.php
+++ b/lib/public/Share/IShareProvider.php
@@ -123,10 +123,11 @@ interface IShareProvider {
* @param string $userId
* @param Folder $node
* @param bool $reshares Also get the shares where $user is the owner instead of just the shares where $user is the initiator
+ * @param bool $shallow Whether the method should stop at the first level, or look into sub-folders.
* @return \OCP\Share\IShare[][]
* @since 11.0.0
*/
- public function getSharesInFolder($userId, Folder $node, $reshares);
+ public function getSharesInFolder($userId, Folder $node, $reshares, $shallow = true);
/**
* Get all shares by the given user
diff --git a/lib/public/User/Backend/ICheckPasswordBackend.php b/lib/public/User/Backend/ICheckPasswordBackend.php
index b3eaf7cedb0..0d4026be859 100644
--- a/lib/public/User/Backend/ICheckPasswordBackend.php
+++ b/lib/public/User/Backend/ICheckPasswordBackend.php
@@ -35,7 +35,7 @@ interface ICheckPasswordBackend {
*
* @param string $loginName The loginname
* @param string $password The password
- * @return string|bool The uid on success false on failure
+ * @return string|false The uid on success false on failure
*/
public function checkPassword(string $loginName, string $password);
}
diff --git a/lib/public/User/Backend/ISetDisplayNameBackend.php b/lib/public/User/Backend/ISetDisplayNameBackend.php
index 922d356bfd7..db62223ad52 100644
--- a/lib/public/User/Backend/ISetDisplayNameBackend.php
+++ b/lib/public/User/Backend/ISetDisplayNameBackend.php
@@ -36,6 +36,9 @@ interface ISetDisplayNameBackend {
* @param string $uid The username
* @param string $displayName The new display name
* @return bool
+ *
+ * @since 25.0.0 Throw InvalidArgumentException
+ * @throws \InvalidArgumentException
*/
public function setDisplayName(string $uid, string $displayName): bool;
}
diff --git a/lib/public/User/Events/UserChangedEvent.php b/lib/public/User/Events/UserChangedEvent.php
index 3a40f8c3f11..f48dd3914e6 100644
--- a/lib/public/User/Events/UserChangedEvent.php
+++ b/lib/public/User/Events/UserChangedEvent.php
@@ -32,16 +32,10 @@ use OCP\IUser;
* @since 18.0.0
*/
class UserChangedEvent extends Event {
-
- /** @var IUser */
- private $user;
-
- /** @var string */
- private $feature;
-
+ private IUser $user;
+ private string $feature;
/** @var mixed */
private $value;
-
/** @var mixed */
private $oldValue;
diff --git a/lib/public/UserMigration/ISizeEstimationMigrator.php b/lib/public/UserMigration/ISizeEstimationMigrator.php
new file mode 100644
index 00000000000..05abe48ea8f
--- /dev/null
+++ b/lib/public/UserMigration/ISizeEstimationMigrator.php
@@ -0,0 +1,43 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright 2022 Christopher Ng <chrng8@gmail.com>
+ *
+ * @author Christopher Ng <chrng8@gmail.com>
+ * @author Côme Chilliet <come.chilliet@nextcloud.com>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCP\UserMigration;
+
+use OCP\IUser;
+
+/**
+ * @since 25.0.0
+ */
+interface ISizeEstimationMigrator {
+ /**
+ * Returns an estimate of the exported data size in KiB.
+ * Should be fast, favor performance over accuracy.
+ *
+ * @since 25.0.0
+ */
+ public function getEstimatedExportSize(IUser $user): int;
+}
diff --git a/lib/public/UserStatus/IUserStatus.php b/lib/public/UserStatus/IUserStatus.php
index bf743dea08d..8803b328ad5 100644
--- a/lib/public/UserStatus/IUserStatus.php
+++ b/lib/public/UserStatus/IUserStatus.php
@@ -65,6 +65,18 @@ interface IUserStatus {
public const INVISIBLE = 'invisible';
/**
+ * @var string
+ * @since 25.0.0
+ */
+ public const MESSAGE_CALL = 'call';
+
+ /**
+ * @var string
+ * @since 25.0.0
+ */
+ public const MESSAGE_AVAILABILITY = 'availability';
+
+ /**
* Get the user this status is connected to
*
* @return string
diff --git a/lib/public/Util.php b/lib/public/Util.php
index e5bb2a955ae..6cd3eaa7f85 100644
--- a/lib/public/Util.php
+++ b/lib/public/Util.php
@@ -48,6 +48,7 @@ namespace OCP;
use OC\AppScriptDependency;
use OC\AppScriptSort;
+use bantu\IniGetWrapper\IniGetWrapper;
/**
* This class provides different helper functions to make the life of a developer easier
@@ -604,4 +605,27 @@ class Util {
}
return $temp;
}
+
+ /**
+ * Check if a function is enabled in the php configuration
+ *
+ * @since 25.0.0
+ */
+ public static function isFunctionEnabled(string $functionName): bool {
+ if (!function_exists($functionName)) {
+ return false;
+ }
+ $ini = \OCP\Server::get(IniGetWrapper::class);
+ $disabled = explode(',', $ini->get('disable_functions') ?: '');
+ $disabled = array_map('trim', $disabled);
+ if (in_array($functionName, $disabled)) {
+ return false;
+ }
+ $disabled = explode(',', $ini->get('suhosin.executor.func.blacklist') ?: '');
+ $disabled = array_map('trim', $disabled);
+ if (in_array($functionName, $disabled)) {
+ return false;
+ }
+ return true;
+ }
}