Compare commits

...

144 Commits

Author SHA1 Message Date
Nextcloud bot
529a85bce3
fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-10-26 02:41:36 +00:00
Nextcloud bot
60aa7c76dc
fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-10-25 02:40:55 +00:00
Nextcloud bot
d73bd2d7e5
fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-10-24 02:41:11 +00:00
Iva Horn
83c6bc3c4f
Merge pull request #8943 from nextcloud/fix/8914-download-folder-now
Fix #8914
2025-10-23 17:31:59 +02:00
Iva Horn
3220e9ec88 fix(file-provider): Fix #8914 by omitting the faulty NSFileProviderRequest argument on enumerator request.
Signed-off-by: Iva Horn <iva.horn@nextcloud.com>
2025-10-23 16:51:53 +02:00
Iva Horn
aecfdf4b48
Merge pull request #8941 from nextcloud/fix/uuid-account-crash
Fix Crash on File Provider Deactivation for Account with UUID Name
2025-10-23 15:35:10 +02:00
Iva Horn
347e7345bd chore: Added "gui" source code folder to Xcode project.
Signed-off-by: Iva Horn <iva.horn@nextcloud.com>
2025-10-23 14:48:44 +02:00
Iva Horn
d509974bfd fix: Crash when disabling file provider for account with UUID as name.
Signed-off-by: Iva Horn <iva.horn@nextcloud.com>
2025-10-23 14:48:44 +02:00
Jyrki Gadinger
b240648fd0
Merge pull request #8925 from nextcloud/bugfix/8743/migrate-global-bandwidth-setting
fix(network): fall back to unlimited bandwidth in case the legacy global limit is still set
2025-10-23 14:37:28 +02:00
Jyrki Gadinger
a313ea6aff fix(network): fall back to unlimited bandwidth in case the legacy global limit is still set
`-2` used to mean "Use global limits", which no longer exist since
3.17.0.  As any negative value results in the auto bandwidth limiter to
engage, this could result in unexpected slower sync speeds after a
client upgrade.

Let's just keep handling that value and assume that we want to use
unlimited bandwidth.

Fixes #8743

Signed-off-by: Jyrki Gadinger <nilsding@nilsding.org>
2025-10-23 13:59:38 +02:00
Jyrki Gadinger
06b87b679d
Merge pull request #8932 from nextcloud/bugfix/8929/fileshare-detection
fix(account): do not consider URLs with a trailing slash as public links
2025-10-23 13:50:10 +02:00
Jyrki Gadinger
bddb39df16 fix(account): do not consider URLs with a trailing slash as public links
Resolves #8929

Signed-off-by: Jyrki Gadinger <nilsding@nilsding.org>
2025-10-23 11:57:10 +02:00
Iva Horn
7ac1a0d1e7
Merge pull request #8924 from nextcloud/fix/removed-obsolete-settings
Removed obsolete settings features in main app
2025-10-23 09:03:44 +02:00
Iva Horn
9d2333c4ed fix(file-provider): Removed obsolete settings features in main app.
- Removed materialized items eviction window.
- Removed manual enumerator signaling button.
- Removed file provider reset button.
- Removed progress user interface in file provider settings.

Signed-off-by: Iva Horn <iva.horn@nextcloud.com>
2025-10-23 08:26:03 +02:00
Nextcloud bot
467a59e88d
fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-10-23 03:02:21 +00:00
Jyrki Gadinger
265385dc62
Merge pull request #8921 from nextcloud/bugfix/8915/lscol-root
fix: ignore directory listing entry for the iterated directory
2025-10-22 17:40:49 +02:00
Jyrki Gadinger
438b0af64b fix: ignore directory listing entry for the iterated directory
The `ignoreFirst` bool had a short lifetime, by the time the lambda slot
handling the `LsColJob::directoryListingIterated` signal was called the
reference to that bool would report a completely different value.  This
could result in the first entry of a directory listing to not be ignored
properly.

This commit changes the way the slot checks whether it's currently
iterating over the very same directory that was just requested by
comparing the request path with the path from the response.

Fixes #8915

Signed-off-by: Jyrki Gadinger <nilsding@nilsding.org>
2025-10-22 11:42:21 +02:00
Nextcloud bot
4417cf60d4
fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-10-22 02:37:54 +00:00
Jyrki Gadinger
d702a5bd6b
Merge pull request #8910 from nextcloud/bugfix/drop-NC19-support
fix: Update minimum supported Nextcloud server version to 20
2025-10-21 13:22:16 +02:00
Rello
3de563e54f
fix: Update minimum supported Nextcloud server version to 20
Signed-off-by: Rello <Rello@users.noreply.github.com>
2025-10-21 16:38:44 +07:00
Nextcloud bot
06c80d731b
fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-10-21 02:38:20 +00:00
Tamás Bari
e738580d5b
Merge pull request #8894 from nextcloud/bugfix/AllignPlusIcon
Fix some of the accounts menu visual issues
2025-10-20 16:19:31 +02:00
Tamás Bari
c301eb2a24 fix: correcting text color for user name
Signed-off-by: Tamás Bari <adaorcpp@gmail.com>
2025-10-20 13:38:47 +02:00
Tamás Bari
33f5c04328 fix: disabling menu items instead of hiding them
Signed-off-by: Tamás Bari <adaorcpp@gmail.com>
2025-10-20 13:38:47 +02:00
Tamás Bari
5acf0f791f fix: user more menu height (especially on Windows)
Signed-off-by: Tamás Bari <adaorcpp@gmail.com>
2025-10-20 13:38:47 +02:00
Tamás Bari
b26c20e642 fix: status message color and reaction to model changes
Signed-off-by: Tamás Bari <adaorcpp@gmail.com>
2025-10-20 13:38:47 +02:00
Tamás Bari
aa7ee74781 fix: ignore windows for highligted menuitem styling
Signed-off-by: Tamás Bari <adaorcpp@gmail.com>
2025-10-20 13:38:47 +02:00
Tamás Bari
be3a001edc fix: ensuring contrasting text color for highlighted menuitems
Signed-off-by: Tamás Bari <adaorcpp@gmail.com>
2025-10-20 13:38:47 +02:00
Tamás Bari
8145c1ef7a fix: accounts menu width adapting to it's contents
Signed-off-by: Tamás Bari <adaorcpp@gmail.com>
2025-10-20 13:38:47 +02:00
Tamás Bari
adaa54cced fix: add account menuitem height and padding
Signed-off-by: Tamás Bari <adaorcpp@gmail.com>
2025-10-20 13:38:47 +02:00
Jyrki Gadinger
4c8debe0ad
Merge pull request #8900 from nextcloud/bugfix/VfsQmlNeutralSyncStatus
fix: Neutral sync icon in VFS sync status
2025-10-20 13:12:10 +02:00
Rello
7ab63e9466 Neutral sync icon in VFS sync status
Signed-off-by: Rello <Rello@users.noreply.github.com>
2025-10-20 12:34:00 +02:00
Iva Horn
a5dfc3ae59
Merge pull request #8890 from nextcloud/feat/file-provider-request-logging-support
Logging NSFileProviderRequest
2025-10-20 12:21:57 +02:00
Iva Horn
e2f44b9a80 fix(file-provider): Updated dependencies.
Signed-off-by: Iva Horn <iva.horn@nextcloud.com>
2025-10-20 10:59:00 +02:00
Iva Horn
2b0ee2e292 fix(file-provider): Adjusted log level of a less important message to debug.
Signed-off-by: Iva Horn <iva.horn@nextcloud.com>
2025-10-20 10:59:00 +02:00
Iva Horn
2186edeeb7 feat: Logging NSFileProviderRequest.
Signed-off-by: Iva Horn <iva.horn@nextcloud.com>
2025-10-20 10:59:00 +02:00
Jyrki Gadinger
39bf9e4ad8
Merge pull request #8902 from nextcloud/ci/noid/bump-xcode
chore(ci): use Xcode 16.3
2025-10-20 10:30:28 +02:00
Jyrki Gadinger
8812fb37f8 chore(ci): use Xcode 16.3
Signed-off-by: Jyrki Gadinger <nilsding@nilsding.org>
2025-10-20 09:45:45 +02:00
Jyrki Gadinger
92c87d460b
Merge pull request #8899 from nextcloud/bugfix/DebugLogFiles
fix: remove "nextcloud" from debug log filenames
2025-10-20 09:01:27 +02:00
Rello
5336d8e909
fix: remove "nextcloud" from debug log filenames
Signed-off-by: Rello <Rello@users.noreply.github.com>
2025-10-20 10:25:25 +07:00
Nextcloud bot
4bf78810ab
fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-10-20 02:36:50 +00:00
Nextcloud bot
c2442bce71
fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-10-18 02:37:23 +00:00
Iva Horn
0402f2fced fix(file-provider): Updated dependencies.
Signed-off-by: Iva Horn <iva.horn@nextcloud.com>
2025-10-17 17:16:50 +02:00
Iva Horn
b2326a7845 fix: Removed redundant NextcloudCapabilitiesKit reference from Xcode project.
It is a transient dependency through NextcloudFileProviderKit anyway and was conflicting with that (besides still referring to claucambra).

Signed-off-by: Iva Horn <iva.horn@nextcloud.com>
2025-10-17 10:04:46 +02:00
Iva Horn
62db000adc fix(file-provider): Updated dependencies.
Signed-off-by: Iva Horn <iva.horn@nextcloud.com>
2025-10-17 10:02:01 +02:00
Iva Horn
7d932f8a48 fix(file-provider): Updated dependencies.
Signed-off-by: Iva Horn <iva.horn@nextcloud.com>
2025-10-17 09:49:16 +02:00
Nextcloud bot
042a943685
fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-10-17 02:37:42 +00:00
Jyrki Gadinger
8ae74384ea
Merge pull request #8889 from nextcloud/feature/noid/xcode-target-is-release
chore: set xcode target to `Debug` only for dev builds
2025-10-16 18:38:47 +02:00
Jyrki Gadinger
7219c3bf20 chore: set xcode target to Debug only for dev builds
Signed-off-by: Jyrki Gadinger <nilsding@nilsding.org>
2025-10-16 17:53:58 +02:00
Iva Horn
b3e705cbe1 feat(file-provider): Updated NextcloudFileProviderKit from 3.2.5 to 3.2.6
Signed-off-by: Iva Horn <iva.horn@nextcloud.com>
2025-10-16 14:11:32 +02:00
Jyrki Gadinger
700fcafe35
Merge pull request #8714 from nextcloud/bugfix/quotadir
fix(quota): return unlimited quota if server query is invalid or file is in folder root.
2025-10-16 10:09:23 +02:00
Jyrki Gadinger
cd8601e3be chore: change the extra quota logging to debug level
Signed-off-by: Jyrki Gadinger <nilsding@nilsding.org>
2025-10-16 09:13:03 +02:00
Jyrki Gadinger
01526c34e5 fix(quota): parse quota values from servers as double
The server can respond with values like `2.58440798353E+12` instead of
a plain number like `2584407983530`.
`QVariant::toLongLong` does not recognise that as a valid number and
returns `0` instead, breaking the sync for some.

Also added a fallback value in case parsing the value as double doesn't
work either.

Fixes #8555

Signed-off-by: Jyrki Gadinger <nilsding@nilsding.org>
2025-10-16 09:13:03 +02:00
Jyrki Gadinger
0d83bb2bf9 fix(discoveryphase): remove additional setting of folderQuota
`LsColJob::propertyMapToRemoteInfo` takes care of that too

Signed-off-by: Jyrki Gadinger <nilsding@nilsding.org>
2025-10-16 09:13:03 +02:00
Jyrki Gadinger
51b5ed53ec fix(discovery): remove extra check for valid server entry
`processFileAnalyzeRemoteInfo` is only called iff `serverEntry.isValid`
anyway

Signed-off-by: Jyrki Gadinger <nilsding@nilsding.org>
2025-10-16 09:13:03 +02:00
Camila Ayres
559e9c21aa chore: print sync information when checking quota.
Signed-off-by: Camila Ayres <hello@camilasan.com>
Signed-off-by: Jyrki Gadinger <nilsding@nilsding.org>
2025-10-16 09:13:03 +02:00
Camila Ayres
5423d8383b fix(quota): return unlimited quota if server query is invalid or file is in folder root.
Signed-off-by: Camila Ayres <hello@camilasan.com>
Signed-off-by: Jyrki Gadinger <nilsding@nilsding.org>
2025-10-16 09:13:03 +02:00
Nextcloud bot
54cb247c0d
fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-10-16 02:36:39 +00:00
Jyrki Gadinger
979b6cf197
Merge pull request #8885 from nextcloud/bugfix/noid/macos-init-disappearing-cloudstorage-directory
fix(file-provider): report a different error if database is not ready
2025-10-15 17:45:53 +02:00
Jyrki Gadinger
ff43776cb7 fix(file-provider): report a different error if database is not ready
As per [the docs for `NSFileProviderReplicatedExtension
item(for:request:completionHandler:)`][1], the system will automatically
retry calling the method if the error is not one of `.notAuthenticated`,
`.serverUnreachable`, or `.noSuchItem`.

From my observations it seems that if the error `.cannotSynchronize` is
returned for the root container (i.e.
`NSFileProviderRootContainerItemIdentifier`), the system/file provider
framework just ends up deleting the newly created sync directory inside
`~/Library/CloudStorage`.  This broke the entry in the Finder sidebar.

After changing the error to `.notAuthenticated` (which is semantically
wrong, but alas), I now could disable/enable the File Provider as often
as I wanted without the Finder sidebar entry to ever break due to the
directory having been removed previously ...  Strange, this.

[1]: https://developer.apple.com/documentation/fileprovider/nsfileproviderreplicatedextension/item(for:request:completionhandler:)#Discussion

Signed-off-by: Jyrki Gadinger <nilsding@nilsding.org>
2025-10-15 16:41:10 +02:00
Iva Horn
383a971aa0 fix(file-provider): Updated dependencies.
- NextcloudKit 7.1.6 → 7.1.7
- NextcloudFileProviderKit 3.2.4 → 3.2.5

Signed-off-by: Iva Horn <iva.horn@nextcloud.com>
2025-10-15 16:14:28 +02:00
Jyrki Gadinger
2566309a15
Merge pull request #8883 from nextcloud/bugfix/8740/macos-tooltips
fix(tray): use native tooltips when available
2025-10-15 13:58:11 +02:00
Jyrki Gadinger
86ceba756d fix(tray): use Item popupType on Windows
otherwise the text clips through the popup shape for some strange reason
...

Signed-off-by: Jyrki Gadinger <nilsding@nilsding.org>
2025-10-15 13:13:12 +02:00
Jyrki Gadinger
9a92dac5f5 fix(tray): use native tooltips when available
Signed-off-by: Jyrki Gadinger <nilsding@nilsding.org>
2025-10-15 12:35:16 +02:00
Jyrki Gadinger
9b8c831696 chore: remove unused NCToolTip
Was removed with fb17a4bf9e

Signed-off-by: Jyrki Gadinger <nilsding@nilsding.org>
2025-10-15 10:46:29 +02:00
Nextcloud bot
eb0b82b175
fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-10-15 02:36:39 +00:00
Iva Horn
b46afb791c fix: Updated NextcloudFileProviderKit reference to 9d3a90e
9d3a90e516
Signed-off-by: Iva Horn <iva.horn@nextcloud.com>
2025-10-14 15:10:02 +02:00
Jyrki Gadinger
e68be46635
Merge pull request #8876 from nextcloud/bugfix/nextclouddev
fix(NextcloudDev): add missing template file.
2025-10-14 09:06:08 +02:00
Camila Ayres
1251cdb0a1 fix(NextcloudDev): add missing template file.
Adjust .gitignore to accept a file with 'build' in the name.

Signed-off-by: Camila Ayres <hello@camilasan.com>
2025-10-14 08:30:08 +02:00
Nextcloud bot
955a47468a
fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-10-14 02:51:54 +00:00
Jyrki Gadinger
32e66991ae
Merge pull request #8872 from nextcloud/fix/token-locks
Fix Token Locks
2025-10-13 10:55:25 +02:00
Iva Horn
272ca1dcf5 fix(file-provider): Updated NextcloudKit and NextcloudFileProviderKit dependencies.
Signed-off-by: Iva Horn <iva.horn@nextcloud.com>
2025-10-13 10:17:09 +02:00
Iva Horn
92f13d4be8 fix(file-provider): Updated logging calls in extension implementation.
Signed-off-by: Iva Horn <iva.horn@icloud.com>
2025-10-13 10:17:09 +02:00
Nextcloud bot
57c5680d60
fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-10-13 07:37:31 +00:00
Jyrki Gadinger
0cad5b1d61
Merge pull request #8870 from nextcloud/rakekniven-patch-1
fix(i18n): Fixed grammar
2025-10-13 09:16:19 +02:00
Jyrki Gadinger
224c9b1f12 fix(test): adapt updated error message strings
Signed-off-by: Jyrki Gadinger <nilsding@nilsding.org>
2025-10-13 08:38:03 +02:00
rakekniven
335015fa2d
fix(i18n): Fixed grammar
Reported at Transifex

Signed-off-by: rakekniven <2069590+rakekniven@users.noreply.github.com>
2025-10-13 08:08:40 +02:00
Nextcloud bot
4a0138e952
fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-10-12 02:58:50 +00:00
Nextcloud bot
da0836f2e8
fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-10-11 02:43:17 +00:00
Nextcloud bot
dc7e1295ab
fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-10-10 03:05:55 +00:00
Matthieu Gallien
cca6805448
Merge pull request #8862 from nextcloud/bugfix/AllignPlusIcon
fix: Adding elide to menuitems
2025-10-09 12:23:07 +02:00
Tamás Bari
d354b329f4 fix: Adding elide to menuitems
Signed-off-by: Tamás Bari <adaorcpp@gmail.com>
2025-10-09 11:44:41 +02:00
Jyrki Gadinger
c0bd552d7e
Merge pull request #8863 from nextcloud/bugfix/noid/acl-error-logging
fix(filesystembase): log correct ACL errors
2025-10-09 11:44:12 +02:00
Jyrki Gadinger
506e2d9358 fix(filesystembase): log correct ACL errors
In #8860 I noticed that the "insufficient memory error" message is
logged if the last error was anything but `ERROR_INSUFFICIENT_BUFFER`

- fix comparison operator
- format Windows error codes with a hex code and message

Signed-off-by: Jyrki Gadinger <nilsding@nilsding.org>
2025-10-09 10:39:26 +02:00
Nextcloud bot
031cda0e5c
fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-10-09 07:05:57 +00:00
Rello
7b89dd1f9e
Merge pull request #8847 from nextcloud/copilot/fix-conflict-dialog-single-quote
fix: HTML-escape file URLs to handle special characters in conflict and case clash dialogs
2025-10-09 09:02:49 +07:00
copilot-swe-agent[bot]
a04811e2d1 fix: properly HTML-escape URLs to handle both single and double quotes
Use Utility::escape() to HTML-escape file URLs before embedding them in
anchor tags. This ensures both single quotes and double quotes (and other
HTML special characters) are properly handled.

The toHtmlEscaped() function converts:
- ' to &#39;
- " to &quot;
- & to &amp;
- < to &lt;
- > to &gt;

This fixes the issue where changing from single to double quote delimiters
would break with double quotes instead.

Signed-off-by: GitHub Copilot <copilot@github.com>

Co-authored-by: nilsding <1809170+nilsding@users.noreply.github.com>
2025-10-09 08:25:18 +07:00
copilot-swe-agent[bot]
4e12a82563 fix: handle single quotes in file paths for conflict and case clash dialogs
Changed HTML anchor attribute delimiters from single quotes to double quotes
in conflictdialog.cpp and caseclashfilenamedialog.cpp to properly handle
file paths containing single quote characters.

Fixes issue where clicking links for files with single quotes in their path
would fail due to malformed HTML.

Signed-off-by: GitHub Copilot <copilot@github.com>

Co-authored-by: Rello <13385119+Rello@users.noreply.github.com>
2025-10-09 08:25:18 +07:00
Rello
bd658a3626
Merge pull request #8851 from nextcloud/bugfix/AllignPlusIcon
Bugfix/allign plus icon
2025-10-09 08:24:32 +07:00
Tamás Bari
53a165f56d fix: Tweaking account dropdown menu style
Signed-off-by: Tamás Bari <adaorcpp@gmail.com>
2025-10-09 07:28:56 +07:00
Tamás Bari
c69d8edc90 fix: Removing non-existent properties
Signed-off-by: Tamás Bari <adaorcpp@gmail.com>
2025-10-09 07:28:56 +07:00
Rello
3adeb70af0 Refactor addAccountButton properties for icon sizing
Signed-off-by: Rello <Rello@users.noreply.github.com>
2025-10-09 07:28:56 +07:00
Matthieu Gallien
e839b25f67
Merge pull request #8849 from nextcloud/bugfix/addChunkUploadHeader
chore(chunk-upload): always sent the total size header
2025-10-08 20:27:07 +02:00
Matthieu Gallien
b533819f20 chore(chunk-upload): always sent the total size header
as documented here
https://docs.nextcloud.com/server/latest/developer_manual/client_apis/WebDAV/chunking.html#uploading-chunks

Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2025-10-08 19:48:51 +02:00
Matthieu Gallien
80d79e411a
Merge pull request #8848 from nextcloud/bugfix/noid/asyncimageresponse-use-qnam-thread
fix(asyncimageresponse): fetch remote resources in the same thread as `Account`
2025-10-08 17:43:16 +02:00
Jyrki Gadinger
89d5df6672 fix(asyncimageresponse): fetch remote resources in the same thread as Account
With this change some warnings from QObject like these are gone now:

    QObject: Cannot create children for a parent that is in a different thread.

I suspect this might have been causing some "random" crashes during
normal usage as well.  QNAMs are supposed to be used from the same
thread they were created in, which (as far as I could tell anyway) isn't
the case within async image providers...

This change is similar to how the `ImageResponse` class inside
`src/gui/tray/usermodel.cpp` fetches a remote resource.

Signed-off-by: Jyrki Gadinger <nilsding@nilsding.org>
2025-10-08 12:59:56 +02:00
Nextcloud bot
fb20bb2810
fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-10-08 03:08:14 +00:00
Matthieu Gallien
da19d87a8f
Merge pull request #8840 from nextcloud/dependabot/github_actions/cpp-linter/cpp-linter-action-2.16.5
chore(deps): Bump cpp-linter/cpp-linter-action from 2.16.4 to 2.16.5
2025-10-07 20:25:58 +02:00
dependabot[bot]
97d75fcb6c
chore(deps): Bump cpp-linter/cpp-linter-action from 2.16.4 to 2.16.5
Bumps [cpp-linter/cpp-linter-action](https://github.com/cpp-linter/cpp-linter-action) from 2.16.4 to 2.16.5.
- [Release notes](https://github.com/cpp-linter/cpp-linter-action/releases)
- [Commits](7dacd91f6a...b7fbdde0f6)

---
updated-dependencies:
- dependency-name: cpp-linter/cpp-linter-action
  dependency-version: 2.16.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-07 17:49:21 +00:00
Matthieu Gallien
954467383a
Merge pull request #8841 from nextcloud/dependabot/github_actions/actions/stale-10.1.0
chore(deps): Bump actions/stale from 10.0.0 to 10.1.0
2025-10-07 19:48:20 +02:00
dependabot[bot]
59a9a751b0
chore(deps): Bump actions/stale from 10.0.0 to 10.1.0
Bumps [actions/stale](https://github.com/actions/stale) from 10.0.0 to 10.1.0.
- [Release notes](https://github.com/actions/stale/releases)
- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
- [Commits](3a9db7e6a4...5f858e3efb)

---
updated-dependencies:
- dependency-name: actions/stale
  dependency-version: 10.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-07 17:13:16 +00:00
Matthieu Gallien
db6a1daa5b
Merge pull request #8839 from nextcloud/dependabot/github_actions/fsfe/reuse-action-6.0.0
chore(deps): Bump fsfe/reuse-action from 5.0.0 to 6.0.0
2025-10-07 19:12:13 +02:00
dependabot[bot]
f16ff7ff40 chore(deps): Bump fsfe/reuse-action from 5.0.0 to 6.0.0
Bumps [fsfe/reuse-action](https://github.com/fsfe/reuse-action) from 5.0.0 to 6.0.0.
- [Release notes](https://github.com/fsfe/reuse-action/releases)
- [Commits](bb774aa972...676e2d560c)

---
updated-dependencies:
- dependency-name: fsfe/reuse-action
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-07 18:32:57 +02:00
Iva Horn
eaac96db85
Merge pull request #8842 from nextcloud/feature/8550-file-provider-localizations
Updated File Provider Translations
2025-10-07 15:35:12 +02:00
Iva Horn
6ba027c906 feat(file-provider): Integrated localizations from Transifex.
- Introduced TransifexStringCatalogSanitizer command-line utility.
- Introduced dedicated Transifex CLI configuration file for NextcloudIntegration project.

Signed-off-by: Iva Horn <iva.horn@icloud.com>
2025-10-07 14:28:44 +02:00
Iva Horn
dd351b73dc
Merge pull request #8818 from nextcloud/feature/8472-branded-symbol
Branded file provider extension SF Symbol
2025-10-07 13:42:12 +02:00
Iva Horn
bb6157e329 feat: Branded file provider extension SF Symbol.
Signed-off-by: Iva Horn <iva.horn@icloud.com>
2025-10-07 12:53:30 +02:00
Iva Horn
3d7c3ffe6b fix: Added some macOS-specific items to .gitignore
Signed-off-by: Iva Horn <iva.horn@icloud.com>
2025-10-07 12:49:53 +02:00
Matthieu Gallien
2e619ec97a
Merge pull request #8671 from nextcloud/bugfix/improvedFolderDeleteNewConflictSolving
fix: folder delete/new conflict will be "delete"
2025-10-07 09:27:21 +02:00
Matthieu Gallien
f02f6c906b chore(autotests): folders on-demand breaks the new test: skip it
Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2025-10-07 08:51:02 +02:00
Matthieu Gallien
e9ee0602e7 feat(conflict): use local device trashbin for delete/new conflicts
try to use local device trash bin when deleting local items during
automated handling of a new/delete conflict

we still always choose the delete but will do our best to keep a copy of
the deleted files in local device trash bin

Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2025-10-07 08:51:02 +02:00
Matthieu Gallien
c4b84a9b30 feat(conflict): use local device trashbin for delete/new conflicts
try to use local device trash bin when deleting local items during
automated handling of a new/delete conflict

we still always choose the delete but will do our best to keep a copy of
the deleted files in local device trash bin

Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2025-10-07 08:51:02 +02:00
Matthieu Gallien
9d7f8653bc chore: adjust logs level and clazy warnings for permanent deletions
Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2025-10-07 08:51:02 +02:00
Matthieu Gallien
6cdbf8acc5 feat(propagation): flexible way to configure special actions on items
will enable to perform optional special actions while execution a
propagation action

will enable to move to trash bin or disable server trash bin when
appropriate

Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2025-10-07 08:51:02 +02:00
Matthieu Gallien
1ec98ee46e fix: folder delete/new conflict will be "delete"
should enable someone to delete a folder even if there is a new/delete
conflict happening for some other users

Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2025-10-07 08:51:02 +02:00
Matthieu Gallien
491e7cd55c chore: fix all occurence of range-loop-detach clazy warning
see https://github.com/KDE/clazy/blob/master/docs/checks/README-range-loop-detach.md

Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2025-10-07 08:51:02 +02:00
Nextcloud bot
0c97accb5c
fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-10-07 03:08:36 +00:00
Iva Horn
37b204af24
Merge pull request #8838 from nextcloud/run-client-from-xcode
Run Developer Build from Xcode
2025-10-06 19:39:17 +02:00
Iva Horn
f219ad6c63 fix: Updated NextcloudFileProviderKit to 3.2.1
This integrates the fixes for the file locking.

Signed-off-by: Iva Horn <iva.horn@icloud.com>
2025-10-06 18:31:50 +02:00
Iva Horn
2adb172fdb fix: Updated NextcloudFileProviderKit reference.
Signed-off-by: Iva Horn <iva.horn@icloud.com>
2025-10-06 18:31:50 +02:00
Iva Horn
f6e69b6be8 fix: Improved one logging call.
Signed-off-by: Iva Horn <iva.horn@icloud.com>
2025-10-06 18:31:50 +02:00
Iva Horn
0bbb377c23 fix: Source mapping for extensions in Xcode debugger.
LLVM could not resolve breakpoints set in Xcode. That requires dSYM files to work. I changed the build setting to produce these files for all targets and debug configuration builds. Now it is possible to also conveniently break in Xcode when attaching to the extension processes.

Signed-off-by: Iva Horn <iva.horn@icloud.com>
2025-10-06 18:31:50 +02:00
Iva Horn
0d953425ca feat: Nextcloud Developer Build from Integration Project
Introduced a new target with external build system in the NextcloudIntegration Xcode project to conveniently run mac-crafter from Xcode.

Signed-off-by: Iva Horn <iva.horn@icloud.com>
2025-10-06 18:31:50 +02:00
Matthieu Gallien
d9fc4e8ead
Merge pull request #6810 from nextcloud/bugfix/upstream-ecm
Use upstream Extra CMake Modules
2025-10-06 18:29:18 +02:00
Matthieu Gallien
92d4dcdb03
chore(ci): use a docker linux image for CI which has extra-cmake-modules
Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2025-10-06 16:46:51 +02:00
Matthieu Gallien
41d03f61e3 fix: proper use of ecm_add_app_icon
remove duplicated call to ecm_add_app_icon

no longer fails to build on Windows due to a missing .ico file

Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2025-10-06 16:46:18 +02:00
Matthieu Gallien
4ebf5ae806 fix(test/csync): make sure the test executable can be found to be run
Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2025-10-06 16:46:18 +02:00
Matthieu Gallien
1f812b54f5 fix(reuse): remove no longer needed BSD-2 license
Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2025-10-06 16:46:18 +02:00
Claudio Cambra
5e9b74594b fix: Update upstream URL for Extra CMake Modules
Signed-off-by: Claudio Cambra <claudio.cambra@nextcloud.com>
2025-10-06 16:46:18 +02:00
Claudio Cambra
f88098ecdb fix: Fix ecm_add_app_icon calls
Signed-off-by: Claudio Cambra <claudio.cambra@nextcloud.com>
2025-10-06 16:46:18 +02:00
Claudio Cambra
3b452d9a72 fix: Mark nextcloudcmd as non gui executable
Signed-off-by: Claudio Cambra <claudio.cambra@nextcloud.com>
2025-10-06 16:46:18 +02:00
Claudio Cambra
87275ec495 feat: Use upstream extra cmake modules
Signed-off-by: Claudio Cambra <claudio.cambra@nextcloud.com>
2025-10-06 16:46:18 +02:00
Elsie Hupp
6b80f6dd0d
Merge pull request #8834 from elsiehupp/maximize-settings-window
Allow Maximizing Settings Window
2025-10-06 09:26:02 -04:00
Elsie Hupp
cc9b32a880
Merge branch 'master' into maximize-settings-window 2025-10-06 08:45:07 -04:00
Matthieu Gallien
a2c2dadcbe
Merge pull request #8836 from nextcloud/bugfix/unbreakSyncingPublicShareLinks
Bugfix/unbreak syncing public share links
2025-10-06 11:40:38 +02:00
Matthieu Gallien
e4fc075c75 fix: do not show share ID for sidebar entries of public share links
we show our sync folders in the files manager sidebar

replace the share token (ID ?) by "Public Share Link"

Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2025-10-06 11:00:52 +02:00
Matthieu Gallien
4067a0dae5 fix: fix typo in log message
Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2025-10-06 11:00:34 +02:00
Matthieu Gallien
3f08e6ac8b fix(sync/publicShareLinks): fix public share link detection
Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2025-10-06 09:31:48 +02:00
Nextcloud bot
c2e909efde
fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-10-06 03:06:17 +00:00
Nextcloud bot
5e7bcaedee
fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-10-05 03:05:51 +00:00
Elsie Hupp
966067391a
Merge branch 'master' into maximize-settings-window 2025-10-04 12:38:42 -04:00
Elsie Hupp
90d1e409f6
fix: allow settings window to enter full screen or be minimized
Signed-off-by: Elsie Hupp <github@elsiehupp.com>
2025-10-04 12:35:35 -04:00
Nextcloud bot
6381e56b1d
fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-10-04 03:05:05 +00:00
Matthieu Gallien
bcde65baa6
chore: prepare for 4.1 release
Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
2025-10-03 10:26:53 +02:00
185 changed files with 37889 additions and 34845 deletions

View File

@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- uses: cpp-linter/cpp-linter-action@7dacd91f6a008a7c714bba700f4d08468a1eb428 # v2.16.4
- uses: cpp-linter/cpp-linter-action@b7fbdde0f6776f478f4d8867c2b746077fa7ea89 # v2.16.5
id: linter
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -8,7 +8,7 @@ jobs:
build:
name: Linux Clang compilation and tests
runs-on: ubuntu-latest
container: ghcr.io/nextcloud/continuous-integration-client-qt6:client-trixie-6.8.2-1
container: ghcr.io/nextcloud/continuous-integration-client-qt6:client-trixie-6.8.2-2
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:

View File

@ -8,7 +8,7 @@ jobs:
build:
name: Linux GCC compilation and tests
runs-on: ubuntu-latest
container: ghcr.io/nextcloud/continuous-integration-client-qt6:client-trixie-6.8.2-1
container: ghcr.io/nextcloud/continuous-integration-client-qt6:client-trixie-6.8.2-2
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:

View File

@ -35,8 +35,8 @@ jobs:
- name: List Xcode installations
run: sudo ls -1 /Applications | grep "Xcode"
- name: Select Xcode 16.2
run: sudo xcode-select -s /Applications/Xcode_16.2.app/Contents/Developer
- name: Select Xcode 16.3
run: sudo xcode-select -s /Applications/Xcode_16.3.app/Contents/Developer
- name: Set up Python ${{ inputs.PYTHON_VERSION }}
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0

View File

@ -43,9 +43,9 @@ jobs:
if: steps.cache-craft-restore.outputs.cache-hit != 'true'
run: sudo ls -1 /Applications | grep "Xcode"
- name: Select Xcode 16.2
- name: Select Xcode 16.3
if: steps.cache-craft-restore.outputs.cache-hit != 'true'
run: sudo xcode-select -s /Applications/Xcode_16.2.app/Contents/Developer
run: sudo xcode-select -s /Applications/Xcode_16.3.app/Contents/Developer
- name: Set up Python ${{ env.PYTHON_VERSION }}
if: steps.cache-craft-restore.outputs.cache-hit != 'true'

View File

@ -24,4 +24,4 @@ jobs:
persist-credentials: false
- name: REUSE Compliance Check
uses: fsfe/reuse-action@bb774aa972c2a89ff34781233d275075cbddf542 # v5.0.0
uses: fsfe/reuse-action@676e2d560c9a403aa252096d99fcab3e1132b0f5 # v6.0.0

View File

@ -17,7 +17,7 @@ jobs:
issues: write
steps:
- uses: actions/stale@3a9db7e6a41a89f618792c92c0e97cc736e1b13f # v10.0.0
- uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0
with:
operations-per-run: 1500
days-before-stale: 28

6
.gitignore vendored
View File

@ -195,3 +195,9 @@ convert.exe
*state-*.png
theme.qrc
*.AppImage
# Xcode Build Artifacts
DerivedData
# Swift Package Manager Build Artifacts
.swiftpm

View File

@ -82,7 +82,20 @@ if (NOT DEFINED PACKAGE)
set(PACKAGE "${LINUX_PACKAGE_SHORTNAME}-client")
endif()
set( CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules )
set(APPLE_SUPPRESS_X11_WARNING ON)
find_package(ECM 6.0.0 REQUIRED NO_MODULE)
set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://invent.kde.org/frameworks/extra-cmake-modules")
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules ${ECM_MODULE_PATH} ${CMAKE_MODULE_PATH})
include(KDEInstallDirs)
include(KDECMakeSettings)
include(ECMMarkNonGuiExecutable)
include(ECMSetupVersion)
#include(KDECompilerSettings NO_POLICY_SCOPE)
include(ECMEnableSanitizers)
include(ECMCoverageOption)

View File

@ -1,9 +0,0 @@
Copyright (c) <year> <owner>
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -36,6 +36,7 @@ If you find any bugs or have any suggestion for improvement, please
> [!TIP]
> For building the client on macOS we have a tool called `mac-crafter`.
> You will find more information about it in [its dedicated README](admin/osx/mac-crafter/README.md).
> Also, please note the [README in the NextcloudIntegration project](shell_integration/MacOSX/NextcloudIntegration/README.md) which provides an even more convenient way to work on and build the desktop client on macOS by using Xcode.
#### 1. 🚀 Set up your local development environment

View File

@ -29,6 +29,16 @@ precedence = "aggregate"
SPDX-FileCopyrightText = "2014 ownCloud GmbH"
SPDX-License-Identifier = "GPL-2.0-or-later"
[[annotations]]
path = [
"admin/osx/TransifexStringCatalogSanitizer/Package.swift",
"admin/osx/TransifexStringCatalogSanitizer/Package.resolved",
"admin/osx/TransifexStringCatalogSanitizer/README.md",
]
precedence = "aggregate"
SPDX-FileCopyrightText = "2025 Nextcloud GmbH and Nextcloud contributors"
SPDX-License-Identifier = "GPL-2.0-or-later"
[[annotations]]
path = ["admin/osx/mac-crafter/Package.resolved", "shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved"]
precedence = "aggregate"
@ -42,13 +52,21 @@ SPDX-FileCopyrightText = "2014 ownCloud GmbH, 2022 Nextcloud GmbH and Nextcloud
SPDX-License-Identifier = "GPL-2.0-or-later"
[[annotations]]
path = ["shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj", "shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.xcworkspace/contents.xcworkspacedata", "shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist", "shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/xcshareddata/xcschemes/FinderSyncExt.xcscheme"]
path = [
"shell_integration/MacOSX/NextcloudIntegration/.gitignore",
"shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj",
"shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
"shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
"shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/xcshareddata/xcschemes/FinderSyncExt.xcscheme",
"shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/xcshareddata/xcschemes/NextcloudDev.xcscheme",
"shell_integration/MacOSX/NextcloudIntegration/README.md"
]
precedence = "aggregate"
SPDX-FileCopyrightText = "2025 Nextcloud GmbH and Nextcloud contributors"
SPDX-License-Identifier = "GPL-2.0-or-later"
[[annotations]]
path = ["shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/Localizable.xcstrings", "shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Localizable.xcstrings"]
path = ["shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/Localizable.xcstrings", "shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Localizable.xcstrings", "shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/Assets.xcassets/*", "shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/Assets.xcassets/FileProviderDomainSymbol.symbolset/*", "shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Assets.xcassets/*", "shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/Assets.xcassets/FileProviderDomainSymbol.symbolset/*"]
precedence = "aggregate"
SPDX-FileCopyrightText = "2015 ownCloud GmbH, 2022 Nextcloud GmbH and Nextcloud contributors"
SPDX-License-Identifier = "GPL-2.0-or-later"

View File

@ -5,20 +5,20 @@
# ------------------------------------
# Version information
# ------------------------------------
set(MIRALL_VERSION_MAJOR 3)
set(MIRALL_VERSION_MINOR 17)
set(MIRALL_VERSION_MAJOR 4)
set(MIRALL_VERSION_MINOR 0)
set(MIRALL_VERSION_PATCH 50)
set(MIRALL_VERSION_YEAR 2025)
set(MIRALL_SOVERSION 0)
set(MIRALL_PREVERSION_HUMAN "3.18.0 alpha") # For preversions where PATCH>=50. Use version + alpha, rc1, rc2, etc.
set(MIRALL_PREVERSION_HUMAN "4.1.0 alpha") # For preversions where PATCH>=50. Use version + alpha, rc1, rc2, etc.
set(NCEXT_BUILD_NUM 47)
set(NCEXT_VERSION 3,0,0,${NCEXT_BUILD_NUM})
# ------------------------------------
# Minimum supported server versions
# According to: https://docs.nextcloud.com/server/latest/admin_manual/release_schedule.html
# According to: https://github.com/nextcloud/server/wiki/Maintenance-and-Release-Schedule
# ------------------------------------
set(NEXTCLOUD_SERVER_VERSION_MIN_SUPPORTED_MAJOR 18)
set(NEXTCLOUD_SERVER_VERSION_MIN_SUPPORTED_MAJOR 20)
set(NEXTCLOUD_SERVER_VERSION_MIN_SUPPORTED_MINOR 0)
set(NEXTCLOUD_SERVER_VERSION_MIN_SUPPORTED_PATCH 0)

View File

@ -0,0 +1,10 @@
# SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
# SPDX-License-Identifier: GPL-2.0-or-later
.DS_Store
/.build
/Packages
xcuserdata/
DerivedData/
.swiftpm
.netrc

View File

@ -0,0 +1,15 @@
{
"originHash" : "29c76d8c60e24badae4a42909eb97c07dfa3a8dbfe5940de1e06ec95358c8fda",
"pins" : [
{
"identity" : "swift-argument-parser",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-argument-parser.git",
"state" : {
"revision" : "309a47b2b1d9b5e991f36961c983ecec72275be3",
"version" : "1.6.1"
}
}
],
"version" : 3
}

View File

@ -0,0 +1,21 @@
// swift-tools-version: 6.0
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "TransifexStringCatalogSanitizer",
dependencies: [
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.2.0"),
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
// Targets can depend on other targets in this package and products from dependencies.
.executableTarget(
name: "TransifexStringCatalogSanitizer",
dependencies: [
.product(name: "ArgumentParser", package: "swift-argument-parser"),
]
),
]
)

View File

@ -0,0 +1,15 @@
# Transifex String Catalog Sanitizer
Sanitize Xcode string catalogs which were pulled from a Transifex online resource.
## Usage
See the integrated help for up to date reference.
```sh
swift run TransifexStringCatalogSanitizer --help
```
## Development
This Swift command-line utility can be easily run and debugged from Xcode.

View File

@ -0,0 +1,83 @@
// SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
// SPDX-License-Identifier: GPL-2.0-or-later
import ArgumentParser
import Foundation
@main
struct TransifexStringCatalogSanitizer: ParsableCommand {
@Argument(help: "The string catalog file to sanitize.")
var input: String
mutating func run() throws {
let url = URL(fileURLWithPath: input)
let data = try Data(contentsOf: url)
guard var root = try JSONSerialization.jsonObject(with: data) as? [String: Any] else {
throw TransifexStringCatalogSanitizerError.jsonObject
}
guard var strings = root["strings"] as? [String: Any] else {
throw TransifexStringCatalogSanitizerError.missingStrings
}
try sanitizeStrings(&strings)
// Update the root with modified strings
root["strings"] = strings
// Write the processed data back to the original file
let processedData = try JSONSerialization.data(withJSONObject: root, options: [.prettyPrinted, .sortedKeys])
try processedData.write(to: url)
}
private func sanitizeStrings(_ strings: inout [String: Any]) throws {
for key in strings.keys.sorted() {
print("💬 \"\(key)\"")
guard var string = strings[key] as? [String: Any] else {
throw TransifexStringCatalogSanitizerError.missingString
}
guard var localizations = string["localizations"] as? [String: Any] else {
throw TransifexStringCatalogSanitizerError.missingLocalizations
}
try sanitizeLocalizations(&localizations)
// Update the string with modified localizations
string["localizations"] = localizations
strings[key] = string
}
}
private func sanitizeLocalizations(_ localizations: inout [String: Any]) throws {
var localizationsToRemove: [String] = []
for localeCode in localizations.keys.sorted() {
guard let localization = localizations[localeCode] as? [String: Any] else {
throw TransifexStringCatalogSanitizerError.missingLocalization
}
guard let stringUnit = localization["stringUnit"] as? [String: Any] else {
throw TransifexStringCatalogSanitizerError.missingStringUnit
}
guard let value = stringUnit["value"] as? String else {
throw TransifexStringCatalogSanitizerError.missingValue
}
if value.isEmpty {
print("\t\(localeCode): empty, will be removed")
localizationsToRemove.append(localeCode)
} else {
print("\t\(localeCode): \"\(value)\"")
}
}
// Remove empty localizations
for localeCode in localizationsToRemove {
localizations.removeValue(forKey: localeCode)
}
}
}

View File

@ -0,0 +1,12 @@
// SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
// SPDX-License-Identifier: GPL-2.0-or-later
enum TransifexStringCatalogSanitizerError: Error {
case jsonObject
case missingLocalization
case missingLocalizations
case missingString
case missingStrings
case missingStringUnit
case missingValue
}

View File

@ -19,6 +19,7 @@ endif(CMAKE_COMPILER_IS_GNUCC AND NOT MINGW)
function (ADD_CMOCKA_TEST _testName _testSource)
add_executable(${_testName} ${_testSource})
set_target_properties(${_testName} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${BIN_OUTPUT_DIRECTORY})
target_link_libraries(${_testName} ${ARGN})
add_test(${_testName} ${CMAKE_CURRENT_BINARY_DIR}/${_testName})
add_test(${_testName} ${BIN_OUTPUT_DIRECTORY}/${_testName})
endfunction (ADD_CMOCKA_TEST)

View File

@ -1,422 +0,0 @@
#.rst:
# ECMAddAppIcon
# -------------
#
# Add icons to executable files and packages.
#
# ::
#
# ecm_add_app_icon(<sources_var>
# ICONS <icon> [<icon> [...]]
# [SIDEBAR_ICONS <icon> [<icon> [...]] # Since 5.49
# [OUTFILE_BASENAME <name>]) # Since 5.49
# )
#
# The given icons, whose names must match the pattern::
#
# <size>-<other_text>.png
#
# will be added to the executable target whose sources are specified by
# ``<sources_var>`` on platforms that support it (Windows and Mac OS X).
# Other icon files are ignored but on Mac SVG files can be supported and
# it is thus possible to mix those with png files in a single macro call.
#
# ``<size>`` is a numeric pixel size (typically 16, 32, 48, 64, 128 or 256).
# ``<other_text>`` can be any other text. See the platform notes below for any
# recommendations about icon sizes.
#
# ``SIDEBAR_ICONS`` can be used to add Mac OS X sidebar
# icons to the generated iconset. They are used when a folder monitored by the
# application is dragged into Finder's sidebar. Since 5.49.
#
# ``OUTFILE_BASENAME`` will be used as the basename for the icon file. If
# you specify it, the icon file will be called ``<OUTFILE_BASENAME>.icns`` on Mac OS X
# and ``<OUTFILE_BASENAME>.ico`` on Windows. If you don't specify it, it defaults
# to ``<sources_var>.<ext>``. Since 5.49.
#
#
# Windows notes
# * Icons are compiled into the executable using a resource file.
# * Icons may not show up in Windows Explorer if the executable
# target does not have the ``WIN32_EXECUTABLE`` property set.
# * One of the tools png2ico (See :find-module:`FindPng2Ico`) or
# icotool (see :find-module:`FindIcoTool`) is required.
# * Supported sizes: 16, 20, 24, 32, 40, 48, 64, 128, 256, 512 and 1024.
#
# Mac OS X notes
# * The executable target must have the ``MACOSX_BUNDLE`` property set.
# * Icons are added to the bundle.
# * If the ksvg2icns tool from KIconThemes is available, .svg and .svgz
# files are accepted; the first that is converted successfully to .icns
# will provide the application icon. SVG files are ignored otherwise.
# * The tool iconutil (provided by Apple) is required for bitmap icons.
# * Supported sizes: 16, 32, 64, 128, 256 (and 512, 1024 after OS X 10.9).
# * At least a 128x128px (or an SVG) icon is required.
# * Larger sizes are automatically used to substitute for smaller sizes on
# "Retina" (high-resolution) displays. For example, a 32px icon, if
# provided, will be used as a 32px icon on standard-resolution displays,
# and as a 16px-equivalent icon (with an "@2x" tag) on high-resolution
# displays. That is why you should provide 64px and 1024px icons although
# they are not supported anymore directly. Instead they will be used as
# 32px@2x and 512px@2x. ksvg2icns handles this internally.
# * This function sets the ``MACOSX_BUNDLE_ICON_FILE`` variable to the name
# of the generated icns file, so that it will be used as the
# ``MACOSX_BUNDLE_ICON_FILE`` target property when you call
# ``add_executable``.
# * Sidebar icons should typically provided in 16, 32, 64, 128 and 256px.
#
# Since 1.7.0.
#=============================================================================
# SPDX-FileCopyrightText: 2014 Alex Merry <alex.merry@kde.org>
# SPDX-FileCopyrightText: 2014 Ralf Habacker <ralf.habacker@freenet.de>
# SPDX-FileCopyrightText: 2006-2009 Alexander Neundorf, <neundorf@kde.org>
# SPDX-FileCopyrightText: 2006, 2007, Laurent Montel, <montel@kde.org>
# SPDX-FileCopyrightText: 2007 Matthias Kretz <kretz@kde.org>
# SPDX-License-Identifier: BSD-3-Clause
#
# Copyright 2014 Alex Merry <alex.merry@kde.org>
# Copyright 2014 Ralf Habacker <ralf.habacker@freenet.de>
# Copyright 2006-2009 Alexander Neundorf, <neundorf@kde.org>
# Copyright 2006, 2007, Laurent Montel, <montel@kde.org>
# Copyright 2007 Matthias Kretz <kretz@kde.org>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. The name of the author may not be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
include(CMakeParseArguments)
function(ecm_add_app_icon appsources)
set(options)
set(oneValueArgs OUTFILE_BASENAME ICON_INDEX DO_NOT_GENERATE_RC_FILE)
set(multiValueArgs ICONS SIDEBAR_ICONS RC_DEPENDENCIES)
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if (ARG_DO_NOT_GENERATE_RC_FILE)
set (_do_not_generate_rc_file TRUE)
else()
set (_do_not_generate_rc_file FALSE)
endif()
if(NOT ARG_ICONS)
message(FATAL_ERROR "No ICONS argument given to ecm_add_app_icon")
endif()
if(ARG_UNPARSED_ARGUMENTS)
message(FATAL_ERROR "Unexpected arguments to ecm_add_app_icon: ${ARG_UNPARSED_ARGUMENTS}")
endif()
if(APPLE)
find_program(KSVG2ICNS NAMES ksvg2icns)
foreach(icon ${ARG_ICONS})
get_filename_component(icon_full ${icon} ABSOLUTE)
get_filename_component(icon_type ${icon_full} EXT)
# do we have ksvg2icns in the path and did we receive an svg (or compressed svg) icon?
if(KSVG2ICNS AND (${icon_type} STREQUAL ".svg" OR ${icon_type} STREQUAL ".svgz"))
# convert the svg icon to an icon resource
execute_process(COMMAND ${KSVG2ICNS} "${icon_full}"
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} RESULT_VARIABLE KSVG2ICNS_ERROR)
if(${KSVG2ICNS_ERROR})
message(AUTHOR_WARNING "ksvg2icns could not generate an OS X application icon from ${icon}")
else()
# install the icns file we just created
get_filename_component(icon_name ${icon_full} NAME_WE)
set(MACOSX_BUNDLE_ICON_FILE ${icon_name}.icns PARENT_SCOPE)
set(${appsources} "${${appsources}};${CMAKE_CURRENT_BINARY_DIR}/${icon_name}.icns" PARENT_SCOPE)
set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/${icon_name}.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
# we're done now
return()
endif()
endif()
endforeach()
endif()
if (WIN32)
_ecm_add_app_icon_categorize_icons("${ARG_ICONS}" "icons" "16;20;24;32;40;48;64;128;256;512;1024")
else()
_ecm_add_app_icon_categorize_icons("${ARG_ICONS}" "icons" "16;24;32;48;64;128;256;512;1024")
endif()
if(ARG_SIDEBAR_ICONS)
_ecm_add_app_icon_categorize_icons("${ARG_SIDEBAR_ICONS}" "sidebar_icons" "16;32;64;128;256")
endif()
set(mac_icons
# Icons: https://developer.apple.com/library/content/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Optimizing/Optimizing.html#//apple_ref/doc/uid/TP40012302-CH7-SW4
${icons_at_16px}
${icons_at_32px}
${icons_at_64px}
${icons_at_128px}
${icons_at_256px}
${icons_at_512px}
${icons_at_1024px})
set(mac_sidebar_icons
# Sidebar Icons: https://developer.apple.com/library/content/documentation/General/Conceptual/ExtensibilityPG/Finder.html#//apple_ref/doc/uid/TP40014214-CH15-SW15
${sidebar_icons_at_16px}
${sidebar_icons_at_32px}
${sidebar_icons_at_64px}
${sidebar_icons_at_128px}
${sidebar_icons_at_256px})
if (NOT (mac_icons OR mac_sidebar_icons))
message(AUTHOR_WARNING "No icons suitable for use on macOS provided")
endif()
set(windows_icons ${icons_at_16px}
${icons_at_20px}
${icons_at_24px}
${icons_at_32px}
${icons_at_40px}
${icons_at_48px}
${icons_at_64px}
${icons_at_128px}
${icons_at_256px}
${icons_at_512px}
${icons_at_1024px})
if (NOT (windows_icons))
message(AUTHOR_WARNING "No icons suitable for use on Windows provided")
endif()
if (ARG_OUTFILE_BASENAME)
set (_outfilebasename "${ARG_OUTFILE_BASENAME}")
else()
set (_outfilebasename "${appsources}")
endif()
set (_outfilename "${CMAKE_CURRENT_BINARY_DIR}/${_outfilebasename}")
if (WIN32 AND windows_icons)
set(saved_CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}")
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_FIND_MODULE_DIR})
find_package(Png2Ico)
find_package(IcoTool)
set(CMAKE_MODULE_PATH "${saved_CMAKE_MODULE_PATH}")
function(create_windows_icon_and_rc command args deps)
add_custom_command(
OUTPUT "${_outfilename}.ico"
COMMAND ${command}
ARGS ${args}
DEPENDS ${deps}
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
)
if (NOT _do_not_generate_rc_file)
# this bit's a little hacky to make the dependency stuff work
file(WRITE "${_outfilename}.rc.in" "IDI_ICON${ARG_ICON_INDEX} ICON DISCARDABLE \"${_outfilename}.ico\"\n")
add_custom_command(
OUTPUT "${_outfilename}.rc"
COMMAND ${CMAKE_COMMAND}
ARGS -E copy "${_outfilename}.rc.in" "${_outfilename}.rc"
DEPENDS ${ARG_RC_DEPENDENCIES} "${_outfilename}.ico"
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
)
endif()
endfunction()
if (IcoTool_FOUND)
list(APPEND icotool_args "-c" "-o" "${_outfilename}.ico")
# According to https://stackoverflow.com/a/40851713/2886832
# Windows always chooses the first icon above 255px, all other ones will be ignored
set(maxSize 0)
foreach(size 256 512 1024)
if(icons_at_${size}px)
set(maxSize "${size}")
endif()
endforeach()
foreach(size 16 20 24 32 40 48 64 128 ${maxSize})
if(NOT icons_at_${size}px)
continue()
endif()
set(icotool_icon_arg "")
if(size STREQUAL "${maxSize}")
# maxSize icon needs to be included as raw png
list(APPEND icotool_args "-r")
endif()
foreach(icon ${icons_at_${size}px})
list(APPEND icotool_args "${icons_at_${size}px}")
endforeach()
endforeach()
create_windows_icon_and_rc(IcoTool::IcoTool "${icotool_args}" "${windows_icons_modern}")
set(${appsources} "${${appsources}};${_outfilename}.rc" PARENT_SCOPE)
# standard png2ico has no rcfile argument
# NOTE: We generally use https://github.com/hiiamok/png2ImageMagickICO
# or similar on windows, which is why we provide resolutions >= 256px here.
# Standard png2ico will fail with this.
elseif(Png2Ico_FOUND AND NOT Png2Ico_HAS_RCFILE_ARGUMENT AND windows_icons)
set(png2ico_args)
list(APPEND png2ico_args "${_outfilename}.ico")
list(APPEND png2ico_args "${windows_icons}")
create_windows_icon_and_rc(Png2Ico::Png2Ico "${png2ico_args}" "${windows_icons}")
set(${appsources} "${${appsources}};${_outfilename}.rc" PARENT_SCOPE)
# png2ico from kdewin provides rcfile argument
elseif(Png2Ico_FOUND AND windows_icons)
add_custom_command(
OUTPUT "${_outfilename}.rc" "${_outfilename}.ico"
COMMAND Png2Ico::Png2Ico
ARGS
--rcfile "${_outfilename}.rc"
"${_outfilename}.ico"
${windows_icons}
DEPENDS ${windows_icons}
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
)
set(${appsources} "${${appsources}};${_outfilename}.rc" PARENT_SCOPE)
# else none of the supported tools was found
else()
message(WARNING "Unable to find the png2ico or icotool utilities or icons in matching sizes - application will not have an application icon!")
endif()
elseif (APPLE AND (mac_icons OR mac_sidebar_icons))
# first generate .iconset directory structure, then convert to .icns format using the Mac OS X "iconutil" utility,
# to create retina compatible icon, you need png source files in pixel resolution 16x16, 32x32, 64x64, 128x128,
# 256x256, 512x512, 1024x1024
find_program(ICONUTIL_EXECUTABLE NAMES iconutil)
if (ICONUTIL_EXECUTABLE)
add_custom_command(
OUTPUT "${_outfilename}.iconset"
COMMAND ${CMAKE_COMMAND}
ARGS -E make_directory "${_outfilename}.iconset"
)
set(iconset_icons)
macro(copy_icon filename sizename type)
add_custom_command(
OUTPUT "${_outfilename}.iconset/${type}_${sizename}.png"
COMMAND ${CMAKE_COMMAND}
ARGS -E copy
"${filename}"
"${_outfilename}.iconset/${type}_${sizename}.png"
DEPENDS
"${_outfilename}.iconset"
"${filename}"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
)
list(APPEND iconset_icons
"${_outfilename}.iconset/${type}_${sizename}.png")
endmacro()
# List of supported sizes and filenames taken from:
# https://developer.apple.com/library/content/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Optimizing/Optimizing.html#//apple_ref/doc/uid/TP40012302-CH7-SW4
foreach(size 16 32 128 256 512)
math(EXPR double_size "2 * ${size}")
foreach(file ${icons_at_${size}px})
copy_icon("${file}" "${size}x${size}" "icon")
endforeach()
foreach(file ${icons_at_${double_size}px})
copy_icon("${file}" "${size}x${size}@2x" "icon")
endforeach()
endforeach()
# List of supported sizes and filenames taken from:
# https://developer.apple.com/library/content/documentation/General/Conceptual/ExtensibilityPG/Finder.html#//apple_ref/doc/uid/TP40014214-CH15-SW15
foreach(file ${sidebar_icons_at_16px})
copy_icon("${file}" "16x16" "sidebar")
endforeach()
foreach(file ${sidebar_icons_at_32px})
copy_icon("${file}" "16x16@2x" "sidebar")
endforeach()
foreach(file ${sidebar_icons_at_32px})
copy_icon("${file}" "18x18" "sidebar")
endforeach()
foreach(file ${sidebar_icons_at_64px})
copy_icon("${file}" "18x18@2x" "sidebar")
endforeach()
foreach(file ${sidebar_icons_at_128px})
copy_icon("${file}" "32x32" "sidebar")
endforeach()
foreach(file ${sidebar_icons_at_256px})
copy_icon("${file}" "32x32@2x" "sidebar")
endforeach()
# generate .icns icon file
add_custom_command(
OUTPUT "${_outfilename}.icns"
COMMAND ${ICONUTIL_EXECUTABLE}
ARGS
--convert icns
--output "${_outfilename}.icns"
"${_outfilename}.iconset"
DEPENDS "${iconset_icons}"
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
)
# This will register the icon into the bundle
set(MACOSX_BUNDLE_ICON_FILE "${_outfilebasename}.icns" PARENT_SCOPE)
# Append the icns file to the sources list so it will be a dependency to the
# main target
set(${appsources} "${${appsources}};${_outfilename}.icns" PARENT_SCOPE)
# Install the icon into the Resources dir in the bundle
set_source_files_properties("${_outfilename}.icns" PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
else()
message(STATUS "Unable to find the iconutil utility - application will not have an application icon!")
endif()
endif()
endfunction()
macro(_ecm_add_app_icon_categorize_icons icons type known_sizes)
set(_${type}_known_sizes)
foreach(size ${known_sizes})
set(${type}_at_${size}px)
list(APPEND _${type}_known_sizes ${size})
endforeach()
foreach(icon ${icons})
get_filename_component(icon_full ${icon} ABSOLUTE)
if (NOT EXISTS "${icon_full}")
message(AUTHOR_WARNING "${icon_full} does not exist, ignoring")
else()
get_filename_component(icon_name ${icon} NAME)
string(REGEX MATCH "([0-9]+)\\-[^/]+\\.([a-z]+)$"
_dummy "${icon_name}")
set(size "${CMAKE_MATCH_1}")
set(ext "${CMAKE_MATCH_2}")
if (NOT (ext STREQUAL "svg" OR ext STREQUAL "svgz"))
if (NOT size)
message(AUTHOR_WARNING "${icon_full} is not named correctly for ecm_add_app_icon - ignoring")
elseif (NOT ext STREQUAL "png")
message(AUTHOR_WARNING "${icon_full} is not a png file - ignoring")
else()
list(FIND _${type}_known_sizes ${size} offset)
if (offset GREATER -1)
list(APPEND ${type}_at_${size}px "${icon_full}")
else()
message(STATUS "not found ${type}_at_${size}px ${icon_full}")
endif()
endif()
endif()
endif()
endforeach()
endmacro()

View File

@ -1,29 +0,0 @@
# SPDX-FileCopyrightText: 2014 Aleix Pol Gonzalez <aleixpol@kde.org>
#
# SPDX-License-Identifier: BSD-3-Clause
#[=======================================================================[.rst:
ECMCoverageOption
--------------------
Allow users to easily enable GCov code coverage support.
Code coverage allows you to check how much of your codebase is covered by
your tests. This module makes it easy to build with support for
`GCov <https://gcc.gnu.org/onlinedocs/gcc/Gcov.html>`_.
When this module is included, a ``BUILD_COVERAGE`` option is added (default
OFF). Turning this option on enables GCC's coverage instrumentation, and
links against ``libgcov``.
Note that this will probably break the build if you are not using GCC.
Since 1.3.0.
#]=======================================================================]
option(BUILD_COVERAGE "Build the project with gcov support" OFF)
if(BUILD_COVERAGE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lgcov")
endif()

View File

@ -1,169 +0,0 @@
# SPDX-FileCopyrightText: 2014 Mathieu Tarral <mathieu.tarral@gmail.com>
#
# SPDX-License-Identifier: BSD-3-Clause
#[=======================================================================[.rst:
ECMEnableSanitizers
-------------------
Enable compiler sanitizer flags.
The following sanitizers are supported:
- Address Sanitizer
- Memory Sanitizer
- Thread Sanitizer
- Leak Sanitizer
- Undefined Behaviour Sanitizer
All of them are implemented in Clang, depending on your version, and
there is an work in progress in GCC, where some of them are currently
implemented.
This module will check your current compiler version to see if it
supports the sanitizers that you want to enable
Usage
=====
Simply add::
include(ECMEnableSanitizers)
to your ``CMakeLists.txt``. Note that this module is included in
KDECompilerSettings, so projects using that module do not need to also
include this one.
The sanitizers are not enabled by default. Instead, you must set
``ECM_ENABLE_SANITIZERS`` (either in your ``CMakeLists.txt`` or on the
command line) to a semicolon-separated list of sanitizers you wish to enable.
The options are:
- address
- memory
- thread
- leak
- undefined
- fuzzer
The sanitizers "address", "memory" and "thread" are mutually exclusive. You
cannot enable two of them in the same build.
"leak" requires the "address" sanitizer.
.. note::
To reduce the overhead induced by the instrumentation of the sanitizers, it
is advised to enable compiler optimizations (``-O1`` or higher).
Example
=======
This is an example of usage::
mkdir build
cd build
cmake -DECM_ENABLE_SANITIZERS='address;leak;undefined' ..
.. note::
Most of the sanitizers will require Clang. To enable it, use::
-DCMAKE_CXX_COMPILER=clang++
Since 1.3.0.
#]=======================================================================]
# MACRO check_compiler_version
#-----------------------------
macro (check_compiler_version gcc_required_version clang_required_version msvc_required_version)
if (
(
CMAKE_CXX_COMPILER_ID MATCHES "GNU"
AND
CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${gcc_required_version}
)
OR
(
CMAKE_CXX_COMPILER_ID MATCHES "Clang"
AND
CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${clang_required_version}
)
OR
(
CMAKE_CXX_COMPILER_ID MATCHES "MSVC"
AND
CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${msvc_required_version}
)
)
# error !
message(FATAL_ERROR "You ask to enable the sanitizer ${CUR_SANITIZER},
but your compiler ${CMAKE_CXX_COMPILER_ID} version ${CMAKE_CXX_COMPILER_VERSION}
does not support it !
You should use at least GCC ${gcc_required_version}, Clang ${clang_required_version}
or MSVC ${msvc_required_version}
(99.99 means not implemented yet)")
endif ()
endmacro ()
# MACRO check_compiler_support
#------------------------------
macro (enable_sanitizer_flags sanitize_option)
if (${sanitize_option} MATCHES "address")
check_compiler_version("4.8" "3.1" "19.28")
if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
set(XSAN_COMPILE_FLAGS "-fsanitize=address")
else()
set(XSAN_COMPILE_FLAGS "-fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls")
set(XSAN_LINKER_FLAGS "-fsanitize=address")
endif()
elseif (${sanitize_option} MATCHES "thread")
check_compiler_version("4.8" "3.1" "99.99")
set(XSAN_COMPILE_FLAGS "-fsanitize=thread")
set(XSAN_LINKER_FLAGS "tsan")
elseif (${sanitize_option} MATCHES "memory")
check_compiler_version("99.99" "3.1" "99.99")
set(XSAN_COMPILE_FLAGS "-fsanitize=memory")
elseif (${sanitize_option} MATCHES "leak")
check_compiler_version("4.9" "3.4" "99.99")
set(XSAN_COMPILE_FLAGS "-fsanitize=leak")
set(XSAN_LINKER_FLAGS "lsan")
elseif (${sanitize_option} MATCHES "undefined")
check_compiler_version("4.9" "3.1" "99.99")
set(XSAN_COMPILE_FLAGS "-fsanitize=undefined -fno-omit-frame-pointer -fno-optimize-sibling-calls")
elseif (${sanitize_option} MATCHES "fuzzer")
check_compiler_version("99.99" "6.0" "99.99")
set(XSAN_COMPILE_FLAGS "-fsanitize=fuzzer")
else ()
message(FATAL_ERROR "Compiler sanitizer option \"${sanitize_option}\" not supported.")
endif ()
endmacro ()
if (ECM_ENABLE_SANITIZERS)
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
# for each element of the ECM_ENABLE_SANITIZERS list
foreach ( CUR_SANITIZER ${ECM_ENABLE_SANITIZERS} )
# lowercase filter
string(TOLOWER ${CUR_SANITIZER} CUR_SANITIZER)
# check option and enable appropriate flags
enable_sanitizer_flags ( ${CUR_SANITIZER} )
# TODO: GCC will not link pthread library if enabled ASan
if(CMAKE_C_COMPILER_ID MATCHES "Clang")
set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${XSAN_COMPILE_FLAGS}" )
link_libraries(${XSAN_LINKER_FLAGS})
endif()
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${XSAN_COMPILE_FLAGS}" )
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
link_libraries(${XSAN_LINKER_FLAGS})
endif()
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
string(REPLACE "-Wl,--no-undefined" "" CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}")
string(REPLACE "-Wl,--no-undefined" "" CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS}")
link_libraries(${XSAN_LINKER_FLAGS})
endif ()
endforeach()
else()
message(STATUS "Tried to enable sanitizers (-DECM_ENABLE_SANITIZERS=${ECM_ENABLE_SANITIZERS}), \
but compiler (${CMAKE_CXX_COMPILER_ID}) does not have sanitizer support")
endif()
endif()

View File

@ -1,300 +0,0 @@
# SPDX-FileCopyrightText: 2014 Alex Merry <alex.merry@kde.org>
# SPDX-License-Identifier: BSD-3-Clause
#
#.rst:
# ECMFindModuleHelpers
# --------------------
#
# Helper macros for find modules: ecm_find_package_version_check(),
# ecm_find_package_parse_components() and
# ecm_find_package_handle_library_components().
#
# ::
#
# ecm_find_package_version_check(<name>)
#
# Prints warnings if the CMake version or the project's required CMake version
# is older than that required by extra-cmake-modules.
#
# ::
#
# ecm_find_package_parse_components(<name>
# RESULT_VAR <variable>
# KNOWN_COMPONENTS <component1> [<component2> [...]]
# [SKIP_DEPENDENCY_HANDLING])
#
# This macro will populate <variable> with a list of components found in
# <name>_FIND_COMPONENTS, after checking that all those components are in the
# list of KNOWN_COMPONENTS; if there are any unknown components, it will print
# an error or warning (depending on the value of <name>_FIND_REQUIRED) and call
# return().
#
# The order of components in <variable> is guaranteed to match the order they
# are listed in the KNOWN_COMPONENTS argument.
#
# If SKIP_DEPENDENCY_HANDLING is not set, for each component the variable
# <name>_<component>_component_deps will be checked for dependent components.
# If <component> is listed in <name>_FIND_COMPONENTS, then all its (transitive)
# dependencies will also be added to <variable>.
#
# ::
#
# ecm_find_package_handle_library_components(<name>
# COMPONENTS <component> [<component> [...]]
# [SKIP_DEPENDENCY_HANDLING])
# [SKIP_PKG_CONFIG])
#
# Creates an imported library target for each component. The operation of this
# macro depends on the presence of a number of CMake variables.
#
# The <name>_<component>_lib variable should contain the name of this library,
# and <name>_<component>_header variable should contain the name of a header
# file associated with it (whatever relative path is normally passed to
# '#include'). <name>_<component>_header_subdir variable can be used to specify
# which subdirectory of the include path the headers will be found in.
# ecm_find_package_components() will then search for the library
# and include directory (creating appropriate cache variables) and create an
# imported library target named <name>::<component>.
#
# Additional variables can be used to provide additional information:
#
# If SKIP_PKG_CONFIG, the <name>_<component>_pkg_config variable is set, and
# pkg-config is found, the pkg-config module given by
# <name>_<component>_pkg_config will be searched for and used to help locate the
# library and header file. It will also be used to set
# <name>_<component>_VERSION.
#
# Note that if version information is found via pkg-config,
# <name>_<component>_FIND_VERSION can be set to require a particular version
# for each component.
#
# If SKIP_DEPENDENCY_HANDLING is not set, the INTERFACE_LINK_LIBRARIES property
# of the imported target for <component> will be set to contain the imported
# targets for the components listed in <name>_<component>_component_deps.
# <component>_FOUND will also be set to false if any of the components in
# <name>_<component>_component_deps are not found. This requires the components
# in <name>_<component>_component_deps to be listed before <component> in the
# COMPONENTS argument.
#
# The following variables will be set:
#
# ``<name>_TARGETS``
# the imported targets
# ``<name>_LIBRARIES``
# the found libraries
# ``<name>_INCLUDE_DIRS``
# the combined required include directories for the components
# ``<name>_DEFINITIONS``
# the "other" CFLAGS provided by pkg-config, if any
# ``<name>_VERSION``
# the value of ``<name>_<component>_VERSION`` for the first component that
# has this variable set (note that components are searched for in the order
# they are passed to the macro), although if it is already set, it will not
# be altered
#
# Note that these variables are never cleared, so if
# ecm_find_package_handle_library_components() is called multiple times with
# different components (typically because of multiple find_package() calls) then
# ``<name>_TARGETS``, for example, will contain all the targets found in any
# call (although no duplicates).
#
# Since pre-1.0.0.
#=============================================================================
# Copyright 2014 Alex Merry <alex.merry@kde.org>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. The name of the author may not be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
include(CMakeParseArguments)
macro(ecm_find_package_version_check module_name)
if(CMAKE_VERSION VERSION_LESS 2.8.12)
message(FATAL_ERROR "CMake 2.8.12 is required by Find${module_name}.cmake")
endif()
if(CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 2.8.12)
message(AUTHOR_WARNING "Your project should require at least CMake 2.8.12 to use Find${module_name}.cmake")
endif()
endmacro()
macro(ecm_find_package_parse_components module_name)
set(ecm_fppc_options SKIP_DEPENDENCY_HANDLING)
set(ecm_fppc_oneValueArgs RESULT_VAR)
set(ecm_fppc_multiValueArgs KNOWN_COMPONENTS DEFAULT_COMPONENTS)
cmake_parse_arguments(ECM_FPPC "${ecm_fppc_options}" "${ecm_fppc_oneValueArgs}" "${ecm_fppc_multiValueArgs}" ${ARGN})
if(ECM_FPPC_UNPARSED_ARGUMENTS)
message(FATAL_ERROR "Unexpected arguments to ecm_find_package_parse_components: ${ECM_FPPC_UNPARSED_ARGUMENTS}")
endif()
if(NOT ECM_FPPC_RESULT_VAR)
message(FATAL_ERROR "Missing RESULT_VAR argument to ecm_find_package_parse_components")
endif()
if(NOT ECM_FPPC_KNOWN_COMPONENTS)
message(FATAL_ERROR "Missing KNOWN_COMPONENTS argument to ecm_find_package_parse_components")
endif()
if(NOT ECM_FPPC_DEFAULT_COMPONENTS)
set(ECM_FPPC_DEFAULT_COMPONENTS ${ECM_FPPC_KNOWN_COMPONENTS})
endif()
if(${module_name}_FIND_COMPONENTS)
set(ecm_fppc_requestedComps ${${module_name}_FIND_COMPONENTS})
if(NOT ECM_FPPC_SKIP_DEPENDENCY_HANDLING)
# Make sure deps are included
foreach(ecm_fppc_comp ${ecm_fppc_requestedComps})
foreach(ecm_fppc_dep_comp ${${module_name}_${ecm_fppc_comp}_component_deps})
list(FIND ecm_fppc_requestedComps "${ecm_fppc_dep_comp}" ecm_fppc_index)
if("${ecm_fppc_index}" STREQUAL "-1")
if(NOT ${module_name}_FIND_QUIETLY)
message(STATUS "${module_name}: ${ecm_fppc_comp} requires ${${module_name}_${ecm_fppc_comp}_component_deps}")
endif()
list(APPEND ecm_fppc_requestedComps "${ecm_fppc_dep_comp}")
endif()
endforeach()
endforeach()
else()
message(STATUS "Skipping dependency handling for ${module_name}")
endif()
list(REMOVE_DUPLICATES ecm_fppc_requestedComps)
# This makes sure components are listed in the same order as
# KNOWN_COMPONENTS (potentially important for inter-dependencies)
set(${ECM_FPPC_RESULT_VAR})
foreach(ecm_fppc_comp ${ECM_FPPC_KNOWN_COMPONENTS})
list(FIND ecm_fppc_requestedComps "${ecm_fppc_comp}" ecm_fppc_index)
if(NOT "${ecm_fppc_index}" STREQUAL "-1")
list(APPEND ${ECM_FPPC_RESULT_VAR} "${ecm_fppc_comp}")
list(REMOVE_AT ecm_fppc_requestedComps ${ecm_fppc_index})
endif()
endforeach()
# if there are any left, they are unknown components
if(ecm_fppc_requestedComps)
set(ecm_fppc_msgType STATUS)
if(${module_name}_FIND_REQUIRED)
set(ecm_fppc_msgType FATAL_ERROR)
endif()
if(NOT ${module_name}_FIND_QUIETLY)
message(${ecm_fppc_msgType} "${module_name}: requested unknown components ${ecm_fppc_requestedComps}")
endif()
return()
endif()
else()
set(${ECM_FPPC_RESULT_VAR} ${ECM_FPPC_DEFAULT_COMPONENTS})
endif()
endmacro()
macro(ecm_find_package_handle_library_components module_name)
set(ecm_fpwc_options SKIP_PKG_CONFIG SKIP_DEPENDENCY_HANDLING)
set(ecm_fpwc_oneValueArgs)
set(ecm_fpwc_multiValueArgs COMPONENTS)
cmake_parse_arguments(ECM_FPWC "${ecm_fpwc_options}" "${ecm_fpwc_oneValueArgs}" "${ecm_fpwc_multiValueArgs}" ${ARGN})
if(ECM_FPWC_UNPARSED_ARGUMENTS)
message(FATAL_ERROR "Unexpected arguments to ecm_find_package_handle_components: ${ECM_FPWC_UNPARSED_ARGUMENTS}")
endif()
if(NOT ECM_FPWC_COMPONENTS)
message(FATAL_ERROR "Missing COMPONENTS argument to ecm_find_package_handle_components")
endif()
include(FindPackageHandleStandardArgs)
find_package(PkgConfig)
foreach(ecm_fpwc_comp ${ECM_FPWC_COMPONENTS})
set(ecm_fpwc_dep_vars)
set(ecm_fpwc_dep_targets)
if(NOT SKIP_DEPENDENCY_HANDLING)
foreach(ecm_fpwc_dep ${${module_name}_${ecm_fpwc_comp}_component_deps})
list(APPEND ecm_fpwc_dep_vars "${module_name}_${ecm_fpwc_dep}_FOUND")
list(APPEND ecm_fpwc_dep_targets "${module_name}::${ecm_fpwc_dep}")
endforeach()
endif()
if(NOT ECM_FPWC_SKIP_PKG_CONFIG AND ${module_name}_${ecm_fpwc_comp}_pkg_config)
pkg_check_modules(PKG_${module_name}_${ecm_fpwc_comp} QUIET
${${module_name}_${ecm_fpwc_comp}_pkg_config})
endif()
find_path(${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR
NAMES ${${module_name}_${ecm_fpwc_comp}_header}
HINTS ${PKG_${module_name}_${ecm_fpwc_comp}_INCLUDE_DIRS}
PATH_SUFFIXES ${${module_name}_${ecm_fpwc_comp}_header_subdir}
)
find_library(${module_name}_${ecm_fpwc_comp}_LIBRARY
NAMES ${${module_name}_${ecm_fpwc_comp}_lib}
HINTS ${PKG_${module_name}_${ecm_fpwc_comp}_LIBRARY_DIRS}
)
set(${module_name}_${ecm_fpwc_comp}_VERSION "${PKG_${module_name}_${ecm_fpwc_comp}_VERSION}")
if(NOT ${module_name}_VERSION)
set(${module_name}_VERSION ${${module_name}_${ecm_fpwc_comp}_VERSION})
endif()
find_package_handle_standard_args(${module_name}_${ecm_fpwc_comp}
FOUND_VAR
${module_name}_${ecm_fpwc_comp}_FOUND
REQUIRED_VARS
${module_name}_${ecm_fpwc_comp}_LIBRARY
${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR
${ecm_fpwc_dep_vars}
VERSION_VAR
${module_name}_${ecm_fpwc_comp}_VERSION
)
mark_as_advanced(
${module_name}_${ecm_fpwc_comp}_LIBRARY
${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR
)
if(${module_name}_${ecm_fpwc_comp}_FOUND)
list(APPEND ${module_name}_LIBRARIES
"${${module_name}_${ecm_fpwc_comp}_LIBRARY}")
list(APPEND ${module_name}_INCLUDE_DIRS
"${${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR}")
set(${module_name}_DEFINITIONS
${${module_name}_DEFINITIONS}
${PKG_${module_name}_${ecm_fpwc_comp}_DEFINITIONS})
if(NOT TARGET ${module_name}::${ecm_fpwc_comp})
add_library(${module_name}::${ecm_fpwc_comp} UNKNOWN IMPORTED)
set_target_properties(${module_name}::${ecm_fpwc_comp} PROPERTIES
IMPORTED_LOCATION "${${module_name}_${ecm_fpwc_comp}_LIBRARY}"
INTERFACE_COMPILE_OPTIONS "${PKG_${module_name}_${ecm_fpwc_comp}_DEFINITIONS}"
INTERFACE_INCLUDE_DIRECTORIES "${${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR}"
INTERFACE_LINK_LIBRARIES "${ecm_fpwc_dep_targets}"
)
endif()
list(APPEND ${module_name}_TARGETS
"${module_name}::${ecm_fpwc_comp}")
endif()
endforeach()
if(${module_name}_LIBRARIES)
list(REMOVE_DUPLICATES ${module_name}_LIBRARIES)
endif()
if(${module_name}_INCLUDE_DIRS)
list(REMOVE_DUPLICATES ${module_name}_INCLUDE_DIRS)
endif()
if(${module_name}_DEFINITIONS)
list(REMOVE_DUPLICATES ${module_name}_DEFINITIONS)
endif()
if(${module_name}_TARGETS)
list(REMOVE_DUPLICATES ${module_name}_TARGETS)
endif()
endmacro()

View File

@ -1,4 +0,0 @@
include(${CMAKE_CURRENT_LIST_DIR}/../modules/ECMFindModuleHelpers.cmake)
# SPDX-FileCopyrightText: 2014 Alex Merry <alex.merry@kde.org>
# SPDX-License-Identifier: BSD-3-Clause

View File

@ -1,62 +0,0 @@
#.rst:
# FindInotify
# --------------
#
# Try to find inotify on this system. This finds:
# - libinotify on Unix like systems, or
# - the kernel's inotify on Linux systems.
#
# This will define the following variables:
#
# ``Inotify_FOUND``
# True if inotify is available
# ``Inotify_LIBRARIES``
# This has to be passed to target_link_libraries()
# ``Inotify_INCLUDE_DIRS``
# This has to be passed to target_include_directories()
#
# On Linux, the libraries and include directories are empty,
# even though ``Inotify_FOUND`` may be set to TRUE. This is because
# no special includes or libraries are needed. On other systems
# these may be needed to use inotify.
#
# Since 5.32.0.
#=============================================================================
# SPDX-FileCopyrightText: 2016 Tobias C. Berner <tcberner@FreeBSD.org>
# SPDX-FileCopyrightText: 2017 Adriaan de Groot <groot@kde.org>
#
# SPDX-License-Identifier: BSD-2-Clause
#=============================================================================
find_path(Inotify_INCLUDE_DIRS sys/inotify.h)
if(Inotify_INCLUDE_DIRS)
# On Linux there is no library to link against, on the BSDs there is.
# On the BSD's, inotify is implemented through a library, libinotify.
if( CMAKE_SYSTEM_NAME MATCHES "Linux")
set(Inotify_FOUND TRUE)
set(Inotify_LIBRARIES "")
set(Inotify_INCLUDE_DIRS "")
else()
find_library(Inotify_LIBRARIES NAMES inotify)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Inotify
FOUND_VAR
Inotify_FOUND
REQUIRED_VARS
Inotify_LIBRARIES
Inotify_INCLUDE_DIRS
)
mark_as_advanced(Inotify_LIBRARIES Inotify_INCLUDE_DIRS)
include(FeatureSummary)
set_package_properties(Inotify PROPERTIES
URL "https://github.com/libinotify-kqueue/"
DESCRIPTION "inotify API on the *BSD family of operating systems."
)
endif()
else()
set(Inotify_FOUND FALSE)
endif()
mark_as_advanced(Inotify_LIBRARIES Inotify_INCLUDE_DIRS)

View File

@ -1,93 +0,0 @@
#.rst:
# FindSharedMimeInfo
# ------------------
#
# Try to find the shared-mime-info package.
#
# This will define the following variables:
#
# ``SharedMimeInfo_FOUND``
# True if system has the shared-mime-info package
# ``UPDATE_MIME_DATABASE_EXECUTABLE``
# The update-mime-database executable
#
# and the following imported targets:
#
# ``SharedMimeInfo::UpdateMimeDatabase``
# The update-mime-database executable
#
# The follow macro is available::
#
# update_xdg_mimetypes(<path>)
#
# Updates the XDG mime database at install time (unless the ``$DESTDIR``
# environment variable is set, in which case it is up to package managers to
# perform this task).
#
# Since pre-1.0.0.
#=============================================================================
# SPDX-FileCopyrightText: 2013-2014 Alex Merry <alex.merry@kdemail.net>
# SPDX-FileCopyrightText: 2007 Pino Toscano <toscano.pino@tiscali.it>
#
# SPDX-License-Identifier: BSD-3-Clause
#=============================================================================
include(${CMAKE_CURRENT_LIST_DIR}/ECMFindModuleHelpersStub.cmake)
ecm_find_package_version_check(SharedMimeInfo)
find_program (UPDATE_MIME_DATABASE_EXECUTABLE NAMES update-mime-database)
if (UPDATE_MIME_DATABASE_EXECUTABLE)
execute_process(
COMMAND "${UPDATE_MIME_DATABASE_EXECUTABLE}" -v
OUTPUT_VARIABLE _smiVersionRaw
ERROR_VARIABLE _smiVersionRaw)
string(REGEX REPLACE "update-mime-database \\([a-zA-Z\\-]+\\) ([0-9]\\.[0-9]+).*"
"\\1" SharedMimeInfo_VERSION_STRING "${_smiVersionRaw}")
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(SharedMimeInfo
FOUND_VAR
SharedMimeInfo_FOUND
REQUIRED_VARS
UPDATE_MIME_DATABASE_EXECUTABLE
VERSION_VAR
SharedMimeInfo_VERSION_STRING)
if(SharedMimeInfo_FOUND AND NOT TARGET SharedMimeInfo::UpdateMimeDatabase)
add_executable(SharedMimeInfo::UpdateMimeDatabase IMPORTED)
set_target_properties(SharedMimeInfo::UpdateMimeDatabase PROPERTIES
IMPORTED_LOCATION "${UPDATE_MIME_DATABASE_EXECUTABLE}"
)
endif()
mark_as_advanced(UPDATE_MIME_DATABASE_EXECUTABLE)
function(UPDATE_XDG_MIMETYPES _path)
get_filename_component(_xdgmimeDir "${_path}" NAME)
if("${_xdgmimeDir}" STREQUAL packages )
get_filename_component(_xdgmimeDir "${_path}" PATH)
else()
set(_xdgmimeDir "${_path}")
endif()
# Note that targets and most variables are not available to install code
install(CODE "
set(DESTDIR_VALUE \"\$ENV{DESTDIR}\")
if (NOT DESTDIR_VALUE)
# under Windows relative paths are used, that's why it runs from CMAKE_INSTALL_PREFIX
message(STATUS \"Updating MIME database at \${CMAKE_INSTALL_PREFIX}/${_xdgmimeDir}\")
execute_process(COMMAND \"${UPDATE_MIME_DATABASE_EXECUTABLE}\" -n \"${_xdgmimeDir}\"
WORKING_DIRECTORY \"\${CMAKE_INSTALL_PREFIX}\")
endif (NOT DESTDIR_VALUE)
")
endfunction()
include(FeatureSummary)
set_package_properties(SharedMimeInfo PROPERTIES
URL https://freedesktop.org/wiki/Software/shared-mime-info/
DESCRIPTION "A database of common MIME types")

View File

@ -49,7 +49,6 @@
<file>src/gui/tray/EncryptionTokenDiscoveryDialog.qml</file>
<file>src/gui/tray/NCBusyIndicator.qml</file>
<file>src/gui/tray/NCIconWithBackgroundImage.qml</file>
<file>src/gui/tray/NCToolTip.qml</file>
<file>src/gui/tray/NCProgressBar.qml</file>
<file>src/gui/tray/EnforcedPlainTextLabel.qml</file>
<file>theme/Style/Style.qml</file>
@ -62,8 +61,5 @@
<file>src/gui/ConflictItemFileInfo.qml</file>
<file>src/gui/macOS/ui/FileProviderSettings.qml</file>
<file>src/gui/macOS/ui/FileProviderFileDelegate.qml</file>
<file>src/gui/macOS/ui/FileProviderEvictionDialog.qml</file>
<file>src/gui/macOS/ui/FileProviderSyncStatus.qml</file>
<file>src/gui/macOS/ui/FileProviderStorageInfo.qml</file>
</qresource>
</RCC>

View File

@ -4,7 +4,7 @@
if(APPLE)
set(OC_OEM_SHARE_ICNS "${CMAKE_BINARY_DIR}/src/gui/${APPLICATION_ICON_NAME}.icns")
if (CMAKE_BUILD_TYPE MATCHES "Debug" OR CMAKE_BUILD_TYPE MATCHES "RelWithDebInfo")
if (NEXTCLOUD_DEV)
set(XCODE_TARGET_CONFIGURATION "Debug")
else()
set(XCODE_TARGET_CONFIGURATION "Release")

View File

@ -0,0 +1,7 @@
# SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
# SPDX-License-Identifier: GPL-2.0-or-later
DerivedData
# exception
!NextcloudDev/Build.xcconfig.template

View File

@ -0,0 +1,19 @@
# SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
# SPDX-License-Identifier: GPL-2.0-or-later
[main]
host = https://app.transifex.com
# Complete mapping from language codes on Transifex to the ones configured in the Xcode project for those where the codes diverge.
lang_map = bg_BG: bg, bn_BD: bn, cs_CZ: cs, cy_GB: cy, es_AR: es-AR, es_DO: es-DO, es_EC: es-EC, es_SV: es-SV, es_GT: es-GT, es_HN: es-HN, es_419: es-419, es_MX: es-MX, es_NI: es-NI, es_PA: es-PA, es_PY: es-PY, es_PE: es-PE, es_PR: es-PR, es_UY: es-UY, et_EE: et, fi_FI: fi, hi_IN: hi, hu_HU: hu, ja_JP: ja, ka_GE: ka, lt_LT: lt, ms_MY: ms, nb_NO: nb-NO, nn_NO: nn-NO, pt_BR: pt-BR, pt_PT: pt-PT, sk_SK: sk, th_TH: th, ur_PK: ur, zh_CN: zh-Hans, zh_HK: zh-HK, zh_TW: zh-Hant-TW, zu_ZA: zu
[o:nextcloud:p:nextcloud:r:client-fileprovider]
file_filter = FileProviderExt/Localizable.xcstrings
source_file = FileProviderExt/Localizable.xcstrings
source_lang = en
type = XCSTRINGS
[o:nextcloud:p:nextcloud:r:client-fileproviderui]
file_filter = FileProviderUIExt/Localizable.xcstrings
source_file = FileProviderUIExt/Localizable.xcstrings
source_lang = en
type = XCSTRINGS

View File

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,15 @@
{
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"symbol-rendering-intent" : "template"
},
"symbols" : [
{
"filename" : "FileProviderDomainSymbol.svg",
"idiom" : "universal"
}
]
}

View File

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--Generator: Apple Native CoreSVG 341-->
<!DOCTYPE svg
PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 3300 2200">
<!--glyph: "", point size: 100.0, font version: "20.0d10e1", template writer version: "138.0.0"-->
<style>.SFSymbolsPreviewWireframe {fill:none;opacity:1.0;stroke:black;stroke-width:0.5}
</style>
<g id="Notes">
<rect height="2200" id="artboard" style="fill:white;opacity:1" width="3300" x="0" y="0"/>
<line style="fill:none;stroke:black;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="292" y2="292"/>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;" transform="matrix(1 0 0 1 263 322)">Weight/Scale Variations</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 559.711 322)">Ultralight</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 856.422 322)">Thin</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 1153.13 322)">Light</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 1449.84 322)">Regular</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 1746.56 322)">Medium</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 2043.27 322)">Semibold</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 2339.98 322)">Bold</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 2636.69 322)">Heavy</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 2933.4 322)">Black</text>
<line style="fill:none;stroke:black;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="1903" y2="1903"/>
<g transform="matrix(0.2 0 0 0.2 263 1933)">
<path d="m46.2402 4.15039c21.7773 0 39.4531-17.627 39.4531-39.4043s-17.6758-39.4043-39.4531-39.4043c-21.7285 0-39.4043 17.627-39.4043 39.4043s17.6758 39.4043 39.4043 39.4043Zm0-7.42188c-17.6758 0-31.9336-14.3066-31.9336-31.9824s14.2578-31.9824 31.9336-31.9824 31.9824 14.3066 31.9824 31.9824-14.3066 31.9824-31.9824 31.9824Zm-17.9688-31.9824c0 2.14844 1.51367 3.61328 3.75977 3.61328h10.498v10.5957c0 2.19727 1.46484 3.71094 3.61328 3.71094 2.24609 0 3.71094-1.51367 3.71094-3.71094v-10.5957h10.5957c2.19727 0 3.71094-1.46484 3.71094-3.61328 0-2.19727-1.51367-3.71094-3.71094-3.71094h-10.5957v-10.5469c0-2.24609-1.46484-3.75977-3.71094-3.75977-2.14844 0-3.61328 1.51367-3.61328 3.75977v10.5469h-10.498c-2.24609 0-3.75977 1.51367-3.75977 3.71094Z"/>
</g>
<g transform="matrix(0.2 0 0 0.2 281.506 1933)">
<path d="m58.5449 14.5508c27.4902 0 49.8047-22.3145 49.8047-49.8047s-22.3145-49.8047-49.8047-49.8047-49.8047 22.3145-49.8047 49.8047 22.3145 49.8047 49.8047 49.8047Zm0-8.30078c-22.9492 0-41.5039-18.5547-41.5039-41.5039s18.5547-41.5039 41.5039-41.5039 41.5039 18.5547 41.5039 41.5039-18.5547 41.5039-41.5039 41.5039Zm-22.6562-41.5039c0 2.39258 1.66016 4.00391 4.15039 4.00391h14.3555v14.4043c0 2.44141 1.66016 4.15039 4.05273 4.15039 2.44141 0 4.15039-1.66016 4.15039-4.15039v-14.4043h14.4043c2.44141 0 4.15039-1.61133 4.15039-4.00391 0-2.44141-1.70898-4.15039-4.15039-4.15039h-14.4043v-14.3555c0-2.49023-1.70898-4.19922-4.15039-4.19922-2.39258 0-4.05273 1.70898-4.05273 4.19922v14.3555h-14.3555c-2.49023 0-4.15039 1.70898-4.15039 4.15039Z"/>
</g>
<g transform="matrix(0.2 0 0 0.2 304.924 1933)">
<path d="m74.8535 28.3203c35.1074 0 63.623-28.4668 63.623-63.5742s-28.5156-63.623-63.623-63.623-63.5742 28.5156-63.5742 63.623 28.4668 63.5742 63.5742 63.5742Zm0-9.08203c-30.127 0-54.4922-24.3652-54.4922-54.4922s24.3652-54.4922 54.4922-54.4922 54.4922 24.3652 54.4922 54.4922-24.3652 54.4922-54.4922 54.4922Zm-28.8574-54.4922c0 2.58789 1.85547 4.39453 4.58984 4.39453h19.7266v19.7754c0 2.68555 1.85547 4.58984 4.44336 4.58984 2.68555 0 4.54102-1.85547 4.54102-4.58984v-19.7754h19.7754c2.68555 0 4.58984-1.80664 4.58984-4.39453 0-2.73438-1.85547-4.58984-4.58984-4.58984h-19.7754v-19.7266c0-2.73438-1.85547-4.63867-4.54102-4.63867-2.58789 0-4.44336 1.9043-4.44336 4.63867v19.7266h-19.7266c-2.73438 0-4.58984 1.85547-4.58984 4.58984Z"/>
</g>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;" transform="matrix(1 0 0 1 263 1953)">Design Variations</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 1971)">Symbols are supported in up to nine weights and three scales.</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 1989)">For optimal layout with text and other symbols, vertically align</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 2007)">symbols with the adjacent text.</text>
<line style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="776" x2="776" y1="1919" y2="1933"/>
<g transform="matrix(0.2 0 0 0.2 776 1933)">
<path d="m16.5527 0.78125c2.58789 0 3.85742-0.976562 4.78516-3.71094l6.29883-17.2363h28.8086l6.29883 17.2363c0.927734 2.73438 2.19727 3.71094 4.73633 3.71094 2.58789 0 4.24805-1.5625 4.24805-4.00391 0-0.830078-0.146484-1.61133-0.537109-2.63672l-22.9004-60.9863c-1.12305-2.97852-3.125-4.49219-6.25-4.49219-3.02734 0-5.07812 1.46484-6.15234 4.44336l-22.9004 61.084c-0.390625 1.02539-0.537109 1.80664-0.537109 2.63672 0 2.44141 1.5625 3.95508 4.10156 3.95508Zm13.4766-28.3691 11.8652-32.8613h0.244141l11.8652 32.8613Z"/>
</g>
<line style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="792.836" x2="792.836" y1="1919" y2="1933"/>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;" transform="matrix(1 0 0 1 776 1953)">Margins</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 776 1971)">Leading and trailing margins on the left and right side of each symbol</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 776 1989)">can be adjusted by modifying the x-location of the margin guidelines.</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 776 2007)">Modifications are automatically applied proportionally to all</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 776 2025)">scales and weights.</text>
<g transform="matrix(0.2 0 0 0.2 1289 1933)">
<path d="m14.209 9.32617 8.49609 8.54492c4.29688 4.3457 9.22852 4.05273 13.8672-1.07422l53.4668-58.9355-4.83398-4.88281-53.0762 58.3984c-1.75781 2.00195-3.41797 2.49023-5.76172 0.146484l-5.85938-5.81055c-2.34375-2.29492-1.80664-4.00391 0.195312-5.81055l57.373-54.0039-4.88281-4.83398-57.959 54.4434c-4.93164 4.58984-5.32227 9.47266-1.02539 13.8184Zm32.0801-90.9668c-2.09961 2.05078-2.24609 4.93164-1.07422 6.88477 1.17188 1.80664 3.4668 2.97852 6.68945 2.14844 7.32422-1.70898 14.9414-2.00195 22.0703 2.68555l-2.92969 7.27539c-1.70898 4.15039-0.830078 7.08008 1.85547 9.81445l11.4746 11.5723c2.44141 2.44141 4.49219 2.53906 7.32422 2.05078l5.32227-0.976562 3.32031 3.36914-0.195312 2.7832c-0.195312 2.49023 0.439453 4.39453 2.88086 6.78711l3.80859 3.71094c2.39258 2.39258 5.46875 2.53906 7.8125 0.195312l14.5508-14.5996c2.34375-2.34375 2.24609-5.32227-0.146484-7.71484l-3.85742-3.80859c-2.39258-2.39258-4.24805-3.17383-6.64062-2.97852l-2.88086 0.244141-3.22266-3.17383 1.2207-5.61523c0.634766-2.83203-0.146484-5.0293-3.07617-7.95898l-10.9863-10.9375c-16.6992-16.6016-38.8672-16.2109-53.3203-1.75781Zm7.4707 1.85547c12.1582-8.88672 28.6133-7.37305 39.7461 3.75977l12.1582 12.0605c1.17188 1.17188 1.36719 2.09961 1.02539 3.80859l-1.61133 7.42188 7.51953 7.42188 4.93164-0.292969c1.26953-0.0488281 1.66016 0.0488281 2.63672 1.02539l2.88086 2.88086-12.207 12.207-2.88086-2.88086c-0.976562-0.976562-1.12305-1.36719-1.07422-2.68555l0.341797-4.88281-7.4707-7.42188-7.61719 1.26953c-1.61133 0.341797-2.34375 0.195312-3.56445-0.976562l-10.0098-10.0098c-1.26953-1.17188-1.41602-2.00195-0.634766-3.85742l4.39453-10.4492c-7.8125-7.27539-17.9688-10.4004-28.125-7.42188-0.78125 0.195312-1.07422-0.439453-0.439453-0.976562Z"/>
</g>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;" transform="matrix(1 0 0 1 1289 1953)">Exporting</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 1289 1971)">Symbols should be outlined when exporting to ensure the</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 1289 1989)">design is preserved when submitting to Xcode.</text>
<text id="template-version" style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;" transform="matrix(1 0 0 1 3036 1933)">Template v.6.0</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;" transform="matrix(1 0 0 1 3036 1951)">Requires Xcode 16 or greater</text>
<text id="descriptive-name" style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;" transform="matrix(1 0 0 1 3036 1969)">Generated from nextcloud</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;" transform="matrix(1 0 0 1 3036 1987)">Typeset at 100.0 points</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 726)">Small</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 1156)">Medium</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 1586)">Large</text>
</g>
<g id="Guides">
<g id="H-reference" style="fill:#27AAE1;stroke:none;" transform="matrix(1 0 0 1 339 696)">
<path d="M0.993654 0L3.63775 0L29.3281-67.1323L30.0303-67.1323L30.0303-70.459L28.1226-70.459ZM11.6885-24.4799L46.9815-24.4799L46.2315-26.7285L12.4385-26.7285ZM55.1196 0L57.7637 0L30.6382-70.459L29.4326-70.459L29.4326-67.1323Z"/>
</g>
<line id="Baseline-S" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="696" y2="696"/>
<line id="Capline-S" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="625.541" y2="625.541"/>
<g id="H-reference" style="fill:#27AAE1;stroke:none;" transform="matrix(1 0 0 1 339 1126)">
<path d="M0.993654 0L3.63775 0L29.3281-67.1323L30.0303-67.1323L30.0303-70.459L28.1226-70.459ZM11.6885-24.4799L46.9815-24.4799L46.2315-26.7285L12.4385-26.7285ZM55.1196 0L57.7637 0L30.6382-70.459L29.4326-70.459L29.4326-67.1323Z"/>
</g>
<line id="Baseline-M" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="1126" y2="1126"/>
<line id="Capline-M" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="1055.54" y2="1055.54"/>
<g id="H-reference" style="fill:#27AAE1;stroke:none;" transform="matrix(1 0 0 1 339 1556)">
<path d="M0.993654 0L3.63775 0L29.3281-67.1323L30.0303-67.1323L30.0303-70.459L28.1226-70.459ZM11.6885-24.4799L46.9815-24.4799L46.2315-26.7285L12.4385-26.7285ZM55.1196 0L57.7637 0L30.6382-70.459L29.4326-70.459L29.4326-67.1323Z"/>
</g>
<line id="Baseline-L" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="1556" y2="1556"/>
<line id="Capline-L" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="1485.54" y2="1485.54"/>
<line id="right-margin-Black-S" style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="2989.55" x2="2989.55" y1="600.785" y2="720.121"/>
<line id="left-margin-Black-S" style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="2877.25" x2="2877.25" y1="600.785" y2="720.121"/>
<line id="right-margin-Regular-S" style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="1501.6" x2="1501.6" y1="600.785" y2="720.121"/>
<line id="left-margin-Regular-S" style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="1398.09" x2="1398.09" y1="600.785" y2="720.121"/>
<line id="right-margin-Ultralight-S" style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="610.493" x2="610.493" y1="600.785" y2="720.121"/>
<line id="left-margin-Ultralight-S" style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="508.93" x2="508.93" y1="600.785" y2="720.121"/>
</g>
<g id="Symbols">
<g id="Black-S" transform="matrix(1 0 0 1 2877.25 696)">
<path class="SFSymbolsPreviewWireframe" d="M56.15-58.0091C45.7881-58.0091 36.9888-51.019 34.275-41.4795C31.9724-46.4959 26.8737-50.0321 20.9526-50.0321C12.8112-50.0321 6.15002-43.3709 6.15002-35.2295C6.15002-27.088 12.8112-20.4269 20.9526-20.4269C26.8737-20.4269 31.9724-23.963 34.3572-28.9795C37.071-19.44 45.8704-12.4499 56.2322-12.4499C66.5118-12.4499 75.2289-19.3578 78.025-28.7328C80.4099-23.7986 85.5085-20.3446 91.3474-20.3446C99.4888-20.3446 106.15-27.0058 106.15-35.1473C106.15-43.2887 99.4888-49.9499 91.3474-49.9499C85.5085-49.9499 80.4921-46.4959 78.025-41.5617C75.2289-51.1012 66.4296-58.0091 56.15-58.0091ZM56.15-49.292C63.9625-49.292 70.2125-43.042 70.2125-35.2295C70.2125-27.417 63.9625-21.167 56.15-21.167C48.3375-21.167 42.0875-27.417 42.0875-35.2295C42.0875-43.1242 48.3375-49.292 56.15-49.292ZM20.9526-41.3973C24.4066-41.3973 27.0381-38.6834 27.0381-35.3117C27.0381-31.8578 24.3243-29.2262 20.9526-29.2262C17.4987-29.2262 14.8671-31.94 14.8671-35.3117C14.8671-38.6834 17.4987-41.3973 20.9526-41.3973ZM91.2651-41.3973C94.7191-41.3973 97.3506-38.6834 97.3506-35.3117C97.3506-31.8578 94.6368-29.2262 91.2651-29.2262C87.8112-29.2262 85.1796-31.94 85.1796-35.3117C85.1796-38.6834 87.8112-41.3973 91.2651-41.3973Z"/>
</g>
<g id="Regular-S" transform="matrix(1 0 0 1 1398.09 696)">
<path class="SFSymbolsPreviewWireframe" d="M51.755-58.0091C41.3931-58.0091 32.5938-51.019 29.88-41.4795C27.5774-46.4959 22.4787-50.0321 16.5576-50.0321C8.41617-50.0321 1.75497-43.3709 1.75497-35.2295C1.75497-27.088 8.41617-20.4269 16.5576-20.4269C22.4787-20.4269 27.5774-23.963 29.9622-28.9795C32.676-19.44 41.4754-12.4499 51.8372-12.4499C62.1168-12.4499 70.8339-19.3578 73.63-28.7328C76.0149-23.7986 81.1135-20.3446 86.9524-20.3446C95.0938-20.3446 101.755-27.0058 101.755-35.1473C101.755-43.2887 95.0938-49.9499 86.9524-49.9499C81.1135-49.9499 76.0971-46.4959 73.63-41.5617C70.8339-51.1012 62.0346-58.0091 51.755-58.0091ZM51.755-49.292C59.5675-49.292 65.8175-43.042 65.8175-35.2295C65.8175-27.417 59.5675-21.167 51.755-21.167C43.9425-21.167 37.6925-27.417 37.6925-35.2295C37.6925-43.1242 43.9425-49.292 51.755-49.292ZM16.5576-41.3973C20.0116-41.3973 22.6431-38.6834 22.6431-35.3117C22.6431-31.8578 19.9293-29.2262 16.5576-29.2262C13.1037-29.2262 10.4721-31.94 10.4721-35.3117C10.4721-38.6834 13.1037-41.3973 16.5576-41.3973ZM86.8701-41.3973C90.3241-41.3973 92.9556-38.6834 92.9556-35.3117C92.9556-31.8578 90.2418-29.2262 86.8701-29.2262C83.4162-29.2262 80.7846-31.94 80.7846-35.3117C80.7846-38.6834 83.4162-41.3973 86.8701-41.3973Z"/>
</g>
<g id="Ultralight-S" transform="matrix(1 0 0 1 508.93 696)">
<path class="SFSymbolsPreviewWireframe" d="M50.7815-58.3266C40.4197-58.3266 31.6203-51.3365 28.9065-41.797C26.6039-46.8135 21.5052-50.3497 15.5841-50.3497C7.44268-50.3497 0.781495-43.6885 0.781495-35.547C0.781495-27.4056 7.44268-20.7444 15.5841-20.7444C21.5052-20.7444 26.6039-24.2806 28.9887-29.297C31.7025-19.7576 40.5019-12.7674 50.8637-12.7674C61.1433-12.7674 69.8604-19.6753 72.6565-29.0503C75.0414-24.1161 80.14-20.6622 85.9789-20.6622C94.1203-20.6622 100.781-27.3233 100.781-35.4648C100.781-43.6062 94.1203-50.2674 85.9789-50.2674C80.14-50.2674 75.1236-46.8135 72.6565-41.8793C69.8604-51.4187 61.0611-58.3266 50.7815-58.3266ZM50.7815-49.6095C58.594-49.6095 64.844-43.3595 64.844-35.547C64.844-27.7345 58.594-21.4845 50.7815-21.4845C42.969-21.4845 36.719-27.7345 36.719-35.547C36.719-43.4418 42.969-49.6095 50.7815-49.6095ZM15.5841-41.7148C19.0381-41.7148 21.6697-39.001 21.6697-35.6293C21.6697-32.1753 18.9558-29.5437 15.5841-29.5437C12.1302-29.5437 9.49861-32.2576 9.49861-35.6293C9.49861-39.001 12.1302-41.7148 15.5841-41.7148ZM85.8966-41.7148C89.3506-41.7148 91.9822-39.001 91.9822-35.6293C91.9822-32.1753 89.2683-29.5437 85.8966-29.5437C82.4427-29.5437 79.8111-32.2576 79.8111-35.6293C79.8111-39.001 82.4427-41.7148 85.8966-41.7148Z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -117,11 +117,9 @@ import OSLog
// MARK: - NSFileProviderReplicatedExtension protocol methods
func item(
for identifier: NSFileProviderItemIdentifier,
request _: NSFileProviderRequest,
completionHandler: @escaping (NSFileProviderItem?, Error?) -> Void
) -> Progress {
func item(for identifier: NSFileProviderItemIdentifier, request: NSFileProviderRequest, completionHandler: @escaping (NSFileProviderItem?, Error?) -> Void) -> Progress {
logger.debug("Received request for item.", [.item: identifier, .request: request])
guard let ncAccount else {
logger.error("Not fetching item because account not set up yet.", [.item: identifier])
completionHandler(nil, NSFileProviderError(.notAuthenticated))
@ -130,7 +128,7 @@ import OSLog
guard let dbManager else {
logger.error("Not fetching item because database is unavailable.", [.item: identifier])
completionHandler(nil, NSFileProviderError(.cannotSynchronize))
completionHandler(nil, NSFileProviderError(.notAuthenticated))
return Progress()
}
@ -163,8 +161,7 @@ import OSLog
) -> Progress {
let actionId = UUID()
insertSyncAction(actionId)
logger.info("Received request to fetch contents of item.", [.item: itemIdentifier])
logger.debug("Received request to fetch contents of item.", [.item: itemIdentifier, .request: request])
guard requestedVersion == nil else {
// TODO: Add proper support for file versioning
@ -233,7 +230,7 @@ import OSLog
) -> Progress {
let actionId = UUID()
insertSyncAction(actionId)
logger.debug("Received request to create item.", [.item: itemTemplate, .name: itemTemplate.filename])
logger.debug("Received request to create item.", [.item: itemTemplate, .name: itemTemplate.filename, .request: request])
guard let ncAccount else {
logger.error(
@ -313,18 +310,17 @@ import OSLog
insertSyncAction(actionId)
let identifier = item.itemIdentifier
let ocId = identifier.rawValue
logger.debug("Received request to modify item.", [.item: item])
logger.debug("Received request to modify item.", [.item: item, .request: request])
guard let ncAccount else {
logger.error("Not modifying item: \(ocId) as account not set up yet.")
logger.error("Not modifying item because account not set up yet.", [.item: identifier])
insertErrorAction(actionId)
completionHandler(item, [], false, NSFileProviderError(.notAuthenticated))
return Progress()
}
guard let ignoredFiles else {
logger.error("Not modifying item: \(ocId) as ignore list not set up yet.")
logger.error("Not modifying item because ignore list not set up yet.", [.item: identifier])
insertErrorAction(actionId)
completionHandler(item, [], false, NSFileProviderError(.notAuthenticated))
return Progress()
@ -332,13 +328,14 @@ import OSLog
guard let dbManager else {
logger.error("Not modifying item because database is unavailable.")
logger.error("Not modifying item because the database is unavailable.")
insertErrorAction(actionId)
completionHandler(item, [], false, NSFileProviderError(.cannotSynchronize))
return Progress()
}
let progress = Progress()
Task {
guard let existingItem = await Item.storedItem(
identifier: identifier,
@ -347,16 +344,19 @@ import OSLog
dbManager: dbManager,
log: log
) else {
logger.error("Not modifying item: \(ocId) as item not found.")
logger.error("Not modifying item because it was not found.", [.item: identifier])
insertErrorAction(actionId)
completionHandler(
item,
[],
false,
NSError.fileProviderErrorForNonExistentItem(withIdentifier: item.itemIdentifier)
)
return
}
let (modifiedItem, error) = await existingItem.modify(
itemTarget: item,
baseVersion: baseVersion,
@ -380,6 +380,7 @@ import OSLog
logger.debug("Calling item modification completion handler.", [.item: item.itemIdentifier, .name: item.filename, .error: error])
completionHandler(modifiedItem ?? item, [], false, error)
}
return progress
}
@ -387,13 +388,13 @@ import OSLog
identifier: NSFileProviderItemIdentifier,
baseVersion _: NSFileProviderItemVersion,
options _: NSFileProviderDeleteItemOptions = [],
request _: NSFileProviderRequest,
request: NSFileProviderRequest,
completionHandler: @escaping (Error?) -> Void
) -> Progress {
let actionId = UUID()
insertSyncAction(actionId)
logger.debug("Received request to delete item.", [.item: identifier])
logger.debug("Received request to delete item.", [.item: identifier, .request: request])
guard let ncAccount else {
logger.error("Not deleting item \(identifier.rawValue), account not set up yet")
@ -456,9 +457,9 @@ import OSLog
return progress
}
func enumerator(
for containerItemIdentifier: NSFileProviderItemIdentifier, request _: NSFileProviderRequest
) throws -> NSFileProviderEnumerator {
func enumerator(for containerItemIdentifier: NSFileProviderItemIdentifier, request: NSFileProviderRequest) throws -> NSFileProviderEnumerator {
logger.debug("System requested enumerator.", [.item: containerItemIdentifier])
guard let ncAccount else {
logger.error("Not providing enumerator for container with identifier \(containerItemIdentifier.rawValue) yet as account not set up")
throw NSFileProviderError(.notAuthenticated)

View File

@ -2,6 +2,14 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleIcons</key>
<dict>
<key>CFBundlePrimaryIcon</key>
<dict>
<key>CFBundleSymbolName</key>
<string>cloud</string>
</dict>
</dict>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundleDisplayName</key>

View File

@ -1,12 +1,124 @@
{
"sourceLanguage": "en",
"strings": {
"Allow automatic freeing up space": {
"extractionState": "manual"
"sourceLanguage" : "en",
"strings" : {
"Allow automatic freeing up space" : {
"extractionState" : "manual",
"localizations" : {
"de" : {
"stringUnit" : {
"state" : "translated",
"value" : "Automatisches Freigeben von Speicherplatz zulassen"
}
},
"de_DE" : {
"stringUnit" : {
"state" : "translated",
"value" : "Automatisches Freigeben von Speicherplatz zulassen"
}
},
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Allow automatic freeing up space"
}
},
"es" : {
"stringUnit" : {
"state" : "translated",
"value" : "Permitir la liberación de espacio automática"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Autoriser la libération automatique de l'espace"
}
},
"gl" : {
"stringUnit" : {
"state" : "translated",
"value" : "Permitir a liberación automática de espazo"
}
},
"pt_BR" : {
"stringUnit" : {
"state" : "translated",
"value" : "Permitir liberação automática de espaço"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Yer açmak için dosyalar otomatik olarak silinsin"
}
},
"zh_HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "允許自動釋放空間"
}
}
}
},
"Always keep downloaded": {
"extractionState": "manual"
"Always keep downloaded" : {
"extractionState" : "manual",
"localizations" : {
"de" : {
"stringUnit" : {
"state" : "translated",
"value" : "Immer heruntergeladen halten"
}
},
"de_DE" : {
"stringUnit" : {
"state" : "translated",
"value" : "Immer heruntergeladen halten"
}
},
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Always keep downloaded"
}
},
"es" : {
"stringUnit" : {
"state" : "translated",
"value" : "Siempre mantener descargado"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Toujours conserver en local"
}
},
"gl" : {
"stringUnit" : {
"state" : "translated",
"value" : "Manter sempre descargado"
}
},
"pt_BR" : {
"stringUnit" : {
"state" : "translated",
"value" : "Sempre manter baixados"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Her zaman indirilmiş tutulsun"
}
},
"zh_HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "一律保留已下載的下載"
}
}
}
}
},
"version": "1.0"
}
"version" : "1.0"
}

View File

@ -38,7 +38,7 @@ class ClientCommunicationService: NSObject, NSFileProviderServiceSource, NSXPCLi
func getFileProviderDomainIdentifier(completionHandler: @escaping (String?, Error?) -> Void) {
let identifier = self.fpExtension.domain.identifier.rawValue
logger.info("Returning file provider domain identifier \(identifier)")
logger.debug("Returning file provider domain identifier.", [.domain: identifier])
completionHandler(identifier, nil)
}

View File

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,15 @@
{
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"symbol-rendering-intent" : "template"
},
"symbols" : [
{
"filename" : "FileProviderDomainSymbol.svg",
"idiom" : "universal"
}
]
}

View File

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--Generator: Apple Native CoreSVG 341-->
<!DOCTYPE svg
PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 3300 2200">
<!--glyph: "", point size: 100.0, font version: "20.0d10e1", template writer version: "138.0.0"-->
<style>.SFSymbolsPreviewWireframe {fill:none;opacity:1.0;stroke:black;stroke-width:0.5}
</style>
<g id="Notes">
<rect height="2200" id="artboard" style="fill:white;opacity:1" width="3300" x="0" y="0"/>
<line style="fill:none;stroke:black;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="292" y2="292"/>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;" transform="matrix(1 0 0 1 263 322)">Weight/Scale Variations</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 559.711 322)">Ultralight</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 856.422 322)">Thin</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 1153.13 322)">Light</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 1449.84 322)">Regular</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 1746.56 322)">Medium</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 2043.27 322)">Semibold</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 2339.98 322)">Bold</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 2636.69 322)">Heavy</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 2933.4 322)">Black</text>
<line style="fill:none;stroke:black;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="1903" y2="1903"/>
<g transform="matrix(0.2 0 0 0.2 263 1933)">
<path d="m46.2402 4.15039c21.7773 0 39.4531-17.627 39.4531-39.4043s-17.6758-39.4043-39.4531-39.4043c-21.7285 0-39.4043 17.627-39.4043 39.4043s17.6758 39.4043 39.4043 39.4043Zm0-7.42188c-17.6758 0-31.9336-14.3066-31.9336-31.9824s14.2578-31.9824 31.9336-31.9824 31.9824 14.3066 31.9824 31.9824-14.3066 31.9824-31.9824 31.9824Zm-17.9688-31.9824c0 2.14844 1.51367 3.61328 3.75977 3.61328h10.498v10.5957c0 2.19727 1.46484 3.71094 3.61328 3.71094 2.24609 0 3.71094-1.51367 3.71094-3.71094v-10.5957h10.5957c2.19727 0 3.71094-1.46484 3.71094-3.61328 0-2.19727-1.51367-3.71094-3.71094-3.71094h-10.5957v-10.5469c0-2.24609-1.46484-3.75977-3.71094-3.75977-2.14844 0-3.61328 1.51367-3.61328 3.75977v10.5469h-10.498c-2.24609 0-3.75977 1.51367-3.75977 3.71094Z"/>
</g>
<g transform="matrix(0.2 0 0 0.2 281.506 1933)">
<path d="m58.5449 14.5508c27.4902 0 49.8047-22.3145 49.8047-49.8047s-22.3145-49.8047-49.8047-49.8047-49.8047 22.3145-49.8047 49.8047 22.3145 49.8047 49.8047 49.8047Zm0-8.30078c-22.9492 0-41.5039-18.5547-41.5039-41.5039s18.5547-41.5039 41.5039-41.5039 41.5039 18.5547 41.5039 41.5039-18.5547 41.5039-41.5039 41.5039Zm-22.6562-41.5039c0 2.39258 1.66016 4.00391 4.15039 4.00391h14.3555v14.4043c0 2.44141 1.66016 4.15039 4.05273 4.15039 2.44141 0 4.15039-1.66016 4.15039-4.15039v-14.4043h14.4043c2.44141 0 4.15039-1.61133 4.15039-4.00391 0-2.44141-1.70898-4.15039-4.15039-4.15039h-14.4043v-14.3555c0-2.49023-1.70898-4.19922-4.15039-4.19922-2.39258 0-4.05273 1.70898-4.05273 4.19922v14.3555h-14.3555c-2.49023 0-4.15039 1.70898-4.15039 4.15039Z"/>
</g>
<g transform="matrix(0.2 0 0 0.2 304.924 1933)">
<path d="m74.8535 28.3203c35.1074 0 63.623-28.4668 63.623-63.5742s-28.5156-63.623-63.623-63.623-63.5742 28.5156-63.5742 63.623 28.4668 63.5742 63.5742 63.5742Zm0-9.08203c-30.127 0-54.4922-24.3652-54.4922-54.4922s24.3652-54.4922 54.4922-54.4922 54.4922 24.3652 54.4922 54.4922-24.3652 54.4922-54.4922 54.4922Zm-28.8574-54.4922c0 2.58789 1.85547 4.39453 4.58984 4.39453h19.7266v19.7754c0 2.68555 1.85547 4.58984 4.44336 4.58984 2.68555 0 4.54102-1.85547 4.54102-4.58984v-19.7754h19.7754c2.68555 0 4.58984-1.80664 4.58984-4.39453 0-2.73438-1.85547-4.58984-4.58984-4.58984h-19.7754v-19.7266c0-2.73438-1.85547-4.63867-4.54102-4.63867-2.58789 0-4.44336 1.9043-4.44336 4.63867v19.7266h-19.7266c-2.73438 0-4.58984 1.85547-4.58984 4.58984Z"/>
</g>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;" transform="matrix(1 0 0 1 263 1953)">Design Variations</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 1971)">Symbols are supported in up to nine weights and three scales.</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 1989)">For optimal layout with text and other symbols, vertically align</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 2007)">symbols with the adjacent text.</text>
<line style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="776" x2="776" y1="1919" y2="1933"/>
<g transform="matrix(0.2 0 0 0.2 776 1933)">
<path d="m16.5527 0.78125c2.58789 0 3.85742-0.976562 4.78516-3.71094l6.29883-17.2363h28.8086l6.29883 17.2363c0.927734 2.73438 2.19727 3.71094 4.73633 3.71094 2.58789 0 4.24805-1.5625 4.24805-4.00391 0-0.830078-0.146484-1.61133-0.537109-2.63672l-22.9004-60.9863c-1.12305-2.97852-3.125-4.49219-6.25-4.49219-3.02734 0-5.07812 1.46484-6.15234 4.44336l-22.9004 61.084c-0.390625 1.02539-0.537109 1.80664-0.537109 2.63672 0 2.44141 1.5625 3.95508 4.10156 3.95508Zm13.4766-28.3691 11.8652-32.8613h0.244141l11.8652 32.8613Z"/>
</g>
<line style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="792.836" x2="792.836" y1="1919" y2="1933"/>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;" transform="matrix(1 0 0 1 776 1953)">Margins</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 776 1971)">Leading and trailing margins on the left and right side of each symbol</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 776 1989)">can be adjusted by modifying the x-location of the margin guidelines.</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 776 2007)">Modifications are automatically applied proportionally to all</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 776 2025)">scales and weights.</text>
<g transform="matrix(0.2 0 0 0.2 1289 1933)">
<path d="m14.209 9.32617 8.49609 8.54492c4.29688 4.3457 9.22852 4.05273 13.8672-1.07422l53.4668-58.9355-4.83398-4.88281-53.0762 58.3984c-1.75781 2.00195-3.41797 2.49023-5.76172 0.146484l-5.85938-5.81055c-2.34375-2.29492-1.80664-4.00391 0.195312-5.81055l57.373-54.0039-4.88281-4.83398-57.959 54.4434c-4.93164 4.58984-5.32227 9.47266-1.02539 13.8184Zm32.0801-90.9668c-2.09961 2.05078-2.24609 4.93164-1.07422 6.88477 1.17188 1.80664 3.4668 2.97852 6.68945 2.14844 7.32422-1.70898 14.9414-2.00195 22.0703 2.68555l-2.92969 7.27539c-1.70898 4.15039-0.830078 7.08008 1.85547 9.81445l11.4746 11.5723c2.44141 2.44141 4.49219 2.53906 7.32422 2.05078l5.32227-0.976562 3.32031 3.36914-0.195312 2.7832c-0.195312 2.49023 0.439453 4.39453 2.88086 6.78711l3.80859 3.71094c2.39258 2.39258 5.46875 2.53906 7.8125 0.195312l14.5508-14.5996c2.34375-2.34375 2.24609-5.32227-0.146484-7.71484l-3.85742-3.80859c-2.39258-2.39258-4.24805-3.17383-6.64062-2.97852l-2.88086 0.244141-3.22266-3.17383 1.2207-5.61523c0.634766-2.83203-0.146484-5.0293-3.07617-7.95898l-10.9863-10.9375c-16.6992-16.6016-38.8672-16.2109-53.3203-1.75781Zm7.4707 1.85547c12.1582-8.88672 28.6133-7.37305 39.7461 3.75977l12.1582 12.0605c1.17188 1.17188 1.36719 2.09961 1.02539 3.80859l-1.61133 7.42188 7.51953 7.42188 4.93164-0.292969c1.26953-0.0488281 1.66016 0.0488281 2.63672 1.02539l2.88086 2.88086-12.207 12.207-2.88086-2.88086c-0.976562-0.976562-1.12305-1.36719-1.07422-2.68555l0.341797-4.88281-7.4707-7.42188-7.61719 1.26953c-1.61133 0.341797-2.34375 0.195312-3.56445-0.976562l-10.0098-10.0098c-1.26953-1.17188-1.41602-2.00195-0.634766-3.85742l4.39453-10.4492c-7.8125-7.27539-17.9688-10.4004-28.125-7.42188-0.78125 0.195312-1.07422-0.439453-0.439453-0.976562Z"/>
</g>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;" transform="matrix(1 0 0 1 1289 1953)">Exporting</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 1289 1971)">Symbols should be outlined when exporting to ensure the</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 1289 1989)">design is preserved when submitting to Xcode.</text>
<text id="template-version" style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;" transform="matrix(1 0 0 1 3036 1933)">Template v.6.0</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;" transform="matrix(1 0 0 1 3036 1951)">Requires Xcode 16 or greater</text>
<text id="descriptive-name" style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;" transform="matrix(1 0 0 1 3036 1969)">Generated from nextcloud</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;" transform="matrix(1 0 0 1 3036 1987)">Typeset at 100.0 points</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 726)">Small</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 1156)">Medium</text>
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 1586)">Large</text>
</g>
<g id="Guides">
<g id="H-reference" style="fill:#27AAE1;stroke:none;" transform="matrix(1 0 0 1 339 696)">
<path d="M0.993654 0L3.63775 0L29.3281-67.1323L30.0303-67.1323L30.0303-70.459L28.1226-70.459ZM11.6885-24.4799L46.9815-24.4799L46.2315-26.7285L12.4385-26.7285ZM55.1196 0L57.7637 0L30.6382-70.459L29.4326-70.459L29.4326-67.1323Z"/>
</g>
<line id="Baseline-S" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="696" y2="696"/>
<line id="Capline-S" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="625.541" y2="625.541"/>
<g id="H-reference" style="fill:#27AAE1;stroke:none;" transform="matrix(1 0 0 1 339 1126)">
<path d="M0.993654 0L3.63775 0L29.3281-67.1323L30.0303-67.1323L30.0303-70.459L28.1226-70.459ZM11.6885-24.4799L46.9815-24.4799L46.2315-26.7285L12.4385-26.7285ZM55.1196 0L57.7637 0L30.6382-70.459L29.4326-70.459L29.4326-67.1323Z"/>
</g>
<line id="Baseline-M" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="1126" y2="1126"/>
<line id="Capline-M" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="1055.54" y2="1055.54"/>
<g id="H-reference" style="fill:#27AAE1;stroke:none;" transform="matrix(1 0 0 1 339 1556)">
<path d="M0.993654 0L3.63775 0L29.3281-67.1323L30.0303-67.1323L30.0303-70.459L28.1226-70.459ZM11.6885-24.4799L46.9815-24.4799L46.2315-26.7285L12.4385-26.7285ZM55.1196 0L57.7637 0L30.6382-70.459L29.4326-70.459L29.4326-67.1323Z"/>
</g>
<line id="Baseline-L" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="1556" y2="1556"/>
<line id="Capline-L" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="1485.54" y2="1485.54"/>
<line id="right-margin-Black-S" style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="2989.55" x2="2989.55" y1="600.785" y2="720.121"/>
<line id="left-margin-Black-S" style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="2877.25" x2="2877.25" y1="600.785" y2="720.121"/>
<line id="right-margin-Regular-S" style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="1501.6" x2="1501.6" y1="600.785" y2="720.121"/>
<line id="left-margin-Regular-S" style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="1398.09" x2="1398.09" y1="600.785" y2="720.121"/>
<line id="right-margin-Ultralight-S" style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="610.493" x2="610.493" y1="600.785" y2="720.121"/>
<line id="left-margin-Ultralight-S" style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="508.93" x2="508.93" y1="600.785" y2="720.121"/>
</g>
<g id="Symbols">
<g id="Black-S" transform="matrix(1 0 0 1 2877.25 696)">
<path class="SFSymbolsPreviewWireframe" d="M56.15-58.0091C45.7881-58.0091 36.9888-51.019 34.275-41.4795C31.9724-46.4959 26.8737-50.0321 20.9526-50.0321C12.8112-50.0321 6.15002-43.3709 6.15002-35.2295C6.15002-27.088 12.8112-20.4269 20.9526-20.4269C26.8737-20.4269 31.9724-23.963 34.3572-28.9795C37.071-19.44 45.8704-12.4499 56.2322-12.4499C66.5118-12.4499 75.2289-19.3578 78.025-28.7328C80.4099-23.7986 85.5085-20.3446 91.3474-20.3446C99.4888-20.3446 106.15-27.0058 106.15-35.1473C106.15-43.2887 99.4888-49.9499 91.3474-49.9499C85.5085-49.9499 80.4921-46.4959 78.025-41.5617C75.2289-51.1012 66.4296-58.0091 56.15-58.0091ZM56.15-49.292C63.9625-49.292 70.2125-43.042 70.2125-35.2295C70.2125-27.417 63.9625-21.167 56.15-21.167C48.3375-21.167 42.0875-27.417 42.0875-35.2295C42.0875-43.1242 48.3375-49.292 56.15-49.292ZM20.9526-41.3973C24.4066-41.3973 27.0381-38.6834 27.0381-35.3117C27.0381-31.8578 24.3243-29.2262 20.9526-29.2262C17.4987-29.2262 14.8671-31.94 14.8671-35.3117C14.8671-38.6834 17.4987-41.3973 20.9526-41.3973ZM91.2651-41.3973C94.7191-41.3973 97.3506-38.6834 97.3506-35.3117C97.3506-31.8578 94.6368-29.2262 91.2651-29.2262C87.8112-29.2262 85.1796-31.94 85.1796-35.3117C85.1796-38.6834 87.8112-41.3973 91.2651-41.3973Z"/>
</g>
<g id="Regular-S" transform="matrix(1 0 0 1 1398.09 696)">
<path class="SFSymbolsPreviewWireframe" d="M51.755-58.0091C41.3931-58.0091 32.5938-51.019 29.88-41.4795C27.5774-46.4959 22.4787-50.0321 16.5576-50.0321C8.41617-50.0321 1.75497-43.3709 1.75497-35.2295C1.75497-27.088 8.41617-20.4269 16.5576-20.4269C22.4787-20.4269 27.5774-23.963 29.9622-28.9795C32.676-19.44 41.4754-12.4499 51.8372-12.4499C62.1168-12.4499 70.8339-19.3578 73.63-28.7328C76.0149-23.7986 81.1135-20.3446 86.9524-20.3446C95.0938-20.3446 101.755-27.0058 101.755-35.1473C101.755-43.2887 95.0938-49.9499 86.9524-49.9499C81.1135-49.9499 76.0971-46.4959 73.63-41.5617C70.8339-51.1012 62.0346-58.0091 51.755-58.0091ZM51.755-49.292C59.5675-49.292 65.8175-43.042 65.8175-35.2295C65.8175-27.417 59.5675-21.167 51.755-21.167C43.9425-21.167 37.6925-27.417 37.6925-35.2295C37.6925-43.1242 43.9425-49.292 51.755-49.292ZM16.5576-41.3973C20.0116-41.3973 22.6431-38.6834 22.6431-35.3117C22.6431-31.8578 19.9293-29.2262 16.5576-29.2262C13.1037-29.2262 10.4721-31.94 10.4721-35.3117C10.4721-38.6834 13.1037-41.3973 16.5576-41.3973ZM86.8701-41.3973C90.3241-41.3973 92.9556-38.6834 92.9556-35.3117C92.9556-31.8578 90.2418-29.2262 86.8701-29.2262C83.4162-29.2262 80.7846-31.94 80.7846-35.3117C80.7846-38.6834 83.4162-41.3973 86.8701-41.3973Z"/>
</g>
<g id="Ultralight-S" transform="matrix(1 0 0 1 508.93 696)">
<path class="SFSymbolsPreviewWireframe" d="M50.7815-58.3266C40.4197-58.3266 31.6203-51.3365 28.9065-41.797C26.6039-46.8135 21.5052-50.3497 15.5841-50.3497C7.44268-50.3497 0.781495-43.6885 0.781495-35.547C0.781495-27.4056 7.44268-20.7444 15.5841-20.7444C21.5052-20.7444 26.6039-24.2806 28.9887-29.297C31.7025-19.7576 40.5019-12.7674 50.8637-12.7674C61.1433-12.7674 69.8604-19.6753 72.6565-29.0503C75.0414-24.1161 80.14-20.6622 85.9789-20.6622C94.1203-20.6622 100.781-27.3233 100.781-35.4648C100.781-43.6062 94.1203-50.2674 85.9789-50.2674C80.14-50.2674 75.1236-46.8135 72.6565-41.8793C69.8604-51.4187 61.0611-58.3266 50.7815-58.3266ZM50.7815-49.6095C58.594-49.6095 64.844-43.3595 64.844-35.547C64.844-27.7345 58.594-21.4845 50.7815-21.4845C42.969-21.4845 36.719-27.7345 36.719-35.547C36.719-43.4418 42.969-49.6095 50.7815-49.6095ZM15.5841-41.7148C19.0381-41.7148 21.6697-39.001 21.6697-35.6293C21.6697-32.1753 18.9558-29.5437 15.5841-29.5437C12.1302-29.5437 9.49861-32.2576 9.49861-35.6293C9.49861-39.001 12.1302-41.7148 15.5841-41.7148ZM85.8966-41.7148C89.3506-41.7148 91.9822-39.001 91.9822-35.6293C91.9822-32.1753 89.2683-29.5437 85.8966-29.5437C82.4427-29.5437 79.8111-32.2576 79.8111-35.6293C79.8111-39.001 82.4427-41.7148 85.8966-41.7148Z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -2,6 +2,14 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleIcons</key>
<dict>
<key>CFBundlePrimaryIcon</key>
<dict>
<key>CFBundleSymbolName</key>
<string>cloud</string>
</dict>
</dict>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundleDisplayName</key>

View File

@ -0,0 +1,8 @@
// SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
// SPDX-License-Identifier: GPL-2.0-or-later
//
// The common name of the signing certificate to use for code signing.
// It must be present in your keychain and a development certificate.
//
CODE_SIGN_IDENTITY=

View File

@ -0,0 +1,40 @@
#!/bin/env zsh
# SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
# SPDX-License-Identifier: GPL-2.0-or-later
# Read the available environment paths which include (for example) Homebrew.
for f in /etc/paths.d/*; do
while read -r line; do
export PATH="$PATH:$line"
done < "$f"
done
if [ -f "$HOME/.zprofile" ]; then
echo "Sourcing $HOME/.zprofile to include possible PATH definitions..."
source "$HOME/.zprofile"
fi
if [ -z "${CODE_SIGN_IDENTITY}" ]; then
echo "Error: CODE_SIGN_IDENTITY is not defined or is empty!"
exit 1
fi
DESKTOP_CLIENT_PROJECT_ROOT="$SOURCE_ROOT/../../.."
if [ -d "$DESKTOP_CLIENT_PROJECT_ROOT/admin/osx/mac-crafter" ]; then
cd "$DESKTOP_CLIENT_PROJECT_ROOT/admin/osx/mac-crafter"
else
echo "Error: Directory '$DESKTOP_CLIENT_PROJECT_ROOT/admin/osx/mac-crafter' does not exist!"
exit 1
fi
swift run mac-crafter \
--build-path="$SOURCE_ROOT/DerivedData" \
--product-path="/Applications" \
--build-type="Debug" \
--dev \
--disable-auto-updater \
--build-file-provider-module \
--code-sign-identity="$CODE_SIGN_IDENTITY" \
"$DESKTOP_CLIENT_PROJECT_ROOT"

View File

@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objectVersion = 70;
objects = {
/* Begin PBXBuildFile section */
@ -11,7 +11,6 @@
531522822B8E01C6002E31BE /* ShareTableItemView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 531522812B8E01C6002E31BE /* ShareTableItemView.xib */; };
5350E4E92B0C534A00F276CB /* ClientCommunicationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5350E4E82B0C534A00F276CB /* ClientCommunicationService.swift */; };
5352B36C29DC44B50011CE03 /* FileProviderExtension+Thumbnailing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5352B36B29DC44B50011CE03 /* FileProviderExtension+Thumbnailing.swift */; };
5358F2B92BAA0F5300E3C729 /* NextcloudCapabilitiesKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5358F2B82BAA0F5300E3C729 /* NextcloudCapabilitiesKit */; };
53651E442BBC0CA300ECAC29 /* SuggestionsTextFieldKit in Frameworks */ = {isa = PBXBuildFile; productRef = 53651E432BBC0CA300ECAC29 /* SuggestionsTextFieldKit */; };
53651E462BBC0D9500ECAC29 /* ShareeSuggestionsDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53651E452BBC0D9500ECAC29 /* ShareeSuggestionsDataSource.swift */; };
536EFBF7295CF58100F4CB13 /* FileProviderSocketLineProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 536EFBF6295CF58100F4CB13 /* FileProviderSocketLineProcessor.swift */; };
@ -56,6 +55,8 @@
AA9987862E72B6EF00B2C428 /* NextcloudKit+clearAccountErrorState.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA9987852E72B6DB00B2C428 /* NextcloudKit+clearAccountErrorState.swift */; };
AAA69D932E3BB09900BBD44D /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = AAA69D922E3BB09900BBD44D /* Localizable.xcstrings */; };
AAC00D2A2E37B29D006010FE /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = AAC00D292E37B29D006010FE /* Localizable.xcstrings */; };
AAF19A682E8D5B4E005FE5B0 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AAF19A672E8D5B4E005FE5B0 /* Assets.xcassets */; };
AAF19A7A2E8D5B63005FE5B0 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AAF19A792E8D5B63005FE5B0 /* Assets.xcassets */; };
C2B573BA1B1CD91E00303B36 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C2B573B91B1CD91E00303B36 /* main.m */; };
C2B573D21B1CD94B00303B36 /* main.m in Resources */ = {isa = PBXBuildFile; fileRef = C2B573B91B1CD91E00303B36 /* main.m */; };
C2B573DE1B1CD9CE00303B36 /* FinderSync.m in Sources */ = {isa = PBXBuildFile; fileRef = C2B573DD1B1CD9CE00303B36 /* FinderSync.m */; };
@ -196,12 +197,18 @@
53FE14642B8F6700006C4193 /* ShareViewDataSourceUIDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewDataSourceUIDelegate.swift; sourceTree = "<group>"; };
53FE14662B8F78B6006C4193 /* ShareOptionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareOptionsView.swift; sourceTree = "<group>"; };
AA02B2AA2E7048C600C72B34 /* Keychain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Keychain.swift; sourceTree = "<group>"; };
AA0A6B1D2E8EA94F007F4A7A /* Craft.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = Craft.sh; sourceTree = "<group>"; };
AA1191B52E8EAFF900E21C7B /* Build.xcconfig.template */ = {isa = PBXFileReference; lastKnownFileType = text; path = Build.xcconfig.template; sourceTree = "<group>"; };
AA27A4E32E93C0D700665051 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
AA7F17E02E7017230000E928 /* Authentication.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Authentication.storyboard; sourceTree = "<group>"; };
AA7F17E22E70173E0000E928 /* AuthenticationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationViewController.swift; sourceTree = "<group>"; };
AA7F17E62E7038340000E928 /* NSError+FileProviderErrorCode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSError+FileProviderErrorCode.swift"; sourceTree = "<group>"; };
AA826BEE2E8EAA7500CE49C4 /* Build.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Build.xcconfig; sourceTree = "<group>"; };
AA9987852E72B6DB00B2C428 /* NextcloudKit+clearAccountErrorState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NextcloudKit+clearAccountErrorState.swift"; sourceTree = "<group>"; };
AAA69D922E3BB09900BBD44D /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = "<group>"; };
AAC00D292E37B29D006010FE /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = "<group>"; };
AAF19A672E8D5B4E005FE5B0 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
AAF19A792E8D5B63005FE5B0 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
C2B573B11B1CD91E00303B36 /* desktopclient.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = desktopclient.app; sourceTree = BUILT_PRODUCTS_DIR; };
C2B573B51B1CD91E00303B36 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
C2B573B91B1CD91E00303B36 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
@ -217,6 +224,10 @@
C2B573F11B1DAD6400303B36 /* warning.iconset */ = {isa = PBXFileReference; lastKnownFileType = folder.iconset; name = warning.iconset; path = ../../icons/nopadding/warning.iconset; sourceTree = SOURCE_ROOT; };
/* End PBXFileReference section */
/* Begin PBXFileSystemSynchronizedRootGroup section */
AAD7F6032EAA11670071D385 /* gui */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); name = gui; path = /Users/iva/Projekte/nextcloud/desktop/src/gui; sourceTree = "<absolute>"; };
/* End PBXFileSystemSynchronizedRootGroup section */
/* Begin PBXFrameworksBuildPhase section */
538E396427F4765000FA63D5 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
@ -239,7 +250,6 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
5358F2B92BAA0F5300E3C729 /* NextcloudCapabilitiesKit in Frameworks */,
53C331B62BCD3AFF0093D38B /* NextcloudFileProviderKit in Frameworks */,
53651E442BBC0CA300ECAC29 /* SuggestionsTextFieldKit in Frameworks */,
);
@ -341,6 +351,7 @@
538E397227F4765000FA63D5 /* Info.plist */,
5350E4EA2B0C9CE100F276CB /* FileProviderExt-Bridging-Header.h */,
AAA69D922E3BB09900BBD44D /* Localizable.xcstrings */,
AAF19A672E8D5B4E005FE5B0 /* Assets.xcassets */,
);
path = FileProviderExt;
sourceTree = "<group>";
@ -369,10 +380,21 @@
53FE14572B8E3A7C006C4193 /* FileProviderUIExt.entitlements */,
53B979852B84C81F002DA742 /* Info.plist */,
AAC00D292E37B29D006010FE /* Localizable.xcstrings */,
AAF19A792E8D5B63005FE5B0 /* Assets.xcassets */,
);
path = FileProviderUIExt;
sourceTree = "<group>";
};
AA0A6B1C2E8EA948007F4A7A /* NextcloudDev */ = {
isa = PBXGroup;
children = (
AA826BEE2E8EAA7500CE49C4 /* Build.xcconfig */,
AA1191B52E8EAFF900E21C7B /* Build.xcconfig.template */,
AA0A6B1D2E8EA94F007F4A7A /* Craft.sh */,
);
path = NextcloudDev;
sourceTree = "<group>";
};
AA7F17DF2E70171A0000E928 /* Authentication */ = {
isa = PBXGroup;
children = (
@ -385,7 +407,10 @@
C2B573941B1CD88000303B36 = {
isa = PBXGroup;
children = (
AAD7F6032EAA11670071D385 /* gui */,
AA27A4E32E93C0D700665051 /* README.md */,
C2B573B31B1CD91E00303B36 /* desktopclient */,
AA0A6B1C2E8EA948007F4A7A /* NextcloudDev */,
C2B573D81B1CD9CE00303B36 /* FinderSyncExt */,
538E396B27F4765000FA63D5 /* FileProviderExt */,
53B9797F2B84C81F002DA742 /* FileProviderUIExt */,
@ -466,6 +491,25 @@
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXLegacyTarget section */
AA0A62F22E8EA929007F4A7A /* NextcloudDev */ = {
isa = PBXLegacyTarget;
buildArgumentsString = "\"$(SOURCE_ROOT)/NextcloudDev/Craft.sh\"";
buildConfigurationList = AA0A62F52E8EA929007F4A7A /* Build configuration list for PBXLegacyTarget "NextcloudDev" */;
buildPhases = (
);
buildToolPath = /bin/zsh;
buildWorkingDirectory = "";
dependencies = (
);
name = NextcloudDev;
packageProductDependencies = (
);
passBuildSettingsInEnvironment = 1;
productName = NextcloudDev;
};
/* End PBXLegacyTarget section */
/* Begin PBXNativeTarget section */
538E396627F4765000FA63D5 /* FileProviderExt */ = {
isa = PBXNativeTarget;
@ -521,7 +565,6 @@
);
name = FileProviderUIExt;
packageProductDependencies = (
5358F2B82BAA0F5300E3C729 /* NextcloudCapabilitiesKit */,
53651E432BBC0CA300ECAC29 /* SuggestionsTextFieldKit */,
53C331B52BCD3AFF0093D38B /* NextcloudFileProviderKit */,
);
@ -593,6 +636,9 @@
53B9797D2B84C81F002DA742 = {
CreatedOnToolsVersion = 15.2;
};
AA0A62F22E8EA929007F4A7A = {
CreatedOnToolsVersion = 26.0.1;
};
C2B573B01B1CD91E00303B36 = {
CreatedOnToolsVersion = 6.3.1;
DevelopmentTeam = 9B5WD74GWJ;
@ -726,7 +772,6 @@
);
mainGroup = C2B573941B1CD88000303B36;
packageReferences = (
5358F2B72BAA045E00E3C729 /* XCRemoteSwiftPackageReference "NextcloudCapabilitiesKit" */,
53651E422BBC0C7F00ECAC29 /* XCRemoteSwiftPackageReference "SuggestionsTextFieldKit" */,
53C331B02BCD28C30093D38B /* XCRemoteSwiftPackageReference "NextcloudFileProviderKit" */,
);
@ -739,6 +784,7 @@
538E396627F4765000FA63D5 /* FileProviderExt */,
53B9797D2B84C81F002DA742 /* FileProviderUIExt */,
53903D0B2956164F00D0B308 /* NCDesktopClientSocketKit */,
AA0A62F22E8EA929007F4A7A /* NextcloudDev */,
);
};
/* End PBXProject section */
@ -748,6 +794,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
AAF19A682E8D5B4E005FE5B0 /* Assets.xcassets in Resources */,
AAA69D932E3BB09900BBD44D /* Localizable.xcstrings in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -767,6 +814,7 @@
AAC00D2A2E37B29D006010FE /* Localizable.xcstrings in Resources */,
531522822B8E01C6002E31BE /* ShareTableItemView.xib in Resources */,
537630912B85F4980026BFAB /* ShareViewController.xib in Resources */,
AAF19A7A2E8D5B63005FE5B0 /* Assets.xcassets in Resources */,
AA7F17E12E7017230000E928 /* Authentication.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -928,7 +976,6 @@
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = "";
ENABLE_TESTING_SEARCH_PATHS = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
@ -985,7 +1032,6 @@
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = "";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_TESTING_SEARCH_PATHS = YES;
@ -1162,7 +1208,6 @@
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GCC_DYNAMIC_NO_PIC = NO;
@ -1227,7 +1272,6 @@
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = "";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
@ -1267,6 +1311,80 @@
};
name = Release;
};
AA0A62F32E8EA929007F4A7A /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = AA826BEE2E8EAA7500CE49C4 /* Build.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
DEBUGGING_SYMBOLS = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GCC_DYNAMIC_NO_PIC = NO;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
OTHER_CFLAGS = "";
OTHER_LDFLAGS = "";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
AA0A62F42E8EA929007F4A7A /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = AA826BEE2E8EAA7500CE49C4 /* Build.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
OTHER_CFLAGS = "";
OTHER_LDFLAGS = "";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
C2B573991B1CD88000303B36 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@ -1288,6 +1406,7 @@
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_TESTING_SEARCH_PATHS = YES;
@ -1323,6 +1442,7 @@
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTING_SEARCH_PATHS = YES;
GCC_NO_COMMON_BLOCKS = YES;
@ -1359,7 +1479,6 @@
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
@ -1416,7 +1535,6 @@
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
@ -1464,7 +1582,6 @@
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
@ -1524,7 +1641,6 @@
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
@ -1584,6 +1700,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
AA0A62F52E8EA929007F4A7A /* Build configuration list for PBXLegacyTarget "NextcloudDev" */ = {
isa = XCConfigurationList;
buildConfigurations = (
AA0A62F32E8EA929007F4A7A /* Debug */,
AA0A62F42E8EA929007F4A7A /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
C2B573981B1CD88000303B36 /* Build configuration list for PBXProject "NextcloudIntegration" */ = {
isa = XCConfigurationList;
buildConfigurations = (
@ -1614,14 +1739,6 @@
/* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
5358F2B72BAA045E00E3C729 /* XCRemoteSwiftPackageReference "NextcloudCapabilitiesKit" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/claucambra/NextcloudCapabilitiesKit.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 2.3.0;
};
};
53651E422BBC0C7F00ECAC29 /* XCRemoteSwiftPackageReference "SuggestionsTextFieldKit" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/claucambra/SuggestionsTextFieldKit.git";
@ -1634,18 +1751,13 @@
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/nextcloud/NextcloudFileProviderKit.git";
requirement = {
branch = main;
kind = branch;
kind = upToNextMajorVersion;
minimumVersion = 3.0.0;
};
};
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
5358F2B82BAA0F5300E3C729 /* NextcloudCapabilitiesKit */ = {
isa = XCSwiftPackageProductDependency;
package = 5358F2B72BAA045E00E3C729 /* XCRemoteSwiftPackageReference "NextcloudCapabilitiesKit" */;
productName = NextcloudCapabilitiesKit;
};
53651E432BBC0CA300ECAC29 /* SuggestionsTextFieldKit */ = {
isa = XCSwiftPackageProductDependency;
package = 53651E422BBC0C7F00ECAC29 /* XCRemoteSwiftPackageReference "SuggestionsTextFieldKit" */;

View File

@ -1,5 +1,5 @@
{
"originHash" : "af7b30e0399513f38616bd75821c834208fc3c22e3f5ec7d1765b1dbb03a46cd",
"originHash" : "74a3274bff88648671702b7d1dfa0c8ea5acb44280987019f6c5bb7055b848b2",
"pins" : [
{
"identity" : "alamofire",
@ -13,10 +13,10 @@
{
"identity" : "nextcloudcapabilitieskit",
"kind" : "remoteSourceControl",
"location" : "https://github.com/claucambra/NextcloudCapabilitiesKit.git",
"location" : "https://github.com/nextcloud/NextcloudCapabilitiesKit.git",
"state" : {
"revision" : "a680e947d6cc2a1c101335797213e4b7d3b22102",
"version" : "2.3.0"
"revision" : "07a481be18943cfa3ce17c91b83d6017cdbb5846",
"version" : "2.4.5"
}
},
{
@ -24,8 +24,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/nextcloud/NextcloudFileProviderKit.git",
"state" : {
"branch" : "main",
"revision" : "b87926142d8bc474c95acc29f66fce102ed27942"
"revision" : "aef90f069659f240380e05f9301e080e92e094aa",
"version" : "3.2.8"
}
},
{
@ -33,8 +33,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/nextcloud/NextcloudKit",
"state" : {
"revision" : "96c2b0e6a8bbf84c69412128f703812aec5982de",
"version" : "7.1.5"
"revision" : "71c06c6b1ea0a8f042133fdd9843901930b827f8",
"version" : "7.1.8"
}
},
{

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "2600"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AA0A62F22E8EA929007F4A7A"
BuildableName = "NextcloudDev"
BlueprintName = "NextcloudDev"
ReferencedContainer = "container:NextcloudIntegration.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "NO"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<PathRunnable
runnableDebuggingMode = "0"
BundleIdentifier = "com.nextcloud.desktopclient"
FilePath = "/Applications/NextcloudDev.app">
</PathRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AA0A62F22E8EA929007F4A7A"
BuildableName = "NextcloudDev"
BlueprintName = "NextcloudDev"
ReferencedContainer = "container:NextcloudIntegration.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,105 @@
# Nextcloud macOS Integration Project
This is an Xcode project to build platform-specific components for the Nextcloud desktop client.
As an example, this includes the file provider extension.
## Localization
Transifex is used for localization.
Currently, [the file provider extension](https://app.transifex.com/nextcloud/nextcloud/client-fileprovider/) and [file provider UI extension](https://app.transifex.com/nextcloud/nextcloud/client-fileproviderui/) both have a resource there.
These localizations are excluded from our usual and automated translation flow due to how Transifex synchronizes Xcode string catalogs and the danger of data loss.
To pull updated localizations from Transifex into the Xcode project manually, follow the steps below.
### Configuration
The dedicated [`.tx/config`](.tx/config) file is used.
## Pull Translations
Run this in the "NextcloudIntegration" project folder of your repository clone:
```sh
tx pull --force --all --mode=reviewed --minimum-perc=50
```
### Sanitize Translations
Transifex returns empty strings for keys with untranslated localizations.
To remove them, we use the Swift command-line utility [TransifexStringCatalogSanitizer](../../../admin/osx/TransifexStringCatalogSanitizer/).
See its dedicated README for usage instructions.
Use it for all updated Xcode string catalogs.
### Cleanup
1. Revert your changes to the Transifex configuration.
2. Review the updated Xcode string catalogs.
3. Commit the updated Xcode string catalogs.
## Nextcloud Developer Build
There is a special target in the Xcode project which integrates the `mac-crafter` command-line tool as an external build system in form of a scheme.
In other words: The "NextcloudDev" scheme enables you to build, run and debug the Nextcloud desktop client with the simple run action in Xcode.
### Requirements
- You must have an Apple Development certificate for signing in your keychain.
### Usage
1. Copy [`Build.xcconfig.template`](NextcloudDev/Build.xcconfig.template) in the "NextcloudDev" source code folder to `Build.xcconfig` in the same location and adjust the values in it to your local setup.
2. Build or run the "NextcloudDev scheme".
### Known Issues
- Right now, the project does not support signing a different app identity than the default one (`com.nextcloud.desktopclient`) which is owned by the Nextcloud GmbH development team registered with Apple.
This means that you have to be signed in with a developer account in Xcode which is part of that development team when building.
- Even when building successfully, Xcode may conclude that the build failed or at least some errors occurred during the build.
During the build, some command outputs messages with an "Error: " prefix.
Since this is the same way the compiler usually reports errors to Xcode, the latter assumes something might have gone wrong.
But no invocation exits with an error code.
Hence, the build can still complete successfully while Xcode might just misinterpret the console output.
You will see at the end of the build output log in Xcode.
### How It Works
- The "NextcloudDev" target runs the [Craft.sh](NextcloudDev/Craft.sh) shell script which is part of this Xcode project.
- `Craft.sh` prepares the execution of and finally runs [`mac-crafter`](https://github.com/nextcloud/desktop/tree/master/admin/osx/mac-crafter) which is part of the Nextcloud desktop client repository to simplify builds on macOS.
- By running `mac-crafter` with the right arguments and options, Xcode can attach to the built app with its debugger and stop at breakpoints.
One of the key factors is the `Debug` build type which flips certain switches in the CMake build scripts ([in example: app hardening or get-task-allow entitlement](https://github.com/nextcloud/desktop/pull/8474/files)).
- The built Nextcloud desktop client app bundle is not placed into a derived data directory of Xcode but `/Applications`.
The standard behavior of placing the product into Xcode's derived data directory would result in absolute reference paths within the scheme file which are not portable across devices and users due to varying user names.
### Hints
Just for reference, a few helpful snippets for inspecting state on breakpoints with the Xcode debugger.
#### Print a `QString`
```lldb
call someString.toStdString()
```
#### Print a `QStringList`
```lldb
call someStrings.join("\n").toStdString()
```
#### Attach to File Provider Extension Process
You can debug the file provider extension process(es) in Xcode by attaching to them by their binary name.
1. Select this menu item in Xcode: _Debug_ → _Attach to Process by PID or Name..._
2. Enter `FileProviderExt`.
If you would also like to debug the file provider UI extension, then you can additionally specify `FileProviderUIExt`.
3. Confirm.
4. If no process is already running, then Xcode will wait for it to be launched to attach automatically.
This usually happens when you launch the main app or set up a new account with file provider enabled.
#### Work on NextcloudFileProviderKit and NextcloudKit
You can directly debug changes to these dependencies edited from this project.
You have to have local repository clones of the packages somewhere locally, though.
Drag and drop the package folders into the project navigator of the NextcloudIntegration project.
This will cause Xcode to resolve to the local and editable package instead of a cached read-only clone from GitHub.
When you then run the build action of this root project, the local dependency is built as well.

View File

@ -31,8 +31,8 @@ if(NOT BUILD_LIBRARIES_ONLY)
add_executable(nextcloudcmd
cmd.h
cmd.cpp)
set_target_properties(nextcloudcmd PROPERTIES
RUNTIME_OUTPUT_NAME "${APPLICATION_EXECUTABLE}cmd")
ecm_mark_nongui_executable(nextcloudcmd)
set_target_properties(nextcloudcmd PROPERTIES RUNTIME_OUTPUT_NAME "${APPLICATION_EXECUTABLE}cmd")
target_link_libraries(nextcloudcmd cmdCore)

View File

@ -779,14 +779,14 @@ bool FileSystem::setAclPermission(const QString &unsafePath, FolderPermissions p
if (!GetFileSecurityW(path.toStdWString().c_str(), info, nullptr, 0, &neededLength)) {
const auto lastError = GetLastError();
if (lastError != ERROR_INSUFFICIENT_BUFFER) {
qCWarning(lcFileSystem) << "error when calling GetFileSecurityW" << path << lastError;
qCWarning(lcFileSystem) << "error when calling GetFileSecurityW" << path << Utility::formatWinError(lastError);
return false;
}
securityDescriptor.reset(new char[neededLength]);
if (!GetFileSecurityW(path.toStdWString().c_str(), info, securityDescriptor.get(), neededLength, &neededLength)) {
qCWarning(lcFileSystem) << "error when calling GetFileSecurityW" << path << GetLastError();
qCWarning(lcFileSystem) << "error when calling GetFileSecurityW" << path << Utility::formatWinError(GetLastError());
return false;
}
}
@ -794,7 +794,7 @@ bool FileSystem::setAclPermission(const QString &unsafePath, FolderPermissions p
int daclPresent = false, daclDefault = false;
PACL resultDacl = nullptr;
if (!GetSecurityDescriptorDacl(securityDescriptor.get(), &daclPresent, &resultDacl, &daclDefault)) {
qCWarning(lcFileSystem) << "error when calling GetSecurityDescriptorDacl" << path << GetLastError();
qCWarning(lcFileSystem) << "error when calling GetSecurityDescriptorDacl" << path << Utility::formatWinError(GetLastError());
return false;
}
if (!daclPresent || !resultDacl) {
@ -805,13 +805,13 @@ bool FileSystem::setAclPermission(const QString &unsafePath, FolderPermissions p
PSID sid = nullptr;
if (!ConvertStringSidToSidW(L"S-1-5-32-545", &sid))
{
qCWarning(lcFileSystem) << "error when calling ConvertStringSidToSidA" << path << GetLastError();
qCWarning(lcFileSystem) << "error when calling ConvertStringSidToSidA" << path << Utility::formatWinError(GetLastError());
return false;
}
ACL_SIZE_INFORMATION aclSize;
if (!GetAclInformation(resultDacl, &aclSize, sizeof(aclSize), AclSizeInformation)) {
qCWarning(lcFileSystem) << "error when calling GetAclInformation" << path << GetLastError();
qCWarning(lcFileSystem) << "error when calling GetAclInformation" << path << Utility::formatWinError(GetLastError());
return false;
}
@ -821,19 +821,19 @@ bool FileSystem::setAclPermission(const QString &unsafePath, FolderPermissions p
std::unique_ptr<ACL> newDacl{reinterpret_cast<PACL>(new char[newAclSize])};
if (!InitializeAcl(newDacl.get(), newAclSize, ACL_REVISION)) {
const auto lastError = GetLastError();
if (lastError != ERROR_INSUFFICIENT_BUFFER) {
if (lastError == ERROR_INSUFFICIENT_BUFFER) {
qCWarning(lcFileSystem) << "insufficient memory error when calling InitializeAcl" << path;
return false;
}
qCWarning(lcFileSystem) << "error when calling InitializeAcl" << path << lastError;
qCWarning(lcFileSystem) << "error when calling InitializeAcl" << path << Utility::formatWinError(lastError);
return false;
}
if (permissions == FileSystem::FolderPermissions::ReadOnly) {
if (!AddAccessDeniedAceEx(newDacl.get(), ACL_REVISION, OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE,
FILE_DELETE_CHILD | DELETE | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA, sid)) {
qCWarning(lcFileSystem) << "error when calling AddAccessDeniedAce << path" << GetLastError();
qCWarning(lcFileSystem) << "error when calling AddAccessDeniedAce" << path << Utility::formatWinError(GetLastError());
return false;
}
}
@ -841,7 +841,7 @@ bool FileSystem::setAclPermission(const QString &unsafePath, FolderPermissions p
for (int i = 0; i < aclSize.AceCount; ++i) {
void *currentAce = nullptr;
if (!GetAce(resultDacl, i, &currentAce)) {
qCWarning(lcFileSystem) << "error when calling GetAce" << path << GetLastError();
qCWarning(lcFileSystem) << "error when calling GetAce" << path << Utility::formatWinError(GetLastError());
return false;
}
@ -854,29 +854,29 @@ bool FileSystem::setAclPermission(const QString &unsafePath, FolderPermissions p
if (!AddAce(newDacl.get(), ACL_REVISION, i + 1, currentAce, currentAceHeader->AceSize)) {
const auto lastError = GetLastError();
if (lastError != ERROR_INSUFFICIENT_BUFFER) {
if (lastError == ERROR_INSUFFICIENT_BUFFER) {
qCWarning(lcFileSystem) << "insufficient memory error when calling AddAce" << path;
return false;
}
if (lastError != ERROR_INVALID_PARAMETER) {
if (lastError == ERROR_INVALID_PARAMETER) {
qCWarning(lcFileSystem) << "invalid parameter error when calling AddAce" << path << "ACL size" << newAclSize;
return false;
}
qCWarning(lcFileSystem) << "error when calling AddAce" << path << lastError << "acl index" << (i + 1);
qCWarning(lcFileSystem) << "error when calling AddAce" << path << Utility::formatWinError(lastError) << "acl index" << (i + 1);
return false;
}
}
SECURITY_DESCRIPTOR newSecurityDescriptor;
if (!InitializeSecurityDescriptor(&newSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION)) {
qCWarning(lcFileSystem) << "error when calling InitializeSecurityDescriptor" << path << GetLastError();
qCWarning(lcFileSystem) << "error when calling InitializeSecurityDescriptor" << path << Utility::formatWinError(GetLastError());
return false;
}
if (!SetSecurityDescriptorDacl(&newSecurityDescriptor, true, newDacl.get(), false)) {
qCWarning(lcFileSystem) << "error when calling SetSecurityDescriptorDacl" << path << GetLastError();
qCWarning(lcFileSystem) << "error when calling SetSecurityDescriptorDacl" << path << Utility::formatWinError(GetLastError());
return false;
}
@ -896,14 +896,14 @@ bool FileSystem::setAclPermission(const QString &unsafePath, FolderPermissions p
}
if (!SetFileSecurityW(childFileStdWString.c_str(), info, &newSecurityDescriptor)) {
qCWarning(lcFileSystem) << "error when calling SetFileSecurityW" << childFile << GetLastError();
qCWarning(lcFileSystem) << "error when calling SetFileSecurityW" << childFile << Utility::formatWinError(GetLastError());
return false;
}
}
}
if (!SetFileSecurityW(QDir::toNativeSeparators(path).toStdWString().c_str(), info, &newSecurityDescriptor)) {
qCWarning(lcFileSystem) << "error when calling SetFileSecurityW" << QDir::toNativeSeparators(path) << GetLastError();
qCWarning(lcFileSystem) << "error when calling SetFileSecurityW" << QDir::toNativeSeparators(path) << Utility::formatWinError(GetLastError());
return false;
}

View File

@ -14,7 +14,7 @@ namespace OCC {
/**
* Represent the quota for each folder retrieved from the server
* bytesUsed: space used in bytes
* bytesAvailale: free space available in bytes or
* bytesAvailable: free space available in bytes or
* -1: Uncomputed free space - new folder (externally created) not yet scanned by the server
* -2: Unknown free space
* -3: Unlimited free space.

View File

@ -733,7 +733,8 @@ void ExcludedFiles::prepare(const BasePathString & basePath)
pattern.append(appendMe);
};
for (auto exclude : _allExcludes.value(basePath)) {
const auto &allValues = _allExcludes.value(basePath);
for (auto exclude : allValues) {
if (exclude[0] == QLatin1Char('\n'))
continue; // empty line
if (exclude[0] == QLatin1Char('\r'))

View File

@ -302,17 +302,12 @@ IF( APPLE )
macOS/fileprovider_mac.mm
macOS/fileproviderdomainmanager.h
macOS/fileproviderdomainmanager_mac.mm
macOS/fileproviderdomainsyncstatus.h
macOS/fileproviderdomainsyncstatus_mac.mm
macOS/fileprovidereditlocallyjob.h
macOS/fileprovidereditlocallyjob.cpp
macOS/fileprovidereditlocallyjob_mac.mm
macOS/fileprovideritemmetadata.h
macOS/fileprovideritemmetadata.cpp
macOS/fileprovideritemmetadata_mac.mm
macOS/fileprovidermaterialiseditemsmodel.h
macOS/fileprovidermaterialiseditemsmodel.cpp
macOS/fileprovidermaterialiseditemsmodel_mac.mm
macOS/fileprovidersettingscontroller.h
macOS/fileprovidersettingscontroller_mac.mm
macOS/fileprovidersocketcontroller.h
@ -320,8 +315,6 @@ IF( APPLE )
macOS/fileprovidersocketserver.h
macOS/fileprovidersocketserver.cpp
macOS/fileprovidersocketserver_mac.mm
macOS/fileproviderstorageuseenumerationobserver.h
macOS/fileproviderstorageuseenumerationobserver.m
macOS/fileproviderutils.h
macOS/fileproviderutils_mac.mm
macOS/fileproviderxpc.h
@ -484,27 +477,6 @@ set(APP_ICON_WIN_FOLDER_SVG "${APP_SECONDARY_ICONS}/${APPLICATION_ICON_NAME}-ico
set(RC_DEPENDENCIES "")
if(WIN32)
if (EXISTS ${APP_ICON_WIN_FOLDER_SVG})
get_filename_component(output_icon_name_win ${APP_ICON_WIN_FOLDER_SVG} NAME_WLE)
# Product icon (for smallest size)
foreach(size IN ITEMS 16;20)
generate_sized_png_from_svg(${APP_ICON_SVG} ${size} OUTPUT_ICON_NAME ${output_icon_name_win} OUTPUT_ICON_PATH "${APP_SECONDARY_ICONS}/")
endforeach()
# Product icon with Windows folder (for sizes larger than 20)
foreach(size IN ITEMS 24;32;40;48;64;128;256;512;1024)
generate_sized_png_from_svg(${APP_ICON_WIN_FOLDER_SVG} ${size} OUTPUT_ICON_NAME ${output_icon_name_win} OUTPUT_ICON_PATH "${APP_SECONDARY_ICONS}/")
endforeach()
file(GLOB_RECURSE OWNCLOUD_ICONS_WIN_FOLDER "${APP_SECONDARY_ICONS}/*-${APPLICATION_ICON_NAME}-icon*")
set(APP_ICON_WIN_FOLDER_ICO_NAME "${APPLICATION_ICON_NAME}-win-folder")
set(RC_DEPENDENCIES "${RC_DEPENDENCIES} ${APP_ICON_WIN_FOLDER_ICO_NAME}.ico")
ecm_add_app_icon(APP_ICON_WIN_FOLDER ICONS "${OWNCLOUD_ICONS_WIN_FOLDER}" SIDEBAR_ICONS "${OWNCLOUD_SIDEBAR_ICONS}" OUTFILE_BASENAME "${APP_ICON_WIN_FOLDER_ICO_NAME}" ICON_INDEX 2)
endif()
endif()
# --------------------------------------
if (NOT ${RC_DEPENDENCIES} STREQUAL "")
string(STRIP ${RC_DEPENDENCIES} RC_DEPENDENCIES)
endif()
@ -527,35 +499,26 @@ if(APPLE)
MESSAGE(STATUS "OWNCLOUD_SIDEBAR_ICONS: ${APPLICATION_ICON_NAME}: ${OWNCLOUD_SIDEBAR_ICONS}")
endif()
ecm_add_app_icon(APP_ICON RC_DEPENDENCIES ${RC_DEPENDENCIES} ICONS "${OWNCLOUD_ICONS}" SIDEBAR_ICONS "${OWNCLOUD_SIDEBAR_ICONS}" OUTFILE_BASENAME "${APPLICATION_ICON_NAME}" ICON_INDEX 1)
# --------------------------------------
if(WIN32 AND EXISTS ${APP_ICON_WIN_FOLDER_SVG})
get_filename_component(output_icon_name_win ${APP_ICON_WIN_FOLDER_SVG} NAME_WLE)
# Product icon (for smallest size)
foreach(size IN ITEMS 16;20)
generate_sized_png_from_svg(${APP_ICON_SVG} ${size} OUTPUT_ICON_NAME ${output_icon_name_win} OUTPUT_ICON_PATH "${APP_SECONDARY_ICONS}/")
endforeach()
if(WIN32)
# merge *.rc.in files for Windows (multiple ICON resources must be placed in a single file, otherwise, this won't work de to a bug in Windows compiler https://developercommunity.visualstudio.com/t/visual-studio-2017-prof-1557-cvt1100-duplicate-res/363156)
function(merge_files IN_FILE OUT_FILE)
file(READ ${IN_FILE} CONTENTS)
message("Merging ${IN_FILE} into ${OUT_FILE}")
file(APPEND ${OUT_FILE} "${CONTENTS}")
endfunction()
message("APP_ICON is: ${APP_ICON}")
if(APP_ICON)
get_filename_component(RC_IN_FOLDER ${APP_ICON}} DIRECTORY)
# Product icon with Windows folder (for sizes larger than 20)
foreach(size IN ITEMS 24;32;40;48;64;128;256;512;1024)
generate_sized_png_from_svg(${APP_ICON_WIN_FOLDER_SVG} ${size} OUTPUT_ICON_NAME ${output_icon_name_win} OUTPUT_ICON_PATH "${APP_SECONDARY_ICONS}/")
endforeach()
file(GLOB_RECURSE RC_IN_FILES "${RC_IN_FOLDER}/*rc.in")
foreach(rc_in_file IN ITEMS ${RC_IN_FILES})
get_filename_component(rc_in_file_name ${rc_in_file} NAME)
get_filename_component(app_icon_name "${APP_ICON}.in" NAME)
if(NOT "${rc_in_file_name}" STREQUAL "${app_icon_name}")
merge_files(${rc_in_file} "${APP_ICON}.in")
if (DEFINED APPLICATION_FOLDER_ICON_INDEX)
MATH(EXPR APPLICATION_FOLDER_ICON_INDEX "${APPLICATION_FOLDER_ICON_INDEX}+1")
message("APPLICATION_FOLDER_ICON_INDEX is now set to: ${APPLICATION_FOLDER_ICON_INDEX}")
endif()
endif()
endforeach()
endif()
file(GLOB_RECURSE OWNCLOUD_ICONS_WIN_FOLDER "${APP_SECONDARY_ICONS}/*-${APPLICATION_ICON_NAME}-icon*")
set(APP_ICON_WIN_FOLDER_ICO_NAME "${APPLICATION_ICON_NAME}-win-folder")
set(RC_DEPENDENCIES "${RC_DEPENDENCIES} ${APP_ICON_WIN_FOLDER_ICO_NAME}.ico")
ecm_add_app_icon(APP_ICON ICONS ${OWNCLOUD_ICONS_WIN_FOLDER} ${OWNCLOUD_ICONS} SIDEBAR_ICONS "${OWNCLOUD_SIDEBAR_ICONS}" OUTFILE_BASENAME "${APPLICATION_ICON_NAME}")
else ()
ecm_add_app_icon(APP_ICON ICONS "${OWNCLOUD_ICONS}" SIDEBAR_ICONS "${OWNCLOUD_SIDEBAR_ICONS}" OUTFILE_BASENAME "${APPLICATION_ICON_NAME}")
endif()
# --------------------------------------
if(UNIX AND NOT APPLE)

View File

@ -116,6 +116,7 @@ ColumnLayout {
}
ToolTip {
popupType: Qt.platform.os === "windows" ? Popup.Item : Popup.Native
text: modelData === undefined ? "" : modelData.shortname
visible: emojiDelegate.hovered
delay: Qt.styleHints.mousePressAndHoldInterval

View File

@ -217,7 +217,7 @@ bool AccountManager::restoreFromLegacySettings()
legacyLocations.append({legacyCfgFileParentFolder + unbrandedCfgFileNamePath, legacyCfgFileGrandParentFolder + unbrandedCfgFileRelativePath});
}
for (const auto &configFile : legacyLocations) {
for (const auto &configFile : std::as_const(legacyLocations)) {
auto oCSettings = std::make_unique<QSettings>(configFile, QSettings::IniFormat);
if (oCSettings->status() != QSettings::Status::NoError) {
qCInfo(lcAccountManager) << "Error reading legacy configuration file" << oCSettings->status();
@ -699,7 +699,7 @@ void AccountManager::deleteAccount(OCC::AccountState *account)
void AccountManager::updateServerHasValidSubscriptionConfig()
{
auto serverHasValidSubscription = false;
for (const auto &account : _accounts) {
for (const auto &account : std::as_const(_accounts)) {
if (!account->account()->serverHasValidSubscription()) {
continue;
}

View File

@ -1143,7 +1143,8 @@ void AccountSettings::forgetEncryptionOnDeviceForAccount(const AccountPtr &accou
void AccountSettings::migrateCertificateForAccount(const AccountPtr &account)
{
for (const auto action : _ui->encryptionMessage->actions()) {
const auto &allActions = _ui->encryptionMessage->actions();
for (const auto action : allActions) {
_ui->encryptionMessage->removeAction(action);
}
@ -1703,7 +1704,8 @@ void AccountSettings::setupE2eEncryption()
void AccountSettings::forgetE2eEncryption()
{
for (const auto action : _ui->encryptionMessage->actions()) {
const auto &allActions = _ui->encryptionMessage->actions();
for (const auto action : allActions) {
_ui->encryptionMessage->removeAction(action);
}
_ui->encryptionMessage->setText({});

View File

@ -293,7 +293,8 @@ Application::Application(int &argc, char **argv)
if (_theme->doNotUseProxy()) {
ConfigFile().setProxyType(QNetworkProxy::NoProxy);
for (const auto &accountState : AccountManager::instance()->accounts()) {
const auto &allAccounts = AccountManager::instance()->accounts();
for (const auto &accountState : allAccounts) {
if (accountState && accountState->account()) {
accountState->account()->setProxyType(QNetworkProxy::NoProxy);
}
@ -401,7 +402,8 @@ Application::Application(int &argc, char **argv)
if (_theme->doNotUseProxy()) {
ConfigFile().setProxyType(QNetworkProxy::NoProxy);
for (const auto &accountState : AccountManager::instance()->accounts()) {
const auto &allAccounts = AccountManager::instance()->accounts();
for (const auto &accountState : allAccounts) {
if (accountState && accountState->account()) {
accountState->account()->setProxyType(QNetworkProxy::NoProxy);
}

View File

@ -9,6 +9,7 @@
#include "account.h"
#include "folder.h"
#include "common/filesystembase.h"
#include "common/utility.h"
#include <QPushButton>
#include <QDir>
@ -202,7 +203,7 @@ void CaseClashFilenameDialog::updateFileWidgetGroup(const QString &filePath,
const auto lastModifiedString = filePathFileInfo.lastModified().toString();
const auto fileSizeString = locale().formattedDataSize(filePathFileInfo.size());
const auto fileUrl = QUrl::fromLocalFile(filePath).toString();
const auto linkString = QStringLiteral("<a href='%1'>%2</a>").arg(fileUrl, linkText);
const auto linkString = QStringLiteral("<a href=\"%1\">%2</a>").arg(Utility::escape(fileUrl), linkText);
const auto mime = QMimeDatabase().mimeTypeForFile(_filePath, QMimeDatabase::MatchExtension);
QIcon fileTypeIcon;

View File

@ -7,6 +7,7 @@
#include "ui_conflictdialog.h"
#include "conflictsolver.h"
#include "common/utility.h"
#include <QDateTime>
#include <QDebug>
@ -121,7 +122,7 @@ void ConflictDialog::updateWidgets()
const auto updateGroup = [this, &mimeDb](const QString &filename, QLabel *linkLabel, const QString &linkText, QLabel *mtimeLabel, QLabel *sizeLabel, QToolButton *button) {
const auto fileUrl = QUrl::fromLocalFile(filename).toString();
linkLabel->setText(QStringLiteral("<a href='%1'>%2</a>").arg(fileUrl).arg(linkText));
linkLabel->setText(QStringLiteral("<a href=\"%1\">%2</a>").arg(Utility::escape(fileUrl)).arg(linkText));
const auto info = QFileInfo(filename);
mtimeLabel->setText(info.lastModified().toString());

View File

@ -196,6 +196,7 @@ Page {
}
ToolTip {
popupType: Qt.platform.os === "windows" ? Popup.Item : Popup.Native
visible: hoverHandler.hovered
text: tagRepeater.fileTagModel.overflowTagsString
}

View File

@ -385,7 +385,8 @@ void FolderMan::backwardMigrationSettingsKeys(QStringList *deleteKeys, QStringLi
const auto foldersVersion = settings->value(QLatin1String(settingsVersionC), 1).toInt();
qCInfo(lcFolderMan) << "FolderDefinition::maxSettingsVersion:" << FolderDefinition::maxSettingsVersion();
if (foldersVersion <= maxFoldersVersion) {
for (const auto &folderAlias : settings->childGroups()) {
const auto &childGroups = settings->childGroups();
for (const auto &folderAlias : childGroups) {
settings->beginGroup(folderAlias);
const auto folderVersion = settings->value(QLatin1String(settingsVersionC), 1).toInt();
if (folderVersion > FolderDefinition::maxSettingsVersion()) {
@ -591,7 +592,7 @@ void FolderMan::setupLegacyFolder(const QString &fileNamePath, AccountState *acc
legacyBlacklist << settings.value(QLatin1String("blackList")).toStringList();
if (!legacyBlacklist.isEmpty()) {
qCInfo(lcFolderMan) << "Legacy selective sync list found:" << legacyBlacklist;
for (const auto &legacyFolder : legacyBlacklist) {
for (const auto &legacyFolder : std::as_const(legacyBlacklist)) {
folder->migrateBlackListPath(legacyFolder);
}
settings.remove(QLatin1String("blackList"));

View File

@ -333,7 +333,7 @@ bool FolderStatusModel::setData(const QModelIndex &index, const QVariant &value,
const auto parentInfo = infoForIndex(parent);
if (parentInfo && parentInfo->_checked != Qt::Checked) {
auto hasUnchecked = false;
for (const auto &sub : parentInfo->_subs) {
for (const auto &sub : std::as_const(parentInfo->_subs)) {
if (sub._checked != Qt::Checked) {
hasUnchecked = true;
break;
@ -714,7 +714,7 @@ void FolderStatusModel::slotUpdateDirectories(const QStringList &list)
}
std::set<QString> selectiveSyncUndecidedSet; // not QSet because it's not sorted
for (const auto &str : selectiveSyncUndecidedList) {
for (const auto &str : std::as_const(selectiveSyncUndecidedList)) {
if (str.startsWith(parentInfo->_path) || parentInfo->_path == QLatin1String("/")) {
selectiveSyncUndecidedSet.insert(str);
}
@ -732,7 +732,7 @@ void FolderStatusModel::slotUpdateDirectories(const QStringList &list)
QVector<SubFolderInfo> newSubs;
newSubs.reserve(sortedSubfolders.size());
for (const auto &path : sortedSubfolders) {
for (const auto &path : std::as_const(sortedSubfolders)) {
auto relativePath = path.mid(pathToRemove.size());
if (parentInfo->_folder->isFileExcludedRelative(relativePath)) {
continue;
@ -781,7 +781,7 @@ void FolderStatusModel::slotUpdateDirectories(const QStringList &list)
} else if (parentInfo->_checked == Qt::Checked) {
newInfo._checked = Qt::Checked;
} else {
for (const auto &str : selectiveSyncBlackList) {
for (const auto &str : std::as_const(selectiveSyncBlackList)) {
if (str == relativePath || str == QLatin1String("/")) {
newInfo._checked = Qt::Unchecked;
break;
@ -940,7 +940,7 @@ void FolderStatusModel::slotApplySelectiveSync()
}
//The part that changed should not be read from the DB on next sync because there might be new folders
// (the ones that are no longer in the blacklist)
for (const auto &it : changes) {
for (const auto &it : std::as_const(changes)) {
folder->journalDb()->schedulePathForRemoteDiscovery(it);
folder->schedulePathForLocalDiscovery(it);
}
@ -1227,7 +1227,7 @@ void FolderStatusModel::slotSyncAllPendingBigFolders()
qCWarning(lcFolderStatus) << "Could not read selective sync list from db.";
return;
}
for (const auto &undecidedFolder : undecidedList) {
for (const auto &undecidedFolder : std::as_const(undecidedList)) {
blackList.removeAll(undecidedFolder);
}
folder->journalDb()->setSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, blackList);
@ -1250,7 +1250,7 @@ void FolderStatusModel::slotSyncAllPendingBigFolders()
}
// The part that changed should not be read from the DB on next sync because there might be new folders
// (the ones that are no longer in the blacklist)
for (const auto &it : undecidedList) {
for (const auto &it : std::as_const(undecidedList)) {
folder->journalDb()->schedulePathForRemoteDiscovery(it);
folder->schedulePathForLocalDiscovery(it);
}

View File

@ -360,7 +360,7 @@ void FolderWizardRemotePath::slotUpdateDirectories(const QStringList &list)
}
QStringList sortedList = list;
Utility::sortFilenames(sortedList);
for (auto path : sortedList) {
for (auto path : std::as_const(sortedList)) {
path.remove(webdavFolder);
// Don't allow to select subfolders of encrypted subfolders

View File

@ -166,12 +166,12 @@ bool createDebugArchive(const QString &filename)
#endif
const auto clientParameters = QCoreApplication::arguments().join(' ').toUtf8();
zip.prepareWriting("__nextcloud_client_parameters.txt", {}, {}, clientParameters.size());
zip.prepareWriting("_client_parameters.txt", {}, {}, clientParameters.size());
zip.writeData(clientParameters, clientParameters.size());
zip.finishWriting(clientParameters.size());
const auto buildInfo = QString(OCC::Theme::instance()->aboutInfo() + "\n\n" + OCC::Theme::instance()->aboutDetails()).toUtf8();
zip.prepareWriting("__nextcloud_client_buildinfo.txt", {}, {}, buildInfo.size());
zip.prepareWriting("_client_buildinfo.txt", {}, {}, buildInfo.size());
zip.writeData(buildInfo, buildInfo.size());
zip.finishWriting(buildInfo.size());
return true;

View File

@ -591,7 +591,7 @@ public:
}
}
QStringList configuredDomainIds() const
QStringList getAccountIdsOfFoundFileProviderDomains() const
{
return _registeredDomains.keys();
}
@ -652,34 +652,36 @@ void FileProviderDomainManager::updateFileProviderDomains()
return;
}
const auto vfsEnabledAccounts = FileProviderSettingsController::instance()->vfsEnabledAccounts();
auto configuredDomains = d->configuredDomainIds();
const auto fileProviderEnabledAccountIds = FileProviderSettingsController::instance()->vfsEnabledAccounts();
auto accountIdsOfFoundFileProviderDomains = d->getAccountIdsOfFoundFileProviderDomains();
for (const auto &accountUserIdAtHost : vfsEnabledAccounts) {
// If the domain has already been set up for this account, then don't set it up again
if (configuredDomains.contains(accountUserIdAtHost)) {
configuredDomains.removeAll(accountUserIdAtHost);
for (const auto &fileProviderEnabledAccountId : fileProviderEnabledAccountIds) {
// If the domain has already been set up for this account, then don't set it up again.
if (accountIdsOfFoundFileProviderDomains.contains(fileProviderEnabledAccountId)) {
accountIdsOfFoundFileProviderDomains.removeAll(fileProviderEnabledAccountId);
continue;
}
if (const auto accountState = AccountManager::instance()->accountFromUserId(accountUserIdAtHost)) {
if (const auto accountState = AccountManager::instance()->accountFromUserId(fileProviderEnabledAccountId)) {
qCDebug(lcMacFileProviderDomainManager) << "Succeed in fetching account state by account id"
<< accountUserIdAtHost
<< fileProviderEnabledAccountId
<< ", adding file provider domain for account.";
addFileProviderDomainForAccount(accountState.data());
} else {
qCWarning(lcMacFileProviderDomainManager) << "Could not fetch account state by account id"
<< accountUserIdAtHost
<< fileProviderEnabledAccountId
<< ", removing account from list of VFS-enabled accounts.";
FileProviderSettingsController::instance()->setVfsEnabledForAccount(accountUserIdAtHost, false);
FileProviderSettingsController::instance()->setVfsEnabledForAccount(fileProviderEnabledAccountId, false);
}
}
for (const auto &remainingDomainUserId : configuredDomains) {
const auto accountId = accountIdFromDomainId(remainingDomainUserId);
const auto accountState = AccountManager::instance()->accountFromUserId(accountId);
for (const auto &remainingAccountId : accountIdsOfFoundFileProviderDomains) {
qCDebug(lcMacFileProviderDomainManager) << "Orphaned file provider domain to remove found for account id"
<< remainingAccountId;
const auto accountState = AccountManager::instance()->accountFromUserId(remainingAccountId);
removeFileProviderDomainForAccount(accountState.data());
}
@ -721,9 +723,6 @@ void FileProviderDomainManager::removeFileProviderDomainForAccount(const Account
}
Q_ASSERT(accountState);
const auto account = accountState->account();
Q_ASSERT(account);
d->removeFileProviderDomain(accountState);
}

View File

@ -1,89 +0,0 @@
/*
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <QObject>
#include <QQmlEngine>
#pragma once
namespace OCC::Mac
{
class FileProviderDomainSyncStatus : public QObject
{
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("FileProviderDomainSyncStatus cannot be instantiated from QML")
Q_PROPERTY(bool syncing READ syncing NOTIFY syncingChanged)
Q_PROPERTY(bool downloading READ downloading NOTIFY downloadingChanged)
Q_PROPERTY(bool uploading READ uploading NOTIFY uploadingChanged)
Q_PROPERTY(double fractionCompleted READ fractionCompleted NOTIFY fractionCompletedChanged)
Q_PROPERTY(double downloadFractionCompleted READ downloadFractionCompleted NOTIFY downloadFractionCompletedChanged)
Q_PROPERTY(double uploadFractionCompleted READ uploadFractionCompleted NOTIFY uploadFractionCompletedChanged)
Q_PROPERTY(int downloadFileTotalCount READ downloadFileTotalCount NOTIFY downloadFileTotalCountChanged)
Q_PROPERTY(int downloadFileCompletedCount READ downloadFileCompletedCount NOTIFY downloadFileCompletedCountChanged)
Q_PROPERTY(int uploadFileTotalCount READ uploadFileTotalCount NOTIFY uploadFileTotalCountChanged)
Q_PROPERTY(int uploadFileCompletedCount READ uploadFileCompletedCount NOTIFY uploadFileCompletedCountChanged)
// TODO: more detailed reporting (time remaining, megabytes, etc.)
Q_PROPERTY(QUrl icon READ icon NOTIFY iconChanged)
public:
explicit FileProviderDomainSyncStatus(const QString &domainIdentifier, QObject *parent = nullptr);
~FileProviderDomainSyncStatus() override;
[[nodiscard]] bool syncing() const;
[[nodiscard]] bool downloading() const;
[[nodiscard]] bool uploading() const;
[[nodiscard]] double fractionCompleted() const;
[[nodiscard]] double downloadFractionCompleted() const;
[[nodiscard]] double uploadFractionCompleted() const;
[[nodiscard]] int downloadFileTotalCount() const;
[[nodiscard]] int downloadFileCompletedCount() const;
[[nodiscard]] int uploadFileTotalCount() const;
[[nodiscard]] int uploadFileCompletedCount() const;
[[nodiscard]] QUrl icon() const;
signals:
void syncingChanged(bool syncing);
void downloadingChanged(bool downloading);
void uploadingChanged(bool uploading);
void fractionCompletedChanged(double fractionCompleted);
void downloadFractionCompletedChanged(double downloadFractionCompleted);
void uploadFractionCompletedChanged(double uploadFractionCompleted);
void downloadFileTotalCountChanged(int downloadFileTotalCount);
void downloadFileCompletedCountChanged(int downloadFileCompletedCount);
void uploadFileTotalCountChanged(int uploadFileTotalCount);
void uploadFileCompletedCountChanged(int uploadFileCompletedCount);
void iconChanged(const QUrl &icon);
private:
void setDownloading(const bool syncing);
void setUploading(const bool syncing);
void setDownloadFractionCompleted(const double fractionCompleted);
void setUploadFractionCompleted(const double fractionCompleted);
void setDownloadFileTotalCount(const int fileTotalCount);
void setDownloadFileCompletedCount(const int fileCompletedCount);
void setUploadFileTotalCount(const int fileTotalCount);
void setUploadFileCompletedCount(const int fileCompletedCount);
void setIcon(const QUrl &icon);
void updateIcon();
bool _downloading = false;
bool _uploading = false;
double _downloadFractionCompleted = 0.0;
double _uploadFractionCompleted = 0.0;
int _downloadFileTotalCount = 0;
int _downloadFileCompletedCount = 0;
int _uploadFileTotalCount = 0;
int _uploadFileCompletedCount = 0;
QUrl _icon;
class MacImplementation;
std::unique_ptr<MacImplementation> d;
};
} // OCC::Mac
Q_DECLARE_METATYPE(OCC::Mac::FileProviderDomainSyncStatus*)

View File

@ -1,261 +0,0 @@
/*
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "fileproviderdomainsyncstatus.h"
#include <QLoggingCategory>
#include "gui/macOS/fileproviderutils.h"
#include "libsync/theme.h"
#import <FileProvider/FileProvider.h>
#import "gui/macOS/progressobserver.h"
namespace OCC::Mac
{
Q_LOGGING_CATEGORY(lcMacFileProviderDomainSyncStatus, "nextcloud.gui.macfileproviderdomainsyncstatus", QtInfoMsg)
class FileProviderDomainSyncStatus::MacImplementation
{
public:
explicit MacImplementation(const QString &domainIdentifier, FileProviderDomainSyncStatus *parent)
: q(parent)
{
_domain = FileProviderUtils::domainForIdentifier(domainIdentifier);
_manager = [NSFileProviderManager managerForDomain:_domain];
if (_manager == nil) {
qCWarning(lcMacFileProviderDomainSyncStatus) << "Could not get manager for domain" << domainIdentifier;
return;
}
[_manager retain];
if (@available(macOS 11.3, *)) {
NSProgress *const downloadProgress = [_manager globalProgressForKind:NSProgressFileOperationKindDownloading];
NSProgress *const uploadProgress = [_manager globalProgressForKind:NSProgressFileOperationKindUploading];
_downloadProgressObserver = [[ProgressObserver alloc] initWithProgress:downloadProgress];
_uploadProgressObserver = [[ProgressObserver alloc] initWithProgress:uploadProgress];
_downloadProgressObserver.progressKVOChangeHandler = ^(NSProgress *const progress){
updateDownload(progress);
};
_uploadProgressObserver.progressKVOChangeHandler = ^(NSProgress *const progress){
updateUpload(progress);
};
}
}
~MacImplementation()
{
[_downloadProgressObserver release];
[_uploadProgressObserver release];
[_domain release];
[_manager release];
}
void updateDownload(NSProgress *const progress) const
{
qCInfo(lcMacFileProviderDomainSyncStatus) << "Download progress changed" << progress.localizedDescription;
if (progress == nil || q == nullptr) {
return;
}
q->setDownloading(!progress.paused && !progress.cancelled && !progress.finished);
q->setDownloadFractionCompleted(progress.fractionCompleted);
q->setDownloadFileTotalCount(progress.fileTotalCount.intValue);
q->setDownloadFileCompletedCount(progress.fileCompletedCount.intValue);
q->updateIcon();
}
void updateUpload(NSProgress *const progress) const
{
qCInfo(lcMacFileProviderDomainSyncStatus) << "Upload progress changed" << progress.localizedDescription;
if (progress == nil || q == nullptr) {
return;
}
q->setUploading(!progress.paused && !progress.cancelled && !progress.finished);
q->setUploadFractionCompleted(progress.fractionCompleted);
q->setUploadFileTotalCount(progress.fileTotalCount.intValue);
q->setUploadFileCompletedCount(progress.fileCompletedCount.intValue);
q->updateIcon();
}
private:
NSFileProviderDomain *_domain = nil;
NSFileProviderManager *_manager = nil;
ProgressObserver *_downloadProgressObserver = nullptr;
ProgressObserver *_uploadProgressObserver = nullptr;
FileProviderDomainSyncStatus *q = nullptr;
};
FileProviderDomainSyncStatus::FileProviderDomainSyncStatus(const QString &domainIdentifier, QObject *parent)
: QObject(parent)
, d(std::make_unique<MacImplementation>(domainIdentifier, this))
{
qRegisterMetaType<FileProviderDomainSyncStatus*>("FileProviderDomainSyncStatus*");
updateIcon();
}
FileProviderDomainSyncStatus::~FileProviderDomainSyncStatus() = default;
bool FileProviderDomainSyncStatus::syncing() const
{
return downloading() || uploading();
}
bool FileProviderDomainSyncStatus::downloading() const
{
return _downloading;
}
bool FileProviderDomainSyncStatus::uploading() const
{
return _uploading;
}
double FileProviderDomainSyncStatus::fractionCompleted() const
{
return (downloadFractionCompleted() + uploadFractionCompleted()) / 2;
}
double FileProviderDomainSyncStatus::downloadFractionCompleted() const
{
return _downloadFractionCompleted;
}
double FileProviderDomainSyncStatus::uploadFractionCompleted() const
{
return _uploadFractionCompleted;
}
int FileProviderDomainSyncStatus::downloadFileTotalCount() const
{
return _downloadFileTotalCount;
}
int FileProviderDomainSyncStatus::downloadFileCompletedCount() const
{
return _downloadFileCompletedCount;
}
int FileProviderDomainSyncStatus::uploadFileTotalCount() const
{
return _uploadFileTotalCount;
}
int FileProviderDomainSyncStatus::uploadFileCompletedCount() const
{
return _uploadFileCompletedCount;
}
QUrl FileProviderDomainSyncStatus::icon() const
{
return _icon;
}
void FileProviderDomainSyncStatus::setDownloading(const bool downloading)
{
if (_downloading == downloading) {
return;
}
_downloading = downloading;
emit downloadingChanged(_downloading);
emit syncingChanged(syncing());
}
void FileProviderDomainSyncStatus::setUploading(const bool uploading)
{
if (_uploading == uploading) {
return;
}
_uploading = uploading;
emit uploadingChanged(_uploading);
emit syncingChanged(syncing());
}
void FileProviderDomainSyncStatus::setDownloadFractionCompleted(const double downloadFractionCompleted)
{
if (_downloadFractionCompleted == downloadFractionCompleted) {
return;
}
_downloadFractionCompleted = downloadFractionCompleted;
emit downloadFractionCompletedChanged(_downloadFractionCompleted);
emit fractionCompletedChanged(fractionCompleted());
}
void FileProviderDomainSyncStatus::setUploadFractionCompleted(const double uploadFractionCompleted)
{
if (_uploadFractionCompleted == uploadFractionCompleted) {
return;
}
_uploadFractionCompleted = uploadFractionCompleted;
emit uploadFractionCompletedChanged(_uploadFractionCompleted);
emit fractionCompletedChanged(fractionCompleted());
}
void FileProviderDomainSyncStatus::setDownloadFileTotalCount(const int fileTotalCount)
{
if (_downloadFileTotalCount == fileTotalCount) {
return;
}
_downloadFileTotalCount = fileTotalCount;
emit downloadFileTotalCountChanged(_downloadFileTotalCount);
}
void FileProviderDomainSyncStatus::setDownloadFileCompletedCount(const int fileCompletedCount)
{
if (_downloadFileCompletedCount == fileCompletedCount) {
return;
}
_downloadFileCompletedCount = fileCompletedCount;
emit downloadFileCompletedCountChanged(_downloadFileCompletedCount);
}
void FileProviderDomainSyncStatus::setUploadFileTotalCount(const int fileTotalCount)
{
if (_uploadFileTotalCount == fileTotalCount) {
return;
}
_uploadFileTotalCount = fileTotalCount;
emit uploadFileTotalCountChanged(_uploadFileTotalCount);
}
void FileProviderDomainSyncStatus::setUploadFileCompletedCount(const int fileCompletedCount)
{
if (_uploadFileCompletedCount == fileCompletedCount) {
return;
}
_uploadFileCompletedCount = fileCompletedCount;
emit uploadFileCompletedCountChanged(_uploadFileCompletedCount);
}
void FileProviderDomainSyncStatus::setIcon(const QUrl &icon)
{
if (_icon == icon) {
return;
}
_icon = icon;
emit iconChanged(_icon);
}
void FileProviderDomainSyncStatus::updateIcon()
{
const auto iconUrl = syncing() ? Theme::instance()->syncStatusRunning() : Theme::instance()->syncStatusOk();
setIcon(iconUrl);
}
} // OCC::Mac

View File

@ -1,166 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "fileprovidermaterialiseditemsmodel.h"
#include <QFileInfo>
namespace OCC {
namespace Mac {
FileProviderMaterialisedItemsModel::FileProviderMaterialisedItemsModel(QObject * const parent)
: QAbstractListModel(parent)
{
}
int FileProviderMaterialisedItemsModel::rowCount(const QModelIndex &parent) const
{
if (parent.isValid()) {
return 0;
}
return _items.count();
}
QVariant FileProviderMaterialisedItemsModel::data(const QModelIndex &index, int role) const
{
const auto item = _items.at(index.row());
switch (role) {
case Qt::DisplayRole:
case FilenameRole:
return item.filename();
case IdentifierRole:
return item.identifier();
case ParentItemIdentifierRole:
return item.parentItemIdentifier();
case DomainIdentifierRole:
return item.domainIdentifier();
case TypeIdentifierRole:
return item.typeIdentifier();
case SymlinkTargetPathRole:
return item.symlinkTargetPath();
case UploadingErrorRole:
return item.uploadingError();
case DownloadingErrorRole:
return item.downloadingError();
case MostRecentEditorNameRole:
return item.mostRecentEditorName();
case OwnerNameRole:
return item.ownerName();
case ContentModificationDateRole:
return item.contentModificationDate();
case CreationDateRole:
return item.creationDate();
case LastUsedDateRole:
return item.lastUsedDate();
case ContentVersionRole:
return item.contentVersion();
case MetadataVersionRole:
return item.metadataVersion();
case TagDataRole:
return item.tagData();
case CapabilitiesRole:
return item.capabilities();
case FileSystemFlagsRole:
return item.fileSystemFlags();
case ChildItemCountRole:
return item.childItemCount();
case TypeOsCodeRole:
return item.typeOsCode();
case CreatorOsCodeRole:
return item.creatorOsCode();
case DocumentSizeRole:
return item.documentSize();
case MostRecentVersionDownloadedRole:
return item.mostRecentVersionDownloaded();
case UploadingRole:
return item.uploading();
case UploadedRole:
return item.uploaded();
case DownloadingRole:
return item.downloading();
case DownloadedRole:
return item.downloaded();
case SharedRole:
return item.shared();
case SharedByCurrentUserRole:
return item.sharedByCurrentUser();
case UserVisiblePathRole:
return item.userVisiblePath();
case FileTypeStringRole:
return item.fileTypeString();
case FileSizeStringRole:
return _locale.formattedDataSize(item.documentSize());
}
return {};
}
QHash<int, QByteArray> FileProviderMaterialisedItemsModel::roleNames() const
{
auto roleNames = QAbstractListModel::roleNames();
roleNames.insert({
{ IdentifierRole, "identifier" },
{ ParentItemIdentifierRole, "parentItemIdentifier" },
{ DomainIdentifierRole, "domainIdentifier" },
{ FilenameRole, "fileName" },
{ TypeIdentifierRole, "typeIdentifier" },
{ SymlinkTargetPathRole, "symlinkTargetPath" },
{ UploadingErrorRole, "uploadingError" },
{ DownloadingErrorRole, "downloadingError" },
{ MostRecentEditorNameRole, "mostRecentEditorName" },
{ OwnerNameRole, "ownerName" },
{ ContentModificationDateRole, "contentModificationDate" },
{ CreationDateRole, "creationDate" },
{ LastUsedDateRole, "lastUsedDate" },
{ ContentVersionRole, "contentVersion" },
{ MetadataVersionRole, "metadataVersion" },
{ TagDataRole, "tagData" },
{ CapabilitiesRole, "capabilities" },
{ FileSystemFlagsRole, "fileSystemFlags" },
{ ChildItemCountRole, "childItemCount" },
{ TypeOsCodeRole, "typeOsCode" },
{ CreatorOsCodeRole, "creatorOsCode" },
{ DocumentSizeRole, "documentSize" },
{ MostRecentVersionDownloadedRole, "mostRecentVersionDownloaded" },
{ UploadingRole, "uploading" },
{ UploadedRole, "uploaded" },
{ DownloadingRole, "downloading" },
{ DownloadedRole, "downloaded" },
{ SharedRole, "shared" },
{ SharedByCurrentUserRole, "sharedByCurrentUser" },
{ UserVisiblePathRole, "userVisiblePath" },
{ FileTypeStringRole, "fileTypeString" },
{ FileSizeStringRole, "fileSizeString" },
});
return roleNames;
}
QVector<FileProviderItemMetadata> FileProviderMaterialisedItemsModel::items() const
{
return _items;
}
void FileProviderMaterialisedItemsModel::setItems(const QVector<FileProviderItemMetadata> &items)
{
if (items == _items) {
return;
}
beginResetModel();
// We are using this in the "Free up space" dialog so, sort by size.
_items = items;
std::ranges::sort(_items, [](const auto &a, const auto &b) {
return a.documentSize() > b.documentSize();
});
endResetModel();
Q_EMIT itemsChanged();
}
} // namespace Mac
} // namespace OCC

View File

@ -1,81 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <QAbstractListModel>
#include <QLocale>
#include "gui/macOS/fileprovideritemmetadata.h"
namespace OCC {
namespace Mac {
class FileProviderMaterialisedItemsModel : public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(QVector<FileProviderItemMetadata> items READ items WRITE setItems NOTIFY itemsChanged)
public:
enum Roles {
IdentifierRole = Qt::UserRole + 1,
ParentItemIdentifierRole,
DomainIdentifierRole,
FilenameRole,
TypeIdentifierRole,
SymlinkTargetPathRole,
UploadingErrorRole,
DownloadingErrorRole,
MostRecentEditorNameRole,
OwnerNameRole,
ContentModificationDateRole,
CreationDateRole,
LastUsedDateRole,
ContentVersionRole,
MetadataVersionRole,
TagDataRole,
CapabilitiesRole,
FileSystemFlagsRole,
ChildItemCountRole,
TypeOsCodeRole,
CreatorOsCodeRole,
DocumentSizeRole,
MostRecentVersionDownloadedRole,
UploadingRole,
UploadedRole,
DownloadingRole,
DownloadedRole,
SharedRole,
SharedByCurrentUserRole,
UserVisiblePathRole,
FileTypeStringRole,
FileSizeStringRole,
};
Q_ENUM(Roles)
explicit FileProviderMaterialisedItemsModel(QObject *parent = nullptr);
[[nodiscard]] int rowCount(const QModelIndex &parent = {}) const override;
[[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
[[nodiscard]] QVector<FileProviderItemMetadata> items() const;
signals:
void itemsChanged();
public slots:
void setItems(const QVector<FileProviderItemMetadata> &items);
void evictItem(const QString &identifier, const QString &domainIdentifier);
private:
QVector<FileProviderItemMetadata> _items;
QLocale _locale;
};
} // namespace Mac
} // namespace OCC

View File

@ -1,81 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "fileprovidermaterialiseditemsmodel.h"
#include <QLoggingCategory>
#import <FileProvider/FileProvider.h>
#include "fileproviderutils.h"
#include "gui/systray.h"
namespace OCC {
namespace Mac {
Q_LOGGING_CATEGORY(lcMacImplFileProviderMaterialisedItemsModelMac, "nextcloud.gui.macfileprovidermaterialiseditemsmodelmac", QtInfoMsg)
void FileProviderMaterialisedItemsModel::evictItem(const QString &identifier, const QString &domainIdentifier)
{
NSFileProviderManager *const manager = FileProviderUtils::managerForDomainIdentifier(domainIdentifier);
if (manager == nil) {
qCWarning(lcMacImplFileProviderMaterialisedItemsModelMac) << "Received null manager for domain"
<< domainIdentifier
<< "cannot evict item"
<< identifier;
Systray::instance()->showMessage(tr("Error"),
tr("An internal error occurred. Please try again later."),
QSystemTrayIcon::Warning);
return;
}
__block BOOL successfullyDeleted = NO;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[manager evictItemWithIdentifier:identifier.toNSString() completionHandler:^(NSError *error) {
if (error != nil) {
const auto errorDesc = QString::fromNSString(error.localizedDescription);
qCWarning(lcMacImplFileProviderMaterialisedItemsModelMac) << "Error evicting item:" << errorDesc;
Systray::instance()->showMessage(tr("Error"),
tr("An error occurred while trying to delete the local copy of this item: %1").arg(errorDesc),
QSystemTrayIcon::Warning);
} else {
successfullyDeleted = YES;
}
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC));
[manager release];
if (successfullyDeleted == NO) {
return;
}
const auto deletedItemIt = std::find_if(_items.cbegin(),
_items.cend(),
[identifier, domainIdentifier](const FileProviderItemMetadata &item) {
return item.identifier() == identifier && item.domainIdentifier() == domainIdentifier;
});
if (deletedItemIt == _items.cend()) {
qCWarning(lcMacImplFileProviderMaterialisedItemsModelMac) << "Could not find item"
<< identifier
<< "in model items.";
return;
}
const auto deletedItemRow = std::distance(_items.cbegin(), deletedItemIt);
beginRemoveRows({}, deletedItemRow, deletedItemRow);
_items.remove(deletedItemRow);
endRemoveRows();
}
} // namespace OCC
} // namespace Mac

View File

@ -8,8 +8,6 @@
#include <QObject>
#include <QtQuickWidgets/QtQuickWidgets>
#include "gui/macOS/fileproviderdomainsyncstatus.h"
class QAbstractListModel;
namespace OCC {
@ -31,30 +29,15 @@ public:
[[nodiscard]] QStringList vfsEnabledAccounts() const;
[[nodiscard]] Q_INVOKABLE bool vfsEnabledForAccount(const QString &userIdAtHost) const;
[[nodiscard]] unsigned long long localStorageUsageForAccount(const QString &userIdAtHost) const;
[[nodiscard]] Q_INVOKABLE float localStorageUsageGbForAccount(const QString &userIdAtHost) const;
[[nodiscard]] unsigned long long remoteStorageUsageForAccount(const QString &userIdAtHost) const;
[[nodiscard]] Q_INVOKABLE float remoteStorageUsageGbForAccount(const QString &userIdAtHost) const;
[[nodiscard]] Q_INVOKABLE bool trashDeletionEnabledForAccount(const QString &userIdAtHost) const;
[[nodiscard]] Q_INVOKABLE bool trashDeletionSetForAccount(const QString &userIdAtHost) const;
[[nodiscard]] Q_INVOKABLE QAbstractListModel *materialisedItemsModelForAccount(const QString &userIdAtHost);
[[nodiscard]] Q_INVOKABLE FileProviderDomainSyncStatus *domainSyncStatusForAccount(const QString &userIdAtHost) const;
public slots:
void setVfsEnabledForAccount(const QString &userIdAtHost, const bool setEnabled, const bool showInformationDialog = true);
void setTrashDeletionEnabledForAccount(const QString &userIdAtHost, const bool setEnabled);
void resetVfsForAccount(const QString &userIdAtHost);
void createEvictionWindowForAccount(const QString &userIdAtHost);
void refreshMaterialisedItemsForAccount(const QString &userIdAtHost);
void signalFileProviderDomain(const QString &userIdAtHost);
signals:
void vfsEnabledAccountsChanged();
void localStorageUsageForAccountChanged(const QString &userIdAtHost);
void remoteStorageUsageForAccountChanged(const QString &userIdAtHost);
void materialisedItemsForAccountChanged(const QString &userIdAtHost);
void trashDeletionEnabledForAccountChanged(const QString &userIdAtHost);
void trashDeletionSetForAccountChanged(const QString &userIdAtHost);

View File

@ -12,31 +12,21 @@
#include "gui/userinfo.h"
#include "gui/macOS/fileprovider.h"
#include "gui/macOS/fileprovideritemmetadata.h"
#include "gui/macOS/fileprovidermaterialiseditemsmodel.h"
#include "gui/macOS/fileproviderutils.h"
// Objective-C imports
#import <Foundation/Foundation.h>
#import "fileproviderstorageuseenumerationobserver.h"
// End of Objective-C imports
namespace {
constexpr auto fpSettingsQmlPath = "qrc:/qml/src/gui/macOS/ui/FileProviderSettings.qml";
constexpr auto fpEvictionDialogQmlPath = "qrc:/qml/src/gui/macOS/ui/FileProviderEvictionDialog.qml";
// QML properties -- make sure they match up in QML file!
constexpr auto fpAccountUserIdAtHostProp = "accountUserIdAtHost";
constexpr auto fpMaterialisedItemsModelProp = "materialisedItemsModel";
// NSUserDefaults entries
constexpr auto enabledAccountsSettingsKey = "enabledAccounts";
float gbFromBytesWithOneDecimal(const unsigned long long bytes)
{
constexpr auto bytesIn100Mb = 1ULL * 1000ULL * 1000ULL * 100ULL;
return ((bytes * 1.0) / bytesIn100Mb) / 10.0;
}
} // namespace
namespace OCC {
@ -57,7 +47,6 @@ public:
{
q = parent;
initialCheck();
fetchMaterialisedFilesStorageUsage();
};
~MacImplementation() override = default;
@ -146,161 +135,18 @@ public:
return overallActResult;
}
[[nodiscard]] unsigned long long localStorageUsageForAccount(const QString &userIdAtHost) const
{
// Return cached value as we fetch asynchronously on initialisation of this class.
// We will then emit a signal when the new value is found.
return _storageUsage.value(userIdAtHost);
}
[[nodiscard]] QVector<FileProviderItemMetadata> materialisedItemsForAccount(const QString &userIdAtHost) const
{
return _materialisedFiles.value(userIdAtHost);
}
void signalFileProviderDomain(const QString &userIdAtHost) const
{
qCInfo(lcFileProviderSettingsController) << "Signalling file provider domain" << userIdAtHost;
NSFileProviderDomain * const domain = FileProviderUtils::domainForIdentifier(userIdAtHost);
NSFileProviderManager * const manager = [NSFileProviderManager managerForDomain:domain];
[domain release];
[manager signalEnumeratorForContainerItemIdentifier:NSFileProviderRootContainerItemIdentifier
completionHandler:^(NSError *const error) {
if (error != nil) {
qCWarning(lcFileProviderSettingsController) << "Could not signal file provider domain, error"
<< error.localizedDescription;
return;
}
qCInfo(lcFileProviderSettingsController) << "Successfully signalled file provider domain";
// TODO: Provide some feedback in the UI
}];
}
[[nodiscard]] FileProviderDomainSyncStatus *domainSyncStatusForAccount(const QString &userIdAtHost) const
{
return _fileProviderDomainSyncStatuses.value(userIdAtHost);
}
public slots:
// NOTE: This method will release the provided args so make sure to retain them beforehand
void enumerateMaterialisedFilesForDomainManager(NSFileProviderManager * const managerForDomain,
NSFileProviderDomain * const domain)
{
const id<NSFileProviderEnumerator> enumerator = [managerForDomain enumeratorForMaterializedItems];
Q_ASSERT(enumerator != nil);
[enumerator retain];
FileProviderStorageUseEnumerationObserver *const storageUseObserver = [[FileProviderStorageUseEnumerationObserver alloc] init];
storageUseObserver.enumerationFinishedHandler = ^(NSError *const error) {
qCInfo(lcFileProviderSettingsController) << "Enumeration finished for" << domain.identifier;
if (error != nil) {
qCWarning(lcFileProviderSettingsController) << "Error while enumerating storage use" << error.localizedDescription;
[storageUseObserver release];
[enumerator release];
return;
}
const auto items = storageUseObserver.materialisedItems;
Q_ASSERT(items != nil);
// Remember that OCC::Account::userIdAtHost == domain.identifier for us
const auto qDomainIdentifier = QString::fromNSString(domain.identifier);
QVector<FileProviderItemMetadata> qMaterialisedItems;
qMaterialisedItems.reserve(items.count);
unsigned long long storageUsage = 0;
for (const id<NSFileProviderItem> item in items) {
const auto itemMetadata = FileProviderItemMetadata::fromNSFileProviderItem(item, qDomainIdentifier);
storageUsage += itemMetadata.documentSize();
qCDebug(lcFileProviderSettingsController) << "Adding item" << itemMetadata.identifier()
<< "with size" << itemMetadata.documentSize()
<< "to storage usage for account" << qDomainIdentifier
<< "with total size" << storageUsage;
qMaterialisedItems.append(itemMetadata);
}
_storageUsage.insert(qDomainIdentifier, storageUsage);
_materialisedFiles.insert(qDomainIdentifier, qMaterialisedItems);
emit q->localStorageUsageForAccountChanged(qDomainIdentifier);
emit q->materialisedItemsForAccountChanged(qDomainIdentifier);
[storageUseObserver release];
[enumerator release];
[managerForDomain release];
[domain release];
};
[enumerator enumerateItemsForObserver:storageUseObserver startingAtPage:NSFileProviderInitialPageSortedByName];
}
private slots:
void updateDomainSyncStatuses()
{
qCInfo(lcFileProviderSettingsController) << "Updating file provider domain sync statuses.";
_fileProviderDomainSyncStatuses.clear();
const auto enabledAccounts = nsEnabledAccounts();
for (NSString *const accountIdentifier in enabledAccounts) {
const auto qAccountIdentifier = QString::fromNSString(accountIdentifier);
const auto domainIdentifier = FileProviderUtils::domainIdentifierForAccountIdentifier(accountIdentifier);
const auto syncStatus = new FileProviderDomainSyncStatus(domainIdentifier, q);
_fileProviderDomainSyncStatuses.insert(qAccountIdentifier, syncStatus);
}
}
private:
[[nodiscard]] NSArray<NSString *> *nsEnabledAccounts() const
{
return (NSArray<NSString *> *)[_userDefaults objectForKey:_accountsKey];
}
void fetchMaterialisedFilesStorageUsage()
{
qCInfo(lcFileProviderSettingsController) << "Fetching used storage space of materialized items.";
[NSFileProviderManager getDomainsWithCompletionHandler: ^(NSArray<NSFileProviderDomain *> *const domains, NSError *const error) {
if (error != nil) {
qCWarning(lcFileProviderSettingsController) << "Could not get file provider domains:"
<< error.localizedDescription
<< "Will try again in 2 secs";
// HACK: Sometimes the system is not in a state where it wants to give us access to
// the file provider domains. We will try again in 2 seconds and hope it works
const auto thisQobject = (QObject*)this;
dispatch_async(dispatch_get_main_queue(), ^{
[NSTimer scheduledTimerWithTimeInterval:2 repeats:NO block:^(NSTimer *const timer) {
Q_UNUSED(timer)
QMetaObject::invokeMethod(thisQobject, [this] { fetchMaterialisedFilesStorageUsage(); });
}];
});
return;
}
for (NSFileProviderDomain *const domain in domains) {
qCInfo(lcFileProviderSettingsController) << "Checking storage use for domain:" << domain.identifier;
NSFileProviderManager *const managerForDomain = [NSFileProviderManager managerForDomain:domain];
if (managerForDomain == nil) {
qCWarning(lcFileProviderSettingsController) << "Got a nil file provider manager for domain"
<< domain.identifier
<< ", returning early.";
return;
}
[managerForDomain retain];
[domain retain];
enumerateMaterialisedFilesForDomainManager(managerForDomain, domain);
}
}];
}
void initialCheck()
{
qCInfo(lcFileProviderSettingsController) << "Running initial checks for file provider settings controller.";
NSArray<NSString *> *const vfsEnabledAccounts = nsEnabledAccounts();
if (vfsEnabledAccounts != nil) {
updateDomainSyncStatuses();
connect(q, &FileProviderSettingsController::vfsEnabledAccountsChanged, this, &MacImplementation::updateDomainSyncStatuses);
return;
}
@ -313,9 +159,7 @@ private:
FileProviderSettingsController *q = nullptr;
NSUserDefaults *_userDefaults = NSUserDefaults.standardUserDefaults;
NSString *_accountsKey = [NSString stringWithUTF8String:enabledAccountsSettingsKey];
QHash<QString, QVector<FileProviderItemMetadata>> _materialisedFiles;
QHash<QString, unsigned long long> _storageUsage;
QHash<QString, FileProviderDomainSyncStatus*> _fileProviderDomainSyncStatuses;
};
FileProviderSettingsController *FileProviderSettingsController::instance()
@ -337,9 +181,6 @@ FileProviderSettingsController::FileProviderSettingsController(QObject *parent)
const auto accountUserIdAtHost = account->userIdAtHostWithPort();
_userInfos.insert(accountUserIdAtHost, userInfo);
connect(userInfo, &UserInfo::fetchedLastInfo, this, [this, accountUserIdAtHost] {
emit remoteStorageUsageForAccountChanged(accountUserIdAtHost);
});
userInfo->setActive(true);
}
}
@ -383,6 +224,10 @@ void FileProviderSettingsController::setVfsEnabledForAccount(const QString &user
bool FileProviderSettingsController::trashDeletionEnabledForAccount(const QString &userIdAtHost) const
{
if (userIdAtHost.isEmpty()) {
return false;
}
const auto xpc = FileProvider::instance()->xpc();
if (!xpc) {
@ -434,140 +279,6 @@ void FileProviderSettingsController::setTrashDeletionEnabledForAccount(const QSt
emit trashDeletionSetForAccountChanged(userIdAtHost);
}
unsigned long long FileProviderSettingsController::localStorageUsageForAccount(const QString &userIdAtHost) const
{
return d->localStorageUsageForAccount(userIdAtHost);
}
float FileProviderSettingsController::localStorageUsageGbForAccount(const QString &userIdAtHost) const
{
return gbFromBytesWithOneDecimal(localStorageUsageForAccount(userIdAtHost));
}
unsigned long long FileProviderSettingsController::remoteStorageUsageForAccount(const QString &userIdAtHost) const
{
const auto userInfoForAccount = _userInfos.value(userIdAtHost);
if (!userInfoForAccount) {
return 0;
}
return userInfoForAccount->lastQuotaUsedBytes();
}
float FileProviderSettingsController::remoteStorageUsageGbForAccount(const QString &userIdAtHost) const
{
return gbFromBytesWithOneDecimal(remoteStorageUsageForAccount(userIdAtHost));
}
QAbstractListModel *FileProviderSettingsController::materialisedItemsModelForAccount(const QString &userIdAtHost)
{
const auto items = d->materialisedItemsForAccount(userIdAtHost);
if (items.isEmpty()) {
return nullptr;
}
const auto model = new FileProviderMaterialisedItemsModel(this);
model->setItems(items);
connect(this, &FileProviderSettingsController::materialisedItemsForAccountChanged,
model, [this, model, userIdAtHost](const QString &accountUserIdAtHost) {
if (accountUserIdAtHost != userIdAtHost) {
return;
}
const auto items = d->materialisedItemsForAccount(userIdAtHost);
model->setItems(items);
});
return model;
}
void FileProviderSettingsController::createEvictionWindowForAccount(const QString &userIdAtHost)
{
const auto engine = Systray::instance()->trayEngine();
QQmlComponent component(engine, QUrl(fpEvictionDialogQmlPath));
const auto model = materialisedItemsModelForAccount(userIdAtHost);
const auto genericDialog = component.createWithInitialProperties({
{fpAccountUserIdAtHostProp, userIdAtHost},
{fpMaterialisedItemsModelProp, QVariant::fromValue(model)},
});
const auto dialog = qobject_cast<QQuickWindow *>(genericDialog);
QObject::connect(dialog, SIGNAL(reloadMaterialisedItems(QString)),
this, SLOT(refreshMaterialisedItemsForAccount(QString)));
Q_ASSERT(dialog);
dialog->show();
}
void FileProviderSettingsController::refreshMaterialisedItemsForAccount(const QString &userIdAtHost)
{
d->enumerateMaterialisedFilesForDomainManager(FileProviderUtils::managerForDomainIdentifier(userIdAtHost),
FileProviderUtils::domainForIdentifier(userIdAtHost));
}
void FileProviderSettingsController::signalFileProviderDomain(const QString &userIdAtHost)
{
d->signalFileProviderDomain(userIdAtHost);
}
FileProviderDomainSyncStatus *FileProviderSettingsController::domainSyncStatusForAccount(const QString &userIdAtHost) const
{
return d->domainSyncStatusForAccount(userIdAtHost);
}
void FileProviderSettingsController::resetVfsForAccount(const QString &userIdAtHost)
{
qCInfo(lcFileProviderSettingsController) << "Resetting virtual files environment for account" << userIdAtHost;
setVfsEnabledForAccount(userIdAtHost, false);
const auto accountState = AccountManager::instance()->accountFromUserId(userIdAtHost);
if (!accountState) {
qCWarning(lcFileProviderSettingsController) << "Could not find account for userIdAtHost" << userIdAtHost
<< "to reset VFS environment.";
return;
}
const auto splitUserId = userIdAtHost.split('@');
if (splitUserId.size() != 2) {
qCWarning(lcFileProviderSettingsController) << "Invalid userIdAtHost format" << userIdAtHost
<< "Expected format: userId@host";
return;
}
const auto accUserId = splitUserId.at(0);
const auto accHost = splitUserId.at(1);
// Delete the database in the group container
const auto groupContainerPath = FileProviderUtils::groupContainerPath();
if (groupContainerPath.isEmpty()) {
qCWarning(lcFileProviderSettingsController) << "Could not determine group container path, cannot reset VFS.";
return;
}
const auto dbsPath = QDir::cleanPath(groupContainerPath + "/FileProviderExt/Database");
qCInfo(lcFileProviderSettingsController) << "Resetting VFS for account" << userIdAtHost
<< "by deleting database files in" << dbsPath;
const auto databases = QDir(dbsPath).entryList(QDir::Files);
for (const auto &dbFile : databases) {
// Format of db file names is "userId_cloud_nc_com-fileproviderextdatabase.realm"
const auto splitDbName = dbFile.split('-');
const auto address = splitDbName.at(0).split('_').mid(1).join('.');
const auto userId = splitDbName.at(0).split('_').first();
if (userId != accUserId || address != accHost) {
qCInfo(lcFileProviderSettingsController) << "Skipping database file" << dbFile
<< "for userId" << userId
<< "and host" << address
<< "as it does not match the account we are resetting.";
continue; // Not the database we are looking for
}
const auto dbPath = QDir(dbsPath).filePath(dbFile);
qCInfo(lcFileProviderSettingsController) << "Deleting database file" << dbPath;
if (QFile::exists(dbPath)) {
QFile::remove(dbPath);
}
}
setVfsEnabledForAccount(userIdAtHost, true);
}
} // namespace Mac
} // namespace OCC

View File

@ -1,16 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#import <Foundation/Foundation.h>
#import <FileProvider/FileProvider.h>
typedef void(^UsageEnumerationFinishedHandler)(NSError *const error);
@interface FileProviderStorageUseEnumerationObserver : NSObject<NSFileProviderEnumerationObserver>
@property (readwrite, strong) UsageEnumerationFinishedHandler enumerationFinishedHandler;
@property (readonly) NSSet<id<NSFileProviderItem>> *materialisedItems;
@end

View File

@ -1,47 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#import "fileproviderstorageuseenumerationobserver.h"
@implementation FileProviderStorageUseEnumerationObserver
- (instancetype)init
{
self = [super init];
if (self) {
_materialisedItems = [NSSet set];
}
return self;
}
// NSFileProviderEnumerationObserver protocol methods
- (void)didEnumerateItems:(NSArray<id<NSFileProviderItem>> *)updatedItems
{
NSMutableSet<id<NSFileProviderItem>> * const existingItems = self.materialisedItems.mutableCopy;
for (const id<NSFileProviderItem> item in updatedItems) {
NSLog(@"StorageUseEnumerationObserver: Enumerating %@ with size %llu",
item.filename, item.documentSize.unsignedLongLongValue);
[existingItems addObject:item];
}
_materialisedItems = existingItems.copy;
}
- (void)finishEnumeratingWithError:(NSError *)error
{
dispatch_async(dispatch_get_main_queue(), ^{
self.enumerationFinishedHandler(error);
});
}
- (void)finishEnumeratingUpToPage:(NSFileProviderPage)nextPage
{
dispatch_async(dispatch_get_main_queue(), ^{
self.enumerationFinishedHandler(nil);
});
}
@end

View File

@ -1,72 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import Style 1.0
import "../../filedetails"
import "../../tray"
import com.nextcloud.desktopclient 1.0
ApplicationWindow {
id: root
signal reloadMaterialisedItems(string accountUserIdAtHost)
property var materialisedItemsModel: null
property string accountUserIdAtHost: ""
LayoutMirroring.enabled: Application.layoutDirection === Qt.RightToLeft
LayoutMirroring.childrenInherit: true
title: qsTr("Remove local copies")
color: palette.base
flags: Qt.Dialog | Qt.WindowStaysOnTopHint
width: 640
height: 480
Component.onCompleted: reloadMaterialisedItems(accountUserIdAtHost)
ColumnLayout {
anchors.fill: parent
RowLayout {
Layout.fillWidth: true
Layout.margins: Style.standardSpacing
EnforcedPlainTextLabel {
text: qsTr("Local copies")
font.bold: true
font.pointSize: Style.headerFontPtSize
Layout.fillWidth: true
}
Button {
padding: Style.smallSpacing
text: qsTr("Reload")
onClicked: reloadMaterialisedItems(accountUserIdAtHost)
}
}
ListView {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.leftMargin: Style.standardSpacing
Layout.rightMargin: Style.standardSpacing
clip: true
model: root.materialisedItemsModel
delegate: FileProviderFileDelegate {
width: parent.width
height: 60
onEvictItem: root.materialisedItemsModel.evictItem(identifier, domainIdentifier)
}
}
}
}

View File

@ -53,63 +53,11 @@ Page {
checked: root.controller.vfsEnabledForAccount(root.accountUserIdAtHost)
onClicked: root.controller.setVfsEnabledForAccount(root.accountUserIdAtHost, checked)
}
Loader {
id: vfsSettingsLoader
Layout.fillWidth: true
Layout.fillHeight: true
active: vfsEnabledCheckBox.checked
sourceComponent: ColumnLayout {
Rectangle {
Layout.fillWidth: true
height: Style.normalBorderWidth
color: palette.dark
}
FileProviderSyncStatus {
syncStatus: root.controller.domainSyncStatusForAccount(root.accountUserIdAtHost)
onDomainSignalRequested: root.controller.signalFileProviderDomain(root.accountUserIdAtHost)
}
FileProviderStorageInfo {
id: storageInfo
localUsedStorage: root.controller.localStorageUsageGbForAccount(root.accountUserIdAtHost)
remoteUsedStorage: root.controller.remoteStorageUsageGbForAccount(root.accountUserIdAtHost)
onEvictDialogRequested: root.controller.createEvictionWindowForAccount(root.accountUserIdAtHost)
Connections {
target: root.controller
function onLocalStorageUsageForAccountChanged(accountUserIdAtHost) {
if (root.accountUserIdAtHost !== accountUserIdAtHost) {
return;
}
storageInfo.localUsedStorage = root.controller.localStorageUsageGbForAccount(root.accountUserIdAtHost);
}
function onRemoteStorageUsageForAccountChanged(accountUserIdAtHost) {
if (root.accountUserIdAtHost !== accountUserIdAtHost) {
return;
}
storageInfo.remoteUsedStorage = root.controller.remoteStorageUsageGbForAccount(root.accountUserIdAtHost);
}
}
}
CheckBox {
text: qsTr("Allow deletion of items in Trash")
checked: root.controller.trashDeletionEnabledForAccount(root.accountUserIdAtHost)
onClicked: root.controller.setTrashDeletionEnabledForAccount(root.accountUserIdAtHost, checked)
}
Button {
text: qsTr("Reset virtual files environment")
onPressed: root.controller.resetVfsForAccount(root.accountUserIdAtHost);
}
}
CheckBox {
text: qsTr("Allow deletion of items in Trash")
checked: root.controller.trashDeletionEnabledForAccount(root.accountUserIdAtHost)
onClicked: root.controller.setTrashDeletionEnabledForAccount(root.accountUserIdAtHost, checked)
}
}
}

View File

@ -1,60 +0,0 @@
/*
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import Style 1.0
import "../../filedetails"
import "../../tray"
import com.nextcloud.desktopclient 1.0
GridLayout {
id: root
signal evictDialogRequested()
required property real localUsedStorage
required property real remoteUsedStorage
Layout.fillWidth: true
columns: 3
EnforcedPlainTextLabel {
Layout.row: 0
Layout.column: 0
Layout.alignment: Layout.AlignLeft | Layout.AlignVCenter
text: qsTr("Local storage use")
font.bold: true
}
EnforcedPlainTextLabel {
Layout.row: 0
Layout.column: 1
Layout.alignment: Layout.AlignRight | Layout.AlignVCenter
Layout.fillWidth: true
text: qsTr("%1 GB of %2 GB remote files synced").arg(root.localUsedStorage.toFixed(2)).arg(root.remoteUsedStorage.toFixed(2));
elide: Text.ElideRight
color: palette.dark
horizontalAlignment: Text.AlignRight
}
Button {
Layout.row: 0
Layout.column: 2
Layout.alignment: Layout.AlignRight | Layout.AlignVCenter
text: qsTr("Free up space …")
onPressed: root.evictDialogRequested()
}
ProgressBar {
Layout.row: 1
Layout.columnSpan: root.columns
Layout.fillWidth: true
value: root.localUsedStorage / root.remoteUsedStorage
}
}

View File

@ -1,71 +0,0 @@
/*
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import Style 1.0
import "../../filedetails"
import "../../tray"
import com.nextcloud.desktopclient 1.0
GridLayout {
id: root
signal domainSignalRequested
required property var syncStatus
rows: syncStatus.syncing ? 2 : 1
NCBusyIndicator {
id: syncIcon
property int size: Style.trayListItemIconSize * 0.8
Layout.row: 0
Layout.rowSpan: root.syncStatus.syncing ? 2 : 1
Layout.column: 0
Layout.preferredWidth: size
Layout.preferredHeight: size
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
padding: 0
spacing: 0
imageSource: root.syncStatus.icon
running: false // avoid rotating the icon when syncing
}
EnforcedPlainTextLabel {
Layout.row: 0
Layout.column: 1
Layout.columnSpan: root.syncStatus.syncing ? 2 : 1
Layout.fillWidth: true
font.bold: true
font.pointSize: Style.headerFontPtSize
text: root.syncStatus.syncing ? qsTr("Syncing") : qsTr("All synced!")
}
NCProgressBar {
Layout.row: 1
Layout.column: 1
Layout.fillWidth: true
value: root.syncStatus.fractionCompleted
visible: root.syncStatus.syncing
}
Button {
id: requestSyncButton
text: qsTr("Request sync")
visible: !root.syncStatus.syncing
hoverEnabled: true
onClicked: root.domainSignalRequested()
ToolTip.visible: hovered
ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
ToolTip.text: qsTr("Request a sync of changes for the VFS environment.\nmacOS may ignore or delay this request.")
}
}

View File

@ -287,7 +287,8 @@ void ownCloudGui::slotComputeOverallSyncStatus()
bool allPaused = true;
QVector<AccountStatePtr> problemAccounts;
for (const auto &account : AccountManager::instance()->accounts()) {
const auto &allAccounts = AccountManager::instance()->accounts();
for (const auto &account : allAccounts) {
if (!account->isSignedOut()) {
allSignedOut = false;
}
@ -308,7 +309,8 @@ void ownCloudGui::slotComputeOverallSyncStatus()
QList<QString> idleFileProviderAccounts;
if (Mac::FileProvider::fileProviderAvailable()) {
for (const auto &accountState : AccountManager::instance()->accounts()) {
const auto &allAccounts = AccountManager::instance()->accounts();
for (const auto &accountState : allAccounts) {
const auto account = accountState->account();
const auto userIdAtHostWithPort = account->userIdAtHostWithPort();
if (!Mac::FileProviderSettingsController::instance()->vfsEnabledForAccount(userIdAtHostWithPort)) {

View File

@ -806,7 +806,7 @@ AccountState *OwncloudSetupWizard::applyAccountChanges()
auto newState = manager->addAccount(newAccount);
if (newAccount->isPublicShareLink()) {
qCInfo(lcWizard()) << "seeting up public share link account";
qCInfo(lcWizard()) << "setting up public share link account";
}
manager->saveAccount(newAccount);

View File

@ -525,7 +525,7 @@ void SelectiveSyncDialog::accept()
//The part that changed should not be read from the DB on next sync because there might be new folders
// (the ones that are no longer in the blacklist)
auto blackListSet = QSet<QString>{blackList.begin(), blackList.end()};
auto changes = (oldBlackListSet - blackListSet) + (blackListSet - oldBlackListSet);
const auto changes = (oldBlackListSet - blackListSet) + (blackListSet - oldBlackListSet);
for (const auto &it : changes) {
_folder->journalDb()->schedulePathForRemoteDiscovery(it);
_folder->schedulePathForLocalDiscovery(it);

View File

@ -149,7 +149,7 @@ SettingsDialog::SettingsDialog(ownCloudGui *gui, QWidget *parent)
customizeStyle();
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint & Qt::Window);
cfg.restoreGeometry(this);
}
@ -335,7 +335,8 @@ void SettingsDialog::customizeStyle()
QString background(palette().base().color().name());
_toolBar->setStyleSheet(TOOLBAR_CSS().arg(background, dark, highlightColor, highlightTextColor));
for (const auto a : _actionGroup->actions()) {
const auto &allActions = _actionGroup->actions();
for (const auto a : allActions) {
QIcon icon = Theme::createColorAwareIcon(a->property("iconPath").toString(), palette());
a->setIcon(icon);
auto *btn = qobject_cast<QToolButton *>(_toolBar->widgetForAction(a));

View File

@ -465,7 +465,10 @@ void ShareManager::createShare(const QString &path,
[=, this](const QJsonDocument &reply) {
// Find existing share permissions (if this was shared with us)
Share::Permissions existingPermissions = SharePermissionAll;
for (const auto &element : reply.object()["ocs"].toObject()["data"].toArray()) {
const auto &replyObject = reply.object();
const auto &ocsObject = replyObject["ocs"].toObject();
const auto &dataArray = ocsObject["data"].toArray();
for (const auto &element : dataArray) {
auto map = element.toObject();
if (map["file_target"] == path)
existingPermissions = Share::Permissions(map["permissions"].toInt());
@ -554,7 +557,7 @@ void ShareManager::fetchSharedWithMe(const QString &path)
const QList<SharePtr> ShareManager::parseShares(const QJsonDocument &reply) const
{
qDebug() << reply;
auto tmpShares = reply.object().value("ocs").toObject().value("data").toArray();
const auto tmpShares = reply.object().value("ocs").toObject().value("data").toArray();
const QString versionString = _account->serverVersion();
qCDebug(lcSharing) << versionString << "Fetched" << tmpShares.count() << "shares";

View File

@ -44,7 +44,7 @@ bool SslDialogErrorHandler::handleErrors(QList<QSslError> errors, const QSslConf
// Check if this host has an active HSTS policy
auto hstsPolicies = qnam->strictTransportSecurityHosts();
for (const auto &policy : hstsPolicies) {
for (const auto &policy : std::as_const(hstsPolicies)) {
if (policy.host() == host && !policy.isExpired()) {
// HSTS is active for this host, don't show the dialog
qCInfo(lcSslErrorDialog) << "SSL certificate error, but HSTS is active. Rejecting connection.";
@ -152,7 +152,7 @@ bool SslErrorDialog::checkFailingCertsKnown(const QList<QSslError> &errors)
msg += QL("<h3>") + tr("Cannot connect securely to <i>%1</i>:").arg(host) + QL("</h3>");
// loop over the unknown certs and line up their errors.
msg += QL("<div id=\"ca_errors\">");
for (const auto &cert : _unknownCerts) {
for (const auto &cert : std::as_const(_unknownCerts)) {
msg += QL("<div id=\"ca_error\">");
// add the errors for this cert
for (const auto &err : errors) {

View File

@ -30,6 +30,7 @@ ItemDelegate {
Accessible.onPressAction: root.clicked()
ToolTip {
popupType: Qt.platform.os === "windows" ? Popup.Item : Popup.Native
visible: root.hovered && !activityContent.childHovered && model.displayLocation !== ""
text: qsTr("In %1").arg(model.displayLocation)
}

View File

@ -178,6 +178,7 @@ RowLayout {
icon.height: Style.activityListButtonIconSize
ToolTip {
popupType: Qt.platform.os === "windows" ? Popup.Item : Qt.platform.os === "windows" ? Popup.Item : Popup.Native
text: qsTr("Open file details")
visible: parent.hovered
}
@ -200,6 +201,7 @@ RowLayout {
display: Button.IconOnly
ToolTip {
popupType: Qt.platform.os === "windows" ? Popup.Item : Qt.platform.os === "windows" ? Popup.Item : Popup.Native
text: qsTr("Dismiss")
visible: parent.hovered
}

View File

@ -53,12 +53,14 @@ Button {
x: (root.x + 2)
y: (root.y + Style.trayWindowHeaderHeight + 2)
width: (Style.rootWidth - 2)
property real widestMenuItemWidth: 0
property real maximumWidthAllowed: trayWindowHeader.width - (root.x + 4)
width: Math.min( widestMenuItemWidth + leftPadding + rightPadding, maximumWidthAllowed )
height: Math.min(implicitHeight, maxMenuHeight)
closePolicy: Menu.CloseOnPressOutsideParent | Menu.CloseOnEscape
onClosed: {
// HACK: reload account Instantiator immediately by restting it - could be done better I guess
// HACK: reload account Instantiator immediately by resetting it - could be done better I guess
// see also onVisibleChanged above
userLineInstantiator.active = false;
userLineInstantiator.active = true;
@ -68,10 +70,30 @@ Button {
id: userLineInstantiator
model: UserModel
delegate: MenuItem {
implicitHeight: instantiatedUserLine.height + Style.standardSpacing
implicitHeight: instantiatedUserLine.height
UserLine {
id: instantiatedUserLine
width: parent.width
width: Math.min(accountMenu.widestMenuItemWidth, accountMenu.maximumWidthAllowed)
Component.onCompleted: {
instantiatedUserLine.updateMenuWidth()
}
onImplicitWidthChanged: {
instantiatedUserLine.updateMenuWidth()
}
Connections {
target: model
function onNameChanged() {
instantiatedUserLine.updateMenuWidth()
}
function onStatusChanged() {
instantiatedUserLine.updateMenuWidth()
}
}
onShowUserStatusSelector: {
userStatusDrawer.openUserStatusDrawer(model.index);
accountMenu.close();
@ -81,6 +103,11 @@ Button {
accountMenu.close();
}
onClicked: UserModel.currentUserId = model.index;
function updateMenuWidth()
{
accountMenu.widestMenuItemWidth = Math.max( instantiatedUserLine.implicitWidth, accountMenu.widestMenuItemWidth )
}
}
}
onObjectAdded: function(index, object) {
@ -95,10 +122,22 @@ Button {
id: addAccountButton
hoverEnabled: true
visible: Systray.enableAddAccount
implicitHeight: Style.trayWindowHeaderHeight
icon.source: "image://svgimage-custom-color/add.svg/" + palette.windowText
icon.width: Style.accountAvatarSize
text: qsTr("Add account")
readonly property real addAccountIconSize: Style.accountAvatarSize * Style.smallIconScaleFactor
readonly property real addAccountHorizontalOffset: ( (Style.accountAvatarSize - addAccountIconSize) / 2 ) + Style.accountIconsMenuMargin
property var iconColor: !addAccountButton.enabled
? addAccountButton.palette.mid
: ((addAccountButton.highlighted || addAccountButton.down) && Qt.platform.os !== "windows"
? addAccountButton.palette.highlightedText
: addAccountButton.palette.text)
icon.source: "image://svgimage-custom-color/add.svg/" + iconColor
icon.width: addAccountIconSize
icon.height: addAccountIconSize
leftPadding: addAccountHorizontalOffset
spacing: Style.userLineSpacing
text: qsTr("Add account")
onClicked: UserModel.addAccount()
Accessible.role: Accessible.MenuItem
@ -119,6 +158,19 @@ Button {
Accessible.role: Accessible.MenuItem
Accessible.name: Systray.syncIsPaused ? qsTr("Resume sync for all") : qsTr("Pause sync for all")
Accessible.onPressAction: syncPauseButton.clicked()
contentItem: Text {
text: parent.text
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
leftPadding: Style.userLineSpacing
elide: Text.ElideRight
color: !parent.enabled
? parent.palette.mid
: ((parent.highlighted || parent.down) && Qt.platform.os !== "windows"
? parent.palette.highlightedText
: parent.palette.text)
}
}
MenuItem {
@ -130,6 +182,19 @@ Button {
Accessible.role: Accessible.MenuItem
Accessible.name: text
Accessible.onPressAction: settingsButton.clicked()
contentItem: Text {
text: parent.text
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
leftPadding: Style.userLineSpacing
elide: Text.ElideRight
color: !parent.enabled
? parent.palette.mid
: ((parent.highlighted || parent.down) && Qt.platform.os !== "windows"
? parent.palette.highlightedText
: parent.palette.text)
}
}
MenuItem {
@ -140,7 +205,20 @@ Button {
onClicked: Systray.shutdown()
Accessible.role: Accessible.MenuItem
Accessible.name: text
Accessible.onPressAction: exitButton.clicked()
Accessible.onPressAction: exitButton.clicked()
contentItem: Text {
text: parent.text
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
leftPadding: Style.userLineSpacing
elide: Text.ElideRight
color: !parent.enabled
? parent.palette.mid
: ((parent.highlighted || parent.down) && Qt.platform.os !== "windows"
? parent.palette.highlightedText
: parent.palette.text)
}
}
}

View File

@ -1,20 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
import QtQml
import QtQuick
import QtQuick.Controls
import Style
ToolTip {
id: toolTip
clip: true
delay: Qt.styleHints.mousePressAndHoldInterval
contentItem: EnforcedPlainTextLabel {
text: toolTip.text
wrapMode: Text.Wrap
}
}

View File

@ -52,6 +52,7 @@ TextField {
}
ToolTip {
popupType: Qt.platform.os === "windows" ? Popup.Item : Popup.Native
visible: sendReplyMessageButton.hovered
text: qsTr("Send reply to chat message")
}

View File

@ -17,6 +17,7 @@ MenuItem {
property string toolTipText: root.text
ToolTip {
popupType: Qt.platform.os === "windows" ? Popup.Item : Popup.Native
visible: root.hovered && root.toolTipText !== ""
text: root.toolTipText
}

View File

@ -53,6 +53,7 @@ HeaderButton {
ToolTip {
id: tooltip
popupType: Qt.platform.os === "windows" ? Popup.Item : Popup.Native
visible: root.hovered && !foldersMenuLoader.isMenuVisible
text: root.userHasGroupFolders ? qsTr("Open local or group folders") : qsTr("Open local folder")
}

View File

@ -30,6 +30,7 @@ MouseArea {
height: Style.unifiedSearchItemHeight
ToolTip {
popupType: Qt.platform.os === "windows" ? Popup.Item : Popup.Native
visible: unifiedSearchResultMouseArea.containsMouse
text: isFetchMoreTrigger ? qsTr("Load more results") : model.resultTitle + "\n\n" + model.subline
}

View File

@ -78,6 +78,12 @@ AbstractButton {
elide: Text.ElideRight
font.pixelSize: Style.topLinePixelSize
font.bold: true
color: !userLine.parent.enabled
? userLine.parent.palette.mid
: ((userLine.parent.highlighted || userLine.parent.down) && Qt.platform.os !== "windows"
? userLine.parent.palette.highlightedText
: userLine.parent.palette.text)
}
RowLayout {
@ -92,6 +98,12 @@ AbstractButton {
id: emoji
visible: model.statusEmoji !== ""
text: statusEmoji
color: !userLine.parent.enabled
? userLine.parent.palette.mid
: ((userLine.parent.highlighted || userLine.parent.down) && Qt.platform.os !== "windows"
? userLine.parent.palette.highlightedText
: userLine.parent.palette.text)
}
EnforcedPlainTextLabel {
@ -101,6 +113,12 @@ AbstractButton {
text: statusMessage
elide: Text.ElideRight
font.pixelSize: Style.subLinePixelSize
color: !userLine.parent.enabled
? userLine.parent.palette.mid
: ((userLine.parent.highlighted || userLine.parent.down) && Qt.platform.os !== "windows"
? userLine.parent.palette.highlightedText
: userLine.parent.palette.text)
}
}
@ -112,9 +130,19 @@ AbstractButton {
text: server
elide: Text.ElideRight
font.pixelSize: Style.subLinePixelSize
color: !userLine.parent.enabled
? userLine.parent.palette.mid
: ((userLine.parent.highlighted || userLine.parent.down) && Qt.platform.os !== "windows"
? userLine.parent.palette.highlightedText
: userLine.parent.palette.text)
}
}
Item { // Spacer
Layout.fillWidth: true
}
Button {
id: userMoreButton
Layout.preferredWidth: Style.headerButtonIconSize
@ -127,46 +155,55 @@ AbstractButton {
onClicked: userMoreButtonMenu.visible ? userMoreButtonMenu.close() : userMoreButtonMenu.popup()
icon.source: "image://svgimage-custom-color/more.svg/" + palette.windowText
property var iconColor: !userLine.parent.enabled
? userLine.parent.palette.mid
: (!hovered && ((userLine.parent.highlighted || userLine.parent.down) && Qt.platform.os !== "windows")
? userLine.parent.palette.highlightedText
: userLine.parent.palette.text)
icon.source: "image://svgimage-custom-color/more.svg/" + iconColor
AutoSizingMenu {
id: userMoreButtonMenu
closePolicy: Menu.CloseOnPressOutsideParent | Menu.CloseOnEscape
height: implicitHeight
MenuItem {
visible: model.isConnected && model.serverHasUserStatus
height: visible ? implicitHeight : 0
id: setStatusButton
enabled: model.isConnected && model.serverHasUserStatus
text: qsTr("Set status")
font.pixelSize: Style.topLinePixelSize
hoverEnabled: true
onClicked: showUserStatusSelector(index)
Accessible.role: Accessible.Button
Accessible.name: text
Accessible.onPressAction: setStatusButton.clicked()
}
MenuItem {
visible: model.isConnected && model.serverHasUserStatus
height: visible ? implicitHeight : 0
id: statusMessageButton
enabled: model.isConnected && model.serverHasUserStatus
text: qsTr("Status message")
font.pixelSize: Style.topLinePixelSize
hoverEnabled: true
onClicked: showUserStatusMessageSelector(index)
Accessible.role: Accessible.Button
Accessible.name: text
Accessible.onPressAction: statusMessageButton.clicked()
}
MenuItem {
id: logInOutButton
enabled: model.canLogout
text: model.isConnected ? qsTr("Log out") : qsTr("Log in")
visible: model.canLogout
height: visible ? implicitHeight : 0
width: parent.width
font.pixelSize: Style.topLinePixelSize
hoverEnabled: true
onClicked: {
model.isConnected ? UserModel.logout(index) : UserModel.login(index)
accountMenu.close()
}
Accessible.role: Accessible.Button
Accessible.name: model.isConnected ? qsTr("Log out") : qsTr("Log in")
onPressed: {
if (model.isConnected) {
UserModel.logout(index)
} else {
@ -174,6 +211,10 @@ AbstractButton {
}
accountMenu.close()
}
Accessible.role: Accessible.Button
Accessible.name: text
Accessible.onPressAction: logInOutButton.clicked()
}
MenuItem {

View File

@ -165,7 +165,7 @@ OCC::Activity Activity::fromActivityJson(const QJsonObject &json, const AccountP
}
auto actions = json.value("actions").toArray();
for (const auto &action : actions) {
for (const auto &action : std::as_const(actions)) {
activity._links.append(ActivityLink::createFomJsonObject(action.toObject()));
}

View File

@ -664,7 +664,7 @@ void ActivityListModel::removeActivityFromActivityList(const Activity &activity)
void ActivityListModel::removeOutdatedNotifications(const OCC::ActivityList &receivedNotifications)
{
ActivityList activitiesToRemove;
for (const auto &activity : _finalList) {
for (const auto &activity : std::as_const(_finalList)) {
if (activity._type != Activity::NotificationType || receivedNotifications.contains(activity)) {
continue;
}

View File

@ -93,8 +93,8 @@ public:
[[nodiscard]] bool canFetchMore(const QModelIndex &) const override;
ActivityList activityList() { return _finalList; }
ActivityList errorsList() { return _notificationErrorsLists; }
[[nodiscard]] const ActivityList& activityList() const { return _finalList; }
[[nodiscard]] const ActivityList& errorsList() const { return _notificationErrorsLists; }
[[nodiscard]] AccountState *accountState() const;

View File

@ -66,21 +66,31 @@ void AsyncImageResponse::processNextImage()
return;
}
OCC::AccountPtr accountInRequestedServer;
OCC::AccountPtr accountInRequestedServer = nullptr;
const auto accountsList = OCC::AccountManager::instance()->accounts();
for (const auto &account : accountsList) {
if (account && account->account() && imagePath.startsWith(account->account()->url().toString())) {
accountInRequestedServer = account->account();
break;
}
}
if (accountInRequestedServer) {
const QUrl iconUrl(_imagePaths.at(_index));
if (iconUrl.isValid() && !iconUrl.scheme().isEmpty()) {
// fetch the remote resource
const auto reply = accountInRequestedServer->sendRawRequest(QByteArrayLiteral("GET"), iconUrl);
connect(reply, &QNetworkReply::finished, this, &AsyncImageResponse::slotProcessNetworkReply);
// fetch the remote resource in the thread of the account (or rather its QNAM)
// for some reason trying to use `accountInRequestedServer` causes clang 21 to crash for me :(
const auto accountQnam = accountInRequestedServer->networkAccessManager();
QMetaObject::invokeMethod(accountQnam, [this, accountInRequestedServer, iconUrl]() -> void {
const auto reply = accountInRequestedServer->sendRawRequest(QByteArrayLiteral("GET"), iconUrl);
connect(reply, &QNetworkReply::finished, this, [this, reply]() -> void {
QMetaObject::invokeMethod(this, [this, reply]() -> void {
processNetworkReply(reply);
});
});
});
++_index;
return;
}
@ -89,9 +99,8 @@ void AsyncImageResponse::processNextImage()
setImageAndEmitFinished();
}
void AsyncImageResponse::slotProcessNetworkReply()
void AsyncImageResponse::processNetworkReply(QNetworkReply *reply)
{
const auto reply = qobject_cast<QNetworkReply *>(sender());
if (!reply) {
setImageAndEmitFinished();
return;

View File

@ -8,6 +8,7 @@
#include <QImage>
#include <QQuickImageProvider>
#include <QFileIconProvider>
#include <QNetworkReply>
class AsyncImageResponse : public QQuickImageResponse
{
@ -18,9 +19,7 @@ public:
private:
void processNextImage();
private slots:
void slotProcessNetworkReply();
void processNetworkReply(QNetworkReply *reply);
QImage _image;
QStringList _imagePaths;

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