Comparar commits

...

444 Commits

Autor SHA1 Mensagem Data
Christian Kamm 49a678a26f timeAgo test: Fix for qt4, clean up 2016-05-12 09:59:12 +02:00
Daniel Molkentin 9290ed6b32 2.2.0 final 2016-05-12 09:41:22 +02:00
Daniel Molkentin 7b0231bfce Merge pull request #4821 from owncloud/notification_doc
Documentation draft to introduce notifications.
2016-05-11 21:16:35 +02:00
Jocelyn Turcotte edc58c045f Fix assert on restore after propagation (#4823)
The assert was there to make sure that this case wasn't happening
to eventually be properly tested. Remove the assert for now but this
codepath should eventually be unit tested using this specific situation.
2016-05-11 18:16:46 +02:00
Klaas Freitag 035058934f Documentation draft to introduce notifications.
Also added a screenshot.
2016-05-11 11:16:08 +02:00
Klaas Freitag 9b1f46e560 Fix exclude unit test, adopt to new Iface of isExcluded. 2016-05-10 16:46:08 +02:00
Klaas Freitag 9e7a8e619b Fix utility test: Pass a command name to do the version check. 2016-05-10 16:26:42 +02:00
Klaas Freitag 9c0cd2b13e Added Changelog for 2.2.0 2016-05-10 15:39:44 +02:00
Daniel Molkentin 58ad781bd4 Update binary submodule 2016-05-10 14:39:22 +02:00
Daniel Molkentin 9d6701ecbe Windows Shell Integration: Fix another spot where the pipe path was constructed manually 2016-05-10 13:49:14 +02:00
Daniel Molkentin d659c54798 Update binary sub module 2016-05-10 10:36:49 +02:00
Daniel Molkentin 0e9170cb36 Windows Shell Integration: Unify path lookups 2016-05-10 10:18:53 +02:00
Markus Goetz 8820bc1c17 Windows: Fix Share menu #4781 2016-05-09 14:37:46 +02:00
Jenkins for ownCloud 74f67c97a9 [tx-robot] updated from transifex 2016-05-09 02:19:15 -04:00
Jenkins for ownCloud fd96b482c5 [tx-robot] updated from transifex 2016-05-07 02:19:16 -04:00
Jocelyn Turcotte 727e73d640 [shell/windows] Fix the windows status push not working (#4784)
Since the windows implementation first does cache lookups using the
path string, directories need to be passed identically as through
RETRIEVE_FILE_STATUS.

Change the convention to never have a trailing slash for directories
in the protocol. This allows the convention to be applied without
having to access the disk (since we'd need to know if the path is
represented by a directory) and also matches the convention of the
rest of the sync engine. Individual file manager plugins are then
responsible of handling pushed paths as not ending with a trailing
slash.

This also:
- Moves the trailing slash removal logic from the SyncFileStatusTracker
  to the SocketApi class
- Remove the unneeded QString::normalized call in fileStatus, since
  this should already be done by the FolderWatcher and plugins
2016-05-06 12:32:01 +02:00
Jenkins for ownCloud d7804d8df3 [tx-robot] updated from transifex 2016-05-06 02:19:00 -04:00
Olivier Goffart 3d9d106bb1 Dolphin plugin: fix build with branding
Make sure that the name of the _export.h file is the same no matter the plugin
2016-05-04 14:19:18 +02:00
Jenkins for ownCloud 15dc3408ef [tx-robot] updated from transifex 2016-05-04 02:19:15 -04:00
Markus Goetz 928643f597 NSIS: Properly uninstall both x64 and x86 DLLs #4762
Viel hilft viel ;-)
2016-05-03 18:35:01 +02:00
Markus Goetz 7110091fdd Qt patches: Update with information about Qt 5.6 2016-05-03 15:16:07 +02:00
Jocelyn Turcotte 064c2b678a Merge branch 'overlayIconFixes' into 2.2 (#4765) 2016-05-03 13:04:34 +02:00
Jocelyn Turcotte e58739de00 [osx] Fix missing overlay icons on client startup
Since the statuses are cached and that we can't invalidate the cache,
sending NOP would need to be overwritten by the default OK status
once the client successfully connected. But instead of remembering
which files we NOPed, rather wait until we are ready to sync before
sending the REGISTER_PATH message to the socket API client. It will
also prevent the client from sending unnecessary RETRIEVE_FILE_STATUS
requests.

Also remove AccountState::canSync, since it does the same as
isConnected and syncing is not an account responsibility.
2016-05-03 13:01:51 +02:00
Jocelyn Turcotte 7c2fdee78b Avoid a SyncFileStatusTracker private overload with the same name
Having an overload as a private function in the same class makes the
code harder to follow. Rename the private fileStatus to syncFileItemStatus.
2016-05-03 13:01:51 +02:00
Jocelyn Turcotte 7bfe46962f Simplify the root status logic
Go through fileStatus like other cases to make sure that all use
cases go through the same code path. This also makes sure to use
lookupProblem which will use lower_bound which is more efficient
for larger sets of sync problems.

This also fixes the issue with lookupProblem that prevented it to
properly match an empty pathToMatch, caused by the fact that the
problem map contains relative paths not starting with a slash.
2016-05-03 13:01:51 +02:00
Jocelyn Turcotte 32b3023a8e Fix the root item sync status #4682
Make sure that we push the new status when the status of the SyncEngine
changed. SyncEngine::started comes a bit late, only when the propagation
starts, although it's better in this case since child folders will
only switch to Sync in aboutToPropagate.

Also fix an issue with SyncEngine::findSyncItem when using an empty
fileName; this would match and return the wrong item, even though
not currently happening with the code since fileStatus won't call
it with an empty fileName anymore.
2016-05-03 13:01:51 +02:00
Jocelyn Turcotte a5df44c757 Fix the sync status push for parent directories #4682
As before, we rely on metadata-update SyncFileItem entries for parent
directories to notify us that a directory contains files to propagate,
and to know when all children were propagated through its itemCompleted
signal.

Those metadata SyncFileItems however have a None direction and we need
to add a explicit directory check to show them as Sync.
This fix also handles new files as well as existing ones, so no need
to keep a separate logic for new files.
2016-05-03 13:01:51 +02:00
Klaas Freitag 641125eac1 Fix nautilus and nemo plugin branding strings. (#4780)
* Fix nautilus and nemo plugin branding strings.

Fix for owncloud/enterprise#1314

* nautilus plugin: Add a comment why the line needs to stay unchanged.
2016-05-03 11:36:56 +02:00
Jenkins for ownCloud d340017a0a [tx-robot] updated from transifex 2016-05-03 02:19:16 -04:00
Olivier Goffart a67173610d Propagator: fix qt4 build
propagatedownload.cpp:712:35: error: 'seenLockedFile' is a protected member of 'OCC::OwncloudPropagator'

Signals are protected in Qt4 but public in Qt5, mark the class accessing it
as friend when compiling with Qt4
2016-05-02 17:34:21 +02:00
Daniel Molkentin e833d01288 Re-Apply: Disambiguate socket API pathes on Windows with user name
Addresses #3411
2016-05-02 16:09:48 +02:00
Olivier Goffart 3047682223 ShareDialog: show the error message in red
Do the visual stuff from designer.
The previous code that was ment to change the color in red did not work
and changed it to gray instead.
Also I don't see why there should be a frame.

Issue #4773
2016-05-02 14:12:04 +02:00
Jenkins for ownCloud c91dd94728 [tx-robot] updated from transifex 2016-05-02 02:19:41 -04:00
Jenkins for ownCloud 52f9f44b51 [tx-robot] updated from transifex 2016-05-01 02:19:18 -04:00
Jenkins for ownCloud 49748191a9 [tx-robot] updated from transifex 2016-04-30 02:19:14 -04:00
ckamm e6b937f508 LockWatcher: Keep an eye on Windows file locks (#4758)
When a conflict-rename or a temporary-rename fails, notify the
LockWatcher. It'll regularly check whether the file has become
accesible again. When it has, another sync is triggered.

owncloud/enterprise#1288
2016-04-29 16:14:18 +02:00
Roeland Douma 31c13f74fb Add spinner during sharee search (#4764)
Fixed #4740

When searching for sharees we should display a loading spinner.
2016-04-29 14:11:27 +02:00
Olivier Goffart be466b47b7 Merge remote-tracking branch 'origin/2.1' into 2.2 2016-04-29 08:52:17 +02:00
Jenkins for ownCloud 817e97c148 [tx-robot] updated from transifex 2016-04-29 02:19:14 -04:00
Markus Goetz 29932004ae Shibboleth: Load username from config for UI (#4751)
For https://github.com/owncloud/enterprise/issues/1034
2016-04-28 14:55:29 +02:00
Daniel Molkentin 6d83e841a9 Merge pull request #4756 from owncloud/revert-4738-fix_3411
Revert "Disambiguate socket API pathes on Windows with user name"
2016-04-28 13:53:06 +02:00
Daniel Molkentin b43a9421d2 Revert "Disambiguate socket API pathes on Windows with user name" 2016-04-28 13:52:15 +02:00
Klaas Freitag e70b78d14b Merge pull request #4738 from owncloud/fix_3411
Disambiguate socket API pathes on Windows with user name
2016-04-28 13:46:10 +02:00
Klaas Freitag 6814342d1f Merge pull request #4748 from owncloud/win_stat_revisited
vio for windows: Use more graceful create file params.
2016-04-28 13:45:30 +02:00
Christian Kamm 68126dcff6 Checksums: Skip uploads of .eml files only #4754
This is a workaround. A correct solution would still update file metadata
such as the mtime. See #4755.
2016-04-28 12:59:39 +02:00
Christian Kamm e7f00339e6 Fix perl tests #4722
* The dav auth realm is "sabre/dav" for newer server versions
* Content checksums are enabled generally, so conflict detection
  is smarter
2016-04-28 10:44:37 +02:00
Christian Kamm a36b4ec863 FolderWizard: Sort remote folders #4612 2016-04-28 09:29:07 +02:00
Christian Kamm 074f8eadb1 Selective sync: Sort folders in FolderStatusModel #4612 2016-04-28 09:29:07 +02:00
Jenkins for ownCloud 6a51ae5b1b [tx-robot] updated from transifex 2016-04-28 02:19:20 -04:00
Christian Kamm af5f2d3860 Protocol: Make timestamp column width fit the text #4721 2016-04-28 07:37:42 +02:00
ckamm 38bad564a0 Merge pull request #4739 from ckamm/trackdirty
Overlay icons: Track touched files #4730
2016-04-28 07:17:17 +02:00
Christian Kamm c2fa3fb4c8 Overlay icons: Track touched files #4730
This uses the file watcher to keep track of files that were modified
in order to assign them the blue icon.

This is transient state that's not persisted across restarts.
2016-04-28 07:16:49 +02:00
Markus Goetz f7082ee3df sqlite: Update to version 3.12.2
For OS X and Windows
2016-04-27 12:22:12 +02:00
Markus Goetz f7c6efb391 ConnectionValidator: Also set short timeout for capabilities job
Default timeout was 5 min.
2016-04-27 12:19:49 +02:00
Klaas Freitag 051a348afd Vio for windows: Removed the overlap param again.
After discussion and studying docs again we agreed that
the parameter is not neccessary here as it only affects
the process that opens the handle. And we do not want to
do async stuff.
2016-04-27 11:37:21 +02:00
Olivier Goffart d0af3ede05 AccountSettings: context menu: don't rely on the alias
Don't rely on the alias to know weather we shuld show a context menu or not.
Use the classify function to know if it's a root folder instead
2016-04-27 11:21:58 +02:00
Olivier Goffart 0da2adcbe0 Workaround ubuntu 16.04 systemtray bug (#4693) (#4747) 2016-04-27 11:21:13 +02:00
Klaas Freitag 07bdd519e9 vio for windows: Use more graceful create file params. 2016-04-27 11:17:42 +02:00
Christian Kamm 0829a94c92 Remote folder selection: improve path typing #4745 #4746
* Scroll to the target typed path
* Show non-404 errors that were triggered by typing paths
2016-04-27 10:37:17 +02:00
Jenkins for ownCloud faf2514be2 [tx-robot] updated from transifex 2016-04-27 02:19:20 -04:00
Markus Goetz 4ea2edcf4a Merge pull request #4741 from owncloud/fix-alias
Accountsettings: Generate an alias for new folders
2016-04-26 17:34:10 +02:00
Olivier Goffart cd29875b76 Folder: Generate an alias for new folders
Before commit 1a51b6718a, the wizard was
making sure folder had an alias but this is no longer the case.
So generate still an unique alias.

Alias is not used in the UI any longer, it's just use for internal purposes.

For issue #4737
2016-04-26 16:53:24 +02:00
Markus Goetz 85bc3b276f Merge pull request #4744 from owncloud/fixAliasesOnceMore2
UI: Fix account name/alias display oddities #4577
2016-04-26 16:53:23 +02:00
Markus Goetz ce5ca8a42e UI: Fix account name/alias display oddities #4577 2016-04-26 16:47:47 +02:00
Daniel Molkentin ce6a365328 Disambiguate socket API pathes on Windows with user name
Addresses #3411
2016-04-26 14:49:45 +02:00
Christian Kamm 09eea7f5f2 Checksums: Use the first supported type if nothing is preferred 2016-04-26 13:10:53 +02:00
ckamm 043350f49d Merge pull request #4736 from ckamm/socketapi_nautilus_invalidate
Overlay icons: Fix invalidation with nautilus #3249
2016-04-26 12:04:53 +02:00
Christian Kamm f657bd0cd9 Overlay icons: Fix invalidation with nautilus #3249
This avoids an extra socketapi call after invalidate_extension_info
is called from a STATUS message.
2016-04-26 11:49:56 +02:00
Christian Kamm b4cf17d99d Overlay icons: Fix duplicate icons in nautilus #3249 2016-04-26 11:02:59 +02:00
Christian Kamm 12bf6e39b7 Share link: Consistent order of options owncloud/core#24122
(cherry picked from commit 8dc178a9f3)
2016-04-26 10:44:29 +02:00
Olivier Goffart 4e7c09de83 QTokenizer: fix Qt4 build
QSharedPointer::reset is new in Qt5
2016-04-26 10:38:20 +02:00
Jenkins for ownCloud 31f2c3e76a [tx-robot] updated from transifex 2016-04-26 02:19:11 -04:00
Jenkins for ownCloud 64b6486327 [tx-robot] updated from transifex 2016-04-25 08:26:49 -04:00
Jenkins for ownCloud 335e333e28 [tx-robot] updated from transifex 2016-04-24 02:19:04 -04:00
Jenkins for ownCloud 41eb5f0a3f [tx-robot] updated from transifex 2016-04-24 01:15:28 -04:00
Carla Schroder 030184fad9 Merge pull request #4704 from owncloud/doc_overlay_icons
Doc: Draft overlay icons
2016-04-22 08:40:09 -07:00
Roeland Douma 7d285d0225 Merge pull request #4717 from owncloud/fix_webdav_property
Only 1 : between namespace and property
2016-04-22 14:40:55 +02:00
Roeland Jago Douma d3a93322d2 Only 1 : between namespace and property 2016-04-22 14:38:43 +02:00
Jenkins for ownCloud 8694db1c49 [tx-robot] updated from transifex 2016-04-22 06:16:18 -04:00
Daniel Molkentin 19eca84d4f Merge pull request #4715 from owncloud/translate-desktop-ini
Adding new resource for the desktop.ini file
2016-04-22 12:05:15 +02:00
Daniel Molkentin 665c3f40e1 Fix search & replace error in Dockerfile 2016-04-22 11:26:46 +02:00
Thomas Müller f8f394eb0a Adding new resource for the desktop.ini file 2016-04-22 10:58:57 +02:00
Olivier Goffart 0711abbf56 Theme: missing virtual keyword 2016-04-22 10:09:07 +02:00
Olivier Goffart 84ac2e64e0 Quota: Add branding option for the base folder (#4714)
As discussed on issue ##4460
Having the quote to be queried on subfolder is wrong in the generic case,
so add a branding option to configure it.

This partially reverts commit ff4cdc3161
2016-04-22 10:05:50 +02:00
Klaas Freitag 57f0603e0e Doc: Applied review feedback for overlay icon docs. 2016-04-22 09:56:53 +02:00
Klaas Freitag b61ae351dd Merge pull request #4713 from owncloud/fix_win-sis
Fix sync to Windows deduplication enabled storages
2016-04-22 09:47:06 +02:00
Jenkins for ownCloud 277a1cfb5a [tx-robot] updated from transifex 2016-04-22 02:19:11 -04:00
Klaas Freitag 6ba7c6e2d3 Windows VIO: Remove unused variable. 2016-04-21 17:40:01 +02:00
Klaas Freitag 055c2ef73f local_vio_win: Fix handling files that have been deduplicated
as part of Windows SIS or Data Deduplication. Fixes #4056
2016-04-21 17:38:43 +02:00
Klaas Freitag e5a5b95b9a Merge pull request #4689 from owncloud/overlays_handle_root
SyncFileStatusTracker: Add a method to handle the root folder of the sync.

Also, handle new files put in the sync in the statustracker logic.
2016-04-21 15:01:11 +02:00
Klaas Freitag 7acdf50a2c SocketAPI: Code simplifications as asked for in the review. 2016-04-21 14:54:13 +02:00
Klaas Freitag 361ebf5464 SyncFileStatusTracker: Remember the state of new files.
In the before-propagate slot, new files that wait to be
pushed to the server are remembered in the _syncProblems
map. That way, the parents show a sync icon properly as
asked for in #4682.

After the item has been transfered properly, the item is
removed from the map again because success is the default.
2016-04-21 13:56:08 +02:00
Olivier Goffart 7a82fac0d5 owncloudcmd: Save the selective sync list on the first run
For issue #4690

In the first run, the journal might not exist, we should not cancel
setting the selective sync list
2016-04-21 12:58:10 +02:00
Daniel Molkentin 8b39c6e6ce Merge pull request #4709 from owncloud/fix_4691
Use QTokenizer to properly parse netrc
2016-04-21 12:50:43 +02:00
Daniel Molkentin e29d7e0128 Use QTokenizer to properly parse netrc
Addresses #4691
2016-04-21 12:46:03 +02:00
Daniel Molkentin dbcb94e7d4 Update doc and Dockerfile for 2.2 builds 2016-04-21 12:25:12 +02:00
Olivier Goffart 522f7afa9e Merge pull request #4663 from 'ckamm/disabletransmissionchecksum'
Disable validation of transmission checksums by default
2016-04-21 11:46:01 +02:00
Olivier Goffart fa69d089cf Checksums: remove the downloadChecksumValidationEnabled option
Added in previous commit from pull request #4663

As discussed, we do not need this option so no need to introduce
a new dependency on the config file in the sync engine
2016-04-21 11:43:44 +02:00
Jenkins for ownCloud 1935c78f17 [tx-robot] updated from transifex 2016-04-21 02:19:12 -04:00
Klaas Freitag b56919d0c4 SocketAPI: Return NOP state if folder is diconnected or paused.
Later, we can send a specific state for that.
2016-04-20 17:14:12 +02:00
Klaas Freitag f72c7f43bf Nautilus plugin: Be more robust against new states from socketapi
Do not bail out if an unknown state comes from SocketApi
2016-04-20 17:13:06 +02:00
Klaas Freitag add29befee Nautilus plugin: Remove the bogus logging 2016-04-20 17:12:21 +02:00
Markus Goetz f9fb7a59dd Merge pull request #4708 from owncloud/aliasGui
UI: Remove alias from more places #4577 #4695
2016-04-20 15:54:05 +02:00
Markus Goetz 1a51b6718a UI: Remove alias from more places #4577 #4695 2016-04-20 14:27:50 +02:00
Jenkins for ownCloud a3a9417630 [tx-robot] updated from transifex 2016-04-20 02:19:08 -04:00
Carla Schroder 53a72148d3 a bit of editing on new section 2016-04-19 10:28:46 -07:00
Klaas Freitag c379593c0f Doc: Draft overlay icons 2016-04-19 18:20:23 +02:00
Klaas Freitag bd37eab3ad SocketAPI: Display a warning on the directory if there are errors
Errors with individual files within a directory now result in
a warning label on the parent directories.
2016-04-19 18:18:00 +02:00
Markus Goetz edb942ba61 Merge pull request #4696 from owncloud/issue4573_negative_content_length
Discovery: Be more explicit about files with unknown size #4573
2016-04-19 16:09:48 +02:00
Markus Goetz a17f37d8ae Discovery: Be more explicit about files with unknown size #4573
Also related https://github.com/owncloud/core/issues/23468
2016-04-19 15:31:16 +02:00
Roeland Douma 33459b48c8 Merge pull request #4700 from owncloud/fix_4409
Use expireDate if returned by the OCS Share API
2016-04-19 13:14:04 +02:00
Roeland Jago Douma 598941948c Use expireDate if returned by the OCS Share API
If an app modifies the expiration date (for example the password policy
app) then on more recent versions of the server we will get the share
object back REST style. We should use that info!

Fixes #4409
2016-04-19 11:22:32 +02:00
Jenkins for ownCloud 038b7db606 [tx-robot] updated from transifex 2016-04-19 02:19:15 -04:00
Roeland Douma 9f30e83413 Merge pull request #4687 from owncloud/fix_share-permissions-namespace
The share-permissions namespace was moved
2016-04-18 21:29:26 +02:00
Klaas Freitag 2ab4caf007 ProtocolWidget: Display the recalculated number of issues.
Should fix #3222
2016-04-18 16:48:37 +02:00
Klaas Freitag 11d3aa3c4f Protocolwidget: Removed some leftovers from refactoring. 2016-04-18 16:47:14 +02:00
Jenkins for ownCloud 93ad61aeef [tx-robot] updated from transifex 2016-04-18 02:19:02 -04:00
Jenkins for ownCloud 6a36d1b4fb [tx-robot] updated from transifex 2016-04-17 02:19:07 -04:00
Daniel Molkentin c0ec481436 No need for WebKitWidgets in libsync anymore 2016-04-16 10:58:12 +02:00
Jenkins for ownCloud df0833d7f9 [tx-robot] updated from transifex 2016-04-16 02:19:16 -04:00
Markus Goetz 6a9655aab6 OS X: Fix overlay installation on El Capitan #4650
The sleep is somehow needed, probably for pkg to rebuild its DB?
The case fix fixes a warning but did not fix the issue itself.
2016-04-15 16:58:58 +02:00
Klaas Freitag 0f3d6d4160 SyncFileStatusTracker: Add a method to handle the root item. 2016-04-15 15:02:02 +02:00
Markus Goetz c4577cb2a1 Merge pull request #4683 from owncloud/fix4657
OS X: Allow ampersand in APPLICATION_NAME #4657
2016-04-15 14:28:38 +02:00
Olivier Goffart fafca26144 AccountManager: take theURL from the Theme rather than from the config if the theme specify it (#4591)
That way an upgrade of the client can actually change the URL
Issue https://github.com/owncloud/enterprise/issues/1113
https://github.com/owncloud/enterprise/issues/1126

In addition to restoring commit 7e5d89293d, this
add a way to override the auth type
2016-04-15 13:16:49 +02:00
Markus Goetz 06c19b0b6f sqlite: Update to version 3.12.1
For OS X and Windows.
2016-04-15 12:02:00 +02:00
Markus Goetz d1649ce4df OS X: Allow ampersand in APPLICATION_NAME #4657 2016-04-15 11:30:49 +02:00
Christian Kamm ea40e314d4 Add server capabilities for checksums #4638
* Add checksums/supportedTypes and checksums/preferredUploadType
  capabilities. The default is that no checksum types are supported.

* Remove the transmissionChecksum config option. Servers must now
  use the capabilities to indicate that they are fine with the
  client sending checksums.

Note: This intentionally breaks brandings that overrode
Theme::transmissionChecksum. The override must be removed and the
server's capabilities must be adjusted to include the new values.
2016-04-15 10:58:14 +02:00
Klaas Freitag e2622310df Merge pull request #4670 from owncloud/implement_ep_1136_2nd_attempt
Make postfixlineedit more userfriendly, rename email id string to "Email"
2016-04-15 10:17:03 +02:00
Klaas Freitag 4ad8e04bc3 Merge pull request #4684 from owncloud/less_socket_api_updates
Less socket api updates
2016-04-15 10:15:07 +02:00
Roeland Jago Douma 74301e4373 The share-permissions namespace was moved
See https://github.com/owncloud/core/pull/24017

This was done since other services we want to intergrate with don't use
the ownCloud namespace.
2016-04-15 10:14:05 +02:00
Markus Goetz 053dcbf1f5 Merge pull request #4686 from owncloud/allow_0_mtime_files
CSync: Allow files with 0 mtime #1603
2016-04-15 09:22:39 +02:00
Jenkins for ownCloud 700c7bcbc6 [tx-robot] updated from transifex 2016-04-15 02:19:14 -04:00
Markus Goetz bc6c57aa0b Exclude: .Trash-* trash folders #4600 2016-04-14 18:27:33 +02:00
Markus Goetz a0521caf52 CSync: Allow files with 0 mtime #1603
I don't see a reason we should not allow this. We detect
a "failed stat reply" in other ways. Maybe this was a legacy
csync decision somewhow that is not valid anymore?
2016-04-14 18:15:16 +02:00
Klaas Freitag 8007331462 SocketAPI: Remove unneeded logging. 2016-04-14 15:39:46 +02:00
Klaas Freitag 12330b38e9 Folderman: Remove one call to updateFolderView.
The reason is that updateFolderView is invoked by the
emitted signal folderSyncStateChange() anyway.

This will reduce the traffic over the SocketAPI nicely,
maybe this was the reason why it was slower than before.
2016-04-14 15:26:11 +02:00
Klaas Freitag 8eb53fa9ae nautilus integration: Handle NOP as empty. 2016-04-14 15:24:46 +02:00
Christian Kamm dfd7d4d2f9 Activity tab visibility fixes #4651 2016-04-14 11:35:16 +02:00
Christian Kamm 484a2c800d Fix typo 2016-04-14 10:59:40 +02:00
Christian Kamm 8d300b049f ShareDialog: Tell a user early if resharing is impossible #4679 2016-04-14 10:16:37 +02:00
ckamm 48e594ebbf Merge pull request #4673 from ckamm/shareperm
Disable unavailable sharing permissions #4383
2016-04-14 09:44:58 +02:00
Christian Kamm 910c61b492 Disable unavailable sharing permissions #4383
Users can't reshare with more permissions than they have themselves.
2016-04-14 09:44:37 +02:00
Jenkins for ownCloud 8155c1c426 [tx-robot] updated from transifex 2016-04-14 02:19:23 -04:00
Daniel Molkentin 97a6d3df82 document Theme::wizardUrlHint() 2016-04-13 15:59:25 +02:00
Olivier Goffart f7aafb081f Dolphin Plugin: rename the files to the branded name #4669 (#4672) 2016-04-13 15:05:04 +02:00
Daniel Molkentin 9cc981c8c7 Make postfixlineedit more userfriendly, rename email id string to "Email". 2016-04-13 14:56:49 +02:00
Klaas Freitag ed3203d25d Merge pull request #4659 from owncloud/fix_client_4651
Fix client #4651
2016-04-13 12:02:45 +02:00
Jenkins for ownCloud a4f606ceab [tx-robot] updated from transifex 2016-04-13 02:19:05 -04:00
Klaas Freitag 15988c6fda SocketAPI: Use NOP only, not NONE in the share api protocol.
Otherwise, plugins might brake, as the linux one did.
2016-04-12 18:28:27 +02:00
Klaas Freitag f443377978 Merge pull request #4664 from ckamm/hiddenfiles
Fix hidden file handling #4655
2016-04-12 17:14:47 +02:00
Olivier Goffart fdcdddca16 CMakeLists: move QtKeychain detection in client 2016-04-12 14:43:26 +02:00
Klaas Freitag bf99306a53 Merge branch 'do_not_close_db_on_done' 2016-04-12 14:36:46 +02:00
Christian Kamm 844777d43f Fix unit test build 2016-04-12 13:43:17 +02:00
Christian Kamm 4f79f1b5e8 Explicitly depend on Qt5Test. 2016-04-12 12:45:55 +02:00
Christian Kamm 6f454feb39 Fix hidden file handling #4655
There were two issues:

* With the refactoring of how Folder and SyncEngine relate, the
  ignore_hidden_files flag on the CSync context was reset after
  each sync run and not updated from the configuration again.

* The folder watcher failed to enumerate hidden folders and thus
  didn't watch for changes inside them. (linux only)
2016-04-12 11:59:07 +02:00
Olivier Goffart 68b7437afb Merge pull request #4658 from owncloud/fix-qt4-build
Fix qt4 build
2016-04-12 10:36:48 +02:00
Christian Kamm 41b950b7e6 Don't call reset() on a query after close() #4662 2016-04-12 10:30:46 +02:00
Jenkins for ownCloud 2d08754f91 [tx-robot] updated from transifex 2016-04-12 02:19:13 -04:00
Klaas Freitag 868edb1f0d SyncJournalDb: Only close the db if the return code is not SQLITE_DONE
SQLITE_DONE is the indicator for not more query results, which is a legal
thing and not an error condition.

Also, check _getFileRecordQuery for null pointer, as close() wipes it.
2016-04-11 17:38:16 +02:00
Klaas Freitag aaf43bd0d3 SyncJournalDb: Handle empty filename and avoid a bogus db query. 2016-04-11 17:36:15 +02:00
Klaas Freitag d5f1d1c6b2 SqlQuery: Add method errorId() to get the numeric error code. 2016-04-11 17:34:59 +02:00
Klaas Freitag 6cb94e8849 Merge pull request #4656 from owncloud/close_journal_fix
Close journal fix - do not keep the journal open or reopen on error.
2016-04-11 16:14:17 +02:00
Klaas Freitag d433c24186 Check if the record returned from getFileRecord is valid.
Handle database fails properly.
2016-04-11 16:04:20 +02:00
Olivier Goffart c48b5c4f61 ActivityWidget: use a QHash for _widgetsToRemove
The problem with QSet is that the QDateTime was part of
the hash, but that does not make sens as it should be unique
per widget and not per <date, widget>

Instead make it a QHash so there is only one entry per widget.
2016-04-11 15:49:24 +02:00
Olivier Goffart 976f4dfabe ActivityWidget: Fix Qt4 build
Don't use API added in Qt5
2016-04-11 15:49:24 +02:00
Olivier Goffart f8dc263338 CMakeLists: fix Qt4 build
Only the src subdirectory needs Qt.
Otherwise it activates Qt4 also for the dolphin plugin which always need Qt5
2016-04-11 15:49:18 +02:00
Klaas Freitag e896d16f32 ActivityWidget: Make sure Notification are visible if Activity disabled
This is the fix for #4651
2016-04-11 15:39:25 +02:00
Klaas Freitag 2ec642aadb ActivityWidget: Properly set widget for the Scrollview.
Also fixed some SizePolicy settings.
2016-04-11 15:38:25 +02:00
Olivier Goffart bd3a079a7b Merge pull request #4648 from owncloud/handle-database-errors
Handle setFileRecord errors #4632
2016-04-11 15:36:46 +02:00
Olivier Goffart 6ee7e82913 Handle setFileRecord errors #4632 2016-04-11 14:11:11 +02:00
Klaas Freitag 648328fbe2 SyncJournalDb: Close the db on error in getFileRecord()
The idea is that the next call to any database operation will try to
reopen the database through the checkConnect() method. So even if there
was a disconnect trom the db file, this will reestablish the connection.
2016-04-11 12:40:19 +02:00
Klaas Freitag ee58cc3b66 SyncEngine: Close the sync journal after the sync run has finished. 2016-04-11 11:31:54 +02:00
Jenkins for ownCloud 74a75f6399 [tx-robot] updated from transifex 2016-04-11 02:19:03 -04:00
Jenkins for ownCloud a0baca25ff [tx-robot] updated from transifex 2016-04-10 02:18:57 -04:00
Jenkins for ownCloud 4b5c9c5ee5 [tx-robot] updated from transifex 2016-04-09 02:19:05 -04:00
ckamm 4ce97633cd Merge pull request #4630 from ckamm/folderwatchwin
Graceful termination of folderwatcher_win #4620
2016-04-09 06:55:30 +02:00
Jenkins for ownCloud f1732d66d6 [tx-robot] updated from transifex 2016-04-08 02:19:09 -04:00
Jenkins for ownCloud 329f512a40 [tx-robot] updated from transifex 2016-04-08 01:15:47 -04:00
Markus Goetz c64720eac5 Merge pull request #4605 from RealRancor/RealRancor-patch-1
Change note to warning in documentation
2016-04-07 15:50:32 +02:00
Klaas Freitag 8f7b833c12 Not Synced Tab: Use tr rather than string concat 2016-04-07 09:18:51 +02:00
Klaas Freitag 48bfcde97d Merge pull request #4640 from owncloud/numberInProtocolTab
ProtocolWidget: Show number of non synced files in tab label.
2016-04-07 09:15:06 +02:00
Klaas Freitag 42439490cc Merge pull request #4645 from owncloud/fix_4633
SelectiveSync: Verify if the list could be read from journal.

If not handle the error rather than assuming the selective sync list is empty.
2016-04-07 09:14:33 +02:00
Jenkins for ownCloud 4dd8d61e4e [tx-robot] updated from transifex 2016-04-07 02:19:10 -04:00
Daniel Molkentin 271bea39ff Update binary subdir 2016-04-06 18:07:35 +02:00
Daniel Molkentin a0827c5ef2 Remove deleted icons from resource file 2016-04-06 17:59:34 +02:00
Klaas Freitag 058cd33324 Not Synced: Display only the number of not synced items in parathesis.
This was Jans wish.
2016-04-06 17:53:42 +02:00
Klaas Freitag e4604b406f SyncEngine: finalize properly on error with syncjournal 2016-04-06 17:20:48 +02:00
Klaas Freitag 7b1f02fcda SelectiveSync: Verify if the list could be read from journal.
If there is a read error from the database while trying to get
the list from database, make sure to not behave badly because
the list is empty.
2016-04-06 15:01:28 +02:00
Klaas Freitag 52a5234122 Merge pull request #4641 from owncloud/bubbleUpConflicts
Bubble up conflicts: Show a tray notification on conflicts and show the conflicts in the not-synced list.
2016-04-06 13:45:47 +02:00
Klaas Freitag 4e17dabcb6 Folder: Fixed a couple of plural translation issues. 2016-04-06 12:22:29 +02:00
Jenkins for ownCloud 15bdb7be64 [tx-robot] updated from transifex 2016-04-06 02:19:26 -04:00
Klaas Freitag 9bdc84c6f4 NotificationJobs: Set ocs header to maintain the session.
This is needed by the server.
2016-04-05 16:52:51 +02:00
Klaas Freitag 252aea25da ProgressDispatcher: Improve wording about conflicts. 2016-04-05 16:38:18 +02:00
Klaas Freitag efb0faa14e Folder: After sync, fire up tray notification if conflicts happened.
If there were conflicts, users want to have a tray notification that warn
about.
This will help for #3222
2016-04-05 16:37:54 +02:00
Klaas Freitag 5b40921587 ProtocolWidget: Show number of non synced files in tab label. 2016-04-05 13:57:38 +02:00
Klaas Freitag 7994b3d91a ProtocolWidget: handle Conflicts as not-synced items 2016-04-05 11:13:12 +02:00
Jenkins for ownCloud 65e740301a [tx-robot] updated from transifex 2016-04-05 02:19:38 -04:00
Jenkins for ownCloud f495daf1cf [tx-robot] updated from transifex 2016-04-05 01:15:46 -04:00
Olivier Goffart 3334067d9f Merge branch '2.1'
Conflicts:
	src/gui/socketapi.cpp
2016-04-04 16:58:49 +02:00
Klaas Freitag 73e2a503d7 NotificationWidget: Some ui refinements. 2016-04-04 12:46:00 +02:00
Klaas Freitag bc6eebddf4 NotificationWidget: Handle empty message and subject properly. 2016-04-04 12:45:39 +02:00
Klaas Freitag 6b0d535120 Merge branch 'notifications' 2016-04-04 10:40:33 +02:00
Jenkins for ownCloud 8b3ea7aa97 [tx-robot] updated from transifex 2016-04-04 02:19:12 -04:00
Jenkins for ownCloud 84ad0d234e [tx-robot] updated from transifex 2016-04-02 02:19:13 -04:00
Jenkins for ownCloud 87e4370bf4 [tx-robot] updated from transifex 2016-04-01 02:19:45 -04:00
Christian Kamm fa1bb309ca Graceful termination of folderwatcher_win #4620 2016-03-31 15:44:42 +02:00
Christian Kamm e05819370b Fix invalid read on account removal 2016-03-31 09:00:32 +02:00
Jenkins for ownCloud d65808b1e1 [tx-robot] updated from transifex 2016-03-31 02:19:30 -04:00
Jocelyn Turcotte a115f4b9d0 Merge pull request #4625 from owncloud/trivialFixes
Trivial fixes
2016-03-30 18:41:56 +02:00
Daniel Molkentin 9ea191f63d Scale correctly with HiDPI displays on Linux with Qt 5.6 2016-03-30 18:25:30 +02:00
Jocelyn Turcotte 17c645ce43 Hide environment variables when running the FinderSync icns script
This otherwise shows up in the console for each incremental build.
2016-03-30 18:00:22 +02:00
Jocelyn Turcotte f427955512 Simplify the build of auto tests
Remove all configure_files:
- Move all tests to cpp files
- Use the QTEST_MAIN macro instead of a generated main.cpp
- Include test*.moc in the cpp to let CMAKE_AUTOMOC call moc
- Pass info through add_definitions instead of generating oc_bin.h with them

This makes sure that build errors points to the original test source
file instead of the generated one in the build directory to be able to
jump and fix errors directly from the IDE's error pane.
2016-03-30 18:00:22 +02:00
Markus Goetz dd8b0c3e4a Merge pull request #4623 from owncloud/share_menu_icon_osx
OS X: Use app icon for "Share with ..." Finder menu #4472
2016-03-30 15:53:54 +02:00
Markus Goetz 6c283c7a48 OS X: Use app icon for "Share with ..." Finder menu #4472
This works for both plain ownCloud and themed clients.
2016-03-30 15:26:43 +02:00
Christian Kamm c150350096 SyncFileStatusTracker: Fix compilation with older Qt 2016-03-30 13:42:08 +02:00
Christian Kamm c35e74d264 Theme: Enable link sharing by default
Was disabled by accident.
2016-03-30 13:33:12 +02:00
Olivier Goffart cdbc25ede8 Merge pull request #4615 from owncloud/socketApiRefactor
Socket API refactor
2016-03-30 13:24:10 +02:00
Jocelyn Turcotte a0260c29c0 Fix the build on Windows 2016-03-30 12:19:09 +02:00
Jocelyn Turcotte 2c0caf8b75 Fix the SocketApi status of CSYNC_FILE_SILENTLY_EXCLUDED files
Bring back the hardcoded status logic for excluded files.
Since the activity log doesn't even mention those files on purpose,
we can't rely on the SyncEngine to notify us about the status of those files.
2016-03-30 12:10:51 +02:00
Jocelyn Turcotte 56064c9366 Fix sibbling directories showing up as warning
Looking up a/aa while an error is present in a/aab/aaba would return
a warning status since a/aa is a substring of a/aab.
Fix the issue by checking if the following character is a slash.
2016-03-30 11:22:01 +02:00
ckamm 98995f45e6 Merge pull request #4618 from ckamm/progressui
Progress layout adjustments
2016-03-30 09:22:27 +02:00
Christian Kamm 5636dc1386 Tooltip updater: Fix compile with Qt4 2016-03-30 09:21:28 +02:00
Jenkins for ownCloud 127291e35d [tx-robot] updated from transifex 2016-03-30 02:19:15 -04:00
Klaas Freitag 885f8b382f ActivityWidget: Handle plural properly in translations.
Even for the case where the number is fixed.
Also fix the translators comments.
2016-03-29 18:01:54 +02:00
Klaas Freitag 8166c52f4a NotificationHandling: Use QByteArray for the verb. 2016-03-29 18:01:49 +02:00
Jocelyn Turcotte ef57d4ae11 Move the SyncFileStatusTracker directory slash suffix logic in a method 2016-03-29 17:55:02 +02:00
Jocelyn Turcotte fbf23b6abb Cleanup after the SyncFileStatusTracker change
- Add missing explicit keywords
- Add doc
- Comment out verbose and partly redundant qDebug statements
2016-03-29 17:39:30 +02:00
Jocelyn Turcotte 47a552f8c2 Use a std::map for SyncFileStatusTracker problems
This prevents having to define a Problem structure with dubious
operator overloads to accomplish the same.
Also use std::map::lower_bound to quickly iterate over the
list of problems.
2016-03-29 17:29:36 +02:00
Klaas Freitag cd3f612857 ActivityWidget: Rename blacklistActivities to blacklistNotifications. 2016-03-29 16:50:58 +02:00
Klaas Freitag 2e30a0e5bc Remove superflous iterator increment 2016-03-29 16:50:15 +02:00
Klaas Freitag 9f438cb768 Doc: Add milliseconds unit to notificationRefreshInterval doc 2016-03-29 14:39:12 +02:00
Klaas Freitag cacb751ab8 Cleaups based on review feedback. 2016-03-29 14:38:11 +02:00
Klaas Freitag 4d59f5ec66 ActivityData: Declare operators outside the class 2016-03-29 14:09:19 +02:00
Klaas Freitag 69e8e15884 Remove explicit time spec specification as it is not needed. 2016-03-29 13:59:08 +02:00
Daniel Molkentin 1730569f77 No Q_NULLPTR in Qt 4.8 2016-03-29 13:28:11 +02:00
Olivier Goffart 98091aeab7 Theme: document forceSystemNetworkProxy (#4458) 2016-03-29 13:08:33 +02:00
Christian Kamm c5fbde412c Folder info layout adjustments #3403
* Remove duplicate remote path
* Use thin progress bar
* Move bandwidth and file info to tooltip
* Shorten overall progress message

This also fixes #4562 by making the layout not dependent on the
width of the displayed text.
2016-03-29 12:45:01 +02:00
Christian Kamm 10a7128d1a Update QTreeView tooltips as they change #3403 2016-03-29 12:26:09 +02:00
Christian Kamm 41f43feecf Add utility function for shorter time duration strings #3403 2016-03-29 12:26:09 +02:00
Christian Kamm 4915bbf8f3 Adjust size description strings #3403
@jancorchardt suggested to not have decimal places on file
sizes, except for GB.
2016-03-29 12:26:09 +02:00
Christian Kamm ffbd5df25f Account UI: Fix log out button text #4578 2016-03-29 12:14:53 +02:00
Jocelyn Turcotte 82190eaa81 Refactor the overlay icon logic to show errors as a warning for parent folders #3634
This also remove all smartness from the SocketApi about the status
of a file and solely use info from the current and last sync.
This simplifies the logic a lot and prevents any discrepancy between
the status shown in the activity log and the one displayed on the
overlay icon of a file.

The main benefit of the additional simplicity is that we are able
to push all new status of a file reliably (including warnings for
parent folders) to properly update the icon on overlay implementations
that don't allow us invalidating the status cache, like on OS X.

Both errors and warning from the last sync are now kept in a set,
which is used to also affect parent folders of an error.

To make sure that errors don't become warning icons on a second
sync, SyncFileItem::_hasBlacklistEntry is also interpreted as an error.
This also renames StatusIgnore to StatusWarning to match this semantic.

SyncEngine::aboutToPropagate is used in favor of SyncEngine::syncItemDiscovered
since the latter is emitted before file permission warnings are set on the
SyncFileItem. SyncEngine::finished is not used since we have all the
needed information in SyncEngine::itemCompleted.
2016-03-28 14:29:48 +02:00
Jocelyn Turcotte 69aa39f1f6 Don's use SyncFileStatus for Folder::createGuiLog
SyncFileStatus' purpose is to track overlay icon status.
Instead of putting comments and default: clauses in switch
on both sides about unused enums, use different enums.

This also remove STATUS_NEW which is the equivalent of
STATUS_SYNC in all shell extension implementations, and
remove STATUS_UPDATED and STATUS_STAT_ERROR which have
the same semantic as STATUS_UPTODATE and STATUS__ERROR.
2016-03-28 14:29:47 +02:00
Jocelyn Turcotte ea5e6d367b Connect the SocketApi directly to the SyncFileStatusTracker
Don't go through the Folder->ProgressDispatcher->SocketApi route and
keep the path logic in SyncFileStatusTracker.
2016-03-28 14:29:47 +02:00
Jocelyn Turcotte da7b9916e5 Move the SocketApi business logic to a libsync SyncFileStatusTracker class
This will allow testing this code and avoid going through too many
layers to get notified and a file status changed.
2016-03-28 14:29:47 +02:00
Jocelyn Turcotte dac4bd8370 Remove null-checks for FolderMan::_socketApi
It's now created in the constructor and won't be null.
2016-03-28 14:29:47 +02:00
Jocelyn Turcotte 6e16e34799 Remove SocketApi::dbFileRecord_capi in favor of JournalDB::getFileRecord 2016-03-28 14:29:47 +02:00
Jocelyn Turcotte 6d3fe9d865 Remove the tainted folder logic
This currently is no-op code since the socket API isn't notified
that the tainted folder list changed, and the result is the same
since a sync will be triggered within the next 5 seconds and the
modified folder will be shown as SYNC at that point anyway.

Removing the dependency to the file watcher allows moving the
status estimation logic to libsync.
2016-03-28 14:29:47 +02:00
Jocelyn Turcotte c090a511fd Remove OwnCloud6 specific sharing code
If users encounter this situation, the share icon will simply not show.
This simplifies the transition to move this code in libsync.
2016-03-28 14:29:47 +02:00
Jocelyn Turcotte a4e0899af4 Remove the mutex from ExcludedFiles
It's always accessed from the main thread.
2016-03-28 14:29:47 +02:00
Jocelyn Turcotte 2d2c7bc9b8 Move the SyncEngine construction to the Folder constructor
The SyncEngine is now created only once, at construction of the
Folder, instead of being reconstructed on each sync.
2016-03-28 13:26:38 +02:00
Jocelyn Turcotte df386b64ba Make the AccountState a construction argument of the Folder
This will help moving the SyncEngine construction in the constructor
and allow moving functionalities from Folder to SyncEngine or its
delegated objects.
2016-03-28 13:07:28 +02:00
Jenkins for ownCloud 80bd86a305 [tx-robot] updated from transifex 2016-03-28 02:18:50 -04:00
Jenkins for ownCloud d55c31d93b [tx-robot] updated from transifex 2016-03-27 02:19:06 -04:00
Jenkins for ownCloud 68cbaf1bd6 [tx-robot] updated from transifex 2016-03-26 02:18:41 -04:00
Jenkins for ownCloud 6867c2ffb3 [tx-robot] updated from transifex 2016-03-25 02:19:21 -04:00
Jenkins for ownCloud ecb34f60f2 [tx-robot] updated from transifex 2016-03-25 01:15:43 -04:00
Jocelyn Turcotte 5989a07339 [Win-shellex] Make sure to unregister obsolete CLSIDs #4595
If the user tries to install an older version of a branded client
with shell extensions, and also install the 2.1 ownCloud client
side-by-side, both shell extensions DLLs will be registered and might
lead to both DLLs versions being loaded by exlorer, causing a system
crash.

Since we don't support both branded and non-branded shell extensions
to work at the same time, at least make sure to unregister any legacy
CLSID that could cause explorer to load the previous version DLL.
2016-03-24 16:35:38 +01:00
Daniel Molkentin 7730e826b0 Merge pull request #4580 from owncloud/implement_ep_1136
Implement EP 1136
2016-03-24 14:58:09 +01:00
Daniel Molkentin 2d24585a8f Implement enforcing a static URL postfix.
Second part of EP 1136
2016-03-24 14:18:52 +01:00
Jenkins for ownCloud 84a6d1e920 [tx-robot] updated from transifex 2016-03-24 02:19:22 -04:00
RealRancor 05fae8ee7f Change note to warning in documentation 2016-03-23 18:19:44 +01:00
Klaas Freitag 1fe5d6bb0c Notifications: Handle Notifications without an action.
The policy that was said is that if a notification has no action, the
client can and should display a close-button. This patch does it.

In additon to that, the client needs a blacklist of closed notifcations
otherwise they would re-appear next time the server notifications are
fetched again.

Also, changed the cleanup of not-longer-used widgets to be more robust.
2016-03-23 17:02:13 +01:00
Klaas Freitag 0c944a06f9 NotificationWidgetUI: Fix sizing and sizePolicy 2016-03-23 16:49:25 +01:00
Klaas Freitag 1bb3a4a45d NotificationWidget: Remove accountName() and add activity() method. 2016-03-23 16:48:38 +01:00
Klaas Freitag 161d21904a ActivityData: Add source file for implementation details
Add the ident() method and operators.
2016-03-23 16:47:13 +01:00
Klaas Freitag ea2f19b78a Docs: Add new config option for the notification sync interval. 2016-03-22 11:38:44 +01:00
Klaas Freitag f70c6282ca Notifications: Remove unused variable. 2016-03-22 11:38:10 +01:00
Klaas Freitag ad60e8ac89 Notifications: Fix handling of notifications to remove from the list.
If a notification is not longer in the list of notifications coming from
the server, it needs to be removed from the widget list.
2016-03-22 10:35:24 +01:00
Klaas Freitag d03fcc95e4 Notifications: Maintain a timeSinceLastCheck for every Account.
In multi-account environment every account needs the own counter.
2016-03-22 09:58:30 +01:00
Jenkins for ownCloud 588646f7a2 [tx-robot] updated from transifex 2016-03-22 02:19:22 -04:00
Daniel Molkentin fb75adcd57 Wizard: Implement alternative forms of inquiring about the userID
Settable in the theme.
2016-03-21 23:50:48 +01:00
Klaas Freitag d407aacc4a Notifications: remove notification widgets if the notification is gone.
If a notification is not longer in the list that comes from the
server, the notification is removed.

That is mainly for the notifications that are created by the
announcement application
2016-03-21 16:32:37 +01:00
Markus Goetz 02a69d5487 Exclude: Weird GoogleDrive "My Saved Places." file #4573 2016-03-21 15:15:59 +01:00
Markus Goetz 57acde758b Documentation: promptDeleteAllFiles #4389 2016-03-21 15:05:49 +01:00
Klaas Freitag f587f35ef0 Fix plural translation handling, remove the superflous arg() 2016-03-21 15:03:28 +01:00
Jenkins for ownCloud 8c00d5e1ee [tx-robot] updated from transifex 2016-03-21 02:19:11 -04:00
Jenkins for ownCloud f02148e8ca [tx-robot] updated from transifex 2016-03-20 02:18:46 -04:00
Jenkins for ownCloud 476d628c01 [tx-robot] updated from transifex 2016-03-19 02:19:17 -04:00
Jocelyn Turcotte c55ac504a3 Merge pull request #4584 from lultimouomo/fix-destruction-order
Fix crash due to destruction order
2016-03-18 18:58:23 +01:00
Luca Niccoli 6735126c09 Fix crash due to destruction order 2016-03-18 16:41:48 +01:00
Klaas Freitag b9663456d8 Notifications: Refresh the notifications based on a config value.
Pulls a timer that polls for new notifications regularly. Add
Config file method for the interval value.
2016-03-18 16:28:20 +01:00
Daniel Molkentin cabeeba7c5 Merge pull request #4582 from owncloud/nsis_uninstall_test
NSIS: Force uninstall before install #4543
2016-03-18 16:01:41 +01:00
Markus Goetz 6c517638d0 NSIS Silent mode: Force uninstall before install #4543 2016-03-18 16:00:29 +01:00
Klaas Freitag 0e1b9a346d Fix plural translation handling, remove the superflous arg() 2016-03-18 15:28:00 +01:00
Christian Kamm 03db1894d8 Fix wizardSelectiveSyncDefaultNothing #4581 2016-03-18 13:52:45 +01:00
Christian Kamm ff4cdc3161 Quota: Change quota path if single folder #4460
Since the quota is a per-folder value, this will make the displayed data
more useful when a single sync folder is configured.

Of course each subfolder could have a different quota again.
2016-03-18 13:34:49 +01:00
Christian Kamm 0febe9b0df Revert "Utility: Make sure to use UTC timestamp to compare in timeAgoInWords"
This reverts commit 41b43bf961.

Using now in UTC should make no difference, but that assert might
trigger...
2016-03-18 13:15:35 +01:00
Christian Kamm ceb4a23dab Fix compile with strict C flags 2016-03-18 12:41:57 +01:00
Klaas Freitag 7f22a07312 Notifications: Check if the account is connected before querying.
Also avoid memory leaks if it is not connected.
2016-03-18 12:29:09 +01:00
Klaas Freitag 328d254f7f Notifications: Remove "done" notification widgets after fife seconds. 2016-03-18 11:25:14 +01:00
Markus Goetz 15f6e133a5 sqlite: Update to version 3.11.1
For OS X and Windows.
2016-03-18 10:17:41 +01:00
Klaas Freitag 0a590b7cbe Notifications: Give feedback if notifcation request succeeded.
Also display a time stamp.
2016-03-18 10:02:11 +01:00
Klaas Freitag f04895a407 Utility: Fix plural translation. 2016-03-18 08:31:13 +01:00
Klaas Freitag 41b43bf961 Utility: Make sure to use UTC timestamp to compare in timeAgoInWords
This should help to fix the problems we see in #4521
2016-03-18 08:31:03 +01:00
Klaas Freitag 05de710b67 Notifications: Display timestamp of the notification in the widget 2016-03-18 08:21:54 +01:00
Klaas Freitag f71fdab997 Fix timeAgoInWords 2016-03-18 08:20:23 +01:00
Jenkins for ownCloud 6d8e570420 [tx-robot] updated from transifex 2016-03-18 02:19:21 -04:00
Christian Kamm d7bd1300a8 Ignores: expand escapes #4568 2016-03-17 14:31:53 +01:00
Christian Kamm 2bba68e059 Handle server timezone data correctly #4521
The date we receive from the server is an ISO8601 datetime that
includes the offset from UTC. Qt does correctly parse this
information and creates the appropriate QDateTime object.

Calling setTimeSpec(UTC) will force the timezone offset to 0 and
thereby change the referenced point in time to an incorrect one.
2016-03-17 13:04:18 +01:00
Christian Kamm 4b19cdeca0 Propfind: Treat broken XML response as failure #4575
Soldiering on with a broken or incomplete response could lead to
incorrect sync behavior.

Since discovery uses LsCol jobs which already handle errors
correctly, this should not have a significant impact.
2016-03-17 11:32:19 +01:00
Olivier Goffart 254361cb87 Shared dialog: move the error label up (#4348) 2016-03-16 18:06:46 +01:00
Klaas Freitag f7f412007e Activity: Some documentation and better varialbe names 2016-03-16 16:31:52 +01:00
Klaas Freitag 45c32ec0b1 NotificationWidget: Remove not needed method. 2016-03-16 16:21:20 +01:00
Jenkins for ownCloud 97e323ac3a [tx-robot] updated from transifex 2016-03-16 02:19:14 -04:00
Christian Kamm 04faee4a0f Doc: low disk space #4443 2016-03-15 15:20:23 +01:00
Christian Kamm 7bd4f95b8c SqlQuery: Write NULL when intended #4548
In SQLite bindings are not cleared by sqlite3_reset() calls, so
skipping a sqlite3_bind call to create a NULL value doesn't work,
instead the previous value will be written.

To fix this, I clear all bindings in SqlQuery::reset and make sure
to explicitly bind NULL when desired in SqlQuery::bind.

To make sure there's no confusion about SqlQuery::reset and
sqlite3_reset, I rename our method to reset_and_clear_bindings().
2016-03-15 14:38:31 +01:00
Jenkins for ownCloud 6e697bae97 [tx-robot] updated from transifex 2016-03-15 02:19:11 -04:00
Jenkins for ownCloud 366abb5350 [tx-robot] updated from transifex 2016-03-15 01:15:43 -04:00
Klaas Freitag a4dcc2784a Notification: Fix plural handling for tray message 2016-03-14 16:21:04 +01:00
Klaas Freitag 9a2f1456c5 ocs jobs: Add a define for OCS job success. 2016-03-14 15:41:20 +01:00
Klaas Freitag 9d219a18f3 ActivityListModel: Code cleanups
based on review feedback.
2016-03-14 15:40:39 +01:00
Klaas Freitag 97f1694f7e ActivityData: Simplified implementation.
Use QVariantHash and removed ActivityList object in favour
of a typedef
2016-03-14 15:39:07 +01:00
Klaas Freitag 73cd5a9c27 Notifications: Cleaner notification string build 2016-03-14 14:41:21 +01:00
ckamm 25baa995ec Merge pull request #4532 from ckamm/content-checksum
Enable content checksums #4375
2016-03-14 09:49:31 +01:00
Jenkins for ownCloud 39a95d3bd5 [tx-robot] updated from transifex 2016-03-14 02:19:15 -04:00
Jenkins for ownCloud 22acc004cc [tx-robot] updated from transifex 2016-03-12 02:19:25 -05:00
Markus Goetz 6c07f08175 Proxies: Enable ownBrander to force system proxy usage #4458 2016-03-11 16:21:40 +01:00
Markus Goetz 62d4ed8087 Protocol: Show by default instead of server activity #4395 2016-03-11 16:08:56 +01:00
Klaas Freitag adf9570a92 Notification: Enhance the tray message
Add the hostname from where the notification comes, as well as
the name of the application to the header.
2016-03-11 12:48:31 +01:00
Klaas Freitag 2c2a18af43 Activitiy: Refactor - move classes to their own source files.
Created a activitydata.h header (only) for the basic data, plus
a separate file for the model. Cleans up the widget source.
2016-03-11 11:41:19 +01:00
Klaas Freitag 2d1ab27cb5 Notifications: Refactor - create a notification handler class
That cleans the ActivityWidget class
2016-03-11 10:48:34 +01:00
Klaas Freitag 903e79a7c4 Notifications: Do a GUI tray notification if new notifciations arrive.
Show a GUI notification once an hour if no new notifications arrive
to not annoy users.
2016-03-11 10:15:28 +01:00
Jenkins for ownCloud 6b8636f79b [tx-robot] updated from transifex 2016-03-11 02:19:14 -05:00
Klaas Freitag 8a0ce463da Notifications: Properly delete the notification check job. 2016-03-10 17:46:00 +01:00
Klaas Freitag 7d13a1d8e1 Notifications: Check capabilities if the notifications are enabled
If not, do not query for them.
2016-03-10 17:46:00 +01:00
Klaas Freitag b97c832306 Capabilities: Add isValid check and check for notifications
The isValid check should be used everywhere the capabilities
are used as the loading of the capabilities is happening
in parallel of the startup, so it is not guaranteed to be
available always.
2016-03-10 17:46:00 +01:00
Klaas Freitag 4a4dac22e2 Notifications: Add a Progress indicator and handle job results.
Parse the replyCode from the button action calls and disable
buttons accordingly.
2016-03-10 17:46:00 +01:00
Klaas Freitag 32e16b323c Display server notifications on the client (#3733)
As interaction is required, the notifications are displayed in a
separate widget above the server activity list.

Note that design and also where we display the notifications can
still be discussed and changed.
2016-03-10 17:46:00 +01:00
Klaas Freitag 688c5502a8 New GUI class NotificationWidget.
It displays a server notification that can come with a dynamic
set of buttons next to a message and a subject (=header)
2016-03-10 17:22:36 +01:00
Klaas Freitag eb00b34191 Minor wording fixes 2016-03-10 17:22:36 +01:00
Klaas Freitag a831b7417f Added temporar icon for notifications. 2016-03-10 17:22:36 +01:00
Klaas Freitag 0eb1041290 AbstractNetworkJob: Add a delete job.
It is needed to easily send delete requests which happen
through the notify API.
2016-03-10 17:22:36 +01:00
Jenkins for ownCloud c0623295e0 [tx-robot] updated from transifex 2016-03-10 02:19:25 -05:00
Jenkins for ownCloud e82a13803d [tx-robot] updated from transifex 2016-03-10 01:15:45 -05:00
Markus Goetz 5cb45bf738 Merge pull request #4549 from owncloud/fix_4545
Add account name to warning message boxes in Folder
2016-03-09 19:12:34 +01:00
Markus Goetz a14b495864 Old servers: Don't nag if version still undetected #4523 2016-03-09 15:57:45 +01:00
Markus Goetz ad1c343cd7 Merge pull request #4552 from owncloud/hide_activity_tab
Activities: Hide if non of the accounts has the app enabled.
2016-03-09 15:34:12 +01:00
Christian Kamm 17003cec19 Settings: Hide update info if skipUpdateCheck #4397
(cherry picked from commit 93de378fb21e934d324d621f66eec98e53f1637a)
2016-03-09 15:32:32 +01:00
Markus Goetz 2843214d09 Merge pull request #4531 from ckamm/old-server
Add warnings for old server versions #4523
2016-03-09 15:16:48 +01:00
Daniel Molkentin b456ded5e7 Show full path in warning message boxes before folder removal
Fixes #4545
2016-03-09 14:07:34 +01:00
Roeland Douma d4848880e1 Merge pull request #4553 from owncloud/add_spinner
Add spinner to show we are creating the share
2016-03-09 09:39:48 +01:00
Jenkins for ownCloud e41bb8af6d [tx-robot] updated from transifex 2016-03-09 02:19:14 -05:00
Jenkins for ownCloud 3a608dda8f [tx-robot] updated from transifex 2016-03-09 01:15:44 -05:00
Roeland Jago Douma 41d38b37cf Add spinner to show we are creating the share
Fixes #3737
2016-03-08 20:54:14 +01:00
Olivier Goffart 54612455e6 Merge pull request #4513 from owncloud/csyncContextToSyncEngine
Move the csync_context creation in SyncEngine
2016-03-08 18:12:31 +01:00
Klaas Freitag 9c5b9f932b Activities: Hide if non of the accounts has the app enabled.
If the ownCloud server does not have the activity app enabled,
it returns 999 as status code. If all the configured accounts
do that, this code hides the entire tab with the server
activities.

This is supposed to fix #4533
2016-03-08 18:01:42 +01:00
Jenkins for ownCloud debaff6f7a [tx-robot] updated from transifex 2016-03-08 02:19:06 -05:00
Jenkins for ownCloud 25ce5f1c22 [tx-robot] updated from transifex 2016-03-06 02:18:40 -05:00
Jenkins for ownCloud a441e4e30d [tx-robot] updated from transifex 2016-03-05 02:19:40 -05:00
Jenkins for ownCloud 50c32f45ac [tx-robot] updated from transifex 2016-03-05 01:15:44 -05:00
Jenkins for ownCloud 5f24575ed9 [tx-robot] updated from transifex 2016-03-04 02:19:10 -05:00
Jocelyn Turcotte e91a5c85ff Move the Logger reference back into src/gui 2016-03-03 20:26:48 +01:00
Jocelyn Turcotte 7561f5c717 Add comment and cleanup the csync exclude list code
Removed the csync_add_exclude_list function that isn't use anywhere
anymore.
2016-03-03 20:26:48 +01:00
Christian Kamm c554f5383c Downloads: Preserve group ownership #4330 2016-03-03 12:02:06 +01:00
Christian Kamm 4f48c888ef Don't use QVector::removeOne, added in Qt 5.4
A QList makes sense there and makes the rest of the code
work with earlier Qt versions.
2016-03-03 10:03:41 +01:00
Jenkins for ownCloud 4ff6dc2992 [tx-robot] updated from transifex 2016-03-03 02:19:13 -05:00
Markus Goetz 266508b691 Merge pull request #4529 from owncloud/dynamic_parallelism_scaling
Propagator: Pump in more requests if we think current ones are quick
2016-03-02 15:23:58 +01:00
Markus Goetz d78c3679e7 Propagator: Pump in more requests if we think current ones are quick
Helps with small file sync #331
When I benchmarked this, it went up to 6 parallelism and
was about 1/3 faster than the previous fixed 3 parallelism.
Doing more than 6 is dangerous because QNAM limits to 6 TCP
connections and also the server might become a bottleneck.

Should also help for #4081
2016-03-02 15:22:21 +01:00
Christian Kamm d6d3502960 Checksums: Compute content checksum on download #4375 2016-03-02 14:28:41 +01:00
Christian Kamm 7ed7512f27 Checksums: Content checksums for all uploads #4375 2016-03-02 14:28:20 +01:00
ckamm ffa78b99d9 Merge pull request #4512 from owncloud/add-checksum-to-database
Checksums: keep the transfer checksum in the database as the content …
2016-03-02 14:04:37 +01:00
Christian Kamm f66c28900a Add warnings for old server versions #4523
* A tray message on every start up
* Red message in account settings
* Folders are paused when the server version switches to
  an unsupported one
2016-03-02 12:54:22 +01:00
Christian Kamm 40c109597e Improve folder pausing API
Previously one could accidentally call Folder::setSyncPaused() and miss
some expected side effects. Before, the correct call was to FolderMan::
slotSetFolderPaused(). Now the setter on Folder has the expected effect.
2016-03-02 11:06:03 +01:00
ckamm 6e9019120f Merge pull request #4527 from ckamm/account-pause
Account pausing #3829
2016-03-02 10:35:40 +01:00
Jenkins for ownCloud 17f40f7efd [tx-robot] updated from transifex 2016-03-02 02:19:13 -05:00
Roeland Douma b0db709960 Merge pull request #4501 from owncloud/fix_4481
Do not send reshare permissions when creating a federated share
2016-03-01 17:03:00 +01:00
Roeland Douma 216956da4a Merge pull request #4493 from owncloud/fix_4185
Disable sharing dialog if account state is not connected
2016-03-01 16:59:40 +01:00
Roeland Jago Douma e4ec09dd87 Do not send reshare permissions when creating a federated share
See https://github.com/owncloud/core/issues/22122#issuecomment-185637344
2016-03-01 16:58:47 +01:00
Roeland Jago Douma ba42d40df9 Disable sharing dialog if account state is not connected
Fixes #4185
2016-03-01 16:55:56 +01:00
Christian Kamm d521232587 AccountState: Allow storing state in settings
This will be useful if we ever want to store account-level gui state.
I built this originally because I thought a paused account would be
this kind of state.
2016-03-01 16:08:23 +01:00
Christian Kamm 10e8f03ea4 Add 'pause all' tray menu entry #3829 2016-03-01 16:07:11 +01:00
Roeland Douma 8877f04835 Merge pull request #4497 from owncloud/fix_4469
Lock the sharee input when sharing
2016-03-01 15:40:29 +01:00
Roeland Douma f24fa46789 Merge pull request #4496 from owncloud/fix_4325
Add theming options control sharing operations
2016-03-01 15:33:04 +01:00
Christian Kamm a9b00a7489 Don't make files read-only when server is too old #4450 2016-03-01 10:11:20 +01:00
Jenkins for ownCloud 3f462403a9 [tx-robot] updated from transifex 2016-03-01 02:18:58 -05:00
Jenkins for ownCloud 6aa418f7c4 [tx-robot] updated from transifex 2016-02-29 12:10:28 -05:00
Jocelyn Turcotte 49f00499f7 Fix a crash when syncing 2016-02-26 18:05:04 +01:00
Jocelyn Turcotte b8227afcaa Move the csync_context creation in SyncEngine
The creation doesn't need to be separated from the SyncEngine anymore.
This allows the SyncEngine to be created in fewer steps if we want to
use it in tests.

This moves most of the direct csync code from Folder into the SyncEngine.
The exclude file logic for the context has been wrapped using the
existing ExcludedFiles class as well.
2016-02-25 20:53:13 +01:00
Jocelyn Turcotte b8dee63d7a Remove superfluous error checking from csync_create and csync_init
Given that we control all call sites, the only way that this can fail is during
OOM. Also remove the code in csync itself to make sure that it's obvious that
any new error case wouldn't be handled by call sites.
2016-02-25 20:53:13 +01:00
Jocelyn Turcotte 75c99bf2b1 Fix TestXmlParse::testParser1 2016-02-25 20:53:13 +01:00
Olivier Goffart e6f81d3965 Checksums: keep the transfer checksum in the database as the content checksum
Currently, we only use this for .eml files. But we can just store this checksum
in the database if it was computed anyway.

Issue #4487
2016-02-25 17:17:14 +01:00
Olivier Goffart 1fafb1325b Revert "AccountManager: take theURL from the Theme rather than from the config if the theme specify it"
The URL may change from shiboleth to normal authentication method.
Also some people were changing the config file manually to test stuff

Issue https://github.com/owncloud/enterprise/issues/1113

This reverts commit 7e5d89293d.
2016-02-25 11:33:26 +01:00
Jocelyn Turcotte 8222295ab1 Merge pull request #4508 from owncloud/fix-crash
PropagatorDownload: fix possible crash
2016-02-25 10:46:45 +01:00
Jenkins for ownCloud 744464aca6 [tx-robot] updated from transifex 2016-02-25 01:15:43 -05:00
Olivier Goffart 60c101d90b PropagatorDownload: fix possible crash
Backtrace seen from the crash reporter where reply() is null.
2016-02-24 19:52:14 +01:00
Olivier Goffart b685f6b6b6 Chunking: change the default chunk size to 10MB
As discussed with Klaas, this seems to be a better compromise.
10MB * 3 prarralel jobs = 30MB in memory, and to retry in case of
disconnection. Which is still reasonable.  And might make the upload
almost twice as fast on fast network where the amount of chunk is the
bottleneck (because of more server processing)

Relates to issue #4354
2016-02-24 16:25:45 +01:00
Olivier Goffart 7e5d89293d AccountManager: take theURL from the Theme rather than from the config if the theme specify it
That way an upgrade of the client can actually change the URL
Issue https://github.com/owncloud/enterprise/issues/1113
2016-02-23 18:10:11 +01:00
Markus Goetz fe7630954e Propagator: Remove 100msec delay between jobs 2016-02-23 14:27:35 +01:00
Markus Goetz 47ce4bd9e5 SelectiveSync: Improve menu #4378 2016-02-23 11:28:03 +01:00
Olivier Goffart 8fe4f1f0d7 Selective sync: Don't show negative size
Relates to issue #4491
2016-02-22 17:26:09 +01:00
Olivier Goffart b3d57f3c7c Cleanup syncengine after the new option not to ask confirmation when everything is removed
Cleanup after pull reuqest  #4389

Do not make the SyncEngine depends on the ConfigFile
2016-02-22 16:14:22 +01:00
Markus Goetz a76ba06817 Merge pull request #4389 from Bottswana/deleteprompt
Add option to disable the delete all files prompt
2016-02-22 15:44:50 +01:00
Markus Goetz 86fb83261a Merge pull request #4470 from RealRancor/chunksize_config
Add chunkSize config to documentation
2016-02-22 15:43:12 +01:00
Roeland Jago Douma cf0762a067 Lock the sharee input when sharing
This prevents accidentally sharing with the same sharee multiple times.
Because creating shares is not instance.

Fixes #4469
2016-02-22 15:14:05 +01:00
Roeland Jago Douma 99b3b752e3 Add theming options control sharing operations
Fixes #4325
2016-02-22 13:53:45 +01:00
Markus Goetz 5d7aa792e7 SyncEngine: Improve error message for ignored files/folders #4143 2016-02-22 11:13:29 +01:00
Hefee 28b694b170 Merge branch '2.1' 2016-02-20 10:34:34 +01:00
Jenkins for ownCloud 64ca8a668a [tx-robot] updated from transifex 2016-02-20 01:15:28 -05:00
Daniel Molkentin 841973d399 Use correct values for items in version.rc 2016-02-17 14:16:29 +01:00
Jenkins for ownCloud cf47523b2c [tx-robot] updated from transifex 2016-02-17 02:19:09 -05:00
Daniel Molkentin 2918e45343 Add comment, transliterate copyright symbol 2016-02-16 17:56:51 +01:00
Daniel Molkentin e3b56fb559 Add file description, copyright to win32 VERSIONINFO struct 2016-02-16 16:19:12 +01:00
Jenkins for ownCloud 667f2a7c4c [tx-robot] updated from transifex 2016-02-16 02:19:01 -05:00
Klaas Freitag e846b36bf6 Tests: Add a test for the timeAgoInWords function 2016-02-15 17:59:24 +01:00
Klaas Freitag 7f18d087e6 Utility: Make the timeAgoInWords method a bit more verbose 2016-02-15 17:59:24 +01:00
Klaas Freitag 3cc9019b37 ActivityWidget: Set timespec to UTC for JSON values.
This will fix the problem in #4439 that the time display
jumps if the timezone is changed on windows.
2016-02-15 17:59:24 +01:00
Daniel Molkentin 37924b9c7f Win32: More complete resource specification
Add application and vendor name

Addresses #4473
2016-02-15 15:51:48 +01:00
Jenkins for ownCloud a6c2ccc6cc [tx-robot] updated from transifex 2016-02-15 02:19:06 -05:00
Jenkins for ownCloud c84051c737 [tx-robot] updated from transifex 2016-02-14 02:18:43 -05:00
Jenkins for ownCloud fe24ba55c4 [tx-robot] updated from transifex 2016-02-13 02:18:44 -05:00
RealRancor ed06fc51a0 Add chunkSize config to documentation 2016-02-12 11:34:27 +01:00
Jenkins for ownCloud 14de3b460b [tx-robot] updated from transifex 2016-02-12 02:18:46 -05:00
Olivier Goffart dd89ab59e4 Use oc:size instead of quota-used-bytes to get the sizes of folder (#4459) 2016-02-11 15:09:47 +01:00
Jenkins for ownCloud 83adb7b55b [tx-robot] updated from transifex 2016-02-11 02:19:16 -05:00
Daniel Molkentin 49ba58e991 Merge pull request #4461 from owncloud/bump-year-to-2016
Bump year to 2016
2016-02-10 20:01:54 +01:00
Lukas Reschke 7db3bc91b5 Bump year to 2016
My yearly contribution to the client 😉
2016-02-10 19:52:55 +01:00
Olivier Goffart c93ecfbfb5 Merge branch restore_backup 2016-02-10 17:47:06 +01:00
Daniel Molkentin 41d8d77535 Merge remote-tracking branch 'origin/2.1' 2016-02-10 12:57:03 +01:00
Olivier Goffart e9307bb797 Use a constant for ConnectionValidator::DefaultCallingIntervalMsec
So there is no runtime initialization
2016-02-10 12:36:09 +01:00
Klaas Freitag 96f50d0c47 Nautilus integration: Remove Shebang from plugin source.
This fixes #4436
2016-02-08 17:00:39 +01:00
Olivier Goffart 93308faeb9 Merge remote-tracking branch 'origin/2.1'
Conflicts:
	doc/building.rst
2016-02-08 14:28:25 +01:00
Olivier Goffart 16ab3ca6a6 Merge pull request #4437 from owncloud/dev/doninstalltests
Don't install tests and cmocka lib
2016-02-08 12:35:29 +01:00
James Botting f442195b8a Remove option from GUI, leaving config option only. 2016-02-05 15:48:17 +00:00
Hefee aa8b772bff Don't install tests and cmocka lib.
Neither tests nor the libcmocka needs to be installed globally.
2016-02-05 14:14:07 +01:00
Daniel Molkentin dd76d72d61 Merge remote-tracking branch 'origin/2.1' 2016-01-26 14:09:38 +01:00
James Botting d8af949a6a Alter sync engine to check new setting before displaying prompt 2016-01-24 18:45:29 +00:00
James Botting 74a8c4aae8 Add additional configuration option to toggle display of delete prompt 2016-01-24 18:45:12 +00:00
Christian Kamm 3b60f6e238 Sharing: Fix resharing items with limited permissions #4357 #4358 2016-01-21 14:21:25 +01:00
Christian Kamm 62ded39416 Update: Check checksum for renamed files #2983
This currently doesn't do much because we only compute content
checksums for .eml files.
2016-01-20 13:21:48 +01:00
Christian Kamm f705c56cb3 AccountSettings: Auto-expand only single folders #4283 2016-01-20 12:41:52 +01:00
Olivier Goffart 1bb76f5343 Attempt to recover from backup restoration on the server
If all the files bring us to past timestamp, it is possibly a backup
restoration in the server. In which case we want don't want to just
overwrite newer files with the older ones.

Issue #2325
2016-01-05 12:14:18 +01:00
Olivier Goffart 639301e9e9 Merge remote-tracking branch 'origin/2.1'
Conflicts:
	VERSION.cmake
2016-01-04 12:38:59 +01:00
Klaas Freitag 8f26bb698d Merge pull request #4245 from masoudcs/masoudcs-patch-1
Fixing bug in opening Activity log from tray icon menu 'Recent Changes/Details...'
2015-12-22 10:15:14 +01:00
Christian Kamm ae806e8214 Merge remote-tracking branch 'origin/2.1' 2015-12-10 11:07:20 +01:00
Jürgen Weigert e67bb1c5ce Merge pull request #4201 from owncloud/jnweiger-patch-1
improved linux build instructions.
2015-12-03 14:05:24 +01:00
Jürgen Weigert f2c9a49c50 fixed formatting lines 212 215 2015-12-03 14:04:43 +01:00
Masoud Kazemi 903dd8acef Fixing bug in opening Activity log from tray icon menu 'Recent Changes/Details...' 2015-12-03 15:25:59 +03:30
Olivier Goffart e27374324d AccountSettings: Remove the F5 reset folder shortcut
This secret key was used to wipe the database. In the past this was
usefull because of many bugs, but now this is not usefull anymore.
And cause trouble because it also erase the selective sync list.

Issue #4182
2015-12-02 12:27:53 +01:00
Daniel Molkentin 895f0b29aa Merge remote-tracking branch 'origin/2.1' 2015-11-30 00:47:10 +01:00
Jürgen Weigert f54f48615d Update building.rst
ah, and the instructions miss the 'cd client' before git submodule init.
2015-11-26 12:06:32 +01:00
Jürgen Weigert 06bb97e6d9 Update building.rst 2015-11-26 12:02:23 +01:00
Jürgen Weigert 0763f65329 improved linux build instructions.
I am using :: lists, as the ``  versions mangle multi-line commands all in one long line.
The cmake command should just end in .., not in ../client
2015-11-26 11:55:23 +01:00
Markus Goetz 40395bdc0e VERSION.cmake: master is now 2.2 2015-11-17 15:47:46 +01:00
266 arquivos alterados com 39827 adições e 26226 exclusões
+6 -29
Ver Arquivo
@@ -20,6 +20,11 @@ if (NOT DEFINED APPLICATION_SHORTNAME)
set ( APPLICATION_SHORTNAME ${APPLICATION_NAME} )
endif()
# For usage in XML files we preprocess
string(REPLACE "&" "&amp;" APPLICATION_NAME_XML_ESCAPED "${APPLICATION_NAME}")
string(REPLACE "<" "&lt;" APPLICATION_NAME_XML_ESCAPED "${APPLICATION_NAME_XML_ESCAPED}")
string(REPLACE ">" "&gt;" APPLICATION_NAME_XML_ESCAPED "${APPLICATION_NAME_XML_ESCAPED}")
set(PACKAGE "${APPLICATION_SHORTNAME}-client")
set( CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules )
@@ -30,7 +35,7 @@ endif()
include(Warnings)
include(${CMAKE_SOURCE_DIR}/VERSION.cmake)
include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR} "${CMAKE_CURRENT_BINARY_DIR}/src/mirall/")
include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR})
# disable the crashreporter if libcrashreporter-qt is not available or we're building for ARM
if( CMAKE_SYSTEM_PROCESSOR MATCHES "arm" OR NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/3rdparty/libcrashreporter-qt/CMakeLists.txt")
@@ -51,9 +56,7 @@ endif()
include(GNUInstallDirs)
include(DefineInstallationPaths)
include(QtVersionAbstraction)
setup_qt()
include(GetGitRevisionDescription)
@@ -126,34 +129,8 @@ if(APPLE)
set( SOCKETAPI_TEAM_IDENTIFIER_PREFIX "" CACHE STRING "SocketApi prefix (including a following dot) that must match the codesign key's TeamIdentifier/Organizational Unit" )
endif()
#### find libs
#find_package(Qt4 4.7.0 COMPONENTS QtCore QtGui QtXml QtNetwork QtTest QtWebkit REQUIRED )
#if( UNIX AND NOT APPLE ) # Fdo notifications
# find_package(Qt4 4.7.0 COMPONENTS QtDBus REQUIRED )
#endif()
if(HAVE_QT5)
if (${Qt5Core_VERSION_MAJOR} EQUAL "5")
if (${Qt5Core_VERSION_MINOR} EQUAL "4" OR ${Qt5Core_VERSION_MINOR} GREATER 4)
else()
message(STATUS "If possible compile me with Qt 5.4 or higher.")
endif()
endif()
else()
message(STATUS "If possible compile me with Qt 5.4 or higher.")
endif()
find_package(OpenSSL 1.0.0 REQUIRED)
if(NOT TOKEN_AUTH_ONLY)
if (Qt5Core_DIR)
find_package(Qt5Keychain REQUIRED)
else()
find_package(QtKeychain REQUIRED)
endif()
endif()
if(APPLE)
find_package(Sparkle)
endif(APPLE)
+23
Ver Arquivo
@@ -1,5 +1,28 @@
ChangeLog
=========
version 2.2.0 (release 2016-05-12)
* Overlay icons: Refactoring - mainly for performance improvements
* Improved error handling with Sync Journal on USB storages (#4632)
* Sharing Completion: Improved UI of completion in sharing from desktop. (#3737)
* Show server notifications on the client (#3733)
* Improved Speed with small files by dynamic parallel request count (#4529)
* LockWatcher: Make sure to sync files after apps released exclusive locks on Windows.
* Improved handling of Win32 file locks and network files
* Workaround Ubuntu 16.04 tray icon bug (#4693)
* Removed the Alias field from the folder definition (#4695)
* Improved netrc parser (#4691)
* Improved user notifications about ignored files and conflicts (#4761, #3222)
* Add warnings for old server versions (#4523)
* Enable tranportation checksums if the server supports based on server capabilities (#3735)
* Default Chunk-size changed to 10MB (#4354)
* Documentation Improvements, ie. about overlay icons
* Translation fixes
* Countless other bugfixes
* Sqlite Update to recent version
* Update of QtKeyChain to support Windows credential store
* Packaging of dolphin overlay icon module for bleeding edge distros
version 2.1.1 (release 2016-02-10)
* UI improvements for HiDPI screens, error messages, RTL languages
* Fix occurences of "Connection Closed" when a new unauthenticated TCP socket is used
+4 -3
Ver Arquivo
@@ -1,10 +1,11 @@
set( MIRALL_VERSION_MAJOR 2 )
set( MIRALL_VERSION_MINOR 1 )
set( MIRALL_VERSION_PATCH 1 )
set( MIRALL_VERSION_MINOR 2 )
set( MIRALL_VERSION_PATCH 0 )
set( MIRALL_VERSION_YEAR 2016 )
set( MIRALL_SOVERSION 0 )
if ( NOT DEFINED MIRALL_VERSION_SUFFIX )
set( MIRALL_VERSION_SUFFIX "git") #e.g. beta1, beta2, rc1
set( MIRALL_VERSION_SUFFIX "") #e.g. beta1, beta2, rc1
endif( NOT DEFINED MIRALL_VERSION_SUFFIX )
if( NOT DEFINED MIRALL_VERSION_BUILD )
+3 -3
Ver Arquivo
@@ -497,7 +497,7 @@
<key>IDENTIFIER</key>
<string>@APPLICATION_REV_DOMAIN_INSTALLER@</string>
<key>NAME</key>
<string>@APPLICATION_NAME@</string>
<string>@APPLICATION_NAME_XML_ESCAPED@</string>
<key>OVERWRITE_PERMISSIONS</key>
<false/>
<key>VERSION</key>
@@ -1245,7 +1245,7 @@
<key>LANGUAGE</key>
<string>English</string>
<key>VALUE</key>
<string>@APPLICATION_NAME@ Client</string>
<string>@APPLICATION_NAME_XML_ESCAPED@ Client</string>
</dict>
</array>
</dict>
@@ -1445,7 +1445,7 @@
</dict>
</array>
<key>NAME</key>
<string>@APPLICATION_NAME@ Installer</string>
<string>@APPLICATION_NAME_XML_ESCAPED@ Installer</string>
<key>REFERENCE_FOLDER_PATH</key>
<string>@CMAKE_INSTALL_DIR@</string>
</dict>
+3 -1
Ver Arquivo
@@ -12,8 +12,10 @@ EOF
if [ -x "$(command -v pluginkit)" ]; then
# add it to DB. This happens automatically too but we try to push it a bit harder for issue #3463
pluginkit -a "/Applications/@APPLICATION_EXECUTABLE@.app/Contents/PlugIns/FinderSyncExt.appex/"
# Since El Capitan we need to sleep #4650
sleep 10s
# enable it
pluginkit -e use -i @APPLICATION_REV_DOMAIN@.FinderSyncExt
fi
exit 0
exit 0
+13 -5
Ver Arquivo
@@ -1,7 +1,7 @@
## Patches used
There are our patches on top of Qt 5.4.0, which we are currently
using for our binary packages on Windows and Mac OS. Most of them
using for our binary packages on Windows and Mac OS X. Most of them
have been sent upstream and are part of newer Qt releases.
All changes are designed to up upstream, and all those that are
@@ -28,18 +28,26 @@ purpose is outlined in each patches' front matter.
* 0007-X-Network-Fix-up-previous-corruption-patch.patch
* 0008-QNAM-Fix-reply-deadlocks-on-server-closing-connectio.patch
* 0014-Fix-SNI-for-TlsV1_0OrLater-TlsV1_1OrLater-and-TlsV1_.patch
* 0016-Fix-possible-crash-when-passing-an-invalid-PAC-URL.patch
* 0011-Make-sure-to-report-correct-NetworkAccessibility.patch
### Upstreamed but not in any release yet (as of 2015-11-16)
### Part of Qt v5.5.2 (UNRELEASED!)
* 0009-QNAM-Assign-proper-channel-before-sslErrors-emission.patch
* 0010-Don-t-let-closed-http-sockets-pass-as-valid-connecti.patch
* 0012-Make-sure-networkAccessibilityChanged-is-emitted.patch
### Part of Qt v5.6 and later
* 0009-QNAM-Assign-proper-channel-before-sslErrors-emission.patch
* 0010-Don-t-let-closed-http-sockets-pass-as-valid-connecti.patch
* 0011-Make-sure-to-report-correct-NetworkAccessibility.patch
* 0012-Make-sure-networkAccessibilityChanged-is-emitted.patch
* 0013-Make-UnknownAccessibility-not-block-requests.patch
* 0015-Remove-legacy-platform-code-in-QSslSocket-for-OS-X-1.patch
* 0016-Fix-possible-crash-when-passing-an-invalid-PAC-URL.patch
* 0019-Ensure-system-tray-icon-is-prepared-even-when-menu-bar.patch
### Not submitted to be part of any release:
### Part of Qt 5.7 and later
* 0015-Remove-legacy-platform-code-in-QSslSocket-for-OS-X-1.patch
### Not submitted upstream to be part of any release:
* 0006-Fix-force-debug-info-with-macx-clang_NOUPSTREAM.patch
This is only needed if you intent to harvest debugging symbols
for breakpad.
+2 -2
Ver Arquivo
@@ -5,11 +5,11 @@ MAINTAINER Daniel Molkentin <danimo@owncloud.com>
ENV TERM ansi
ENV HOME /root
ENV REFRESHED_AT 20160202
ENV REFRESHED_AT 20160421
RUN zypper --non-interactive --gpg-auto-import-keys refresh
RUN zypper --non-interactive --gpg-auto-import-keys ar http://download.opensuse.org/repositories/windows:/mingw/openSUSE_42.1/windows:mingw.repo
RUN zypper --non-interactive --gpg-auto-import-keys ar http://download.opensuse.org/repositories/isv:ownCloud:toolchains:mingw:win32:2.1/openSUSE_Leap_42.1/isv:ownCloud:toolchains:mingw:win32:2.1.repo
RUN zypper --non-interactive --gpg-auto-import-keys ar http://download.opensuse.org/repositories/isv:ownCloud:toolchains:mingw:win32:2.2/openSUSE_Leap_42.1/isv:ownCloud:toolchains:mingw:win32:2.2.repo
RUN zypper --non-interactive --gpg-auto-import-keys install cmake make mingw32-cross-binutils mingw32-cross-cpp mingw32-cross-gcc \
mingw32-cross-gcc-c++ mingw32-cross-pkg-config mingw32-filesystem \
mingw32-headers mingw32-runtime site-config mingw32-libwebp \
+3 -3
Ver Arquivo
@@ -1,11 +1,11 @@
# Auto-generated - do not modify
StrCpy $MUI_FINISHPAGE_SHOWREADME_TEXT_STRING "Toon releaseopmerkingen"
StrCpy $MUI_FINISHPAGE_SHOWREADME_TEXT_STRING "Toon opmerkingen bij deze uitgave"
StrCpy $ConfirmEndProcess_MESSAGEBOX_TEXT "Gevonden ${APPLICATION_EXECUTABLE} proces(sen) moet worden gestopt.$\nWilt u dat het installatieprogramma dat voor u doet?"
StrCpy $ConfirmEndProcess_KILLING_PROCESSES_TEXT "Stoppen ${APPLICATION_EXECUTABLE} processen."
StrCpy $ConfirmEndProcess_KILL_NOT_FOUND_TEXT "Het te stoppen proces is niet gevonden!"
StrCpy $PageReinstall_NEW_Field_1 "Er is een oudere versie van ${APPLICATION_NAME} geïnstalleerd op uw systeem. geadviseerd wordt om de huidige versie te de-installeren voordat de nieuwe versie wordt geïnstalleerd. Selecteer de uit te voeren actie en klik op Verder om door te gaan."
StrCpy $PageReinstall_NEW_Field_2 "De-installeren voor installeren"
StrCpy $PageReinstall_NEW_Field_3 "Niet de-installeren"
StrCpy $PageReinstall_NEW_Field_2 "Verwijder oude versie"
StrCpy $PageReinstall_NEW_Field_3 "Behoud oude versie"
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_TITLE "Al geïnstalleerd"
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_SUBTITLE "Kies hoe u ${APPLICATION_NAME} wilt installeren."
StrCpy $PageReinstall_OLD_Field_1 "Er is al een recentere versie van ${APPLICATION_NAME} geïnstalleerd! Installeren van een oudere versie wordt niet aangeraden. Als u echt de oudere versie wilt installeren, adviseren we de huidige versie eerst te verwijderen. Kies de actie die u wilt uitvoeren en druk op Verder om door te gaan."
+1 -1
Ver Arquivo
@@ -38,6 +38,6 @@ StrCpy $UAC_ERROR_ELEVATE "Rechte k
StrCpy $UAC_INSTALLER_REQUIRE_ADMIN "Dieses Installationsprogramm erfordert Administrator-Rechte, bitte erneut versuchen"
StrCpy $INIT_INSTALLER_RUNNING "Das Installationsprogramm wird bereits ausgeführt."
StrCpy $UAC_UNINSTALLER_REQUIRE_ADMIN "Das Deinstallationsprogramm erfordert Administrator-Rechte. Bitte erneut versuchen."
StrCpy $UAC_ERROR_LOGON_SERVICE "Anmeldedienst läuft nicht, abbruch!"
StrCpy $INIT_UNINSTALLER_RUNNING "Das Deinstallationsprogramm wird bereits ausgeführt."
StrCpy $SectionGroup_Shortcuts "Verknüpfungen"
StrCpy $UAC_ERROR_LOGON_SERVICE "Logon service is not running, aborting!"
+4 -4
Ver Arquivo
@@ -7,7 +7,7 @@ StrCpy $PageReinstall_NEW_Field_1 "Az ${APPLICATION_NAME} alkalmazás egy régeb
StrCpy $PageReinstall_NEW_Field_2 "Eltávolítás telepítés előtt"
StrCpy $PageReinstall_NEW_Field_3 "Ne távolítsa el"
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_TITLE "Már telepítve"
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_SUBTITLE "Válaszd ki, hogy szeretnéd telepíteni a következő alkalmazást ${APPLICATION_NAME}."
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_SUBTITLE "Válassza ki, hogy szeretné telepíteni a következő alkalmazást: ${APPLICATION_NAME}."
StrCpy $PageReinstall_OLD_Field_1 "Az ${APPLICATION_NAME} alkalmazás egy újabb verziója már megtalálható a rendszeren. Nem ajánlott egy régebbi verzió telepítése. Ha valóban szeretné a régebbi verziót telepíteni, akkor ajánlott a jelenleg telepített verzió eltávolítása. Válassza ki milyen műveletet szeretne végrehajtani, és nyomja meg a $\"Következő$\" gombot a folytatáshoz."
StrCpy $PageReinstall_SAME_Field_1 "Az ${APPLICATION_NAME} alkalmazás ${VERSION} verziója már telepítve van.$↩$\nKérjük válaszd ki milyen műveletet szeretnél végrehajtani, és nyomd meg a „Következő” gombot."
StrCpy $PageReinstall_SAME_Field_2 "Komponens hozzáadása/újratelepítése"
@@ -31,13 +31,13 @@ StrCpy $UNINSTALLER_FILE_Detail "Elltávolító írása"
StrCpy $UNINSTALLER_REGISTRY_Detail "Telepítési registry kulcsok írása"
StrCpy $UNINSTALLER_FINISHED_Detail "Befejezve!"
StrCpy $UNINSTALL_MESSAGEBOX "Nem sikerült az ${APPLICATION_NAME} alkalmazás telepítése a '$INSTDIR' könyvtárba.$\n$\nSzeretné mindenképpen folytatni (nem ajánlott)?"
StrCpy $UNINSTALL_ABORT "Az eltávolítást egy felhasználó megszakította"
StrCpy $UNINSTALL_ABORT "Az eltávolítást a felhasználó megszakította"
StrCpy $INIT_NO_QUICK_LAUNCH "Gyorsindító hivatkozás (N/A)"
StrCpy $INIT_NO_DESKTOP "Asztali hivatkozás (felülírja a meglévőt)"
StrCpy $UAC_ERROR_ELEVATE "Nem sikerült felemelni, hiba:"
StrCpy $UAC_INSTALLER_REQUIRE_ADMIN "A telepítő futtatásához adminisztrátori hozzáférés szükséges, próbáld újra."
StrCpy $UAC_INSTALLER_REQUIRE_ADMIN "A telepítő futtatásához adminisztrátori hozzáférés szükséges, próbálja újra."
StrCpy $INIT_INSTALLER_RUNNING "A telepítő már fut."
StrCpy $UAC_UNINSTALLER_REQUIRE_ADMIN "Az eltávolító futtatásához adminisztrátori hozzáférés szükséges, próbáld újra."
StrCpy $UAC_UNINSTALLER_REQUIRE_ADMIN "Az eltávolító futtatásához adminisztrátori hozzáférés szükséges, próbálja újra."
StrCpy $UAC_ERROR_LOGON_SERVICE "A bejelentkező szolgáltatás nem fut, megszakítás!"
StrCpy $INIT_UNINSTALLER_RUNNING "Az eltávolító már fut."
StrCpy $SectionGroup_Shortcuts "Parancsikonok"
+2 -2
Ver Arquivo
@@ -9,6 +9,7 @@ StrCpy $PageReinstall_NEW_Field_3 "Nie usuwaj "
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_TITLE "Ju¿ zainstalowane"
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_SUBTITLE "Wybierz jak chcesz zainstalowaæ ${APPLICATION_NAME}."
StrCpy $PageReinstall_OLD_Field_1 "Zainstalowana jest nowsza wersja ${APPLICATION_NAME}! Niezalecane jest instalowanie starszej wersji. Jeœli naprawdê chcesz zainstalowaæ starsz¹ wersjê lepiej najpierw odinstalowaæ obecn¹ aplikacjê. Wybierz operacjê któr¹ chcesz wykonaæ i naciœnij przycisk Dalej."
StrCpy $PageReinstall_SAME_Field_1 "${APPLICATION_NAME} ${VERSION} jest ju¿ zainstalowany.$\nWybierz operacjê któr¹ chcesz wykonaæ i naciœnij przycisk Dalej."
StrCpy $PageReinstall_SAME_Field_2 "Doda/Przeinstaluj komponenty"
StrCpy $PageReinstall_SAME_Field_3 "Odinstaluj ${APPLICATION_NAME}"
StrCpy $UNINSTALLER_APPDATA_TITLE "Odinstaluj ${APPLICATION_NAME}"
@@ -37,7 +38,6 @@ StrCpy $UAC_ERROR_ELEVATE "Niemo
StrCpy $UAC_INSTALLER_REQUIRE_ADMIN "Ten instalator potrzebuje uprawnieñ administratora, spróbuj ponownie"
StrCpy $INIT_INSTALLER_RUNNING "Instalator ju¿ jest uruchomiony."
StrCpy $UAC_UNINSTALLER_REQUIRE_ADMIN "Ten dezinstalator potrzebuje uprawnieñ administratora, spróbuj ponownie"
StrCpy $UAC_ERROR_LOGON_SERVICE "Proces logowania nie jest uruchomiony, przerywam !"
StrCpy $INIT_UNINSTALLER_RUNNING "Dezinstalator ju¿ jest uruchomiony."
StrCpy $SectionGroup_Shortcuts "Skróty"
StrCpy $PageReinstall_SAME_Field_1 "${APPLICATION_NAME} ${VERSION} is already installed.$\r$\nSelect the operation you want to perform and click Next to continue."
StrCpy $UAC_ERROR_LOGON_SERVICE "Logon service is not running, aborting!"
+1 -1
Ver Arquivo
@@ -1,6 +1,6 @@
# Auto-generated - do not modify
StrCpy $MUI_FINISHPAGE_SHOWREADME_TEXT_STRING "Mostrar notas de lançamento"
StrCpy $ConfirmEndProcess_MESSAGEBOX_TEXT "Processos(s) ${APPLICATION_EXECUTABLE} em execução. Estes processos precisam de ser interrompidos.$\nDeseja que o instalador os termine automaticamente?"
StrCpy $ConfirmEndProcess_MESSAGEBOX_TEXT "Existem ${APPLICATION_EXECUTABLE} processo(s) em execução que precisa(m) de ser interrompido(s).$\nDeseja que o instalador o(s) termine automaticamente?"
StrCpy $ConfirmEndProcess_KILLING_PROCESSES_TEXT "A terminar os processos ${APPLICATION_EXECUTABLE}."
StrCpy $ConfirmEndProcess_KILL_NOT_FOUND_TEXT "Não foi encontrado o processo a terminar!"
StrCpy $PageReinstall_NEW_Field_1 "Uma versão antiga de ${APPLICATION_NAME} está instalada no sistema. É recomendado que você desinstale a versão atual antes de instalar a mais recente. Selecione a operação que deseja executar e clique em $\"Avançar$\" para continuar."
+3 -3
Ver Arquivo
@@ -1,8 +1,8 @@
# Auto-generated - do not modify
StrCpy $MUI_FINISHPAGE_SHOWREADME_TEXT_STRING "查看版本日志"
StrCpy $ConfirmEndProcess_MESSAGEBOX_TEXT "有 ${APPLICATION_EXECUTABLE} 项进程需要关闭。$\n您想让安装程序关闭这些进程吗?"
StrCpy $ConfirmEndProcess_KILLING_PROCESSES_TEXT "杀死${APPLICATION_EXECUTABLE}进程。"
StrCpy $ConfirmEndProcess_KILL_NOT_FOUND_TEXT "未找到要杀死的进程!"
StrCpy $ConfirmEndProcess_KILLING_PROCESSES_TEXT "停止${APPLICATION_EXECUTABLE}进程。"
StrCpy $ConfirmEndProcess_KILL_NOT_FOUND_TEXT "未找到要停止的进程!"
StrCpy $PageReinstall_NEW_Field_1 "您的系统已经安装${APPLICATION_NAME}较老版本。建议安装前卸载当前版本。选择将要执行的操作,点击下一步继续。"
StrCpy $PageReinstall_NEW_Field_2 "在安装前先卸载"
StrCpy $PageReinstall_NEW_Field_3 "不要卸载"
@@ -38,6 +38,6 @@ StrCpy $UAC_ERROR_ELEVATE "无法获得权限,错误:"
StrCpy $UAC_INSTALLER_REQUIRE_ADMIN "安装程序需要管理员权限,请重试"
StrCpy $INIT_INSTALLER_RUNNING "安装程序已经运行。"
StrCpy $UAC_UNINSTALLER_REQUIRE_ADMIN "卸载程序需要管理员权限,请重试"
StrCpy $UAC_ERROR_LOGON_SERVICE "登录服务器未运行!"
StrCpy $INIT_UNINSTALLER_RUNNING "卸载程序已经运行。"
StrCpy $SectionGroup_Shortcuts "快捷方式"
StrCpy $UAC_ERROR_LOGON_SERVICE "Logon service is not running, aborting!"
+3 -3
Ver Arquivo
@@ -14,6 +14,8 @@ StrCpy $PageReinstall_SAME_Field_3 "Avinstallera ${APPLICATION_NAME}"
StrCpy $UNINSTALLER_APPDATA_TITLE "Avinstallera ${APPLICATION_NAME}"
StrCpy $PageReinstall_SAME_MUI_HEADER_TEXT_SUBTITLE "Välj underhålls alternativ att utföra."
StrCpy $SEC_APPLICATION_DETAILS "Installerar ${APPLICATION_NAME} väsentligheter."
StrCpy $OPTION_SECTION_SC_SHELL_EXT_SECTION "Integration för Windows Explorer"
StrCpy $OPTION_SECTION_SC_SHELL_EXT_DetailPrint "Installerar integration för Windows Explorer"
StrCpy $OPTION_SECTION_SC_START_MENU_SECTION "Start-meny program genväg"
StrCpy $OPTION_SECTION_SC_START_MENU_DetailPrint "Lägger till genväg för ${APPLICATION_NAME} till Start-menyn."
StrCpy $OPTION_SECTION_SC_DESKTOP_SECTION "Skrivbordsgenväg"
@@ -35,9 +37,7 @@ StrCpy $UAC_ERROR_ELEVATE "Kunde inte få förhöjda rättigheter, fel:"
StrCpy $UAC_INSTALLER_REQUIRE_ADMIN "Detta installationsprogram kräver adminstratörs rättigheter, försök igen"
StrCpy $INIT_INSTALLER_RUNNING "Installationsprogrammet körs redan."
StrCpy $UAC_UNINSTALLER_REQUIRE_ADMIN "Detta avinstallationsprogram kräver administratörsrättigheter, försök igen"
StrCpy $UAC_ERROR_LOGON_SERVICE "Login-service körs inte, avbryter!"
StrCpy $INIT_UNINSTALLER_RUNNING "Avinstallationsprogrammet körs redan."
StrCpy $SectionGroup_Shortcuts "Genvägar"
StrCpy $PageReinstall_SAME_Field_1 "${APPLICATION_NAME} ${VERSION} is already installed.$\r$\nSelect the operation you want to perform and click Next to continue."
StrCpy $OPTION_SECTION_SC_SHELL_EXT_SECTION "Integration for Windows Explorer"
StrCpy $OPTION_SECTION_SC_SHELL_EXT_DetailPrint "Installing Integration for Windows Explorer"
StrCpy $UAC_ERROR_LOGON_SERVICE "Logon service is not running, aborting!"
+3 -3
Ver Arquivo
@@ -3,13 +3,13 @@ StrCpy $MUI_FINISHPAGE_SHOWREADME_TEXT_STRING "แสดงบันทึกป
StrCpy $ConfirmEndProcess_MESSAGEBOX_TEXT "พบว่ากระบวนการ ${APPLICATION_EXECUTABLE} จะต้องหยุดทำงาน$\nคุณต้องการติดตั้งเพื่อหยุดการทำงานเหล่านี้ของคุณ?"
StrCpy $ConfirmEndProcess_KILLING_PROCESSES_TEXT "ฆ่ากระบวนการทำงาน ${APPLICATION_EXECUTABLE}"
StrCpy $ConfirmEndProcess_KILL_NOT_FOUND_TEXT "ไม่พบการฆ่ากระบวนการ!"
StrCpy $PageReinstall_NEW_Field_1 "รุ่นเก่าของ ${APPLICATION_NAME} มีการติดตั้งในระบบของคุณ ขอแนะนำให้คุณถอนการติดตั้งรุ่นปัจจุบันออกก่อน เลือกการดำเนินการที่คุณต้องการที่จะดำเนินการและคลิกถัดไปเพื่อดำเนินการต่อ"
StrCpy $PageReinstall_NEW_Field_1 "รุ่นเก่าของ ${APPLICATION_NAME} ได้ถูกติดตั้งในระบบของคุณ ขอแนะนำให้คุณถอนการติดตั้งรุ่นปัจจุบันออกก่อน แล้วเลือกการดำเนินการที่คุณต้องการหลังจากนั้นคลิกถัดไปเพื่อดำเนินการต่อ"
StrCpy $PageReinstall_NEW_Field_2 "ถอนการติดตั้งก่อนการติดตั้ง"
StrCpy $PageReinstall_NEW_Field_3 "อย่าถอนการติดตั้ง"
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_TITLE "ติดตั้งแล้ว"
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_SUBTITLE "เลือกวิธีที่คุณต้องการติดตั้ง ${APPLICATION_NAME}"
StrCpy $PageReinstall_OLD_Field_1 "รุ่นใหม่ของ ${APPLICATION_NAME} ถูกติดตั้งแล้ว! เราไม่แนะนำให้คุณติดตั้งรุ่นเก่า ถ้าคุณอยากจะติดตั้งรุ่นเก่าก็สามารถอนการติดตั้งได้"
StrCpy $PageReinstall_SAME_Field_1 "${APPLICATION_NAME} ${VERSION} ถูกติดตั้งไปแล้ว$ $\nเลือกดำเนินงานที่คุณต้องการและคลิกถัดไปเพื่อดำเนินการต่อ"
StrCpy $PageReinstall_OLD_Field_1 "รุ่นใหม่ของ ${APPLICATION_NAME} ถูกติดตั้งแล้ว! เราไม่แนะนำให้คุณติดตั้งรุ่นที่เก่ากว่า ถ้าคุณอยากจะติดตั้งรุ่นเก่าก็สามารถอนการติดตั้งได้"
StrCpy $PageReinstall_SAME_Field_1 "${APPLICATION_NAME} รุ่น ${VERSION} ถูกติดตั้งไปแล้ว$ $\nเลือกดำเนินงานที่คุณต้องการและคลิกถัดไปเพื่อดำเนินการต่อ"
StrCpy $PageReinstall_SAME_Field_2 "ส่วนประกอบ เพิ่ม/ติดตั้งใหม่ "
StrCpy $PageReinstall_SAME_Field_3 "ถอนการติดตั้ง ${APPLICATION_NAME}"
StrCpy $UNINSTALLER_APPDATA_TITLE "ถอนการติดตั้ง ${APPLICATION_NAME}"
+2 -2
Ver Arquivo
@@ -9,6 +9,7 @@ StrCpy $PageReinstall_NEW_Field_3 "Не видаляти"
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_TITLE "Установлено"
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_SUBTITLE "Оберіть, як ви хочете установити ${APPLICATION_NAME}."
StrCpy $PageReinstall_OLD_Field_1 "Знайдено новішу версію ${APPLICATION_NAME}! Ми не рекомендуємо встановлювати стару версію. Якщо ви все ж бажаєте встановити цю версію, спочатку видаліть поточну версію. Оберіть подальшу дію та натисніть $\"Далі$\"."
StrCpy $PageReinstall_SAME_Field_1 "${APPLICATION_NAME} ${VERSION} вже встановлено.$\n$\nОберіть подальшу дію та натисніть $\"Далі$\" для продовження."
StrCpy $PageReinstall_SAME_Field_2 "Додати/Перевстановити компоненти"
StrCpy $PageReinstall_SAME_Field_3 "Видалити ${APPLICATION_NAME}"
StrCpy $UNINSTALLER_APPDATA_TITLE "Видалити ${APPLICATION_NAME}"
@@ -37,7 +38,6 @@ StrCpy $UAC_ERROR_ELEVATE "Неможливо підняти, помилка:"
StrCpy $UAC_INSTALLER_REQUIRE_ADMIN "Для установки потрібні права адміністратора, спробуйте ще раз"
StrCpy $INIT_INSTALLER_RUNNING "Установка вже запущена."
StrCpy $UAC_UNINSTALLER_REQUIRE_ADMIN "Для видалення потрібні права адміністратора, спробуйте ще раз"
StrCpy $UAC_ERROR_LOGON_SERVICE "Сервіс авторизації не запущений, припиняю!"
StrCpy $INIT_UNINSTALLER_RUNNING "Програма видалення вже запущено."
StrCpy $SectionGroup_Shortcuts "Ярлики"
StrCpy $PageReinstall_SAME_Field_1 "${APPLICATION_NAME} ${VERSION} is already installed.$\r$\nSelect the operation you want to perform and click Next to continue."
StrCpy $UAC_ERROR_LOGON_SERVICE "Logon service is not running, aborting!"
+1 -1
Submodule binary updated: 71633edc9a...d27d472817
+1
Ver Arquivo
@@ -22,5 +22,6 @@
<file>resources/account.png</file>
<file>resources/more.png</file>
<file>resources/delete.png</file>
<file>resources/bell.png</file>
</qresource>
</RCC>
-11
Ver Arquivo
@@ -20,15 +20,4 @@ function (ADD_CMOCKA_TEST _testName _testSource)
add_executable(${_testName} ${_testSource})
target_link_libraries(${_testName} ${ARGN})
add_test(${_testName} ${CMAKE_CURRENT_BINARY_DIR}/${_testName})
if(UNIT_TESTING)
INSTALL(
TARGETS
${_testName}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
endif(UNIT_TESTING)
endfunction (ADD_CMOCKA_TEST)
+2 -2
Ver Arquivo
@@ -17,7 +17,7 @@
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleLongVersionString</key>
<string>@APPLICATION_NAME@ @MIRALL_VERSION_STRING@</string>
<string>@APPLICATION_NAME_XML_ESCAPED@ @MIRALL_VERSION_STRING@</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>
@@ -27,7 +27,7 @@
<key>CFBundleShortVersionString</key>
<string>@MIRALL_VERSION_STRING@</string>
<key>NSHumanReadableCopyright</key>
<string>(C) 2014-2015 @APPLICATION_VENDOR@</string>
<string>(C) 2014-2016 @APPLICATION_VENDOR@</string>
<key>SUShowReleaseNotes</key>
<false/>
<key>LSMinimumBundleVersion</key>
+11 -1
Ver Arquivo
@@ -620,7 +620,6 @@ Section Uninstall
!insertmacro UnInstallLib REGDLL NOTSHARED REBOOT_PROTECTED "$INSTDIR\shellext\OCOverlays_x64.dll"
!insertmacro UnInstallLib DLL NOTSHARED REBOOT_PROTECTED "$INSTDIR\shellext\OCUtil_x64.dll"
!undef LIBRARY_X64
${Else}
DetailPrint "Uninstalling x86 overlay DLLs"
!insertmacro UnInstallLib REGDLL NOTSHARED REBOOT_PROTECTED "$INSTDIR\shellext\OCContextMenu_x86.dll"
!insertmacro UnInstallLib REGDLL NOTSHARED REBOOT_PROTECTED "$INSTDIR\shellext\OCOverlays_x86.dll"
@@ -757,6 +756,12 @@ Function .onInit
;Shutdown ${APPLICATION_NAME} in case Add/Remove re-installer option used.
Call EnsureOwncloudShutdown
ReadRegStr $R0 ${MEMENTO_REGISTRY_ROOT} "${MEMENTO_REGISTRY_KEY}" "InstallLocation"
${If} ${Silent}
${AndIf} $R0 != ""
ExecWait '"$R0\Uninstall.exe" /S _?=$R0'
${EndIf}
FunctionEnd
Function .onInstSuccess
@@ -766,6 +771,11 @@ Function .onInstSuccess
WriteRegDWORD HKLM "Software\${APPLICATION_VENDOR}\${APPLICATION_NAME}" "skipUpdateCheck" "1"
${EndIf}
; TODO: Only needed to when updating from 2.1.{0,1}. Remove in due time.
Delete /REBOOTOK $INSTDIR\bearer\qgenericbearer.dll
Delete /REBOOTOK $INSTDIR\bearer\qnativewifibearer.dll
RMDir /REBOOTOK $INSTDIR\bearer
${If} ${Silent}
${AndIf} $InstallRunIfSilent == "yes"
Call LaunchApplication
+3
Ver Arquivo
@@ -18,6 +18,9 @@ if( Qt5Core_FOUND )
find_package(Qt5Network REQUIRED)
find_package(Qt5Xml REQUIRED)
find_package(Qt5Concurrent REQUIRED)
if(UNIT_TESTING)
find_package(Qt5Test REQUIRED)
endif()
if(NOT TOKEN_AUTH_ONLY)
find_package(Qt5WebKitWidgets REQUIRED)
find_package(Qt5WebKit REQUIRED)
-6
Ver Arquivo
@@ -128,10 +128,4 @@ else()
)
endif()
# INSTALL(
# FILES
# ${csync_HDRS}
# DESTINATION
# ${INCLUDE_INSTALL_DIR}/${APPLICATION_NAME}
# )
+10 -70
Ver Arquivo
@@ -25,6 +25,7 @@
#define _GNU_SOURCE
#endif
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
@@ -89,14 +90,11 @@ static int _data_cmp(const void *key, const void *data) {
return 0;
}
int csync_create(CSYNC **csync, const char *local, const char *remote) {
void csync_create(CSYNC **csync, const char *local, const char *remote) {
CSYNC *ctx;
size_t len = 0;
ctx = c_malloc(sizeof(CSYNC));
if (ctx == NULL) {
return -1;
}
ctx->status_code = CSYNC_STATUS_OK;
@@ -121,46 +119,21 @@ int csync_create(CSYNC **csync, const char *local, const char *remote) {
ctx->ignore_hidden_files = true;
*csync = ctx;
return 0;
}
int csync_init(CSYNC *ctx) {
int rc;
if (ctx == NULL) {
errno = EBADF;
return -1;
}
ctx->status_code = CSYNC_STATUS_OK;
void csync_init(CSYNC *ctx) {
assert(ctx);
/* Do not initialize twice */
if (ctx->status & CSYNC_STATUS_INIT) {
return 1;
}
/* check for uri */
if (csync_fnmatch("owncloud://*", ctx->remote.uri, 0) == 0 && csync_fnmatch("ownclouds://*", ctx->remote.uri, 0) == 0) {
ctx->status_code = CSYNC_STATUS_NO_MODULE;
rc = -1;
goto out;
}
assert(!(ctx->status & CSYNC_STATUS_INIT));
ctx->status_code = CSYNC_STATUS_OK;
ctx->local.type = LOCAL_REPLICA;
ctx->remote.type = REMOTE_REPLICA;
if (c_rbtree_create(&ctx->local.tree, _key_cmp, _data_cmp) < 0) {
ctx->status_code = CSYNC_STATUS_TREE_ERROR;
rc = -1;
goto out;
}
if (c_rbtree_create(&ctx->remote.tree, _key_cmp, _data_cmp) < 0) {
ctx->status_code = CSYNC_STATUS_TREE_ERROR;
rc = -1;
goto out;
}
c_rbtree_create(&ctx->local.tree, _key_cmp, _data_cmp);
c_rbtree_create(&ctx->remote.tree, _key_cmp, _data_cmp);
ctx->remote.root_perms = 0;
@@ -168,11 +141,6 @@ int csync_init(CSYNC *ctx) {
/* initialize random generator */
srand(time(NULL));
rc = 0;
out:
return rc;
}
int csync_update(CSYNC *ctx) {
@@ -580,29 +548,17 @@ int csync_commit(CSYNC *ctx) {
ctx->remote.read_from_db = 0;
ctx->read_remote_from_db = true;
ctx->db_is_empty = false;
ctx->ignore_hidden_files = true; // do NOT sync hidden files by default.
/* Create new trees */
rc = c_rbtree_create(&ctx->local.tree, _key_cmp, _data_cmp);
if (rc < 0) {
ctx->status_code = CSYNC_STATUS_TREE_ERROR;
goto out;
}
rc = c_rbtree_create(&ctx->remote.tree, _key_cmp, _data_cmp);
if (rc < 0) {
ctx->status_code = CSYNC_STATUS_TREE_ERROR;
goto out;
}
c_rbtree_create(&ctx->local.tree, _key_cmp, _data_cmp);
c_rbtree_create(&ctx->remote.tree, _key_cmp, _data_cmp);
ctx->status = CSYNC_STATUS_INIT;
SAFE_FREE(ctx->error_string);
rc = 0;
out:
return rc;
}
@@ -622,9 +578,6 @@ int csync_destroy(CSYNC *ctx) {
}
ctx->statedb.db = NULL;
/* destroy exclude list */
csync_exclude_destroy(ctx);
_csync_clean_ctx(ctx);
SAFE_FREE(ctx->local.uri);
@@ -640,19 +593,6 @@ int csync_destroy(CSYNC *ctx) {
return rc;
}
int csync_add_exclude_list(CSYNC *ctx, const char *path) {
if (ctx == NULL || path == NULL) {
return -1;
}
return csync_exclude_load(path, &ctx->excludes);
}
void csync_clear_exclude_list(CSYNC *ctx)
{
csync_exclude_clear(ctx);
}
void *csync_get_userdata(CSYNC *ctx) {
if (ctx == NULL) {
return NULL;
+2 -24
Ver Arquivo
@@ -316,10 +316,8 @@ typedef const char* (*csync_checksum_hook) (
* @brief Allocate a csync context.
*
* @param csync The context variable to allocate.
*
* @return 0 on success, less than 0 if an error occurred.
*/
int csync_create(CSYNC **csync, const char *local, const char *remote);
void csync_create(CSYNC **csync, const char *local, const char *remote);
/**
* @brief Initialize the file synchronizer.
@@ -327,10 +325,8 @@ int csync_create(CSYNC **csync, const char *local, const char *remote);
* This function loads the configuration
*
* @param ctx The context to initialize.
*
* @return 0 on success, less than 0 if an error occurred.
*/
int csync_init(CSYNC *ctx);
void csync_init(CSYNC *ctx);
/**
* @brief Update detection
@@ -370,24 +366,6 @@ int csync_commit(CSYNC *ctx);
*/
int csync_destroy(CSYNC *ctx);
/**
* @brief Add an additional exclude list.
*
* @param ctx The context to add the exclude list.
*
* @param path The path pointing to the file.
*
* @return 0 on success, less than 0 if an error occurred.
*/
int csync_add_exclude_list(CSYNC *ctx, const char *path);
/**
* @brief Removes all items imported from exclude lists.
*
* @param ctx The context to add the exclude list.
*/
void csync_clear_exclude_list(CSYNC *ctx);
/**
* @brief Get the userdata saved in the context.
*
+43 -19
Ver Arquivo
@@ -47,6 +47,45 @@ int _csync_exclude_add(c_strlist_t **inList, const char *string) {
return c_strlist_add_grow(inList, string);
}
/** Expands C-like escape sequences.
*
* The returned string is heap-allocated and owned by the caller.
*/
static const char *csync_exclude_expand_escapes(const char * input)
{
size_t i_len = strlen(input) + 1;
char *out = c_malloc(i_len); // out can only be shorter
size_t i = 0;
size_t o = 0;
for (; i < i_len; ++i) {
if (input[i] == '\\') {
// at worst input[i+1] is \0
switch (input[i+1]) {
case '\'': out[o++] = '\''; break;
case '"': out[o++] = '"'; break;
case '?': out[o++] = '?'; break;
case '\\': out[o++] = '\\'; break;
case 'a': out[o++] = '\a'; break;
case 'b': out[o++] = '\b'; break;
case 'f': out[o++] = '\f'; break;
case 'n': out[o++] = '\n'; break;
case 'r': out[o++] = '\r'; break;
case 't': out[o++] = '\t'; break;
case 'v': out[o++] = '\v'; break;
default:
out[o++] = input[i];
out[o++] = input[i+1];
break;
}
++i;
} else {
out[o++] = input[i];
}
}
return out;
}
int csync_exclude_load(const char *fname, c_strlist_t **list) {
int fd = -1;
int i = 0;
@@ -99,8 +138,10 @@ int csync_exclude_load(const char *fname, c_strlist_t **list) {
if (entry != buf + i) {
buf[i] = '\0';
if (*entry != '#') {
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "Adding entry: %s", entry);
rc = _csync_exclude_add(list, entry);
const char *unescaped = csync_exclude_expand_escapes(entry);
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "Adding entry: %s", unescaped);
rc = _csync_exclude_add(list, unescaped);
SAFE_FREE(unescaped);
if (rc < 0) {
goto out;
}
@@ -117,23 +158,6 @@ out:
return rc;
}
void csync_exclude_clear(CSYNC *ctx) {
c_strlist_clear(ctx->excludes);
}
void csync_exclude_destroy(CSYNC *ctx) {
c_strlist_destroy(ctx->excludes);
}
CSYNC_EXCLUDE_TYPE csync_excluded(CSYNC *ctx, const char *path, int filetype) {
CSYNC_EXCLUDE_TYPE match = CSYNC_NOT_EXCLUDED;
match = csync_excluded_no_ctx( ctx->excludes, path, filetype );
return match;
}
// See http://support.microsoft.com/kb/74496 and
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
// Additionally, we ignore '$Recycle.Bin', see https://github.com/owncloud/client/issues/2955
-31
Ver Arquivo
@@ -47,37 +47,6 @@ int _csync_exclude_add(c_strlist_t **inList, const char *string);
*/
int csync_exclude_load(const char *fname, c_strlist_t **list);
/**
* @brief Clear the exclude list in memory.
*
* @param ctx The synchronizer context.
*/
void csync_exclude_clear(CSYNC *ctx);
/**
* @brief Destroy the exclude list in memory.
*
* @param ctx The synchronizer context.
*/
void csync_exclude_destroy(CSYNC *ctx);
/**
* @brief Check if the given path should be excluded.
*
* This excludes also paths which can't be used without unix extensions.
*
* The exclude list is checked against the full path, each component of
* the path and all leading directory strings, e.g.
* '/foo/bar/file' checks ('/foo/bar/file', 'foo', 'bar', 'file',
* '/foo/bar', '/foo').
*
* @param ctx The synchronizer context.
* @param path The patch to check.
*
* @return 2 if excluded and needs cleanup, 1 if excluded, 0 if not.
*/
CSYNC_EXCLUDE_TYPE csync_excluded(CSYNC *ctx, const char *path, int filetype);
/**
* @brief Check if the given path should be excluded in a traversal situation.
*
+1
Ver Arquivo
@@ -33,6 +33,7 @@
* than fmmatch anyway, which does not care for flags.
**/
#define FNM_PATHNAME (1 << 0) /* No wildcard can ever match `/'. */
#define FNM_CASEFOLD (1 << 4) /* Compare without regard to case. */
#endif
int csync_fnmatch(__const char *__pattern, __const char *__name, int __flags);
+34 -34
Ver Arquivo
@@ -245,33 +245,9 @@ static int _csync_detect_update(CSYNC *ctx, const char *file,
st->etag = NULL;
st->child_modified = 0;
st->has_ignored_files = 0;
/* FIXME: Under which conditions are the following two ifs true and the code
* is executed? */
if (type == CSYNC_FTW_TYPE_FILE ) {
if (fs->mtime == 0) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "file: %s - mtime is zero!", path);
tmp = csync_statedb_get_stat_by_hash(ctx, h);
if(_last_db_return_error(ctx)) {
SAFE_FREE(st);
SAFE_FREE(tmp);
ctx->status_code = CSYNC_STATUS_UNSUCCESSFUL;
return -1;
}
if (tmp == NULL) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "file: %s - not found in db, IGNORE!", path);
st->instruction = CSYNC_INSTRUCTION_IGNORE;
} else {
SAFE_FREE(st);
st = tmp;
st->instruction = CSYNC_INSTRUCTION_NONE;
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "file: %s - tmp non zero, mtime %lu", path, st->modtime );
tmp = NULL;
}
goto fastout; /* Skip copying of the etag. That's an important difference to upstream
* without etags. */
}
}
@@ -321,7 +297,10 @@ static int _csync_detect_update(CSYNC *ctx, const char *file,
// zero size in statedb can happen during migration
|| (tmp->size != 0 && fs->size != tmp->size))) {
if (fs->size == tmp->size && tmp->checksumTypeId) {
// Checksum comparison at this stage is only enabled for .eml files,
// check #4754 #4755
bool isEmlFile = csync_fnmatch("*.eml", file, FNM_CASEFOLD) == 0;
if (isEmlFile && fs->size == tmp->size && tmp->checksumTypeId) {
if (ctx->callbacks.checksum_hook) {
st->checksum = ctx->callbacks.checksum_hook(
file, tmp->checksumTypeId,
@@ -333,6 +312,7 @@ static int _csync_detect_update(CSYNC *ctx, const char *file,
checksumIdentical = strncmp(st->checksum, tmp->checksum, 1000) == 0;
}
if (checksumIdentical) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "NOTE: Checksums are identical, file did not actually change: %s", path);
st->instruction = CSYNC_INSTRUCTION_NONE;
st->should_update_metadata = true;
goto out;
@@ -401,24 +381,41 @@ static int _csync_detect_update(CSYNC *ctx, const char *file,
tmp_vio_type = CSYNC_VIO_FILE_TYPE_UNKNOWN;
}
if (tmp && tmp->inode == fs->inode && tmp_vio_type == fs->type
// Default to NEW unless we're sure it's a rename.
st->instruction = CSYNC_INSTRUCTION_NEW;
bool isRename =
tmp && tmp->inode == fs->inode && tmp_vio_type == fs->type
&& (tmp->modtime == fs->mtime || fs->type == CSYNC_VIO_FILE_TYPE_DIRECTORY)
#ifdef NO_RENAME_EXTENSION
&& _csync_sameextension(tmp->path, path)
#endif
) {
;
// Verify the checksum where possible
if (isRename && tmp->checksumTypeId && ctx->callbacks.checksum_hook
&& fs->type == CSYNC_VIO_FILE_TYPE_REGULAR) {
st->checksum = ctx->callbacks.checksum_hook(
file, tmp->checksumTypeId,
ctx->callbacks.checksum_userdata);
if (st->checksum) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "checking checksum of potential rename %s %s <-> %s", path, st->checksum, tmp->checksum);
st->checksumTypeId = tmp->checksumTypeId;
isRename = strncmp(st->checksum, tmp->checksum, 1000) == 0;
}
}
if (isRename) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "pot rename detected based on inode # %" PRId64 "", (uint64_t) fs->inode);
/* inode found so the file has been renamed */
st->instruction = CSYNC_INSTRUCTION_EVAL_RENAME;
if (fs->type == CSYNC_VIO_FILE_TYPE_DIRECTORY) {
csync_rename_record(ctx, tmp->path, path);
}
goto out;
} else {
/* file not found in statedb */
st->instruction = CSYNC_INSTRUCTION_NEW;
goto out;
}
goto out;
} else {
/* Remote Replica Rename check */
tmp = csync_statedb_get_stat_by_file_id(ctx, fs->file_id);
@@ -518,7 +515,6 @@ out:
strncpy(st->remotePerm, fs->remotePerm, REMOTE_PERM_BUF_SIZE);
}
fastout: /* target if the file information is read from database into st */
st->phash = h;
st->pathlen = len;
memcpy(st->path, (len ? path : ""), len + 1);
@@ -563,7 +559,11 @@ int csync_walker(CSYNC *ctx, const char *file, const csync_vio_file_stat_t *fs,
switch (flag) {
case CSYNC_FTW_FLAG_FILE:
if (ctx->current == REMOTE_REPLICA) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "file: %s [file_id=%s size=%" PRIu64 "]", file, fs->file_id, fs->size);
if (fs->fields & CSYNC_VIO_FILE_STAT_FIELDS_SIZE) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "file: %s [file_id=%s size=%" PRIu64 "]", file, fs->file_id, fs->size);
} else {
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "file: %s [file_id=%s size=UNKNOWN]", file, fs->file_id);
}
} else {
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "file: %s [inode=%" PRIu64 " size=%" PRIu64 "]", file, fs->inode, fs->size);
}
+5 -8
Ver Arquivo
@@ -38,13 +38,12 @@
#define NIL &_sentinel /* all leafs are sentinels */
static c_rbnode_t _sentinel = {NULL, NIL, NIL, NULL, NULL, BLACK};
int c_rbtree_create(c_rbtree_t **rbtree, c_rbtree_compare_func *key_compare, c_rbtree_compare_func *data_compare) {
c_rbtree_t *tree = NULL;
void c_rbtree_create(c_rbtree_t **rbtree, c_rbtree_compare_func *key_compare, c_rbtree_compare_func *data_compare) {
assert(rbtree);
assert(key_compare);
assert(data_compare);
if (rbtree == NULL || key_compare == NULL || data_compare == NULL) {
errno = EINVAL;
return -1;
}
c_rbtree_t *tree = NULL;
tree = c_malloc(sizeof(*tree));
tree->root = NIL;
@@ -53,8 +52,6 @@ int c_rbtree_create(c_rbtree_t **rbtree, c_rbtree_compare_func *key_compare, c_r
tree->size = 0;
*rbtree = tree;
return 0;
}
static c_rbnode_t *_rbtree_subtree_dup(const c_rbnode_t *node, c_rbtree_t *new_tree, c_rbnode_t *new_parent) {
+1 -3
Ver Arquivo
@@ -135,10 +135,8 @@ struct c_rbnode_s {
*
* @param data_compare Callback function to compare a key as data with thee
* data inside a red-black tree node.
*
* @return 0 on success, -1 if an error occurred with errno set.
*/
int c_rbtree_create(c_rbtree_t **rbtree, c_rbtree_compare_func *key_compare, c_rbtree_compare_func *data_compare);
void c_rbtree_create(c_rbtree_t **rbtree, c_rbtree_compare_func *key_compare, c_rbtree_compare_func *data_compare);
/**
* @brief Duplicate a red-black tree.
+12 -6
Ver Arquivo
@@ -139,7 +139,6 @@ csync_vio_file_stat_t *csync_vio_local_readdir(csync_vio_handle_t *dhandle) {
dhandle_t *handle = NULL;
csync_vio_file_stat_t *file_stat = NULL;
ULARGE_INTEGER FileIndex;
DWORD rem;
handle = (dhandle_t *) dhandle;
@@ -168,9 +167,14 @@ csync_vio_file_stat_t *csync_vio_local_readdir(csync_vio_handle_t *dhandle) {
}
file_stat->name = c_utf8_from_locale(handle->ffd.cFileName);
file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE;
if (handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT
&& handle->ffd.dwReserved0 & IO_REPARSE_TAG_SYMLINK) {
file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE;
if ( (handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
&& (handle->ffd.dwReserved0 & IO_REPARSE_TAG_SYMLINK)
// The SIS or DEDUP flag points to a MS deduplication feature of
// certain file storage products. It is not a normal symlink
// that should be ignored.
&& (! (handle->ffd.dwReserved0 & IO_REPARSE_TAG_SIS))
&& (! (handle->ffd.dwReserved0 & IO_REPARSE_TAG_DEDUP)) ) {
file_stat->flags = CSYNC_VIO_FILE_FLAGS_SYMLINK;
file_stat->type = CSYNC_VIO_FILE_TYPE_SYMBOLIC_LINK;
} else if (handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_DEVICE
@@ -226,8 +230,10 @@ int csync_vio_local_stat(const char *uri, csync_vio_file_stat_t *buf) {
ULARGE_INTEGER FileIndex;
mbchar_t *wuri = c_utf8_path_to_locale( uri );
h = CreateFileW( wuri, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL+FILE_FLAG_BACKUP_SEMANTICS+FILE_FLAG_OPEN_REPARSE_POINT, NULL );
h = CreateFileW( wuri, 0, FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
NULL );
if( h == INVALID_HANDLE_VALUE ) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_CRIT, "CreateFileW failed on %s", uri );
errno = GetLastError();
-4
Ver Arquivo
@@ -53,7 +53,3 @@ add_cmocka_test(check_csync_update csync_tests/check_csync_update.c ${TEST_TARGE
# encoding
add_cmocka_test(check_encoding_functions encoding_tests/check_encoding.c ${TEST_TARGET_LIBRARIES})
if(UNIT_TESTING)
INSTALL( FILES "${CMOCKA_LIBRARIES}" DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif(UNIT_TESTING)
+3 -16
Ver Arquivo
@@ -33,8 +33,7 @@ static void setup(void **state) {
rc = system("mkdir -p /tmp/check_csync2");
assert_int_equal(rc, 0);
rc = csync_create(&csync, "/tmp/check_csync1", "/tmp/check_csync2");
assert_int_equal(rc, 0);
csync_create(&csync, "/tmp/check_csync1", "/tmp/check_csync2");
*state = csync;
}
@@ -49,10 +48,9 @@ static void setup_module(void **state) {
rc = system("mkdir -p /tmp/check_csync2");
assert_int_equal(rc, 0);
rc = csync_create(&csync, "/tmp/check_csync1", "dummy://foo/bar");
assert_int_equal(rc, 0);
csync_create(&csync, "/tmp/check_csync1", "dummy://foo/bar");
rc = csync_init(csync);
csync_init(csync);
*state = csync;
}
@@ -74,16 +72,6 @@ static void teardown(void **state) {
*state = NULL;
}
static void check_csync_commit_null(void **state)
{
int rc;
(void) state; /* unused */
rc = csync_commit(NULL);
assert_int_equal(rc, -1);
}
static void check_csync_commit(void **state)
{
CSYNC *csync = *state;
@@ -110,7 +98,6 @@ static void check_csync_commit_dummy(void **state)
int torture_run_tests(void)
{
const UnitTest tests[] = {
unit_test_setup_teardown(check_csync_commit_null, setup, teardown),
unit_test_setup_teardown(check_csync_commit, setup, teardown),
unit_test_setup_teardown(check_csync_commit_dummy, setup_module, teardown),
};
+1 -2
Ver Arquivo
@@ -42,8 +42,7 @@ static void check_csync_create(void **state)
(void) state; /* unused */
rc = csync_create(&csync, "/tmp/csync1", "/tmp/csync2");
assert_int_equal(rc, 0);
csync_create(&csync, "/tmp/csync1", "/tmp/csync2");
rc = csync_destroy(csync);
assert_int_equal(rc, 0);
+62 -44
Ver Arquivo
@@ -32,8 +32,7 @@ static void setup(void **state) {
CSYNC *csync;
int rc;
rc = csync_create(&csync, "/tmp/check_csync1", "/tmp/check_csync2");
assert_int_equal(rc, 0);
csync_create(&csync, "/tmp/check_csync1", "/tmp/check_csync2");
*state = csync;
}
@@ -42,8 +41,7 @@ static void setup_init(void **state) {
CSYNC *csync;
int rc;
rc = csync_create(&csync, "/tmp/check_csync1", "/tmp/check_csync2");
assert_int_equal(rc, 0);
csync_create(&csync, "/tmp/check_csync1", "/tmp/check_csync2");
rc = csync_exclude_load(EXCLUDE_LIST_FILE, &(csync->excludes));
assert_int_equal(rc, 0);
@@ -102,84 +100,84 @@ static void check_csync_excluded(void **state)
CSYNC *csync = *state;
int rc;
rc = csync_excluded(csync, "", CSYNC_FTW_TYPE_FILE);
rc = csync_excluded_no_ctx(csync->excludes, "", CSYNC_FTW_TYPE_FILE);
assert_int_equal(rc, CSYNC_NOT_EXCLUDED);
rc = csync_excluded(csync, "/", CSYNC_FTW_TYPE_FILE);
rc = csync_excluded_no_ctx(csync->excludes, "/", CSYNC_FTW_TYPE_FILE);
assert_int_equal(rc, CSYNC_NOT_EXCLUDED);
rc = csync_excluded(csync, "krawel_krawel", CSYNC_FTW_TYPE_FILE);
rc = csync_excluded_no_ctx(csync->excludes, "krawel_krawel", CSYNC_FTW_TYPE_FILE);
assert_int_equal(rc, CSYNC_NOT_EXCLUDED);
rc = csync_excluded(csync, ".kde/share/config/kwin.eventsrc", CSYNC_FTW_TYPE_FILE);
rc = csync_excluded_no_ctx(csync->excludes, ".kde/share/config/kwin.eventsrc", CSYNC_FTW_TYPE_FILE);
assert_int_equal(rc, CSYNC_NOT_EXCLUDED);
rc = csync_excluded(csync, ".htaccess/cache-maximegalon/cache1.txt", CSYNC_FTW_TYPE_FILE);
rc = csync_excluded_no_ctx(csync->excludes, ".htaccess/cache-maximegalon/cache1.txt", CSYNC_FTW_TYPE_FILE);
assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST);
rc = csync_excluded(csync, "mozilla/.htaccess", CSYNC_FTW_TYPE_DIR);
rc = csync_excluded_no_ctx(csync->excludes, "mozilla/.htaccess", CSYNC_FTW_TYPE_DIR);
assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST);
/*
* Test for patterns in subdirs. '.beagle' is defined as a pattern and has
* to be found in top dir as well as in directories underneath.
*/
rc = csync_excluded(csync, ".apdisk", CSYNC_FTW_TYPE_DIR);
rc = csync_excluded_no_ctx(csync->excludes, ".apdisk", CSYNC_FTW_TYPE_DIR);
assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST);
rc = csync_excluded(csync, "foo/.apdisk", CSYNC_FTW_TYPE_DIR);
rc = csync_excluded_no_ctx(csync->excludes, "foo/.apdisk", CSYNC_FTW_TYPE_DIR);
assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST);
rc = csync_excluded(csync, "foo/bar/.apdisk", CSYNC_FTW_TYPE_DIR);
rc = csync_excluded_no_ctx(csync->excludes, "foo/bar/.apdisk", CSYNC_FTW_TYPE_DIR);
assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST);
rc = csync_excluded(csync, ".java", CSYNC_FTW_TYPE_FILE);
rc = csync_excluded_no_ctx(csync->excludes, ".java", CSYNC_FTW_TYPE_FILE);
assert_int_equal(rc, CSYNC_NOT_EXCLUDED);
/* Files in the ignored dir .java will also be ignored. */
rc = csync_excluded(csync, ".apdisk/totally_amazing.jar", CSYNC_FTW_TYPE_FILE);
rc = csync_excluded_no_ctx(csync->excludes, ".apdisk/totally_amazing.jar", CSYNC_FTW_TYPE_FILE);
assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST);
/* and also in subdirs */
rc = csync_excluded(csync, "projects/.apdisk/totally_amazing.jar", CSYNC_FTW_TYPE_FILE);
rc = csync_excluded_no_ctx(csync->excludes, "projects/.apdisk/totally_amazing.jar", CSYNC_FTW_TYPE_FILE);
assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST);
/* csync-journal is ignored in general silently. */
rc = csync_excluded(csync, ".csync_journal.db", CSYNC_FTW_TYPE_FILE);
rc = csync_excluded_no_ctx(csync->excludes, ".csync_journal.db", CSYNC_FTW_TYPE_FILE);
assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED);
rc = csync_excluded(csync, ".csync_journal.db.ctmp", CSYNC_FTW_TYPE_FILE);
rc = csync_excluded_no_ctx(csync->excludes, ".csync_journal.db.ctmp", CSYNC_FTW_TYPE_FILE);
assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED);
rc = csync_excluded(csync, "subdir/.csync_journal.db", CSYNC_FTW_TYPE_FILE);
rc = csync_excluded_no_ctx(csync->excludes, "subdir/.csync_journal.db", CSYNC_FTW_TYPE_FILE);
assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED);
/* pattern ]*.directory - ignore and remove */
rc = csync_excluded(csync, "my.~directory", CSYNC_FTW_TYPE_FILE);
rc = csync_excluded_no_ctx(csync->excludes, "my.~directory", CSYNC_FTW_TYPE_FILE);
assert_int_equal(rc, CSYNC_FILE_EXCLUDE_AND_REMOVE);
rc = csync_excluded(csync, "/a_folder/my.~directory", CSYNC_FTW_TYPE_FILE);
rc = csync_excluded_no_ctx(csync->excludes, "/a_folder/my.~directory", CSYNC_FTW_TYPE_FILE);
assert_int_equal(rc, CSYNC_FILE_EXCLUDE_AND_REMOVE);
/* Not excluded because the pattern .netscape/cache requires directory. */
rc = csync_excluded(csync, ".netscape/cache", CSYNC_FTW_TYPE_FILE);
rc = csync_excluded_no_ctx(csync->excludes, ".netscape/cache", CSYNC_FTW_TYPE_FILE);
assert_int_equal(rc, CSYNC_NOT_EXCLUDED);
/* Not excluded */
rc = csync_excluded(csync, "unicode/中文.hé", CSYNC_FTW_TYPE_FILE);
rc = csync_excluded_no_ctx(csync->excludes, "unicode/中文.hé", CSYNC_FTW_TYPE_FILE);
assert_int_equal(rc, CSYNC_NOT_EXCLUDED);
/* excluded */
rc = csync_excluded(csync, "unicode/пятницы.txt", CSYNC_FTW_TYPE_FILE);
rc = csync_excluded_no_ctx(csync->excludes, "unicode/пятницы.txt", CSYNC_FTW_TYPE_FILE);
assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST);
rc = csync_excluded(csync, "unicode/中文.💩", CSYNC_FTW_TYPE_FILE);
rc = csync_excluded_no_ctx(csync->excludes, "unicode/中文.💩", CSYNC_FTW_TYPE_FILE);
assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST);
/* path wildcards */
rc = csync_excluded(csync, "foobar/my_manuscript.out", CSYNC_FTW_TYPE_FILE);
rc = csync_excluded_no_ctx(csync->excludes, "foobar/my_manuscript.out", CSYNC_FTW_TYPE_FILE);
assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST);
rc = csync_excluded(csync, "latex_tmp/my_manuscript.run.xml", CSYNC_FTW_TYPE_FILE);
rc = csync_excluded_no_ctx(csync->excludes, "latex_tmp/my_manuscript.run.xml", CSYNC_FTW_TYPE_FILE);
assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST);
rc = csync_excluded(csync, "word_tmp/my_manuscript.run.xml", CSYNC_FTW_TYPE_FILE);
rc = csync_excluded_no_ctx(csync->excludes, "word_tmp/my_manuscript.run.xml", CSYNC_FTW_TYPE_FILE);
assert_int_equal(rc, CSYNC_NOT_EXCLUDED);
rc = csync_excluded(csync, "latex/my_manuscript.tex.tmp", CSYNC_FTW_TYPE_FILE);
rc = csync_excluded_no_ctx(csync->excludes, "latex/my_manuscript.tex.tmp", CSYNC_FTW_TYPE_FILE);
assert_int_equal(rc, CSYNC_NOT_EXCLUDED);
rc = csync_excluded(csync, "latex/songbook/my_manuscript.tex.tmp", CSYNC_FTW_TYPE_FILE);
rc = csync_excluded_no_ctx(csync->excludes, "latex/songbook/my_manuscript.tex.tmp", CSYNC_FTW_TYPE_FILE);
assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST);
}
@@ -241,45 +239,45 @@ static void check_csync_pathes(void **state)
_csync_exclude_add( &(csync->excludes), "/exclude" );
/* Check toplevel dir, the pattern only works for toplevel dir. */
rc = csync_excluded(csync, "/exclude", CSYNC_FTW_TYPE_DIR);
rc = csync_excluded_no_ctx(csync->excludes, "/exclude", CSYNC_FTW_TYPE_DIR);
assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST);
rc = csync_excluded(csync, "/foo/exclude", CSYNC_FTW_TYPE_DIR);
rc = csync_excluded_no_ctx(csync->excludes, "/foo/exclude", CSYNC_FTW_TYPE_DIR);
assert_int_equal(rc, CSYNC_NOT_EXCLUDED);
/* check for a file called exclude. Must still work */
rc = csync_excluded(csync, "/exclude", CSYNC_FTW_TYPE_FILE);
rc = csync_excluded_no_ctx(csync->excludes, "/exclude", CSYNC_FTW_TYPE_FILE);
assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST);
rc = csync_excluded(csync, "/foo/exclude", CSYNC_FTW_TYPE_FILE);
rc = csync_excluded_no_ctx(csync->excludes, "/foo/exclude", CSYNC_FTW_TYPE_FILE);
assert_int_equal(rc, CSYNC_NOT_EXCLUDED);
/* Add an exclude for directories only: excl/ */
_csync_exclude_add( &(csync->excludes), "excl/" );
rc = csync_excluded(csync, "/excl", CSYNC_FTW_TYPE_DIR);
rc = csync_excluded_no_ctx(csync->excludes, "/excl", CSYNC_FTW_TYPE_DIR);
assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST);
rc = csync_excluded(csync, "meep/excl", CSYNC_FTW_TYPE_DIR);
rc = csync_excluded_no_ctx(csync->excludes, "meep/excl", CSYNC_FTW_TYPE_DIR);
assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST);
rc = csync_excluded(csync, "meep/excl/file", CSYNC_FTW_TYPE_FILE);
rc = csync_excluded_no_ctx(csync->excludes, "meep/excl/file", CSYNC_FTW_TYPE_FILE);
assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST);
rc = csync_excluded(csync, "/excl", CSYNC_FTW_TYPE_FILE);
rc = csync_excluded_no_ctx(csync->excludes, "/excl", CSYNC_FTW_TYPE_FILE);
assert_int_equal(rc, CSYNC_NOT_EXCLUDED);
_csync_exclude_add(&csync->excludes, "/excludepath/withsubdir");
rc = csync_excluded(csync, "/excludepath/withsubdir", CSYNC_FTW_TYPE_DIR);
rc = csync_excluded_no_ctx(csync->excludes, "/excludepath/withsubdir", CSYNC_FTW_TYPE_DIR);
assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST);
rc = csync_excluded(csync, "/excludepath/withsubdir", CSYNC_FTW_TYPE_FILE);
rc = csync_excluded_no_ctx(csync->excludes, "/excludepath/withsubdir", CSYNC_FTW_TYPE_FILE);
assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST);
rc = csync_excluded(csync, "/excludepath/withsubdir2", CSYNC_FTW_TYPE_DIR);
rc = csync_excluded_no_ctx(csync->excludes, "/excludepath/withsubdir2", CSYNC_FTW_TYPE_DIR);
assert_int_equal(rc, CSYNC_NOT_EXCLUDED);
rc = csync_excluded(csync, "/excludepath/withsubdir/foo", CSYNC_FTW_TYPE_DIR);
rc = csync_excluded_no_ctx(csync->excludes, "/excludepath/withsubdir/foo", CSYNC_FTW_TYPE_DIR);
assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST);
}
@@ -314,8 +312,8 @@ static void check_csync_excluded_performance(void **state)
gettimeofday(&before, 0);
for (int i = 0; i < N; ++i) {
totalRc += csync_excluded(csync, "/this/is/quite/a/long/path/with/many/components", CSYNC_FTW_TYPE_DIR);
totalRc += csync_excluded(csync, "/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/29", CSYNC_FTW_TYPE_FILE);
totalRc += csync_excluded_no_ctx(csync->excludes, "/this/is/quite/a/long/path/with/many/components", CSYNC_FTW_TYPE_DIR);
totalRc += csync_excluded_no_ctx(csync->excludes, "/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/29", CSYNC_FTW_TYPE_FILE);
}
assert_int_equal(totalRc, CSYNC_NOT_EXCLUDED); // mainly to avoid optimization
@@ -346,6 +344,25 @@ static void check_csync_excluded_performance(void **state)
}
}
static void check_csync_exclude_expand_escapes(void **state)
{
(void)state;
const char *str = csync_exclude_expand_escapes(
"keep \\' \\\" \\? \\\\ \\a \\b \\f \\n \\r \\t \\v \\z");
assert_true(0 == strcmp(
str, "keep ' \" ? \\ \a \b \f \n \r \t \v \\z"));
SAFE_FREE(str);
str = csync_exclude_expand_escapes("");
assert_true(0 == strcmp(str, ""));
SAFE_FREE(str);
str = csync_exclude_expand_escapes("\\");
assert_true(0 == strcmp(str, "\\"));
SAFE_FREE(str);
}
int torture_run_tests(void)
{
const UnitTest tests[] = {
@@ -356,6 +373,7 @@ int torture_run_tests(void)
unit_test_setup_teardown(check_csync_pathes, setup_init, teardown),
unit_test_setup_teardown(check_csync_is_windows_reserved_word, setup_init, teardown),
unit_test_setup_teardown(check_csync_excluded_performance, setup_init, teardown),
unit_test(check_csync_exclude_expand_escapes),
};
return run_tests(tests);
+3 -20
Ver Arquivo
@@ -33,8 +33,7 @@ static void setup(void **state) {
rc = system("mkdir -p /tmp/check_csync2");
assert_int_equal(rc, 0);
rc = csync_create(&csync, "/tmp/check_csync1", "/tmp/check_csync2");
assert_int_equal(rc, 0);
csync_create(&csync, "/tmp/check_csync1", "/tmp/check_csync2");
*state = csync;
}
@@ -49,8 +48,7 @@ static void setup_module(void **state) {
rc = system("mkdir -p /tmp/check_csync2");
assert_int_equal(rc, 0);
rc = csync_create(&csync, "/tmp/check_csync1", "dummy://foo/bar");
assert_int_equal(rc, 0);
csync_create(&csync, "/tmp/check_csync1", "dummy://foo/bar");
*state = csync;
}
@@ -73,34 +71,19 @@ static void teardown(void **state) {
*state = NULL;
}
static void check_csync_init_null(void **state)
{
int rc;
(void) state; /* unused */
rc = csync_init(NULL);
assert_int_equal(rc, -1);
}
static void check_csync_init(void **state)
{
CSYNC *csync = *state;
int rc;
rc = csync_init(csync);
assert_int_equal(rc, 0);
csync_init(csync);
assert_int_equal(csync->status & CSYNC_STATUS_INIT, 1);
rc = csync_init(csync);
assert_int_equal(rc, 1);
}
int torture_run_tests(void)
{
const UnitTest tests[] = {
unit_test_setup_teardown(check_csync_init_null, setup, teardown),
unit_test_setup_teardown(check_csync_init, setup, teardown),
unit_test_setup_teardown(check_csync_init, setup_module, teardown),
};
+1 -2
Ver Arquivo
@@ -37,8 +37,7 @@ static void setup(void **state) {
rc = system("mkdir -p /tmp/check_csync2");
assert_int_equal(rc, 0);
rc = csync_create(&csync, "/tmp/check_csync1", "/tmp/check_csync2");
assert_int_equal(rc, 0);
csync_create(&csync, "/tmp/check_csync1", "/tmp/check_csync2");
*state = csync;
}
@@ -37,8 +37,7 @@ static void setup(void **state) {
rc = system("mkdir -p /tmp/check_csync1");
assert_int_equal(rc, 0);
rc = csync_create(&csync, "/tmp/check_csync1", "/tmp/check_csync2");
assert_int_equal(rc, 0);
csync_create(&csync, "/tmp/check_csync1", "/tmp/check_csync2");
csync->statedb.file = c_strdup( TESTDB );
*state = csync;
@@ -42,10 +42,8 @@ static void setup(void **state)
assert_int_equal(rc, 0);
rc = system("mkdir -p /tmp/check_csync");
assert_int_equal(rc, 0);
rc = csync_create(&csync, "/tmp/check_csync1", "/tmp/check_csync2");
assert_int_equal(rc, 0);
rc = csync_init(csync);
assert_int_equal(rc, 0);
csync_create(&csync, "/tmp/check_csync1", "/tmp/check_csync2");
csync_init(csync);
sqlite3 *db = NULL;
rc = sqlite3_open_v2(TESTDB, &db, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, NULL);
+4 -8
Ver Arquivo
@@ -93,10 +93,8 @@ static void setup(void **state)
assert_int_equal(rc, 0);
rc = system("mkdir -p /tmp/check_csync2");
assert_int_equal(rc, 0);
rc = csync_create(&csync, "/tmp/check_csync1", "/tmp/check_csync2");
assert_int_equal(rc, 0);
rc = csync_init(csync);
assert_int_equal(rc, 0);
csync_create(&csync, "/tmp/check_csync1", "/tmp/check_csync2");
csync_init(csync);
/* Create a new db with metadata */
sqlite3 *db;
@@ -126,10 +124,8 @@ static void setup_ftw(void **state)
assert_int_equal(rc, 0);
rc = system("mkdir -p /tmp/check_csync2");
assert_int_equal(rc, 0);
rc = csync_create(&csync, "/tmp", "/tmp");
assert_int_equal(rc, 0);
rc = csync_init(csync);
assert_int_equal(rc, 0);
csync_create(&csync, "/tmp", "/tmp");
csync_init(csync);
sqlite3 *db = NULL;
rc = sqlite3_open_v2(TESTDB, &db, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, NULL);
+18 -24
Ver Arquivo
@@ -85,6 +85,16 @@ sub fromFileName($)
}
}
sub setCredentials
{
my ($dav, $user, $passwd) = @_;
$dav->credentials(-url=> $owncloud, -realm=>"sabre/dav",
-user=> $user, -pass=> $passwd);
$dav->credentials(-url=> $owncloud, -realm=>"ownCloud",
-user=> $user, -pass=> $passwd);
}
sub initTesting(;$)
{
@@ -127,9 +137,7 @@ sub initTesting(;$)
my $ua = HTTP::DAV::UserAgent->new(keep_alive => 1 );
$d = HTTP::DAV->new(-useragent => $ua);
$d->credentials( -url=> $owncloud, -realm=>"ownCloud",
-user=> $user,
-pass=> $passwd );
setCredentials($d, $user, $passwd);
# $d->DebugLevel(3);
$prefix = "t1" unless( defined $prefix );
@@ -193,9 +201,7 @@ sub removeRemoteDir($;$)
my $url = testDirUrl() . $dir;
if( $optionsRef && $optionsRef->{user} && $optionsRef->{passwd} ) {
$d->credentials( -url=> $owncloud, -realm=>"ownCloud",
-user=> $optionsRef->{user},
-pass=> $optionsRef->{passwd} );
setCredentials($d, $optionsRef->{user}, $optionsRef->{passwd});
if( $optionsRef->{url} ) {
$url = $optionsRef->{url} . $dir;
}
@@ -219,9 +225,7 @@ sub createRemoteDir(;$$)
my $url = testDirUrl() . $dir;
if( $optionsRef && $optionsRef->{user} && $optionsRef->{passwd} ) {
$d->credentials( -url=> $owncloud, -realm=>"ownCloud",
-user=> $optionsRef->{user},
-pass=> $optionsRef->{passwd} );
setCredentials($d, $optionsRef->{user}, $optionsRef->{passwd});
if( $optionsRef->{url} ) {
$url = $optionsRef->{url} . $dir;
}
@@ -396,9 +400,7 @@ sub traverse( $$;$ )
my %seen;
$d->credentials( -url=> $owncloud, -realm=>"ownCloud",
-user=> $user,
-pass=> $passwd );
setCredentials($d, $user, $passwd);
$d->open( $owncloud );
if( my $r = $d->propfind( -url => $url, -depth => 1 ) ) {
@@ -513,9 +515,7 @@ sub put_to_dir( $$;$ )
my $targetUrl = testDirUrl();
if( $optionsRef && $optionsRef->{user} && $optionsRef->{passwd} ) {
$d->credentials( -url=> $owncloud, -realm=>"ownCloud",
-user=> $optionsRef->{user},
-pass=> $optionsRef->{passwd} );
setCredentials($d, $optionsRef->{user}, $optionsRef->{passwd});
if( $optionsRef->{url} ) {
$targetUrl = $optionsRef->{url};
}
@@ -649,9 +649,7 @@ sub moveRemoteFile($$;$)
{
my ($from, $to, $no_testdir) = @_;
$d->credentials( -url=> $owncloud, -realm=>"ownCloud",
-user=> $user,
-pass=> $passwd );
setCredentials($d, $user, $passwd);
my $fromUrl = testDirUrl(). $from;
my $toUrl = testDirUrl() . $to;
@@ -725,9 +723,7 @@ sub createShare($$)
my $dd = HTTP::DAV->new();
$dd->credentials( -url=> $owncloud, -realm=>"ownCloud",
-user=> $share_user,
-pass=> $share_passwd );
setCredentials($dd, $share_user, $share_passwd);
$dd->open( $owncloud);
# create a remote dir
@@ -769,9 +765,7 @@ sub removeShare($$)
my $dd = HTTP::DAV->new();
$dd->credentials( -url => $owncloud, -realm=>"ownCloud",
-user => $share_user,
-pass => $share_passwd );
setCredentials($dd, $share_user, $share_passwd);
$dd->open( $owncloud);
my $ua = LWP::UserAgent->new(ssl_opts => { verify_hostname => 0 });
+2 -2
Ver Arquivo
@@ -146,9 +146,9 @@ assertLocalAndRemoteDir( '', 0);
# create a true conflict.
printInfo( "Create a conflict." );
system( "echo \"This is more stuff\" >> /tmp/kernelcrash.txt" );
system( "echo \"This is more server stuff\" >> /tmp/kernelcrash.txt" );
put_to_dir( '/tmp/kernelcrash.txt', 'remoteToLocal1' );
system( "sleep 2 && touch " . localDir() . "remoteToLocal1/kernelcrash.txt" );
system( "sleep 2 && echo \"This is more client stuff\" >> " . localDir() . "remoteToLocal1/kernelcrash.txt" );
csync();
assertLocalAndRemoteDir( '', 1);
+1 -1
Ver Arquivo
@@ -71,7 +71,7 @@ assert($emlpropafter->get_property( "getlastmodified" ) eq
$emlpropbefore->get_property( "getlastmodified" ));
printInfo( "Change content of eml file (but not size)");
system( "sed -i -e 's/in/IN/' $locDir/test.eml" );
system( "sleep 1 && sed -i -e 's/in/IN/' $locDir/test.eml" );
csync( );
+4 -27
Ver Arquivo
@@ -84,10 +84,8 @@ static void destructor(void *data) {
static void setup(void **state) {
c_rbtree_t *tree = NULL;
int rc;
rc = c_rbtree_create(&tree, key_cmp, data_cmp);
assert_int_equal(rc, 0);
c_rbtree_create(&tree, key_cmp, data_cmp);
*state = tree;
}
@@ -97,8 +95,7 @@ static void setup_complete_tree(void **state) {
int i = 0;
int rc;
rc = c_rbtree_create(&tree, key_cmp, data_cmp);
assert_int_equal(rc, 0);
c_rbtree_create(&tree, key_cmp, data_cmp);
for (i = 0; i < 100; i++) {
test_t *testdata = NULL;
@@ -131,31 +128,13 @@ static void check_c_rbtree_create_free(void **state)
(void) state; /* unused */
rc = c_rbtree_create(&tree, key_cmp, data_cmp);
assert_int_equal(rc, 0);
c_rbtree_create(&tree, key_cmp, data_cmp);
assert_int_equal(tree->size, 0);
rc = c_rbtree_free(tree);
assert_int_equal(rc, 0);
}
static void check_c_rbtree_create_null(void **state)
{
c_rbtree_t *tree = NULL;
int rc;
(void) state; /* unused */
rc = c_rbtree_create(NULL, key_cmp, data_cmp);
assert_int_equal(rc, -1);
rc = c_rbtree_create(&tree, NULL, data_cmp);
assert_int_equal(rc, -1);
rc = c_rbtree_create(&tree, key_cmp, NULL);
assert_int_equal(rc, -1);
}
static void check_c_rbtree_free_null(void **state)
{
int rc;
@@ -175,8 +154,7 @@ static void check_c_rbtree_insert_delete(void **state)
(void) state; /* unused */
rc = c_rbtree_create(&tree, key_cmp, data_cmp);
assert_int_equal(rc, 0);
c_rbtree_create(&tree, key_cmp, data_cmp);
testdata = malloc(sizeof(test_t));
testdata->key = 42;
@@ -369,7 +347,6 @@ int torture_run_tests(void)
{
const UnitTest tests[] = {
unit_test(check_c_rbtree_create_free),
unit_test(check_c_rbtree_create_null),
unit_test(check_c_rbtree_free_null),
unit_test(check_c_rbtree_insert_delete),
unit_test_setup_teardown(check_c_rbtree_insert_random, setup, teardown),
+1 -2
Ver Arquivo
@@ -49,8 +49,7 @@ static void setup(void **state)
rc = system("rm -rf /tmp/csync_test");
assert_int_equal(rc, 0);
rc = csync_create(&csync, "/tmp/csync1", "/tmp/csync2");
assert_int_equal(rc, 0);
csync_create(&csync, "/tmp/csync1", "/tmp/csync2");
csync->replica = LOCAL_REPLICA;
+1 -2
Ver Arquivo
@@ -97,8 +97,7 @@ static void setup_testenv(void **state) {
statevar *mystate = malloc( sizeof(statevar) );
mystate->result = NULL;
rc = csync_create(&(mystate->csync), "/tmp/csync1", "/tmp/csync2");
assert_int_equal(rc, 0);
csync_create(&(mystate->csync), "/tmp/csync1", "/tmp/csync2");
mystate->csync->replica = LOCAL_REPLICA;
+5
Ver Arquivo
@@ -18,3 +18,8 @@ ownCloud Command Line Client
----------------------------
.. index:: owncloudcmd
.. include:: owncloudcmd.rst
Low Disk Space
--------------
.. index:: disk space
.. include:: lowdiskspace.rst
+3 -2
Ver Arquivo
@@ -11,7 +11,7 @@ desktop client.
.. note:: Build instructions are subject to change as development proceeds.
Please check the version for which you want to build.
These instructions are updated to work with version 2.1 of the ownCloud Client.
These instructions are updated to work with version 2.2 of the ownCloud Client.
Getting Source Code
-------------------
@@ -214,6 +214,7 @@ To build the most up-to-date version of the client:
1. Clone the latest versions of the client from Git_ as follows::
git clone git://github.com/owncloud/client.git
cd client
git submodule init
git submodule update
@@ -224,7 +225,7 @@ To build the most up-to-date version of the client:
3. Configure the client build::
cmake -DCMAKE_BUILD_TYPE="Debug" ../client
cmake -DCMAKE_BUILD_TYPE="Debug" ..
.. note:: You must use absolute paths for the ``include`` and ``library``
directories.
+6
Ver Arquivo
@@ -22,3 +22,9 @@ You can change the following configuration settings (must be under the ``[ownClo
- ``maxLogLines`` (default: ``20000``) -- Specifies the maximum number of log lines displayed in the log window.
- ``chunkSize`` (default: ``5242880``) -- Specifies the chunk size of uploaded files in bytes.
- ``promptDeleteAllFiles`` (default: ``true``) -- If a UI prompt should ask for confirmation if it was detected that all files and folders were deleted.
- ``notificationRefreshInterval`` (default``300,000``) -- Specifies the default interval of checking for new server notifications in milliseconds.
Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 82 KiB

+5
Ver Arquivo
@@ -0,0 +1,5 @@
When disk space is low the ownCloud Client will be unable to synchronize all files. This section describes its behavior in a low disk space situation as well as the options that influence it.
1. Synchronization of a folder aborts entirely if the remaining disk space falls below 50 MB. This threshold can be adjusted with the ``OWNCLOUD_CRITICAL_FREE_SPACE_BYTES`` environment variable.
2. Downloads that would reduce the free disk space below 250 MB will be skipped or aborted. The download will be retried regularly and other synchronization is unaffected. This threshold can be adjusted with the ``OWNCLOUD_FREE_SPACE_BYTES`` environment variable.
+59 -2
Ver Arquivo
@@ -11,7 +11,7 @@ in the system tray (Windows, KDE), status bar (Mac OS X), or notification area
.. figure:: images/icon.png
:alt: Status icon, little cloud with green circle and white checkmark
The status indicator uses overlay icons to indicate the current status of your
The status indicator uses icons to indicate the current status of your
synchronization. The green circle with the white checkmark tells you that your
synchronization is current and you are connected to your ownCloud server.
@@ -24,7 +24,7 @@ The blue icon with the white semi-circles means synchronization is in progress.
:alt: Status icon, little cloud with yellow circle and vertical parallel
lines
The yellow overlay icon with the parallel lines tells you your synchronization
The yellow icon with the parallel lines tells you your synchronization
has been paused. (Most likely by you.)
.. figure:: images/icon-offline.png
@@ -133,6 +133,39 @@ appear as a new tab in the settings dialog, where you can adjust its settings
at
any time. Use **Account** > **Remove** to delete accounts.
File Manager Overlay Icons
--------------------------
The ownCloud sync client provides overlay icons, in addition to the normal file
type icons, for your system file manager (Explorer on Windows, Finder on Mac and
Nautilus on Linux) to indicate the sync status of your ownCloud files.
The overlay icons are similar to the systray icons introduced above. They
behave differently on files and directories according to sync status
and errors.
The overlay icon of an individual file indicates its current sync state. If the
file is in sync with the server version, it displays a green checkmark.
If the file is ignored from syncing, for example because it is on your
exclude list, or because it is a symbolic link, it displays a warning icon.
If there is a sync error, or the file is blacklisted, it displays an
eye-catching red X.
If the file is waiting to be synced, or is currently syncing, the overlay
icon displays a blue cycling icon.
When the client is offline, no icons are shown to reflect that the
folder is currently out of sync and no changes are synced to the server.
The overlay icon of a synced directory indicates the status of the files in the
directory. If there are any sync errors, the directory is marked with a warning
icon.
If a directory includes ignored files that are marked with warning icons
that does not change the status of the parent directories.
Sharing From Your Desktop
-------------------------
@@ -174,6 +207,30 @@ such as files not synced.
.. figure:: images/client-8.png
:alt: Activity windows logs all server and client activities.
Server Notifications
--------------------
Starting with version 2.2.0 the client will display notifications which origin
from the server. Notifications require a kind of manual interaction from the
user and they are triggered by certain events happening on the server. One
example is that somebody created a share from a remote ownCloud for the user
which has to approved. The notification will be something like "Do you want
to accept the share from user Joe on host owncloud.joesdomain.com", and the
user of the client can either click an Accept- or Decline-button.
The desktop client checks for avialable notifications automatically on a regular
base.
If a notification is due for the user, it is displayed integrated in the
activity tab, and the user receives a tray notification to draw attention.
.. figure:: images/client12.png
:alt: Activity window with notification.
There is also the Announcement App that allows administrators to create
notifications for users. This is a convenient way to send information to
all users.
General Window
--------------
+1 -1
Ver Arquivo
@@ -49,7 +49,7 @@ Other command line switches supported by ``owncloudcmd`` include the following:
Exclude list file
``--unsyncedfolders [file]``
File containing the list of unsynced folders (selective sync)
File containing the list of unsynced remote folders (selective sync)
``--max-sync-retries [n]``
Retries maximum n times (defaults to 3)
+1 -1
Ver Arquivo
@@ -78,7 +78,7 @@ Other issues can affect synchronization of your ownCloud files:
- If you are operating your own server, and use the local storage backend (the
default), make sure that ownCloud has exclusive access to the directory.
.. note:: The data directory on the server is exclusive to ownCloud and must not be modified manually.
.. warning:: The data directory on the server is exclusive to ownCloud and must not be modified manually.
- If you are using a different file backend on the server, you can try to exclude a bug in the
backend by reverting to the built-in backend.
+11
Ver Arquivo
@@ -8,3 +8,14 @@ GenericName=Folder Sync
Icon=@APPLICATION_EXECUTABLE@
Keywords=@APPLICATION_NAME@;syncing;file;sharing;
X-GNOME-Autostart-Delay=3
# Translations
Comment[sq]=Klient njëkohësimesh @APPLICATION_NAME@ për desktop
GenericName[sq]=Njëkohësim Dosjesh
Name[sq]=Klient njëkohësimesh @APPLICATION_NAME@ për desktop
Icon[sq]=@APPLICATION_EXECUTABLE@
Comment[nl]=@APPLICATION_NAME@ desktop synchronisatie client
GenericName[nl]=Mappen sync
Name[nl]=@APPLICATION_NAME@ desktop sync client
Icon[nl]=@APPLICATION_EXECUTABLE@
Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 900 B

+11 -2
Ver Arquivo
@@ -7,6 +7,9 @@ add_custom_target( legacy_mac_overlayplugin ALL
OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX=${SOCKETAPI_TEAM_IDENTIFIER_PREFIX}
COMMENT building Legacy Mac Overlay icons)
# Contrary to popular belief, this is called like this no matter what theme/OEM.
set(OC_OEM_SHARE_ICNS "${CMAKE_BINARY_DIR}/src/gui/ownCloud.icns")
# The bundle identifier and application group need to have compatible values with the client
# to be able to open a Mach port across the extension's sandbox boundary.
# Pass the info through the xcodebuild command line and make sure that the project uses
@@ -14,15 +17,21 @@ add_custom_target( legacy_mac_overlayplugin ALL
add_custom_target( mac_overlayplugin ALL
xcodebuild -project ${CMAKE_SOURCE_DIR}/shell_integration/MacOSX/OwnCloudFinderSync/OwnCloudFinderSync.xcodeproj
-target FinderSyncExt -configuration Release SYMROOT=${CMAKE_CURRENT_BINARY_DIR}
OC_APPLICATION_NAME=${APPLICATION_NAME}
OC_OEM_SHARE_ICNS=${OC_OEM_SHARE_ICNS}
OC_APPLICATION_NAME="${APPLICATION_NAME}"
OC_APPLICATION_REV_DOMAIN=${APPLICATION_REV_DOMAIN}
OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX=${SOCKETAPI_TEAM_IDENTIFIER_PREFIX}
COMMENT building Mac Overlay icons)
add_dependencies(mac_overlayplugin ${APPLICATION_EXECUTABLE}) # for the ownCloud.icns to be generated
# legacy
INSTALL( DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/Release/SyncStateFinder.osax/Contents
DESTINATION ${CMAKE_INSTALL_PREFIX}/Library/ScriptingAdditions/SyncStateFinder.osax/ )
# >= 10.10.x
INSTALL(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/Release/FinderSyncExt.appex
DESTINATION ${OWNCLOUD_OSX_BUNDLE}/Contents/Plugins
DESTINATION ${OWNCLOUD_OSX_BUNDLE}/Contents/PlugIns
USE_SOURCE_PERMISSIONS)
endif(APPLE)
@@ -103,7 +103,8 @@
if (_shareMenuTitle && !onlyRootsSelected) {
NSMenu *menu = [[NSMenu alloc] initWithTitle:@""];
[menu addItemWithTitle:_shareMenuTitle action:@selector(shareMenuAction:) keyEquivalent:@"title"];
NSMenuItem *item = [menu addItemWithTitle:_shareMenuTitle action:@selector(shareMenuAction:) keyEquivalent:@"title"];
item.image = [[NSBundle mainBundle] imageForResource:@"app.icns"];
return menu;
}
@@ -178,6 +178,7 @@
C2B573D31B1CD9CE00303B36 /* Sources */,
C2B573D41B1CD9CE00303B36 /* Frameworks */,
C2B573D51B1CD9CE00303B36 /* Resources */,
5B3335471CA058E200E11A45 /* ShellScript */,
);
buildRules = (
);
@@ -253,6 +254,23 @@
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
5B3335471CA058E200E11A45 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "if [[ ${OC_OEM_SHARE_ICNS} ]]; then\n cp ${OC_OEM_SHARE_ICNS} ${BUILT_PRODUCTS_DIR}/FinderSyncExt.appex/Contents/Resources/app.icns\nfi";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
C2B573AD1B1CD91E00303B36 /* Sources */ = {
isa = PBXSourcesBuildPhase;
@@ -429,6 +447,7 @@
MTL_ENABLE_DEBUG_INFO = YES;
OC_APPLICATION_NAME = ownCloud;
OC_APPLICATION_REV_DOMAIN = com.owncloud.desktopclient;
OC_OEM_SHARE_ICNS = "";
OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX = "";
ONLY_ACTIVE_ARCH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -476,6 +495,7 @@
MTL_ENABLE_DEBUG_INFO = NO;
OC_APPLICATION_NAME = ownCloud;
OC_APPLICATION_REV_DOMAIN = com.owncloud.desktopclient;
OC_OEM_SHARE_ICNS = "";
OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX = "";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
+17 -10
Ver Arquivo
@@ -28,19 +28,26 @@ include(GenerateExportHeader)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
add_definitions(-DQT_USE_FAST_CONCATENATION -DQT_USE_FAST_OPERATOR_PLUS)
if (NOT DEFINED APPLICATION_EXECUTABLE )
set ( APPLICATION_EXECUTABLE "owncloud" )
endif()
#---HELPER---
add_library(ownclouddolphinpluginhelper SHARED ownclouddolphinpluginhelper.cpp)
target_link_libraries(ownclouddolphinpluginhelper Qt5::Network)
generate_export_header(ownclouddolphinpluginhelper)
install(TARGETS ownclouddolphinpluginhelper LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
set(OWNCLOUDDOLPHINHELPER ${APPLICATION_EXECUTABLE}dolphinpluginhelper)
add_library(${OWNCLOUDDOLPHINHELPER} SHARED ownclouddolphinpluginhelper.cpp)
target_link_libraries(${OWNCLOUDDOLPHINHELPER} Qt5::Network)
generate_export_header(${OWNCLOUDDOLPHINHELPER} BASE_NAME ownclouddolphinpluginhelper)
install(TARGETS ${OWNCLOUDDOLPHINHELPER} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
#---OVERLAY PLUGIN---
kcoreaddons_add_plugin(ownclouddolphinoverlayplugin INSTALL_NAMESPACE "kf5/overlayicon"
set(OWNCLOUDDOLPHINOVERLAYPLUGIN ${APPLICATION_EXECUTABLE}dolphinoverlayplugin)
kcoreaddons_add_plugin(${OWNCLOUDDOLPHINOVERLAYPLUGIN} INSTALL_NAMESPACE "kf5/overlayicon"
JSON ownclouddolphinoverlayplugin.json SOURCES ownclouddolphinoverlayplugin.cpp)
target_link_libraries(ownclouddolphinoverlayplugin KF5::CoreAddons KF5::KIOCore KF5::KIOWidgets ownclouddolphinpluginhelper)
target_link_libraries(${OWNCLOUDDOLPHINOVERLAYPLUGIN} KF5::CoreAddons KF5::KIOCore KF5::KIOWidgets ${OWNCLOUDDOLPHINHELPER})
#---ACTION PLUGIN---
add_library(ownclouddolphinactionplugin MODULE ownclouddolphinactionplugin.cpp)
target_link_libraries(ownclouddolphinactionplugin KF5::CoreAddons KF5::KIOCore KF5::KIOWidgets ownclouddolphinpluginhelper)
install(FILES ownclouddolphinactionplugin.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR})
install(TARGETS ownclouddolphinactionplugin DESTINATION ${KDE_INSTALL_PLUGINDIR})
set(OWNCLOUDDOLPHINACTIONPLUGIN ${APPLICATION_EXECUTABLE}dolphinactionplugin)
add_library(${OWNCLOUDDOLPHINACTIONPLUGIN} MODULE ownclouddolphinactionplugin.cpp)
target_link_libraries(${OWNCLOUDDOLPHINACTIONPLUGIN} KF5::CoreAddons KF5::KIOCore KF5::KIOWidgets ${OWNCLOUDDOLPHINHELPER})
install(FILES ownclouddolphinactionplugin.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR} RENAME ${OWNCLOUDDOLPHINACTIONPLUGIN}.desktop)
install(TARGETS ${OWNCLOUDDOLPHINACTIONPLUGIN} DESTINATION ${KDE_INSTALL_PLUGINDIR})
Arquivo executável → Arquivo normal
+78 -38
Ver Arquivo
@@ -1,4 +1,3 @@
#!/usr/bin/python3
#
# Copyright (C) by Klaas Freitag <freitag@owncloud.com>
#
@@ -22,11 +21,15 @@ import socket
from gi.repository import GObject, Nautilus
print("Initializing owncloud-client-nautilus extension")
# Do not touch the following line.
# Please do not touch the following line.
# The reason is that we use a script to adopt this file for branding
# by replacing this line with the branding app name. If the following
# line is changed, the script can not match the pattern and fails.
appname = 'ownCloud'
print("Initializing "+appname+"-client-nautilus extension")
def get_local_path(url):
if url[0:7] == 'file://':
url = url[7:]
@@ -67,6 +70,7 @@ class SocketConnect(GObject.GObject):
GObject.timeout_add(5000, self._connectToSocketServer)
def sendCommand(self, cmd):
# print("Server command: " + cmd)
if self.connected:
try:
self._sock.send(cmd)
@@ -181,10 +185,10 @@ class MenuExtension(GObject.GObject, Nautilus.MenuProvider):
if not syncedFile:
return items
# Create an menu item
# Create a menu item
labelStr = "Share with " + appname + "..."
item = Nautilus.MenuItem(name='NautilusPython::ShareItem', label=labelStr,
tip='Share file %s through ownCloud' % file.get_name())
tip='Share file {} through {}'.format(file.get_name(), appname) )
item.connect("activate", self.menu_share, file)
items.append(item)
@@ -226,41 +230,43 @@ class SyncStateExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.Info
for p in socketConnect.nautilusVFSFile_table:
if p == path or p.startswith(path):
item = socketConnect.nautilusVFSFile_table[p]['item']
update_items.append(item)
update_items.append(p)
for item in update_items:
item.invalidate_extension_info()
for path1 in update_items:
socketConnect.nautilusVFSFile_table[path1]['item'].invalidate_extension_info()
# Handles a single line of server response and sets the emblem
def handle_commands(self, action, args):
Emblems = { 'OK' : appname +'_ok',
'SYNC' : appname +'_sync',
'NEW' : appname +'_sync',
'IGNORE' : appname +'_warn',
'ERROR' : appname +'_error',
'OK+SWM' : appname +'_ok_shared',
'SYNC+SWM' : appname +'_sync_shared',
'NEW+SWM' : appname +'_sync_shared',
'IGNORE+SWM': appname +'_warn_shared',
'ERROR+SWM' : appname +'_error_shared',
'NOP' : appname +'_error'
}
# file = args[0] # For debug only
# print("Action for " + file + ": " + args[0]) # For debug only
if action == 'STATUS':
newState = args[0]
emblem = Emblems[newState]
filename = ':'.join(args[1:])
if emblem:
itemStore = self.find_item_for_file(filename)
if itemStore:
if( not itemStore['state'] or newState != itemStore['state'] ):
item = itemStore['item']
item.add_emblem(emblem)
# print("Setting emblem on " + filename + "<>" + emblem + "<>") # For debug only
socketConnect.nautilusVFSFile_table[filename] = {'item': item, 'state':newState}
itemStore = self.find_item_for_file(filename)
if itemStore:
if( not itemStore['state'] or newState != itemStore['state'] ):
item = itemStore['item']
# print("Setting emblem on " + filename + "<>" + emblem + "<>") # For debug only
# If an emblem is already set for this item, we need to
# clear the existing extension info before setting a new one.
#
# That will also trigger a new call to
# update_file_info for this item! That's why we set
# skipNextUpdate to True: we don't want to pull the
# current data from the client after getting a push
# notification.
invalidate = itemStore['state'] != None
if invalidate:
item.invalidate_extension_info()
self.set_emblem(item, newState)
socketConnect.nautilusVFSFile_table[filename] = {
'item': item,
'state': newState,
'skipNextUpdate': invalidate }
elif action == 'UPDATE_VIEW':
# Search all items underneath this path and invalidate them
@@ -272,6 +278,25 @@ class SyncStateExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.Info
elif action == 'UNREGISTER_PATH':
self.invalidate_items_underneath(args[0])
def set_emblem(self, item, state):
Emblems = { 'OK' : appname +'_ok',
'SYNC' : appname +'_sync',
'NEW' : appname +'_sync',
'IGNORE' : appname +'_warn',
'ERROR' : appname +'_error',
'OK+SWM' : appname +'_ok_shared',
'SYNC+SWM' : appname +'_sync_shared',
'NEW+SWM' : appname +'_sync_shared',
'IGNORE+SWM': appname +'_warn_shared',
'ERROR+SWM' : appname +'_error_shared',
'NOP' : ''
}
emblem = 'NOP' # Show nothing if no emblem is defined.
if state in Emblems:
emblem = Emblems[state]
item.add_emblem(emblem)
def update_file_info(self, item):
if item.get_uri_scheme() != 'file':
return
@@ -280,13 +305,28 @@ class SyncStateExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.Info
if item.is_directory():
filename += '/'
inScope = False
for reg_path in socketConnect.registered_paths:
if filename.startswith(reg_path):
socketConnect.nautilusVFSFile_table[filename] = {'item': item, 'state':''}
# item.add_string_attribute('share_state', "share state") # ?
self.askForOverlay(filename)
inScope = True
break
else:
# print("Not in scope:" + filename) # For debug only
pass
if not inScope:
return
# Ask for the current state from the client -- unless this update was
# triggered by receiving a STATUS message from the client in the first
# place.
itemStore = self.find_item_for_file(filename)
if itemStore and itemStore['skipNextUpdate'] and itemStore['state']:
itemStore['skipNextUpdate'] = False
itemStore['item'] = item
self.set_emblem(item, itemStore['state'])
else:
socketConnect.nautilusVFSFile_table[filename] = {
'item': item,
'state': None,
'skipNextUpdate': False }
# item.add_string_attribute('share_state', "share state") # ?
self.askForOverlay(filename)
@@ -36,8 +36,7 @@ using namespace std;
OCClientInterface::ContextMenuInfo OCClientInterface::FetchInfo()
{
auto pipename = std::wstring(L"\\\\.\\pipe\\");
pipename += L"ownCloud";
auto pipename = CommunicationSocket::DefaultPipePath();
CommunicationSocket socket;
if (!WaitNamedPipe(pipename.data(), PIPE_TIMEOUT)) {
@@ -72,8 +71,7 @@ OCClientInterface::ContextMenuInfo OCClientInterface::FetchInfo()
void OCClientInterface::ShareObject(const std::wstring &path)
{
auto pipename = std::wstring(L"\\\\.\\pipe\\");
pipename += L"ownCloud";
auto pipename = CommunicationSocket::DefaultPipePath();
CommunicationSocket socket;
if (!WaitNamedPipe(pipename.data(), PIPE_TIMEOUT)) {
+31 -25
Ver Arquivo
@@ -108,31 +108,6 @@ HRESULT RegisterCLSID(LPCOLESTR guidStr, PCWSTR overlayStr, PCWSTR szModule)
return hResult;
}
HRESULT _stdcall DllRegisterServer(void)
{
HRESULT hResult = S_OK;
wchar_t szModule[MAX_PATH];
if (GetModuleFileName(instanceHandle, szModule, ARRAYSIZE(szModule)) == 0) {
hResult = HRESULT_FROM_WIN32(GetLastError());
return hResult;
}
hResult = RegisterCLSID(OVERLAY_GUID_ERROR, OVERLAY_NAME_ERROR, szModule);
if (!SUCCEEDED(hResult)) { return hResult; }
hResult = RegisterCLSID(OVERLAY_GUID_OK, OVERLAY_NAME_OK, szModule);
if (!SUCCEEDED(hResult)) { return hResult; }
hResult = RegisterCLSID(OVERLAY_GUID_OK_SHARED, OVERLAY_NAME_OK_SHARED, szModule);
if (!SUCCEEDED(hResult)) { return hResult; }
hResult = RegisterCLSID(OVERLAY_GUID_SYNC, OVERLAY_NAME_SYNC, szModule);
if (!SUCCEEDED(hResult)) { return hResult; }
hResult = RegisterCLSID(OVERLAY_GUID_WARNING, OVERLAY_NAME_WARNING, szModule);
return hResult;
}
HRESULT UnregisterCLSID(LPCOLESTR guidStr, PCWSTR overlayStr)
{
HRESULT hResult = S_OK;
@@ -155,6 +130,37 @@ HRESULT UnregisterCLSID(LPCOLESTR guidStr, PCWSTR overlayStr)
return hResult;
}
HRESULT _stdcall DllRegisterServer(void)
{
HRESULT hResult = S_OK;
wchar_t szModule[MAX_PATH];
if (GetModuleFileName(instanceHandle, szModule, ARRAYSIZE(szModule)) == 0) {
hResult = HRESULT_FROM_WIN32(GetLastError());
return hResult;
}
// Unregister any obsolete CLSID when we register here
// Those CLSID were removed in 2.1, but we need to make sure to prevent any previous version
// of the extension on the system from loading at the same time as a new version to avoid crashing explorer.
UnregisterCLSID(OVERLAY_GUID_ERROR_SHARED, OVERLAY_NAME_ERROR_SHARED);
UnregisterCLSID(OVERLAY_GUID_SYNC_SHARED, OVERLAY_NAME_SYNC_SHARED);
UnregisterCLSID(OVERLAY_GUID_WARNING_SHARED, OVERLAY_NAME_WARNING_SHARED);
hResult = RegisterCLSID(OVERLAY_GUID_ERROR, OVERLAY_NAME_ERROR, szModule);
if (!SUCCEEDED(hResult)) { return hResult; }
hResult = RegisterCLSID(OVERLAY_GUID_OK, OVERLAY_NAME_OK, szModule);
if (!SUCCEEDED(hResult)) { return hResult; }
hResult = RegisterCLSID(OVERLAY_GUID_OK_SHARED, OVERLAY_NAME_OK_SHARED, szModule);
if (!SUCCEEDED(hResult)) { return hResult; }
hResult = RegisterCLSID(OVERLAY_GUID_SYNC, OVERLAY_NAME_SYNC, szModule);
if (!SUCCEEDED(hResult)) { return hResult; }
hResult = RegisterCLSID(OVERLAY_GUID_WARNING, OVERLAY_NAME_WARNING, szModule);
return hResult;
}
STDAPI DllUnregisterServer(void)
{
HRESULT hResult = S_OK;
@@ -14,19 +14,25 @@
#define OVERLAY_GUID_ERROR L"{0960F090-F328-48A3-B746-276B1E3C3722}"
#define OVERLAY_GUID_ERROR_SHARED L"{0960F091-F328-48A3-B746-276B1E3C3722}"
#define OVERLAY_GUID_OK L"{0960F092-F328-48A3-B746-276B1E3C3722}"
#define OVERLAY_GUID_OK_SHARED L"{0960F093-F328-48A3-B746-276B1E3C3722}"
#define OVERLAY_GUID_SYNC L"{0960F094-F328-48A3-B746-276B1E3C3722}"
#define OVERLAY_GUID_SYNC_SHARED L"{0960F095-F328-48A3-B746-276B1E3C3722}"
#define OVERLAY_GUID_WARNING L"{0960F096-F328-48A3-B746-276B1E3C3722}"
#define OVERLAY_GUID_WARNING_SHARED L"{0960F097-F328-48A3-B746-276B1E3C3722}"
#define OVERLAY_GENERIC_NAME L"OC Overlay Handler"
// two spaces to put us ahead of the competition :/
#define OVERLAY_NAME_ERROR L" OCError"
#define OVERLAY_NAME_ERROR_SHARED L" OCErrorShared"
#define OVERLAY_NAME_OK L" OCOK"
#define OVERLAY_NAME_OK_SHARED L" OCOKShared"
#define OVERLAY_NAME_SYNC L" OCSync"
#define OVERLAY_NAME_SYNC_SHARED L" OCSyncShared"
#define OVERLAY_NAME_WARNING L" OCWarning"
#define OVERLAY_NAME_WARNING_SHARED L" OCWarningShared"
#define REGISTRY_OVERLAY_KEY L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ShellIconOverlayIdentifiers"
#define REGISTRY_CLSID L"CLSID"
@@ -24,11 +24,31 @@
#include <fstream>
#define BUFSIZE 1024
#define DEFAULT_BUFLEN 4096
using namespace std;
#define DEFAULT_BUFLEN 4096
namespace {
std::wstring getUserName() {
DWORD len = DEFAULT_BUFLEN;
TCHAR buf[DEFAULT_BUFLEN];
if (GetUserName(buf, &len)) {
return std::wstring(&buf[0], len);
} else {
return std::wstring();
}
}
}
std::wstring CommunicationSocket::DefaultPipePath()
{
auto pipename = std::wstring(L"\\\\.\\pipe\\");
pipename += L"ownCloud\\";
pipename += getUserName();
return pipename;
}
CommunicationSocket::CommunicationSocket()
: _pipe(INVALID_HANDLE_VALUE)
@@ -26,6 +26,8 @@
class __declspec(dllexport) CommunicationSocket
{
public:
static std::wstring DefaultPipePath();
CommunicationSocket();
~CommunicationSocket();
@@ -43,4 +45,4 @@ private:
bool _connected;
};
#endif
#endif
@@ -31,13 +31,10 @@
using namespace std;
// This code is run in a thread
void RemotePathChecker::workerThreadLoop()
{
auto pipename = std::wstring(L"\\\\.\\pipe\\");
pipename += L"ownCloud";
auto pipename = CommunicationSocket::DefaultPipePath();
bool connected = false;
CommunicationSocket socket;
std::unordered_set<std::wstring> asked;
@@ -241,4 +238,4 @@ RemotePathChecker::FileState RemotePathChecker::_StrToFileState(const std::wstri
}
return StateNone;
}
}
-353
Ver Arquivo
@@ -1,353 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "fancylineedit.h"
#include <QEvent>
#include <QDebug>
#include <QString>
#include <QPropertyAnimation>
#include <QApplication>
#include <QMenu>
#include <QMouseEvent>
#include <QLabel>
#include <QAbstractButton>
#include <QPainter>
#include <QStyle>
#include <QPaintEvent>
#include <QDesktopWidget>
/*! Opens a menu at the specified widget position.
* This functions computes the position where to show the menu, and opens it with
* QMenu::exec().
* \param menu The menu to open
* \param widget The widget next to which to open the menu
*/
static void execMenuAtWidget(QMenu *menu, QWidget *widget)
{
QPoint p;
QRect screen = qApp->desktop()->availableGeometry(widget);
QSize sh = menu->sizeHint();
QRect rect = widget->rect();
if (widget->isRightToLeft()) {
if (widget->mapToGlobal(QPoint(0, rect.bottom())).y() + sh.height() <= screen.height())
p = widget->mapToGlobal(rect.bottomRight());
else
p = widget->mapToGlobal(rect.topRight() - QPoint(0, sh.height()));
p.rx() -= sh.width();
} else {
if (widget->mapToGlobal(QPoint(0, rect.bottom())).y() + sh.height() <= screen.height())
p = widget->mapToGlobal(rect.bottomLeft());
else
p = widget->mapToGlobal(rect.topLeft() - QPoint(0, sh.height()));
}
p.rx() = qMax(screen.left(), qMin(p.x(), screen.right() - sh.width()));
p.ry() += 1;
menu->exec(p);
}
/*!
\class Utils::FancyLineEdit
\brief A line edit with an embedded pixmap on one side that is connected to
a menu.
Additionally, it can display a grayed hintText (like "Type Here to")
when not focused and empty. When connecting to the changed signals and
querying text, one has to be aware that the text is set to that hint
text if isShowingHintText() returns true (that is, does not contain
valid user input).
*/
enum { margin = 6 };
#define ICONBUTTON_HEIGHT 18
#define FADE_TIME 160
namespace Utils {
// --------- FancyLineEditPrivate
class FancyLineEditPrivate : public QObject
{
public:
explicit FancyLineEditPrivate(FancyLineEdit *parent);
virtual bool eventFilter(QObject *obj, QEvent *event) Q_DECL_OVERRIDE;
FancyLineEdit *m_lineEdit;
QPixmap m_pixmap[2];
QMenu *m_menu[2];
bool m_menuTabFocusTrigger[2];
IconButton *m_iconbutton[2];
bool m_iconEnabled[2];
};
FancyLineEditPrivate::FancyLineEditPrivate(FancyLineEdit *parent) :
QObject(parent), m_lineEdit(parent)
{
for (int i = 0; i < 2; ++i) {
m_menu[i] = 0;
m_menuTabFocusTrigger[i] = false;
m_iconbutton[i] = new IconButton(parent);
m_iconbutton[i]->installEventFilter(this);
m_iconbutton[i]->hide();
m_iconbutton[i]->setAutoHide(false);
m_iconEnabled[i] = false;
}
}
bool FancyLineEditPrivate::eventFilter(QObject *obj, QEvent *event)
{
int buttonIndex = -1;
for (int i = 0; i < 2; ++i) {
if (obj == m_iconbutton[i]) {
buttonIndex = i;
break;
}
}
if (buttonIndex == -1)
return QObject::eventFilter(obj, event);
switch (event->type()) {
case QEvent::FocusIn:
if (m_menuTabFocusTrigger[buttonIndex] && m_menu[buttonIndex]) {
m_lineEdit->setFocus();
execMenuAtWidget(m_menu[buttonIndex], m_iconbutton[buttonIndex]);
return true;
}
default:
break;
}
return QObject::eventFilter(obj, event);
}
// --------- FancyLineEdit
FancyLineEdit::FancyLineEdit(QWidget *parent) :
QLineEdit(parent),
d(new FancyLineEditPrivate(this))
{
ensurePolished();
updateMargins();
connect(this, SIGNAL(textChanged(QString)), this, SLOT(checkButtons(QString)));
connect(d->m_iconbutton[Left], SIGNAL(clicked()), this, SLOT(iconClicked()));
connect(d->m_iconbutton[Right], SIGNAL(clicked()), this, SLOT(iconClicked()));
}
void FancyLineEdit::checkButtons(const QString &text)
{
if (m_oldText.isEmpty() || text.isEmpty()) {
for (int i = 0; i < 2; ++i) {
if (d->m_iconbutton[i]->hasAutoHide())
d->m_iconbutton[i]->animateShow(!text.isEmpty());
}
m_oldText = text;
}
}
FancyLineEdit::~FancyLineEdit()
{
}
void FancyLineEdit::setButtonVisible(Side side, bool visible)
{
d->m_iconbutton[side]->setVisible(visible);
d->m_iconEnabled[side] = visible;
updateMargins();
}
bool FancyLineEdit::isButtonVisible(Side side) const
{
return d->m_iconEnabled[side];
}
void FancyLineEdit::iconClicked()
{
IconButton *button = qobject_cast<IconButton *>(sender());
int index = -1;
for (int i = 0; i < 2; ++i)
if (d->m_iconbutton[i] == button)
index = i;
if (index == -1)
return;
if (d->m_menu[index]) {
execMenuAtWidget(d->m_menu[index], button);
} else {
emit buttonClicked((Side)index);
if (index == Left)
emit leftButtonClicked();
else if (index == Right)
emit rightButtonClicked();
}
}
void FancyLineEdit::updateMargins()
{
bool leftToRight = (layoutDirection() == Qt::LeftToRight);
Side realLeft = (leftToRight ? Left : Right);
Side realRight = (leftToRight ? Right : Left);
int leftMargin = d->m_iconbutton[realLeft]->pixmap().width() + 8;
int rightMargin = d->m_iconbutton[realRight]->pixmap().width() + 8;
// Note KDE does not reserve space for the highlight color
if (style()->inherits("OxygenStyle")) {
leftMargin = qMax(24, leftMargin);
rightMargin = qMax(24, rightMargin);
}
QMargins margins((d->m_iconEnabled[realLeft] ? leftMargin : 0), 0,
(d->m_iconEnabled[realRight] ? rightMargin : 0), 0);
setTextMargins(margins);
}
void FancyLineEdit::updateButtonPositions()
{
QRect contentRect = rect();
for (int i = 0; i < 2; ++i) {
Side iconpos = (Side)i;
if (layoutDirection() == Qt::RightToLeft)
iconpos = (iconpos == Left ? Right : Left);
if (iconpos == FancyLineEdit::Right) {
const int iconoffset = textMargins().right() + 4;
d->m_iconbutton[i]->setGeometry(contentRect.adjusted(width() - iconoffset, 0, 0, 0));
} else {
const int iconoffset = textMargins().left() + 4;
d->m_iconbutton[i]->setGeometry(contentRect.adjusted(0, 0, -width() + iconoffset, 0));
}
}
}
void FancyLineEdit::resizeEvent(QResizeEvent *)
{
updateButtonPositions();
}
void FancyLineEdit::setButtonPixmap(Side side, const QPixmap &buttonPixmap)
{
d->m_iconbutton[side]->setPixmap(buttonPixmap);
updateMargins();
updateButtonPositions();
update();
}
QPixmap FancyLineEdit::buttonPixmap(Side side) const
{
return d->m_pixmap[side];
}
void FancyLineEdit::setButtonMenu(Side side, QMenu *buttonMenu)
{
d->m_menu[side] = buttonMenu;
d->m_iconbutton[side]->setIconOpacity(1.0);
}
QMenu *FancyLineEdit::buttonMenu(Side side) const
{
return d->m_menu[side];
}
bool FancyLineEdit::hasMenuTabFocusTrigger(Side side) const
{
return d->m_menuTabFocusTrigger[side];
}
void FancyLineEdit::setMenuTabFocusTrigger(Side side, bool v)
{
if (d->m_menuTabFocusTrigger[side] == v)
return;
d->m_menuTabFocusTrigger[side] = v;
d->m_iconbutton[side]->setFocusPolicy(v ? Qt::TabFocus : Qt::NoFocus);
}
bool FancyLineEdit::hasAutoHideButton(Side side) const
{
return d->m_iconbutton[side]->hasAutoHide();
}
void FancyLineEdit::setAutoHideButton(Side side, bool h)
{
d->m_iconbutton[side]->setAutoHide(h);
if (h)
d->m_iconbutton[side]->setIconOpacity(text().isEmpty() ? 0.0 : 1.0);
else
d->m_iconbutton[side]->setIconOpacity(1.0);
}
void FancyLineEdit::setButtonToolTip(Side side, const QString &tip)
{
d->m_iconbutton[side]->setToolTip(tip);
}
void FancyLineEdit::setButtonFocusPolicy(Side side, Qt::FocusPolicy policy)
{
d->m_iconbutton[side]->setFocusPolicy(policy);
}
// IconButton - helper class to represent a clickable icon
IconButton::IconButton(QWidget *parent)
: QAbstractButton(parent), m_iconOpacity(0), m_autoHide(false)
{
setCursor(Qt::ArrowCursor);
setFocusPolicy(Qt::NoFocus);
}
void IconButton::paintEvent(QPaintEvent *)
{
QPainter painter(this);
QRect pixmapRect = QRect(0, 0, m_pixmap.width(), m_pixmap.height());
pixmapRect.moveCenter(rect().center());
if (m_autoHide)
painter.setOpacity(m_iconOpacity);
painter.drawPixmap(pixmapRect, m_pixmap);
}
void IconButton::animateShow(bool visible)
{
if (visible) {
QPropertyAnimation *animation = new QPropertyAnimation(this, "iconOpacity");
animation->setDuration(FADE_TIME);
animation->setEndValue(1.0);
animation->start(QAbstractAnimation::DeleteWhenStopped);
} else {
QPropertyAnimation *animation = new QPropertyAnimation(this, "iconOpacity");
animation->setDuration(FADE_TIME);
animation->setEndValue(0.0);
animation->start(QAbstractAnimation::DeleteWhenStopped);
}
}
} // namespace Utils
-121
Ver Arquivo
@@ -1,121 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#ifndef FANCYLINEEDIT_H
#define FANCYLINEEDIT_H
#include <QLineEdit>
#include <QAbstractButton>
namespace Utils {
class FancyLineEditPrivate;
class IconButton: public QAbstractButton
{
Q_OBJECT
Q_PROPERTY(float iconOpacity READ iconOpacity WRITE setIconOpacity)
Q_PROPERTY(bool autoHide READ hasAutoHide WRITE setAutoHide)
Q_PROPERTY(QPixmap pixmap READ pixmap WRITE setPixmap)
public:
explicit IconButton(QWidget *parent = 0);
void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE;
void setPixmap(const QPixmap &pixmap) { m_pixmap = pixmap; update(); }
QPixmap pixmap() const { return m_pixmap; }
float iconOpacity() { return m_iconOpacity; }
void setIconOpacity(float value) { m_iconOpacity = value; update(); }
void animateShow(bool visible);
void setAutoHide(bool hide) { m_autoHide = hide; }
bool hasAutoHide() const { return m_autoHide; }
private:
float m_iconOpacity;
bool m_autoHide;
QPixmap m_pixmap;
};
class FancyLineEdit : public QLineEdit
{
Q_OBJECT
Q_ENUMS(Side)
public:
enum Side {Left = 0, Right = 1};
explicit FancyLineEdit(QWidget *parent = 0);
~FancyLineEdit();
QPixmap buttonPixmap(Side side) const;
void setButtonPixmap(Side side, const QPixmap &pixmap);
QMenu *buttonMenu(Side side) const;
void setButtonMenu(Side side, QMenu *menu);
void setButtonVisible(Side side, bool visible);
bool isButtonVisible(Side side) const;
void setButtonToolTip(Side side, const QString &);
void setButtonFocusPolicy(Side side, Qt::FocusPolicy policy);
// Set whether tabbing in will trigger the menu.
void setMenuTabFocusTrigger(Side side, bool v);
bool hasMenuTabFocusTrigger(Side side) const;
// Set if icon should be hidden when text is empty
void setAutoHideButton(Side side, bool h);
bool hasAutoHideButton(Side side) const;
#if QT_VERSION <= 0x040700
void setPlaceholderText( const QString& ) { }
#endif
signals:
void buttonClicked(Utils::FancyLineEdit::Side side);
void leftButtonClicked();
void rightButtonClicked();
private slots:
void checkButtons(const QString &);
void iconClicked();
protected:
virtual void resizeEvent(QResizeEvent *e) Q_DECL_OVERRIDE;
private:
void updateMargins();
void updateButtonPositions();
friend class Utils::FancyLineEditPrivate;
FancyLineEditPrivate *d;
QString m_oldText;
};
} // namespace Utils
#endif // FANCYLINEEDIT_H
+264
Ver Arquivo
@@ -0,0 +1,264 @@
/****************************************************************************
**
** Copyright (C) 2014 Daniel Molkentin <daniel@molkentin.de>
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtNetwork module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef TOKENIZER_H
#define TOKENIZER_H
#include <QString>
#include <QByteArray>
#include <QSharedPointer>
QT_BEGIN_NAMESPACE
template <class T, class const_iterator>
struct QTokenizerPrivate {
typedef typename T::value_type char_type;
struct State {
bool inQuote;
bool inEscape;
char_type quoteChar;
State() : inQuote(false), inEscape(false), quoteChar('\0') {}
};
QTokenizerPrivate(const T& _string, const T& _delims) :
string(_string)
, begin(string.begin())
, end(string.end())
, tokenBegin(end)
, tokenEnd(begin)
, delimiters(_delims)
, isDelim(false)
, returnDelimiters(false)
, returnQuotes(false)
{
}
bool isDelimiter(char_type c) const {
return delimiters.contains(c);
}
bool isQuote(char_type c) const {
return quotes.contains(c);
}
// Returns true if a delimiter was not hit
bool nextChar(State* state, char_type c) {
if (state->inQuote) {
if (state->inEscape) {
state->inEscape = false;
} else if (c == '\\') {
state->inEscape = true;
} else if (c == state->quoteChar) {
state->inQuote = false;
}
} else {
if (isDelimiter(c))
return false;
state->inQuote = isQuote(state->quoteChar = c);
}
return true;
}
T string;
// ### copies begin and end for performance, premature optimization?
const_iterator begin;
const_iterator end;
const_iterator tokenBegin;
const_iterator tokenEnd;
T delimiters;
T quotes;
bool isDelim;
bool returnDelimiters;
bool returnQuotes;
};
template <class T, class const_iterator>
class QTokenizer {
public:
typedef typename T::value_type char_type;
/*!
\class QTokenizer
\inmodule QtNetwork
\brief QTokenizer tokenizes Strings on QString, QByteArray,
std::string or std::wstring
Example Usage:
\code
QString str = ...;
QByteArrayTokenizer tokenizer(str, "; ");
tokenizer.setQuoteCharacters("\"'");
tokenizer.setReturnDelimiters(true);
while (tokenizer.hasNext()) {
QByteArray token = tokenizer.next();
bool isDelimiter = tokenizer.isDelimiter();
...
}
\endcode
\param string The string to tokenize
\param delimiters A string containing delimiters
\sa QStringTokenizer, QByteArrayTokenizer, StringTokenizer, WStringTokenizer
*/
QTokenizer(const T& string, const T& delimiters)
: d(new QTokenizerPrivate<T, const_iterator>(string, delimiters))
{ }
/*!
Whether or not to return delimiters as tokens
\see setQuoteCharacters
*/
void setReturnDelimiters(bool enable) { d->returnDelimiters = enable; }
/*!
Sets characters that are considered to start and end quotes.
When between two characters considered a quote, delimiters will
be ignored.
When between quotes, blackslash characters will cause the QTokenizer
to skip the next character.
\param quotes Characters that delimit quotes.
*/
void setQuoteCharacters(const T& quotes) { d->quotes = quotes; }
/*!
Whether or not to return delimiters as tokens
\see setQuoteCharacters
*/
void setReturnQuoteCharacters(bool enable) { d->returnQuotes = enable; }
/*!
Retrieve next token.
Returns true if there are more tokens, false otherwise.
\sa next()
*/
bool hasNext()
{
typename QTokenizerPrivate<T, const_iterator>::State state;
d->isDelim = false;
for (;;) {
d->tokenBegin = d->tokenEnd;
if (d->tokenEnd == d->end)
return false;
d->tokenEnd++;
if (d->nextChar(&state, *d->tokenBegin))
break;
if (d->returnDelimiters) {
d->isDelim = true;
return true;
}
}
while (d->tokenEnd != d->end && d->nextChar(&state, *d->tokenEnd)) {
d->tokenEnd++;
}
return true;
}
/*!
Resets the tokenizer to the starting position.
*/
void reset() {
d->tokenEnd = d->begin;
}
/*!
Returns true if the current token is a delimiter,
if one more more delimiting characters have been set.
*/
bool isDelimiter() const { return d->isDelim; }
/*!
Returns the current token.
Use \c hasNext() to fetch the next token.
*/
T next() const {
int len = d->tokenEnd-d->tokenBegin;
const_iterator tmpStart = d->tokenBegin;
if (!d->returnQuotes && len > 1 && d->isQuote(*d->tokenBegin)) {
tmpStart++;
len -= 2;
}
return T(tmpStart, len);
}
private:
friend class QStringTokenizer;
QSharedPointer<QTokenizerPrivate<T, const_iterator> > d;
};
class QStringTokenizer : public QTokenizer<QString, QString::const_iterator> {
public:
QStringTokenizer(const QString &string, const QString &delim) :
QTokenizer<QString, QString::const_iterator>(string, delim) {}
/**
* @brief Like \see next(), but returns a lightweight string reference
* @return A reference to the token within the string
*/
QStringRef stringRef() {
int begin = d->tokenBegin-d->begin;
int end = d->tokenEnd-d->tokenBegin;
if (!d->returnQuotes && d->isQuote(*d->tokenBegin)) {
begin++;
end -= 2;
}
return QStringRef(&d->string, begin, end);
}
};
typedef QTokenizer<QByteArray, QByteArray::const_iterator> QByteArrayTokenizer;
typedef QTokenizer<std::string, std::string::const_iterator> StringTokenizer;
typedef QTokenizer<std::wstring, std::wstring::const_iterator> WStringTokenizer;
QT_END_NAMESPACE
#endif // TOKENIZER_H
+2
Ver Arquivo
@@ -0,0 +1,2 @@
TEMPLATE = subdirs
SUBDIRS = test
+8
Ver Arquivo
@@ -0,0 +1,8 @@
TEMPLATE = app
QT += testlib
CONFIG += testlib
TARGET = test
INCLUDEPATH += . ..
# Input
SOURCES += tst_qtokenizer.cpp
+139
Ver Arquivo
@@ -0,0 +1,139 @@
#include <QtTest>
#include "qtokenizer.h"
namespace {
const QString simple = QLatin1String("A simple tokenizer test");
const QString quoted = QLatin1String("\"Wait for me!\" he shouted");
}
class TestTokenizer : public QObject
{
Q_OBJECT
private slots:
void tokenizeQStringSimple() {
QStringTokenizer tokenizer(simple, " ");
QCOMPARE(tokenizer.hasNext(), true);
QCOMPARE(tokenizer.next(), QLatin1String("A"));
QCOMPARE(tokenizer.hasNext(), true);
QCOMPARE(tokenizer.next(), QLatin1String("simple"));
QCOMPARE(tokenizer.hasNext(), true);
QCOMPARE(tokenizer.next(), QLatin1String("tokenizer"));
QCOMPARE(tokenizer.hasNext(), true);
QCOMPARE(tokenizer.next(), QLatin1String("test"));
QCOMPARE(tokenizer.hasNext(), false);
}
void tokenizeQStringSimpleRef() {
QStringTokenizer tokenizer(simple, " ");
QCOMPARE(tokenizer.hasNext(), true);
QVERIFY(tokenizer.stringRef() == QLatin1String("A"));
QCOMPARE(tokenizer.hasNext(), true);
QVERIFY(tokenizer.stringRef() == QLatin1String("simple"));
QCOMPARE(tokenizer.hasNext(), true);
QVERIFY(tokenizer.stringRef() == QLatin1String("tokenizer"));
QCOMPARE(tokenizer.hasNext(), true);
QVERIFY(tokenizer.stringRef() == QLatin1String("test"));
QCOMPARE(tokenizer.hasNext(), false);
}
void tokenizeQStringQuoted() {
const QString multiquote(QLatin1String("\"'Billy - the Kid' is dead!\""));
QStringTokenizer tokenizer(multiquote, " -");
tokenizer.setQuoteCharacters("\"");
tokenizer.setReturnQuoteCharacters(true);
QCOMPARE(tokenizer.hasNext(), true);
QCOMPARE(tokenizer.next(), QLatin1String("\"'Billy - the Kid' is dead!\""));
QCOMPARE(tokenizer.hasNext(), false);
}
void tokenizeQStringSkipQuotes() {
const QString multiquote(QLatin1String("\"'Billy - the Kid' is dead!\""));
QStringTokenizer tokenizer(multiquote, " ");
tokenizer.setQuoteCharacters("\"");
tokenizer.setReturnQuoteCharacters(false);
QCOMPARE(tokenizer.hasNext(), true);
QCOMPARE(tokenizer.next(), QLatin1String("'Billy - the Kid' is dead!"));
QCOMPARE(tokenizer.stringRef().toString(), QLatin1String("'Billy - the Kid' is dead!"));
QCOMPARE(tokenizer.hasNext(), false);
}
void tokenizeQStringWithDelims() {
const QString delims(QLatin1String("I;Insist,On/a-Delimiter"));
QStringTokenizer tokenizer(delims, ";,/-");
tokenizer.setReturnDelimiters(true);
QCOMPARE(tokenizer.hasNext(), true);
QCOMPARE(tokenizer.isDelimiter(), false);
QCOMPARE(tokenizer.hasNext(), true);
QCOMPARE(tokenizer.isDelimiter(), true);
QCOMPARE(tokenizer.hasNext(), true);
QCOMPARE(tokenizer.isDelimiter(), false);
QCOMPARE(tokenizer.hasNext(), true);
QCOMPARE(tokenizer.isDelimiter(), true);
QCOMPARE(tokenizer.hasNext(), true);
QCOMPARE(tokenizer.isDelimiter(), false);
QCOMPARE(tokenizer.hasNext(), true);
QCOMPARE(tokenizer.isDelimiter(), true);
QCOMPARE(tokenizer.hasNext(), true);
QCOMPARE(tokenizer.isDelimiter(), false);
QCOMPARE(tokenizer.hasNext(), true);
QCOMPARE(tokenizer.isDelimiter(), true);
QCOMPARE(tokenizer.hasNext(), true);
QCOMPARE(tokenizer.isDelimiter(), false);
QCOMPARE(tokenizer.hasNext(), false);
}
void resetTokenizer() {
for (int i = 0; i < 2; i++) {
QStringTokenizer tokenizer(simple, " ");
QCOMPARE(tokenizer.hasNext(), true);
QCOMPARE(tokenizer.next(), QLatin1String("A"));
QCOMPARE(tokenizer.hasNext(), true);
QCOMPARE(tokenizer.next(), QLatin1String("simple"));
QCOMPARE(tokenizer.hasNext(), true);
QCOMPARE(tokenizer.next(), QLatin1String("tokenizer"));
QCOMPARE(tokenizer.hasNext(), true);
QCOMPARE(tokenizer.next(), QLatin1String("test"));
QCOMPARE(tokenizer.hasNext(), false);
tokenizer.reset();
}
}
// ### QByteArray, other types
};
QTEST_APPLESS_MAIN(TestTokenizer)
#include "tst_qtokenizer.moc"
+13879 -8791
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+336 -46
Ver Arquivo
@@ -111,9 +111,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
#define SQLITE_VERSION "3.9.1"
#define SQLITE_VERSION_NUMBER 3009001
#define SQLITE_SOURCE_ID "2015-10-16 17:31:12 767c1727fec4ce11b83f25b3f1bfcfe68a2c8b02"
#define SQLITE_VERSION "3.12.2"
#define SQLITE_VERSION_NUMBER 3012002
#define SQLITE_SOURCE_ID "2016-04-18 17:30:31 92dc59fd5ad66f646666042eb04195e3a61a9e8e"
/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -347,7 +347,7 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**);
** from [sqlite3_malloc()] and passed back through the 5th parameter.
** To avoid memory leaks, the application should invoke [sqlite3_free()]
** on error message strings returned through the 5th parameter of
** of sqlite3_exec() after the error message string is no longer needed.
** sqlite3_exec() after the error message string is no longer needed.
** ^If the 5th parameter to sqlite3_exec() is not NULL and no errors
** occur, then sqlite3_exec() sets the pointer in its 5th parameter to
** NULL before returning.
@@ -478,6 +478,7 @@ SQLITE_API int SQLITE_STDCALL sqlite3_exec(
#define SQLITE_IOERR_GETTEMPPATH (SQLITE_IOERR | (25<<8))
#define SQLITE_IOERR_CONVPATH (SQLITE_IOERR | (26<<8))
#define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27<<8))
#define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8))
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
#define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
@@ -793,8 +794,13 @@ struct sqlite3_io_methods {
** <li>[[SQLITE_FCNTL_FILE_POINTER]]
** The [SQLITE_FCNTL_FILE_POINTER] opcode is used to obtain a pointer
** to the [sqlite3_file] object associated with a particular database
** connection. See the [sqlite3_file_control()] documentation for
** additional information.
** connection. See also [SQLITE_FCNTL_JOURNAL_POINTER].
**
** <li>[[SQLITE_FCNTL_JOURNAL_POINTER]]
** The [SQLITE_FCNTL_JOURNAL_POINTER] opcode is used to obtain a pointer
** to the [sqlite3_file] object associated with the journal file (either
** the [rollback journal] or the [write-ahead log]) for a particular database
** connection. See also [SQLITE_FCNTL_FILE_POINTER].
**
** <li>[[SQLITE_FCNTL_SYNC_OMITTED]]
** No longer in use.
@@ -881,6 +887,15 @@ struct sqlite3_io_methods {
** pointer in case this file-control is not implemented. This file-control
** is intended for diagnostic use only.
**
** <li>[[SQLITE_FCNTL_VFS_POINTER]]
** ^The [SQLITE_FCNTL_VFS_POINTER] opcode finds a pointer to the top-level
** [VFSes] currently in use. ^(The argument X in
** sqlite3_file_control(db,SQLITE_FCNTL_VFS_POINTER,X) must be
** of type "[sqlite3_vfs] **". This opcodes will set *X
** to a pointer to the top-level VFS.)^
** ^When there are multiple VFS shims in the stack, this opcode finds the
** upper-most shim only.
**
** <li>[[SQLITE_FCNTL_PRAGMA]]
** ^Whenever a [PRAGMA] statement is parsed, an [SQLITE_FCNTL_PRAGMA]
** file control is sent to the open [sqlite3_file] object corresponding
@@ -999,6 +1014,8 @@ struct sqlite3_io_methods {
#define SQLITE_FCNTL_WAL_BLOCK 24
#define SQLITE_FCNTL_ZIPVFS 25
#define SQLITE_FCNTL_RBU 26
#define SQLITE_FCNTL_VFS_POINTER 27
#define SQLITE_FCNTL_JOURNAL_POINTER 28
/* deprecated names */
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
@@ -1211,7 +1228,7 @@ struct sqlite3_vfs {
const char *(*xNextSystemCall)(sqlite3_vfs*, const char *zName);
/*
** The methods above are in versions 1 through 3 of the sqlite_vfs object.
** New fields may be appended in figure versions. The iVersion
** New fields may be appended in future versions. The iVersion
** value will increment whenever this happens.
*/
};
@@ -1598,29 +1615,34 @@ struct sqlite3_mem_methods {
** </dd>
**
** [[SQLITE_CONFIG_PAGECACHE]] <dt>SQLITE_CONFIG_PAGECACHE</dt>
** <dd> ^The SQLITE_CONFIG_PAGECACHE option specifies a static memory buffer
** <dd> ^The SQLITE_CONFIG_PAGECACHE option specifies a memory pool
** that SQLite can use for the database page cache with the default page
** cache implementation.
** This configuration should not be used if an application-define page
** cache implementation is loaded using the [SQLITE_CONFIG_PCACHE2]
** configuration option.
** This configuration option is a no-op if an application-define page
** cache implementation is loaded using the [SQLITE_CONFIG_PCACHE2].
** ^There are three arguments to SQLITE_CONFIG_PAGECACHE: A pointer to
** 8-byte aligned
** memory, the size of each page buffer (sz), and the number of pages (N).
** 8-byte aligned memory (pMem), the size of each page cache line (sz),
** and the number of cache lines (N).
** The sz argument should be the size of the largest database page
** (a power of two between 512 and 65536) plus some extra bytes for each
** page header. ^The number of extra bytes needed by the page header
** can be determined using the [SQLITE_CONFIG_PCACHE_HDRSZ] option
** to [sqlite3_config()].
** can be determined using [SQLITE_CONFIG_PCACHE_HDRSZ].
** ^It is harmless, apart from the wasted memory,
** for the sz parameter to be larger than necessary. The first
** argument should pointer to an 8-byte aligned block of memory that
** is at least sz*N bytes of memory, otherwise subsequent behavior is
** undefined.
** ^SQLite will use the memory provided by the first argument to satisfy its
** memory needs for the first N pages that it adds to cache. ^If additional
** page cache memory is needed beyond what is provided by this option, then
** SQLite goes to [sqlite3_malloc()] for the additional storage space.</dd>
** for the sz parameter to be larger than necessary. The pMem
** argument must be either a NULL pointer or a pointer to an 8-byte
** aligned block of memory of at least sz*N bytes, otherwise
** subsequent behavior is undefined.
** ^When pMem is not NULL, SQLite will strive to use the memory provided
** to satisfy page cache needs, falling back to [sqlite3_malloc()] if
** a page cache line is larger than sz bytes or if all of the pMem buffer
** is exhausted.
** ^If pMem is NULL and N is non-zero, then each database connection
** does an initial bulk allocation for page cache memory
** from [sqlite3_malloc()] sufficient for N cache lines if N is positive or
** of -1024*N bytes if N is negative, . ^If additional
** page cache memory is needed beyond what is provided by the initial
** allocation, then SQLite goes to [sqlite3_malloc()] separately for each
** additional cache line. </dd>
**
** [[SQLITE_CONFIG_HEAP]] <dt>SQLITE_CONFIG_HEAP</dt>
** <dd> ^The SQLITE_CONFIG_HEAP option specifies a static memory buffer
@@ -1798,6 +1820,20 @@ struct sqlite3_mem_methods {
** is enabled (using the [PRAGMA threads] command) and the amount of content
** to be sorted exceeds the page size times the minimum of the
** [PRAGMA cache_size] setting and this value.
**
** [[SQLITE_CONFIG_STMTJRNL_SPILL]]
** <dt>SQLITE_CONFIG_STMTJRNL_SPILL
** <dd>^The SQLITE_CONFIG_STMTJRNL_SPILL option takes a single parameter which
** becomes the [statement journal] spill-to-disk threshold.
** [Statement journals] are held in memory until their size (in bytes)
** exceeds this threshold, at which point they are written to disk.
** Or if the threshold is -1, statement journals are always held
** exclusively in memory.
** Since many statement journals never become large, setting the spill
** threshold to a value such as 64KiB can greatly reduce the amount of
** I/O required to support statement rollback.
** The default value for this setting is controlled by the
** [SQLITE_STMTJRNL_SPILL] compile-time option.
** </dl>
*/
#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
@@ -1825,6 +1861,7 @@ struct sqlite3_mem_methods {
#define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */
#define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */
#define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */
#define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */
/*
** CAPI3REF: Database Connection Configuration Options
@@ -1882,11 +1919,25 @@ struct sqlite3_mem_methods {
** following this call. The second parameter may be a NULL pointer, in
** which case the trigger setting is not reported back. </dd>
**
** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
** <dd> ^This option is used to enable or disable the two-argument
** version of the [fts3_tokenizer()] function which is part of the
** [FTS3] full-text search engine extension.
** There should be two additional arguments.
** The first argument is an integer which is 0 to disable fts3_tokenizer() or
** positive to enable fts3_tokenizer() or negative to leave the setting
** unchanged.
** The second parameter is a pointer to an integer into which
** is written 0 or 1 to indicate whether fts3_tokenizer is disabled or enabled
** following this call. The second parameter may be a NULL pointer, in
** which case the new setting is not reported back. </dd>
**
** </dl>
*/
#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */
#define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */
#define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */
#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */
#define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */
#define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */
#define SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER 1004 /* int int* */
/*
@@ -4389,8 +4440,8 @@ SQLITE_API unsigned int SQLITE_STDCALL sqlite3_value_subtype(sqlite3_value*);
** previously obtained from [sqlite3_value_dup()]. ^If V is a NULL pointer
** then sqlite3_value_free(V) is a harmless no-op.
*/
SQLITE_API SQLITE_EXPERIMENTAL sqlite3_value *SQLITE_STDCALL sqlite3_value_dup(const sqlite3_value*);
SQLITE_API SQLITE_EXPERIMENTAL void SQLITE_STDCALL sqlite3_value_free(sqlite3_value*);
SQLITE_API sqlite3_value *SQLITE_STDCALL sqlite3_value_dup(const sqlite3_value*);
SQLITE_API void SQLITE_STDCALL sqlite3_value_free(sqlite3_value*);
/*
** CAPI3REF: Obtain Aggregate Function Context
@@ -5609,6 +5660,17 @@ struct sqlite3_module {
** ^Information about the ORDER BY clause is stored in aOrderBy[].
** ^Each term of aOrderBy records a column of the ORDER BY clause.
**
** The colUsed field indicates which columns of the virtual table may be
** required by the current scan. Virtual table columns are numbered from
** zero in the order in which they appear within the CREATE TABLE statement
** passed to sqlite3_declare_vtab(). For the first 63 columns (columns 0-62),
** the corresponding bit is set within the colUsed mask if the column may be
** required by SQLite. If the table has at least 64 columns and any column
** to the right of the first 63 is required, then bit 63 of colUsed is also
** set. In other words, column iCol may be required if the expression
** (colUsed & ((sqlite3_uint64)1 << (iCol>=63 ? 63 : iCol))) evaluates to
** non-zero.
**
** The [xBestIndex] method must fill aConstraintUsage[] with information
** about what parameters to pass to xFilter. ^If argvIndex>0 then
** the right-hand side of the corresponding aConstraint[] is evaluated
@@ -5664,7 +5726,7 @@ struct sqlite3_index_info {
/* Inputs */
int nConstraint; /* Number of entries in aConstraint */
struct sqlite3_index_constraint {
int iColumn; /* Column on left-hand side of constraint */
int iColumn; /* Column constrained. -1 for ROWID */
unsigned char op; /* Constraint operator */
unsigned char usable; /* True if this constraint is usable */
int iTermOffset; /* Used internally - xBestIndex should ignore */
@@ -5688,6 +5750,8 @@ struct sqlite3_index_info {
sqlite3_int64 estimatedRows; /* Estimated number of rows returned */
/* Fields below are only available in SQLite 3.9.0 and later */
int idxFlags; /* Mask of SQLITE_INDEX_SCAN_* flags */
/* Fields below are only available in SQLite 3.10.0 and later */
sqlite3_uint64 colUsed; /* Input: Mask of columns used by statement */
};
/*
@@ -5703,12 +5767,15 @@ struct sqlite3_index_info {
** an operator that is part of a constraint term in the wHERE clause of
** a query that uses a [virtual table].
*/
#define SQLITE_INDEX_CONSTRAINT_EQ 2
#define SQLITE_INDEX_CONSTRAINT_GT 4
#define SQLITE_INDEX_CONSTRAINT_LE 8
#define SQLITE_INDEX_CONSTRAINT_LT 16
#define SQLITE_INDEX_CONSTRAINT_GE 32
#define SQLITE_INDEX_CONSTRAINT_MATCH 64
#define SQLITE_INDEX_CONSTRAINT_EQ 2
#define SQLITE_INDEX_CONSTRAINT_GT 4
#define SQLITE_INDEX_CONSTRAINT_LE 8
#define SQLITE_INDEX_CONSTRAINT_LT 16
#define SQLITE_INDEX_CONSTRAINT_GE 32
#define SQLITE_INDEX_CONSTRAINT_MATCH 64
#define SQLITE_INDEX_CONSTRAINT_LIKE 65
#define SQLITE_INDEX_CONSTRAINT_GLOB 66
#define SQLITE_INDEX_CONSTRAINT_REGEXP 67
/*
** CAPI3REF: Register A Virtual Table Implementation
@@ -6572,7 +6639,8 @@ SQLITE_API int SQLITE_STDCALL sqlite3_status64(
** The value written into the *pCurrent parameter is undefined.</dd>)^
**
** [[SQLITE_STATUS_PARSER_STACK]] ^(<dt>SQLITE_STATUS_PARSER_STACK</dt>
** <dd>This parameter records the deepest parser stack. It is only
** <dd>The *pHighwater parameter records the deepest parser stack.
** The *pCurrent value is undefined. The *pHighwater value is only
** meaningful if SQLite is compiled with [YYTRACKMAXSTACKDEPTH].</dd>)^
** </dl>
**
@@ -7358,18 +7426,43 @@ SQLITE_API int SQLITE_STDCALL sqlite3_strnicmp(const char *, const char *, int);
/*
** CAPI3REF: String Globbing
*
** ^The [sqlite3_strglob(P,X)] interface returns zero if string X matches
** the glob pattern P, and it returns non-zero if string X does not match
** the glob pattern P. ^The definition of glob pattern matching used in
** ^The [sqlite3_strglob(P,X)] interface returns zero if and only if
** string X matches the [GLOB] pattern P.
** ^The definition of [GLOB] pattern matching used in
** [sqlite3_strglob(P,X)] is the same as for the "X GLOB P" operator in the
** SQL dialect used by SQLite. ^The sqlite3_strglob(P,X) function is case
** sensitive.
** SQL dialect understood by SQLite. ^The [sqlite3_strglob(P,X)] function
** is case sensitive.
**
** Note that this routine returns zero on a match and non-zero if the strings
** do not match, the same as [sqlite3_stricmp()] and [sqlite3_strnicmp()].
**
** See also: [sqlite3_strlike()].
*/
SQLITE_API int SQLITE_STDCALL sqlite3_strglob(const char *zGlob, const char *zStr);
/*
** CAPI3REF: String LIKE Matching
*
** ^The [sqlite3_strlike(P,X,E)] interface returns zero if and only if
** string X matches the [LIKE] pattern P with escape character E.
** ^The definition of [LIKE] pattern matching used in
** [sqlite3_strlike(P,X,E)] is the same as for the "X LIKE P ESCAPE E"
** operator in the SQL dialect understood by SQLite. ^For "X LIKE P" without
** the ESCAPE clause, set the E parameter of [sqlite3_strlike(P,X,E)] to 0.
** ^As with the LIKE operator, the [sqlite3_strlike(P,X,E)] function is case
** insensitive - equivalent upper and lower case ASCII characters match
** one another.
**
** ^The [sqlite3_strlike(P,X,E)] function matches Unicode characters, though
** only ASCII characters are case folded.
**
** Note that this routine returns zero on a match and non-zero if the strings
** do not match, the same as [sqlite3_stricmp()] and [sqlite3_strnicmp()].
**
** See also: [sqlite3_strglob()].
*/
SQLITE_API int SQLITE_STDCALL sqlite3_strlike(const char *zGlob, const char *zStr, unsigned int cEsc);
/*
** CAPI3REF: Error Logging Interface
**
@@ -7425,7 +7518,7 @@ SQLITE_API void SQLITE_CDECL sqlite3_log(int iErrCode, const char *zFormat, ...)
** previously registered write-ahead log callback. ^Note that the
** [sqlite3_wal_autocheckpoint()] interface and the
** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and will
** those overwrite any prior [sqlite3_wal_hook()] settings.
** overwrite any prior [sqlite3_wal_hook()] settings.
*/
SQLITE_API void *SQLITE_STDCALL sqlite3_wal_hook(
sqlite3*,
@@ -7790,6 +7883,145 @@ SQLITE_API int SQLITE_STDCALL sqlite3_stmt_scanstatus(
*/
SQLITE_API void SQLITE_STDCALL sqlite3_stmt_scanstatus_reset(sqlite3_stmt*);
/*
** CAPI3REF: Flush caches to disk mid-transaction
**
** ^If a write-transaction is open on [database connection] D when the
** [sqlite3_db_cacheflush(D)] interface invoked, any dirty
** pages in the pager-cache that are not currently in use are written out
** to disk. A dirty page may be in use if a database cursor created by an
** active SQL statement is reading from it, or if it is page 1 of a database
** file (page 1 is always "in use"). ^The [sqlite3_db_cacheflush(D)]
** interface flushes caches for all schemas - "main", "temp", and
** any [attached] databases.
**
** ^If this function needs to obtain extra database locks before dirty pages
** can be flushed to disk, it does so. ^If those locks cannot be obtained
** immediately and there is a busy-handler callback configured, it is invoked
** in the usual manner. ^If the required lock still cannot be obtained, then
** the database is skipped and an attempt made to flush any dirty pages
** belonging to the next (if any) database. ^If any databases are skipped
** because locks cannot be obtained, but no other error occurs, this
** function returns SQLITE_BUSY.
**
** ^If any other error occurs while flushing dirty pages to disk (for
** example an IO error or out-of-memory condition), then processing is
** abandoned and an SQLite [error code] is returned to the caller immediately.
**
** ^Otherwise, if no error occurs, [sqlite3_db_cacheflush()] returns SQLITE_OK.
**
** ^This function does not set the database handle error code or message
** returned by the [sqlite3_errcode()] and [sqlite3_errmsg()] functions.
*/
SQLITE_API int SQLITE_STDCALL sqlite3_db_cacheflush(sqlite3*);
/*
** CAPI3REF: Low-level system error code
**
** ^Attempt to return the underlying operating system error code or error
** number that caused the most reason I/O error or failure to open a file.
** The return value is OS-dependent. For example, on unix systems, after
** [sqlite3_open_v2()] returns [SQLITE_CANTOPEN], this interface could be
** called to get back the underlying "errno" that caused the problem, such
** as ENOSPC, EAUTH, EISDIR, and so forth.
*/
SQLITE_API int SQLITE_STDCALL sqlite3_system_errno(sqlite3*);
/*
** CAPI3REF: Database Snapshot
** KEYWORDS: {snapshot}
** EXPERIMENTAL
**
** An instance of the snapshot object records the state of a [WAL mode]
** database for some specific point in history.
**
** In [WAL mode], multiple [database connections] that are open on the
** same database file can each be reading a different historical version
** of the database file. When a [database connection] begins a read
** transaction, that connection sees an unchanging copy of the database
** as it existed for the point in time when the transaction first started.
** Subsequent changes to the database from other connections are not seen
** by the reader until a new read transaction is started.
**
** The sqlite3_snapshot object records state information about an historical
** version of the database file so that it is possible to later open a new read
** transaction that sees that historical version of the database rather than
** the most recent version.
**
** The constructor for this object is [sqlite3_snapshot_get()]. The
** [sqlite3_snapshot_open()] method causes a fresh read transaction to refer
** to an historical snapshot (if possible). The destructor for
** sqlite3_snapshot objects is [sqlite3_snapshot_free()].
*/
typedef struct sqlite3_snapshot sqlite3_snapshot;
/*
** CAPI3REF: Record A Database Snapshot
** EXPERIMENTAL
**
** ^The [sqlite3_snapshot_get(D,S,P)] interface attempts to make a
** new [sqlite3_snapshot] object that records the current state of
** schema S in database connection D. ^On success, the
** [sqlite3_snapshot_get(D,S,P)] interface writes a pointer to the newly
** created [sqlite3_snapshot] object into *P and returns SQLITE_OK.
** ^If schema S of [database connection] D is not a [WAL mode] database
** that is in a read transaction, then [sqlite3_snapshot_get(D,S,P)]
** leaves the *P value unchanged and returns an appropriate [error code].
**
** The [sqlite3_snapshot] object returned from a successful call to
** [sqlite3_snapshot_get()] must be freed using [sqlite3_snapshot_free()]
** to avoid a memory leak.
**
** The [sqlite3_snapshot_get()] interface is only available when the
** SQLITE_ENABLE_SNAPSHOT compile-time option is used.
*/
SQLITE_API SQLITE_EXPERIMENTAL int SQLITE_STDCALL sqlite3_snapshot_get(
sqlite3 *db,
const char *zSchema,
sqlite3_snapshot **ppSnapshot
);
/*
** CAPI3REF: Start a read transaction on an historical snapshot
** EXPERIMENTAL
**
** ^The [sqlite3_snapshot_open(D,S,P)] interface attempts to move the
** read transaction that is currently open on schema S of
** [database connection] D so that it refers to historical [snapshot] P.
** ^The [sqlite3_snapshot_open()] interface returns SQLITE_OK on success
** or an appropriate [error code] if it fails.
**
** ^In order to succeed, a call to [sqlite3_snapshot_open(D,S,P)] must be
** the first operation, apart from other sqlite3_snapshot_open() calls,
** following the [BEGIN] that starts a new read transaction.
** ^A [snapshot] will fail to open if it has been overwritten by a
** [checkpoint].
** ^A [snapshot] will fail to open if the database connection D has not
** previously completed at least one read operation against the database
** file. (Hint: Run "[PRAGMA application_id]" against a newly opened
** database connection in order to make it ready to use snapshots.)
**
** The [sqlite3_snapshot_open()] interface is only available when the
** SQLITE_ENABLE_SNAPSHOT compile-time option is used.
*/
SQLITE_API SQLITE_EXPERIMENTAL int SQLITE_STDCALL sqlite3_snapshot_open(
sqlite3 *db,
const char *zSchema,
sqlite3_snapshot *pSnapshot
);
/*
** CAPI3REF: Destroy a snapshot
** EXPERIMENTAL
**
** ^The [sqlite3_snapshot_free(P)] interface destroys [sqlite3_snapshot] P.
** The application must eventually free every [sqlite3_snapshot] object
** using this routine to avoid a memory leak.
**
** The [sqlite3_snapshot_free()] interface is only available when the
** SQLITE_ENABLE_SNAPSHOT compile-time option is used.
*/
SQLITE_API SQLITE_EXPERIMENTAL void SQLITE_STDCALL sqlite3_snapshot_free(sqlite3_snapshot*);
/*
** Undo the hack that converts floating point types to integer for
@@ -8006,6 +8238,9 @@ struct Fts5PhraseIter {
** an OOM condition or IO error), an appropriate SQLite error code is
** returned.
**
** This function may be quite inefficient if used with an FTS5 table
** created with the "columnsize=0" option.
**
** xColumnText:
** This function attempts to retrieve the text of column iCol of the
** current document. If successful, (*pz) is set to point to a buffer
@@ -8026,15 +8261,29 @@ struct Fts5PhraseIter {
** the query within the current row. Return SQLITE_OK if successful, or
** an error code (i.e. SQLITE_NOMEM) if an error occurs.
**
** This API can be quite slow if used with an FTS5 table created with the
** "detail=none" or "detail=column" option. If the FTS5 table is created
** with either "detail=none" or "detail=column" and "content=" option
** (i.e. if it is a contentless table), then this API always returns 0.
**
** xInst:
** Query for the details of phrase match iIdx within the current row.
** Phrase matches are numbered starting from zero, so the iIdx argument
** should be greater than or equal to zero and smaller than the value
** output by xInstCount().
**
** Usually, output parameter *piPhrase is set to the phrase number, *piCol
** to the column in which it occurs and *piOff the token offset of the
** first token of the phrase. The exception is if the table was created
** with the offsets=0 option specified. In this case *piOff is always
** set to -1.
**
** Returns SQLITE_OK if successful, or an error code (i.e. SQLITE_NOMEM)
** if an error occurs.
**
** This API can be quite slow if used with an FTS5 table created with the
** "detail=none" or "detail=column" option.
**
** xRowid:
** Returns the rowid of the current row.
**
@@ -8118,7 +8367,7 @@ struct Fts5PhraseIter {
** Fts5PhraseIter iter;
** int iCol, iOff;
** for(pApi->xPhraseFirst(pFts, iPhrase, &iter, &iCol, &iOff);
** iOff>=0;
** iCol>=0;
** pApi->xPhraseNext(pFts, &iter, &iCol, &iOff)
** ){
** // An instance of phrase iPhrase at offset iOff of column iCol
@@ -8126,13 +8375,51 @@ struct Fts5PhraseIter {
**
** The Fts5PhraseIter structure is defined above. Applications should not
** modify this structure directly - it should only be used as shown above
** with the xPhraseFirst() and xPhraseNext() API methods.
** with the xPhraseFirst() and xPhraseNext() API methods (and by
** xPhraseFirstColumn() and xPhraseNextColumn() as illustrated below).
**
** This API can be quite slow if used with an FTS5 table created with the
** "detail=none" or "detail=column" option. If the FTS5 table is created
** with either "detail=none" or "detail=column" and "content=" option
** (i.e. if it is a contentless table), then this API always iterates
** through an empty set (all calls to xPhraseFirst() set iCol to -1).
**
** xPhraseNext()
** See xPhraseFirst above.
**
** xPhraseFirstColumn()
** This function and xPhraseNextColumn() are similar to the xPhraseFirst()
** and xPhraseNext() APIs described above. The difference is that instead
** of iterating through all instances of a phrase in the current row, these
** APIs are used to iterate through the set of columns in the current row
** that contain one or more instances of a specified phrase. For example:
**
** Fts5PhraseIter iter;
** int iCol;
** for(pApi->xPhraseFirstColumn(pFts, iPhrase, &iter, &iCol);
** iCol>=0;
** pApi->xPhraseNextColumn(pFts, &iter, &iCol)
** ){
** // Column iCol contains at least one instance of phrase iPhrase
** }
**
** This API can be quite slow if used with an FTS5 table created with the
** "detail=none" option. If the FTS5 table is created with either
** "detail=none" "content=" option (i.e. if it is a contentless table),
** then this API always iterates through an empty set (all calls to
** xPhraseFirstColumn() set iCol to -1).
**
** The information accessed using this API and its companion
** xPhraseFirstColumn() may also be obtained using xPhraseFirst/xPhraseNext
** (or xInst/xInstCount). The chief advantage of this API is that it is
** significantly more efficient than those alternatives when used with
** "detail=column" tables.
**
** xPhraseNextColumn()
** See xPhraseFirstColumn above.
*/
struct Fts5ExtensionApi {
int iVersion; /* Currently always set to 1 */
int iVersion; /* Currently always set to 3 */
void *(*xUserData)(Fts5Context*);
@@ -8162,8 +8449,11 @@ struct Fts5ExtensionApi {
int (*xSetAuxdata)(Fts5Context*, void *pAux, void(*xDelete)(void*));
void *(*xGetAuxdata)(Fts5Context*, int bClear);
void (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*);
int (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*);
void (*xPhraseNext)(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff);
int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*);
void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol);
};
/*
+22
Ver Arquivo
@@ -5,10 +5,32 @@ set(BIN_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
set(synclib_NAME ${APPLICATION_EXECUTABLE}sync)
include(QtVersionAbstraction)
setup_qt()
if(HAVE_QT5 AND NOT BUILD_WITH_QT4)
if (${Qt5Core_VERSION_MAJOR} EQUAL "5")
if (${Qt5Core_VERSION_MINOR} EQUAL "4" OR ${Qt5Core_VERSION_MINOR} GREATER 4)
else()
message(STATUS "If possible compile me with Qt 5.4 or higher.")
endif()
endif()
else()
message(STATUS "If possible compile me with Qt 5.4 or higher.")
endif()
if (APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
endif()
if(NOT TOKEN_AUTH_ONLY)
if (Qt5Core_DIR)
find_package(Qt5Keychain REQUIRED)
else()
find_package(QtKeychain REQUIRED)
endif()
endif()
add_subdirectory(libsync)
if (NOT BUILD_LIBRARIES_ONLY)
add_subdirectory(gui)
+3
Ver Arquivo
@@ -17,6 +17,9 @@ include_directories(${CMAKE_SOURCE_DIR}/csync/src
${CMAKE_BINARY_DIR}/csync/src
)
# Need tokenizer for netrc parser
include_directories(${CMAKE_SOURCE_DIR}/src/3rdparty/qtokenizer)
if(NOT BUILD_LIBRARIES_ONLY)
add_executable(${cmd_NAME} ${cmd_SRC})
qt5_use_modules(${cmd_NAME} Network Sql)
+24 -50
Ver Arquivo
@@ -153,7 +153,7 @@ void help()
std::cout << " Proxy is http://server:port" << std::endl;
std::cout << " --trust Trust the SSL certification." << std::endl;
std::cout << " --exclude [file] Exclude list file" << std::endl;
std::cout << " --unsyncedfolders [file] File containing the list of unsynced folders (selective sync)" << std::endl;
std::cout << " --unsyncedfolders [file] File containing the list of unsynced remote folders (selective sync)" << std::endl;
std::cout << " --user, -u [name] Use [name] as the login name" << std::endl;
std::cout << " --password, -p [pass] Use [pass] as password" << std::endl;
std::cout << " -n Use netrc (5) for login" << std::endl;
@@ -249,23 +249,23 @@ void parseOptions( const QStringList& app_args, CmdOptions *options )
*/
void selectiveSyncFixup(OCC::SyncJournalDb *journal, const QStringList &newList)
{
if (!journal->exists()) {
return;
}
SqlDatabase db;
if (!db.openOrCreateReadWrite(journal->databaseFilePath())) {
return;
}
auto oldBlackListSet = journal->getSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList).toSet();
auto blackListSet = newList.toSet();
auto changes = (oldBlackListSet - blackListSet) + (blackListSet - oldBlackListSet);
foreach(const auto &it, changes) {
journal->avoidReadFromDbOnNextSync(it);
}
bool ok;
journal->setSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, newList);
auto oldBlackListSet = journal->getSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, &ok).toSet();
if( ok ) {
auto blackListSet = newList.toSet();
auto changes = (oldBlackListSet - blackListSet) + (blackListSet - oldBlackListSet);
foreach(const auto &it, changes) {
journal->avoidReadFromDbOnNextSync(it);
}
journal->setSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, newList);
}
}
@@ -385,26 +385,10 @@ int main(int argc, char **argv) {
int restartCount = 0;
restart_sync:
CSYNC *_csync_ctx;
if( csync_create( &_csync_ctx, options.source_dir.toUtf8(),
remUrl.constData()) < 0 ) {
qFatal("Unable to create csync-context!");
return EXIT_FAILURE;
}
csync_set_log_level(options.silent ? 1 : 11);
opts = &options;
if( csync_init( _csync_ctx ) < 0 ) {
qFatal("Could not initialize csync!");
return EXIT_FAILURE;
}
// ignore hidden files or not
_csync_ctx->ignore_hidden_files = options.ignoreHiddenFiles;
if( !options.proxy.isNull() ) {
QString host;
int port = 0;
@@ -431,18 +415,6 @@ restart_sync:
}
}
// Exclude lists
QString systemExcludeListFn = ConfigFile::excludeFileFromSystem();
int loadedSystemExcludeList = false;
if (!systemExcludeListFn.isEmpty()) {
loadedSystemExcludeList = csync_add_exclude_list(_csync_ctx, systemExcludeListFn.toLocal8Bit());
}
int loadedUserExcludeList = false;
if (!options.exclude.isEmpty()) {
loadedUserExcludeList = csync_add_exclude_list(_csync_ctx, options.exclude.toLocal8Bit());
}
QStringList selectiveSyncList;
if (!options.unsyncedfolders.isEmpty()) {
QFile f(options.unsyncedfolders);
@@ -460,30 +432,32 @@ restart_sync:
}
}
if (loadedSystemExcludeList != 0 && loadedUserExcludeList != 0) {
// Always make sure at least one list has been loaded
qFatal("Cannot load system exclude list or list supplied via --exclude");
return EXIT_FAILURE;
}
Cmd cmd;
SyncJournalDb db(options.source_dir);
if (!selectiveSyncList.empty()) {
selectiveSyncFixup(&db, selectiveSyncList);
}
SyncEngine engine(account, _csync_ctx, options.source_dir, QUrl(options.target_url).path(), folder, &db);
SyncEngine engine(account, options.source_dir, QUrl(options.target_url), folder, &db);
engine.setIgnoreHiddenFiles(options.ignoreHiddenFiles);
QObject::connect(&engine, SIGNAL(finished(bool)), &app, SLOT(quit()));
QObject::connect(&engine, SIGNAL(transmissionProgress(ProgressInfo)), &cmd, SLOT(transmissionProgressSlot()));
// Exclude lists
engine.excludedFiles().addExcludeFilePath(ConfigFile::excludeFileFromSystem());
if( QFile::exists(options.exclude) )
engine.excludedFiles().addExcludeFilePath(options.exclude);
if (!engine.excludedFiles().reloadExcludes()) {
// Always make sure at least one list has been loaded
qFatal("Cannot load system exclude list or list supplied via --exclude");
return EXIT_FAILURE;
}
// Have to be done async, else, an error before exec() does not terminate the event loop.
QMetaObject::invokeMethod(&engine, "startSync", Qt::QueuedConnection);
app.exec();
csync_destroy(_csync_ctx);
if (engine.isAnotherSyncNeeded()) {
if (restartCount < options.restartTimes) {
restartCount++;
+30 -16
Ver Arquivo
@@ -16,6 +16,10 @@
#include <QFile>
#include <QTextStream>
#include <qtokenizer.h>
#include <QDebug>
#include "netrcparser.h"
namespace OCC {
@@ -28,11 +32,11 @@ QString passwordKeyword = QLatin1String("password");
}
NetrcParser::NetrcParser(const QString &fileName)
: _fileName(fileName)
NetrcParser::NetrcParser(const QString &file)
{
if (_fileName.isEmpty()) {
_fileName = QDir::homePath()+QLatin1String("/.netrc");
_netrcLocation = file;
if (_netrcLocation.isEmpty()) {
_netrcLocation = QDir::homePath()+QLatin1String("/.netrc");
}
}
@@ -49,29 +53,39 @@ void NetrcParser::tryAddEntryAndClear(QString& machine, LoginPair& pair, bool& i
bool NetrcParser::parse()
{
QFile netrc(_fileName);
QFile netrc(_netrcLocation);
if (!netrc.open(QIODevice::ReadOnly)) {
return false;
}
QString content = netrc.readAll();
QStringTokenizer tokenizer(content, " \n\t");
tokenizer.setQuoteCharacters("\"'");
QTextStream ts(&netrc);
LoginPair pair;
QString machine;
bool isDefault = false;
while (!ts.atEnd()) {
QString next;
ts >> next;
if (next == defaultKeyword) {
while (tokenizer.hasNext()) {
QString key = tokenizer.next();
if (key == defaultKeyword) {
tryAddEntryAndClear(machine, pair, isDefault);
isDefault = true;
continue; // don't read a value
}
if (next == machineKeyword) {
if (!tokenizer.hasNext()) {
qDebug() << "error fetching value for" << key;
return false;
}
QString value = tokenizer.next();
if (key == machineKeyword) {
tryAddEntryAndClear(machine, pair, isDefault);
ts >> machine;
} else if (next == loginKeyword) {
ts >> pair.first;
} else if (next == passwordKeyword) {
ts >> pair.second;
machine = value;
} else if (key == loginKeyword) {
pair.first = value;
} else if (key == passwordKeyword) {
pair.second = value;
} // ignore unsupported tokens
}
+2 -2
Ver Arquivo
@@ -29,7 +29,7 @@ class NetrcParser
public:
typedef QPair<QString, QString> LoginPair;
NetrcParser(const QString &fileName = QString::null);
NetrcParser(const QString &file = QString());
bool parse();
LoginPair find(const QString &machine);
@@ -37,7 +37,7 @@ private:
void tryAddEntryAndClear(QString &machine, LoginPair &pair, bool &isDefault);
QHash<QString, LoginPair> _entries;
LoginPair _default;
QString _fileName;
QString _netrcLocation;
};
} // namespace OCC
+9 -2
Ver Arquivo
@@ -32,6 +32,7 @@ set(client_UI
owncloudsetuppage.ui
addcertificatedialog.ui
proxyauthdialog.ui
notificationwidget.ui
wizard/owncloudadvancedsetuppage.ui
wizard/owncloudconnectionmethoddialog.ui
wizard/owncloudhttpcredspage.ui
@@ -53,6 +54,7 @@ set(client_SRCS
folderwizard.cpp
generalsettings.cpp
ignorelisteditor.cpp
lockwatcher.cpp
logbrowser.cpp
networksettings.cpp
ocsjob.cpp
@@ -62,6 +64,8 @@ set(client_SRCS
owncloudgui.cpp
owncloudsetupwizard.cpp
protocolwidget.cpp
activitydata.cpp
activitylistmodel.cpp
activitywidget.cpp
activityitemdelegate.cpp
selectivesyncdialog.cpp
@@ -84,11 +88,16 @@ set(client_SRCS
proxyauthhandler.cpp
proxyauthdialog.cpp
synclogdialog.cpp
tooltipupdater.cpp
notificationwidget.cpp
notificationconfirmjob.cpp
servernotificationhandler.cpp
creds/credentialsfactory.cpp
creds/httpcredentialsgui.cpp
creds/shibbolethcredentials.cpp
creds/shibboleth/shibbolethwebview.cpp
creds/shibboleth/shibbolethuserjob.cpp
wizard/postfixlineedit.cpp
wizard/abstractcredswizardpage.cpp
wizard/owncloudadvancedsetuppage.cpp
wizard/owncloudconnectionmethoddialog.cpp
@@ -133,7 +142,6 @@ list(APPEND client_SRCS folderwatcher_mac.cpp)
ENDIF()
set(3rdparty_SRC
../3rdparty/fancylineedit/fancylineedit.cpp
../3rdparty/QProgressIndicator/QProgressIndicator.cpp
../3rdparty/qtlockedfile/qtlockedfile.cpp
../3rdparty/qtsingleapplication/qtlocalpeer.cpp
@@ -157,7 +165,6 @@ else()
endif()
set(3rdparty_INC
${CMAKE_SOURCE_DIR}/src/3rdparty/fancylineedit
${CMAKE_SOURCE_DIR}/src/3rdparty/qjson
${CMAKE_SOURCE_DIR}/src/3rdparty/QProgressIndicator
${CMAKE_SOURCE_DIR}/src/3rdparty/qtlockedfile
+55 -18
Ver Arquivo
@@ -30,6 +30,7 @@ static const char httpUserC[] = "http_user";
static const char caCertsKeyC[] = "CaCertificates";
static const char accountsC[] = "Accounts";
static const char versionC[] = "version";
static const char serverVersionC[] = "serverVersion";
}
@@ -53,9 +54,11 @@ bool AccountManager::restore()
foreach (const auto& accountId, settings->childGroups()) {
settings->beginGroup(accountId);
if (auto acc = load(*settings)) {
if (auto acc = loadAccountHelper(*settings)) {
acc->_id = accountId;
addAccount(acc);
if (auto accState = AccountState::loadFromSettings(acc, *settings)) {
addAccountState(accState);
}
}
settings->endGroup();
}
@@ -110,7 +113,7 @@ bool AccountManager::restoreFromLegacySettings()
// Try to load the single account.
if (!settings->childKeys().isEmpty()) {
if (auto acc = load(*settings)) {
if (auto acc = loadAccountHelper(*settings)) {
if (migratedCreds) {
acc->setMigrated(true);
}
@@ -127,7 +130,8 @@ void AccountManager::save(bool saveCredentials)
settings->setValue(QLatin1String(versionC), 2);
foreach (const auto &acc, _accounts) {
settings->beginGroup(acc->account()->id());
save(acc->account(), *settings, saveCredentials);
saveAccountHelper(acc->account().data(), *settings, saveCredentials);
acc->writeToSettings(*settings);
settings->endGroup();
}
@@ -135,21 +139,34 @@ void AccountManager::save(bool saveCredentials)
qDebug() << "Saved all account settings, status:" << settings->status();
}
void AccountManager::wantsAccountSavedSlot(AccountPtr a)
void AccountManager::saveAccount(Account* a)
{
qDebug() << "Saving account" << a->url().toString();
auto settings = Account::settingsWithGroup(QLatin1String(accountsC));
settings->beginGroup(a->id());
save(a, *settings, false); // don't save credentials they might not have been loaded yet
saveAccountHelper(a, *settings, false); // don't save credentials they might not have been loaded yet
settings->endGroup();
settings->sync();
qDebug() << "Saved account settings, status:" << settings->status();
}
void AccountManager::save(const AccountPtr& acc, QSettings& settings, bool saveCredentials)
void AccountManager::saveAccountState(AccountState* a)
{
qDebug() << "Saving account state" << a->account()->url().toString();
auto settings = Account::settingsWithGroup(QLatin1String(accountsC));
settings->beginGroup(a->account()->id());
a->writeToSettings(*settings);
settings->endGroup();
settings->sync();
qDebug() << "Saved account state settings, status:" << settings->status();
}
void AccountManager::saveAccountHelper(Account* acc, QSettings& settings, bool saveCredentials)
{
settings.setValue(QLatin1String(urlC), acc->_url.toString());
settings.setValue(QLatin1String(serverVersionC), acc->_serverVersion);
if (acc->_credentials) {
if (saveCredentials) {
// Only persist the credentials if the parameter is set, on migration from 1.8.x
@@ -190,22 +207,35 @@ void AccountManager::save(const AccountPtr& acc, QSettings& settings, bool saveC
}
}
AccountPtr AccountManager::load(QSettings& settings)
AccountPtr AccountManager::loadAccountHelper(QSettings& settings)
{
auto acc = createAccount();
acc->setUrl(settings.value(QLatin1String(urlC)).toUrl());
QString authType = settings.value(QLatin1String(authTypeC)).toString();
QString overrideUrl = Theme::instance()->overrideServerUrl();
if( !overrideUrl.isEmpty() ) {
// if there is a overrideUrl, don't even bother reading from the config as all the accounts
// must use the overrideUrl
acc->setUrl(overrideUrl);
auto forceAuth = Theme::instance()->forceConfigAuthType();
if (!forceAuth.isEmpty()) {
authType = forceAuth;
}
} else {
acc->setUrl(settings.value(QLatin1String(urlC)).toUrl());
}
acc->_serverVersion = settings.value(QLatin1String(serverVersionC)).toString();
// We want to only restore settings for that auth type and the user value
acc->_settingsMap.insert(QLatin1String(userC), settings.value(userC));
QString authTypePrefix = settings.value(authTypeC).toString() + "_";
QString authTypePrefix = authType + "_";
Q_FOREACH(QString key, settings.childKeys()) {
if (!key.startsWith(authTypePrefix))
continue;
acc->_settingsMap.insert(key, settings.value(key));
}
acc->setCredentials(CredentialsFactory::create(settings.value(QLatin1String(authTypeC)).toString()));
acc->setCredentials(CredentialsFactory::create(authType));
// now the cert, it is in the general group
settings.beginGroup(QLatin1String("General"));
@@ -233,13 +263,9 @@ AccountState *AccountManager::addAccount(const AccountPtr& newAccount)
}
newAccount->_id = id;
QObject::connect(newAccount.data(), SIGNAL(wantsAccountSaved(AccountPtr)),
this, SLOT(wantsAccountSavedSlot(AccountPtr)));
AccountStatePtr newAccountState(new AccountState(newAccount));
_accounts << newAccountState;
emit accountAdded(newAccountState.data());
return newAccountState.data();
auto newAccountState = new AccountState(newAccount);
addAccountState(newAccountState);
return newAccountState;
}
void AccountManager::deleteAccount(AccountState* account)
@@ -296,4 +322,15 @@ QString AccountManager::generateFreeAccountId() const
}
}
void AccountManager::addAccountState(AccountState* accountState)
{
QObject::connect(accountState->account().data(),
SIGNAL(wantsAccountSaved(Account*)),
SLOT(saveAccount(Account*)));
AccountStatePtr ptr(accountState);
_accounts << ptr;
emit accountAdded(accountState);
}
}
+12 -5
Ver Arquivo
@@ -18,8 +18,6 @@
namespace OCC {
typedef QSharedPointer<AccountState> AccountStatePtr;
/**
@brief The AccountManager class
@ingroup gui
@@ -76,15 +74,24 @@ public:
static AccountPtr createAccount();
private:
void save(const AccountPtr& account, QSettings& settings, bool saveCredentials = true);
AccountPtr load(QSettings& settings);
// saving and loading Account to settings
void saveAccountHelper(Account* account, QSettings& settings, bool saveCredentials = true);
AccountPtr loadAccountHelper(QSettings& settings);
bool restoreFromLegacySettings();
bool isAccountIdAvailable(const QString& id) const;
QString generateFreeAccountId() const;
// Adds an account to the tracked list, emitting accountAdded()
void addAccountState(AccountState* accountState);
public slots:
void wantsAccountSavedSlot(AccountPtr a);
/// Saves account data, not including the credentials
void saveAccount(Account* a);
/// Saves account state data, not including the account
void saveAccountState(AccountState* a);
Q_SIGNALS:
+30 -47
Ver Arquivo
@@ -29,11 +29,13 @@
#include "accountmanager.h"
#include "owncloudsetupwizard.h"
#include "creds/abstractcredentials.h"
#include "tooltipupdater.h"
#include <math.h>
#include <QDebug>
#include <QDesktopServices>
#include <QDir>
#include <QListWidgetItem>
#include <QMessageBox>
#include <QAction>
@@ -87,6 +89,8 @@ AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent) :
#else
ui->_folderList->setMinimumWidth( 300 );
#endif
new ToolTipUpdater(ui->_folderList);
createAccountToolbox();
connect(AccountManager::instance(), SIGNAL(accountAdded(AccountState*)),
SLOT(slotAccountAdded(AccountState*)));
@@ -103,11 +107,6 @@ AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent) :
connect(_model, SIGNAL(rowsInserted(QModelIndex,int,int)),
this, SLOT(refreshSelectiveSyncStatus()));
QAction *resetFolderAction = new QAction(this);
resetFolderAction->setShortcut(QKeySequence(Qt::Key_F5));
connect(resetFolderAction, SIGNAL(triggered()), SLOT(slotResetCurrentFolder()));
addAction(resetFolderAction);
QAction *syncNowAction = new QAction(this);
syncNowAction->setShortcut(QKeySequence(Qt::Key_F6));
connect(syncNowAction, SIGNAL(triggered()), SLOT(slotSyncCurrentFolderNow()));
@@ -198,8 +197,7 @@ void AccountSettings::slotCustomContextMenuRequested(const QPoint &pos)
return;
}
QString alias = _model->data( index, FolderStatusDelegate::FolderAliasRole ).toString();
if (alias.isEmpty()) {
if (_model->classify(index) != FolderStatusModel::RootFolder) {
return;
}
@@ -213,9 +211,11 @@ void AccountSettings::slotCustomContextMenuRequested(const QPoint &pos)
QAction *ac = menu->addAction(tr("Open folder"));
connect(ac, SIGNAL(triggered(bool)), this, SLOT(slotOpenCurrentFolder()));
ac = menu->addAction(tr("Choose what to sync"));
ac->setEnabled(folderConnected);
connect(ac, SIGNAL(triggered(bool)), this, SLOT(doExpand()));
if (!ui->_folderList->isExpanded(index)) {
ac = menu->addAction(tr("Choose what to sync"));
ac->setEnabled(folderConnected);
connect(ac, SIGNAL(triggered(bool)), this, SLOT(doExpand()));
}
ac = menu->addAction(folderPaused ? tr("Resume sync") : tr("Pause sync"));
connect(ac, SIGNAL(triggered(bool)), this, SLOT(slotEnableCurrentFolder()));
@@ -276,7 +276,6 @@ void AccountSettings::slotFolderWizardAccepted()
qDebug() << "* Folder wizard completed";
FolderDefinition definition;
definition.alias = folderWizard->field(QLatin1String("alias")).toString();
definition.localPath = FolderDefinition::prepareLocalPath(
folderWizard->field(QLatin1String("sourceFolder")).toString());
definition.targetPath = folderWizard->property("targetPath").toString();
@@ -333,10 +332,13 @@ void AccountSettings::slotRemoveCurrentFolder()
QString alias = _model->data( selected, FolderStatusDelegate::FolderAliasRole ).toString();
qDebug() << "Remove Folder alias " << alias;
if( !alias.isEmpty() ) {
FolderMan *folderMan = FolderMan::instance();
QString shortGuiLocalPath = folderMan->folder(alias)->shortGuiLocalPath();
QMessageBox messageBox(QMessageBox::Question,
tr("Confirm Folder Sync Connection Removal"),
tr("<p>Do you really want to stop syncing the folder <i>%1</i>?</p>"
"<p><b>Note:</b> This will <b>not</b> delete any files.</p>").arg(alias),
"<p><b>Note:</b> This will <b>not</b> delete any files.</p>").arg(shortGuiLocalPath),
QMessageBox::NoButton,
this);
QPushButton* yesButton =
@@ -348,7 +350,6 @@ void AccountSettings::slotRemoveCurrentFolder()
return;
}
FolderMan *folderMan = FolderMan::instance();
folderMan->slotRemoveFolder( folderMan->folder(alias) );
_model->removeRow(row);
@@ -359,31 +360,6 @@ void AccountSettings::slotRemoveCurrentFolder()
}
}
void AccountSettings::slotResetCurrentFolder()
{
QModelIndex selected = ui->_folderList->selectionModel()->currentIndex();
if( selected.isValid() ) {
QString alias = _model->data( selected, FolderStatusDelegate::FolderAliasRole ).toString();
if (alias.isEmpty())
return;
int ret = QMessageBox::question( 0, tr("Confirm Folder Reset"),
tr("<p>Do you really want to reset folder <i>%1</i> and rebuild your client database?</p>"
"<p><b>Note:</b> This function is designed for maintenance purposes only. "
"No files will be removed, but this can cause significant data traffic and "
"take several minutes or hours to complete, depending on the size of the folder. "
"Only use this option if advised by your administrator.</p>").arg(alias),
QMessageBox::Yes|QMessageBox::No );
if( ret == QMessageBox::Yes ) {
FolderMan *folderMan = FolderMan::instance();
if(Folder *f = folderMan->folder(alias)) {
f->slotTerminateSync();
f->wipe();
}
folderMan->slotScheduleAllFolders();
}
}
}
void AccountSettings::slotOpenCurrentFolder()
{
QModelIndex selected = ui->_folderList->selectionModel()->currentIndex();
@@ -465,8 +441,7 @@ void AccountSettings::slotEnableCurrentFolder()
if ( f->isBusy() && terminate ) {
f->slotTerminateSync();
}
f->setSyncPaused(!currentlyPaused); // toggle the pause setting
folderMan->slotSetFolderPaused( f, !currentlyPaused );
f->setSyncPaused(!currentlyPaused);
// keep state for the icon setting.
if( currentlyPaused ) _wasDisabledBefore = true;
@@ -541,7 +516,11 @@ void AccountSettings::slotAccountStateChanged(int state)
}
if (state == AccountState::Connected) {
showConnectionLabel( tr("Connected to %1.").arg(serverWithUser) );
QStringList errors;
if (account->serverVersionUnsupported()) {
errors << tr("The server version %1 is old and unsupported! Proceed at your own risk.").arg(account->serverVersion());
}
showConnectionLabel( tr("Connected to %1.").arg(serverWithUser), errors );
} else if (state == AccountState::ServiceUnavailable) {
showConnectionLabel( tr("Server %1 is temporarily unavailable.").arg(server) );
} else if (state == AccountState::SignedOut) {
@@ -569,11 +548,10 @@ void AccountSettings::slotAccountStateChanged(int state)
}
/* set the correct label for the Account toolbox button */
if( _accountState ) {
bool isConnected = _accountState->isConnected();
if( isConnected ) {
_toggleSignInOutAction->setText(tr("Log out"));
} else {
if( _accountState->isSignedOut() ) {
_toggleSignInOutAction->setText(tr("Log in"));
} else {
_toggleSignInOutAction->setText(tr("Log out"));
}
}
}
@@ -628,7 +606,8 @@ void AccountSettings::refreshSelectiveSyncStatus()
continue;
}
auto undecidedList = folder->journalDb()->getSelectiveSyncList(SyncJournalDb::SelectiveSyncUndecidedList);
bool ok;
auto undecidedList = folder->journalDb()->getSelectiveSyncList(SyncJournalDb::SelectiveSyncUndecidedList, &ok);
QString p;
foreach(const auto &it, undecidedList) {
// FIXME: add the folder alias in a hoover hint.
@@ -725,7 +704,11 @@ bool AccountSettings::event(QEvent* e)
_quotaInfo.setActive(isVisible());
}
if (e->type() == QEvent::Show) {
ui->_folderList->setExpanded(_model->index(0, 0), true);
// Expand the folder automatically only if there's only one, see #4283
// The 2 is 1 folder + 1 'add folder' button
if (_model->rowCount() <= 2) {
ui->_folderList->setExpanded(_model->index(0, 0), true);
}
}
return QWidget::event(e);
}
-1
Ver Arquivo
@@ -72,7 +72,6 @@ protected slots:
void slotEnableCurrentFolder();
void slotSyncCurrentFolderNow();
void slotRemoveCurrentFolder();
void slotResetCurrentFolder();
void slotOpenCurrentFolder();
void slotFolderWizardAccepted();
void slotFolderWizardRejected();
+13
Ver Arquivo
@@ -46,6 +46,16 @@ AccountState::~AccountState()
{
}
AccountState *AccountState::loadFromSettings(AccountPtr account, QSettings& /*settings*/)
{
auto accountState = new AccountState(account);
return accountState;
}
void AccountState::writeToSettings(QSettings& /*settings*/)
{
}
AccountPtr AccountState::account() const
{
return _account;
@@ -85,6 +95,9 @@ void AccountState::setState(State state)
} else if (oldState == SignedOut && _state == Disconnected) {
checkConnectivity();
}
if (oldState == Connected || _state == Connected) {
emit isConnectedChanged();
}
}
// might not have changed but the underlying _connectionErrors might have
+18 -3
Ver Arquivo
@@ -29,11 +29,13 @@ namespace OCC {
class AccountState;
class Account;
typedef QExplicitlySharedDataPointer<AccountState> AccountStatePtr;
/**
* @brief Extra info about an ownCloud server account.
* @ingroup gui
*/
class AccountState : public QObject {
class AccountState : public QObject, public QSharedData {
Q_OBJECT
public:
enum State {
@@ -64,9 +66,21 @@ public:
typedef ConnectionValidator::Status ConnectionStatus;
/// Use the account as parent
AccountState(AccountPtr account);
explicit AccountState(AccountPtr account);
~AccountState();
/** Creates an account state from settings and an Account object.
*
* Use from AccountManager with a prepared QSettings object only.
*/
static AccountState* loadFromSettings(AccountPtr account, QSettings& settings);
/** Writes account state information to settings.
*
* It does not write the Account data.
*/
void writeToSettings(QSettings& settings);
AccountPtr account() const;
ConnectionStatus connectionStatus() const;
@@ -113,6 +127,7 @@ private:
signals:
void stateChanged(int state);
void isConnectedChanged();
protected Q_SLOTS:
void slotConnectionValidatorResult(ConnectionValidator::Status status, const QStringList& errors);
@@ -133,6 +148,6 @@ private:
}
Q_DECLARE_METATYPE(OCC::AccountState*)
Q_DECLARE_METATYPE(QSharedPointer<OCC::AccountState>)
Q_DECLARE_METATYPE(OCC::AccountStatePtr)
#endif //ACCOUNTINFO_H
+35
Ver Arquivo
@@ -0,0 +1,35 @@
/*
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include <QtCore>
#include "activitydata.h"
namespace OCC
{
bool operator<( const Activity& rhs, const Activity& lhs ) {
return rhs._dateTime.toMSecsSinceEpoch() > lhs._dateTime.toMSecsSinceEpoch();
}
bool operator==( const Activity& rhs, const Activity& lhs ) {
return (rhs._type == lhs._type && rhs._id== lhs._id && rhs._accName == lhs._accName);
}
Activity::Identifier Activity::ident() const {
return Identifier( _id, _accName );
}
}
+89
Ver Arquivo
@@ -0,0 +1,89 @@
/*
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#ifndef ACTIVITYDATA_H
#define ACTIVITYDATA_H
#include <QtCore>
namespace OCC {
/**
* @brief The ActivityLink class describes actions of an activity
*
* These are part of notifications which are mapped into activities.
*/
class ActivityLink
{
public:
QString _label;
QString _link;
QByteArray _verb;
bool _isPrimary;
};
/* ==================================================================== */
/**
* @brief Activity Structure
* @ingroup gui
*
* contains all the information describing a single activity.
*/
class Activity
{
public:
typedef QPair<qlonglong, QString> Identifier;
enum Type {
ActivityType,
NotificationType
};
Type _type;
qlonglong _id;
QString _subject;
QString _message;
QString _file;
QUrl _link;
QDateTime _dateTime;
QString _accName;
QVector <ActivityLink> _links;
/**
* @brief Sort operator to sort the list youngest first.
* @param val
* @return
*/
Identifier ident() const;
};
bool operator==( const Activity& rhs, const Activity& lhs );
bool operator<( const Activity& rhs, const Activity& lhs );
/* ==================================================================== */
/**
* @brief The ActivityList
* @ingroup gui
*
* A QList based list of Activities
*/
typedef QList<Activity> ActivityList;
}
#endif // ACTIVITYDATA_H
+228
Ver Arquivo
@@ -0,0 +1,228 @@
/*
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include <QtCore>
#include <QAbstractListModel>
#include <QWidget>
#include <QIcon>
#include "account.h"
#include "accountstate.h"
#include "accountmanager.h"
#include "folderman.h"
#include "accessmanager.h"
#include "activityitemdelegate.h"
#include "activitydata.h"
#include "activitylistmodel.h"
namespace OCC {
ActivityListModel::ActivityListModel(QWidget *parent)
:QAbstractListModel(parent)
{
}
QVariant ActivityListModel::data(const QModelIndex &index, int role) const
{
Activity a;
if (!index.isValid())
return QVariant();
a = _finalList.at(index.row());
AccountStatePtr ast = AccountManager::instance()->account(a._accName);
QStringList list;
switch (role) {
case ActivityItemDelegate::PathRole:
list = FolderMan::instance()->findFileInLocalFolders(a._file, ast->account());
if( list.count() > 0 ) {
return QVariant(list.at(0));
}
// File does not exist anymore? Let's try to open its path
list = FolderMan::instance()->findFileInLocalFolders(QFileInfo(a._file).path(), ast->account());
if( list.count() > 0 ) {
return QVariant(list.at(0));
}
return QVariant();
break;
case ActivityItemDelegate::ActionIconRole:
return QVariant(); // FIXME once the action can be quantified, display on Icon
break;
case ActivityItemDelegate::UserIconRole:
return QIcon(QLatin1String(":/client/resources/account.png"));
break;
case Qt::ToolTipRole:
case ActivityItemDelegate::ActionTextRole:
return a._subject;
break;
case ActivityItemDelegate::LinkRole:
return a._link;
break;
case ActivityItemDelegate::AccountRole:
return a._accName;
break;
case ActivityItemDelegate::PointInTimeRole:
return Utility::timeAgoInWords(a._dateTime);
break;
case ActivityItemDelegate::AccountConnectedRole:
return (ast && ast->isConnected());
break;
default:
return QVariant();
}
return QVariant();
}
int ActivityListModel::rowCount(const QModelIndex&) const
{
return _finalList.count();
}
// current strategy: Fetch 100 items per Account
// ATTENTION: This method is const and thus it is not possible to modify
// the _activityLists hash or so. Doesn't make it easier...
bool ActivityListModel::canFetchMore(const QModelIndex& ) const
{
if( _activityLists.count() == 0 ) return true;
for(auto i = _activityLists.begin() ; i != _activityLists.end(); ++i) {
AccountState *ast = i.key();
if( ast && ast->isConnected() ) {
ActivityList activities = i.value();
if( activities.count() == 0 &&
! _currentlyFetching.contains(ast) ) {
return true;
}
}
}
return false;
}
void ActivityListModel::startFetchJob(AccountState* s)
{
if( !s->isConnected() ) {
return;
}
JsonApiJob *job = new JsonApiJob(s->account(), QLatin1String("ocs/v1.php/cloud/activity"), this);
QObject::connect(job, SIGNAL(jsonReceived(QVariantMap, int)),
this, SLOT(slotActivitiesReceived(QVariantMap, int)));
job->setProperty("AccountStatePtr", QVariant::fromValue<AccountState*>(s));
QList< QPair<QString,QString> > params;
params.append(qMakePair(QString::fromLatin1("page"), QString::fromLatin1("0")));
params.append(qMakePair(QString::fromLatin1("pagesize"), QString::fromLatin1("100")));
job->addQueryParams(params);
_currentlyFetching.insert(s);
qDebug() << Q_FUNC_INFO << "Start fetching activities for " << s->account()->displayName();
job->start();
}
void ActivityListModel::slotActivitiesReceived(const QVariantMap& json, int statusCode)
{
auto activities = json.value("ocs").toMap().value("data").toList();
ActivityList list;
AccountState* ast = qvariant_cast<AccountState*>(sender()->property("AccountStatePtr"));
_currentlyFetching.remove(ast);
foreach( auto activ, activities ) {
auto json = activ.toMap();
Activity a;
a._type = Activity::ActivityType;
a._accName = ast->account()->displayName();
a._id = json.value("id").toLongLong();
a._subject = json.value("subject").toString();
a._message = json.value("message").toString();
a._file = json.value("file").toString();
a._link = json.value("link").toUrl();
a._dateTime = json.value("date").toDateTime();
list.append(a);
}
_activityLists[ast] = list;
emit activityJobStatusCode(ast, statusCode);
combineActivityLists();
}
void ActivityListModel::combineActivityLists()
{
ActivityList resultList;
foreach( ActivityList list, _activityLists.values() ) {
resultList.append(list);
}
std::sort( resultList.begin(), resultList.end() );
beginResetModel();
_finalList.clear();
endResetModel();
beginInsertRows(QModelIndex(), 0, resultList.count());
_finalList = resultList;
endInsertRows();
}
void ActivityListModel::fetchMore(const QModelIndex &)
{
QList<AccountStatePtr> accounts = AccountManager::instance()->accounts();
foreach (const AccountStatePtr& asp, accounts) {
if( !_activityLists.contains(asp.data()) && asp->isConnected() ) {
_activityLists[asp.data()] = ActivityList();
startFetchJob(asp.data());
}
}
}
void ActivityListModel::slotRefreshActivity(AccountState *ast)
{
if(ast && _activityLists.contains(ast)) {
_activityLists.remove(ast);
}
startFetchJob(ast);
}
void ActivityListModel::slotRemoveAccount(AccountState *ast )
{
if( _activityLists.contains(ast) ) {
int i = 0;
const QString accountToRemove = ast->account()->displayName();
QMutableListIterator<Activity> it(_finalList);
while (it.hasNext()) {
Activity activity = it.next();
if( activity._accName == accountToRemove ) {
beginRemoveRows(QModelIndex(), i, i+1);
it.remove();
endRemoveRows();
}
}
_activityLists.remove(ast);
_currentlyFetching.remove(ast);
}
}
}
+67
Ver Arquivo
@@ -0,0 +1,67 @@
/*
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#ifndef ACTIVITYLISTMODEL_H
#define ACTIVITYLISTMODEL_H
#include <QtCore>
#include "activitydata.h"
namespace OCC {
class AccountState;
/**
* @brief The ActivityListModel
* @ingroup gui
*
* Simple list model to provide the list view with data.
*/
class ActivityListModel : public QAbstractListModel
{
Q_OBJECT
public:
explicit ActivityListModel(QWidget *parent=0);
QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE;
int rowCount(const QModelIndex& parent = QModelIndex()) const Q_DECL_OVERRIDE;
bool canFetchMore(const QModelIndex& ) const Q_DECL_OVERRIDE;
void fetchMore(const QModelIndex&) Q_DECL_OVERRIDE;
ActivityList activityList() { return _finalList; }
public slots:
void slotRefreshActivity(AccountState* ast);
void slotRemoveAccount( AccountState *ast );
private slots:
void slotActivitiesReceived(const QVariantMap& json, int statusCode);
signals:
void activityJobStatusCode(AccountState* ast, int statusCode);
private:
void startFetchJob(AccountState* s);
void combineActivityLists();
QMap<AccountState*, ActivityList> _activityLists;
ActivityList _finalList;
QSet<AccountState*> _currentlyFetching;
};
}
#endif // ACTIVITYLISTMODEL_H
+402 -234
Ver Arquivo
@@ -16,8 +16,8 @@
#include <QtWidgets>
#endif
#include "activitylistmodel.h"
#include "activitywidget.h"
#include "configfile.h"
#include "syncresult.h"
#include "logger.h"
#include "utility.h"
@@ -33,236 +33,28 @@
#include "activityitemdelegate.h"
#include "protocolwidget.h"
#include "QProgressIndicator.h"
#include "notificationwidget.h"
#include "notificationconfirmjob.h"
#include "servernotificationhandler.h"
#include "theme.h"
#include "ocsjob.h"
#include "ui_activitywidget.h"
#include <climits>
// time span in milliseconds which has to be between two
// refreshes of the notifications
#define NOTIFICATION_REQUEST_FREE_PERIOD 15000
namespace OCC {
void ActivityList::setAccountName( const QString& name )
{
_accountName = name;
}
QString ActivityList::accountName() const
{
return _accountName;
}
/* ==================================================================== */
ActivityListModel::ActivityListModel(QWidget *parent)
:QAbstractListModel(parent)
{
}
QVariant ActivityListModel::data(const QModelIndex &index, int role) const
{
Activity a;
if (!index.isValid())
return QVariant();
a = _finalList.at(index.row());
AccountStatePtr ast = AccountManager::instance()->account(a._accName);
QStringList list;
if (role == Qt::EditRole)
return QVariant();
switch (role) {
case ActivityItemDelegate::PathRole:
list = FolderMan::instance()->findFileInLocalFolders(a._file, ast->account());
if( list.count() > 0 ) {
return QVariant(list.at(0));
}
// File does not exist anymore? Let's try to open its path
list = FolderMan::instance()->findFileInLocalFolders(QFileInfo(a._file).path(), ast->account());
if( list.count() > 0 ) {
return QVariant(list.at(0));
}
return QVariant();
break;
case ActivityItemDelegate::ActionIconRole:
return QVariant(); // FIXME once the action can be quantified, display on Icon
break;
case ActivityItemDelegate::UserIconRole:
return QIcon(QLatin1String(":/client/resources/account.png"));
break;
case Qt::ToolTipRole:
case ActivityItemDelegate::ActionTextRole:
return a._subject;
break;
case ActivityItemDelegate::LinkRole:
return a._link;
break;
case ActivityItemDelegate::AccountRole:
return a._accName;
break;
case ActivityItemDelegate::PointInTimeRole:
return Utility::timeAgoInWords(a._dateTime);
break;
case ActivityItemDelegate::AccountConnectedRole:
return (ast && ast->isConnected());
break;
default:
return QVariant();
}
return QVariant();
}
int ActivityListModel::rowCount(const QModelIndex&) const
{
return _finalList.count();
}
// current strategy: Fetch 100 items per Account
bool ActivityListModel::canFetchMore(const QModelIndex& ) const
{
if( _activityLists.count() == 0 ) return true;
QMap<AccountState*, ActivityList>::const_iterator i = _activityLists.begin();
while (i != _activityLists.end()) {
AccountState *ast = i.key();
if( !ast->isConnected() ) {
return false;
}
ActivityList activities = i.value();
if( activities.count() == 0 &&
! _currentlyFetching.contains(ast) ) {
return true;
}
++i;
}
return false;
}
void ActivityListModel::startFetchJob(AccountState* s)
{
if( !s->isConnected() ) {
return;
}
JsonApiJob *job = new JsonApiJob(s->account(), QLatin1String("ocs/v1.php/cloud/activity"), this);
QObject::connect(job, SIGNAL(jsonReceived(QVariantMap, int)),
this, SLOT(slotActivitiesReceived(QVariantMap, int)));
job->setProperty("AccountStatePtr", QVariant::fromValue<AccountState*>(s));
QList< QPair<QString,QString> > params;
params.append(qMakePair(QString::fromLatin1("page"), QString::fromLatin1("0")));
params.append(qMakePair(QString::fromLatin1("pagesize"), QString::fromLatin1("100")));
job->addQueryParams(params);
_currentlyFetching.insert(s);
qDebug() << "Start fetching activities for " << s->account()->displayName();
job->start();
}
void ActivityListModel::slotActivitiesReceived(const QVariantMap& json, int statusCode)
{
auto activities = json.value("ocs").toMap().value("data").toList();
qDebug() << "*** activities" << activities;
ActivityList list;
AccountState* ai = qvariant_cast<AccountState*>(sender()->property("AccountStatePtr"));
_currentlyFetching.remove(ai);
list.setAccountName( ai->account()->displayName());
foreach( auto activ, activities ) {
auto json = activ.toMap();
Activity a;
a._accName = ai->account()->displayName();
a._id = json.value("id").toLongLong();
a._subject = json.value("subject").toString();
a._message = json.value("message").toString();
a._file = json.value("file").toString();
a._link = json.value("link").toUrl();
a._dateTime = json.value("date").toDateTime();
list.append(a);
}
_activityLists[ai] = list;
if( statusCode == 999 ) {
emit accountWithoutActivityApp(ai);
}
combineActivityLists();
}
void ActivityListModel::combineActivityLists()
{
ActivityList resultList;
foreach( ActivityList list, _activityLists.values() ) {
resultList.append(list);
}
std::sort( resultList.begin(), resultList.end() );
beginInsertRows(QModelIndex(), 0, resultList.count()-1);
_finalList = resultList;
endInsertRows();
}
void ActivityListModel::fetchMore(const QModelIndex &)
{
QList<AccountStatePtr> accounts = AccountManager::instance()->accounts();
foreach (AccountStatePtr asp, accounts) {
bool newItem = false;
// if the account is not yet managed, add an empty list.
if( !_activityLists.contains(asp.data()) ) {
_activityLists[asp.data()] = ActivityList();
newItem = true;
}
ActivityList activities = _activityLists[asp.data()];
if( newItem ) {
startFetchJob(asp.data());
}
}
}
void ActivityListModel::slotRefreshActivity(AccountState *ast)
{
if(ast && _activityLists.contains(ast)) {
qDebug() << "**** Refreshing Activity list for" << ast->account()->displayName();
_activityLists.remove(ast);
}
startFetchJob(ast);
}
void ActivityListModel::slotRemoveAccount(AccountState *ast )
{
if( _activityLists.contains(ast) ) {
int i = 0;
const QString accountToRemove = ast->account()->displayName();
QMutableListIterator<Activity> it(_finalList);
while (it.hasNext()) {
Activity activity = it.next();
if( activity._accName == accountToRemove ) {
beginRemoveRows(QModelIndex(), i, i+1);
it.remove();
endRemoveRows();
}
}
_activityLists.remove(ast);
_currentlyFetching.remove(ast);
}
}
/* ==================================================================== */
ActivityWidget::ActivityWidget(QWidget *parent) :
QWidget(parent),
_ui(new Ui::ActivityWidget)
_ui(new Ui::ActivityWidget),
_notificationRequestsRunning(0)
{
_ui->setupUi(this);
@@ -278,10 +70,22 @@ ActivityWidget::ActivityWidget(QWidget *parent) :
_ui->_activityList->setAlternatingRowColors(true);
_ui->_activityList->setModel(_model);
_ui->_notifyLabel->hide();
_ui->_notifyScroll->hide();
// Create a widget container for the notifications. The ui file defines
// a scroll area that get a widget with a layout as children
QWidget *w = new QWidget;
_notificationsLayout = new QVBoxLayout;
w->setLayout(_notificationsLayout);
_notificationsLayout->setAlignment(Qt::AlignTop);
_ui->_notifyScroll->setAlignment(Qt::AlignTop);
_ui->_notifyScroll->setWidget(w);
showLabels();
connect(_model, SIGNAL(accountWithoutActivityApp(AccountState*)),
this, SLOT(slotAccountWithoutActivityApp(AccountState*)));
connect(_model, SIGNAL(activityJobStatusCode(AccountState*,int)),
this, SLOT(slotAccountActivityStatus(AccountState*,int)));
_copyBtn = _ui->_dialogButtonBox->addButton(tr("Copy"), QDialogButtonBox::ActionRole);
_copyBtn->setToolTip( tr("Copy the activity list to the clipboard."));
@@ -291,6 +95,9 @@ ActivityWidget::ActivityWidget(QWidget *parent) :
connect( _ui->_activityList, SIGNAL(activated(QModelIndex)), this,
SLOT(slotOpenFile(QModelIndex)));
connect( &_removeTimer, SIGNAL(timeout()), this, SLOT(slotCheckToCleanWidgets()) );
_removeTimer.setInterval(1000);
}
ActivityWidget::~ActivityWidget()
@@ -298,11 +105,26 @@ ActivityWidget::~ActivityWidget()
delete _ui;
}
void ActivityWidget::slotRefresh(AccountState *ptr)
void ActivityWidget::slotRefreshActivities(AccountState *ptr)
{
_model->slotRefreshActivity(ptr);
}
void ActivityWidget::slotRefreshNotifications(AccountState *ptr)
{
// start a server notification handler if no notification requests
// are running
if( _notificationRequestsRunning == 0 ) {
ServerNotificationHandler *snh = new ServerNotificationHandler;
connect(snh, SIGNAL(newNotificationList(ActivityList)), this,
SLOT(slotBuildNotificationDisplay(ActivityList)));
snh->slotFetchNotifications(ptr);
} else {
qDebug() << Q_FUNC_INFO << "========> notification request counter not zero.";
}
}
void ActivityWidget::slotRemoveAccount( AccountState *ptr )
{
_model->slotRemoveAccount(ptr);
@@ -314,6 +136,8 @@ void ActivityWidget::showLabels()
_ui->_headerLabel->setTextFormat(Qt::RichText);
_ui->_headerLabel->setText(t);
_ui->_notifyLabel->setText(tr("Action Required: Notifications"));
t.clear();
QSetIterator<QString> i(_accountsWithoutActivities);
while (i.hasNext() ) {
@@ -323,12 +147,18 @@ void ActivityWidget::showLabels()
_ui->_bottomLabel->setText(t);
}
void ActivityWidget::slotAccountWithoutActivityApp(AccountState *ast)
void ActivityWidget::slotAccountActivityStatus(AccountState *ast, int statusCode)
{
if( ast && ast->account() ) {
if( !(ast && ast->account()) ) {
return;
}
if( statusCode == 999 ) {
_accountsWithoutActivities.insert(ast->account()->displayName());
} else {
_accountsWithoutActivities.remove(ast->account()->displayName());
}
checkActivityTabVisibility();
showLabels();
}
@@ -381,9 +211,25 @@ void ActivityWidget::storeActivityList( QTextStream& ts )
}
}
void ActivityWidget::checkActivityTabVisibility()
{
int accountCount = AccountManager::instance()->accounts().count();
bool hasAccountsWithActivity =
_accountsWithoutActivities.count() != accountCount;
bool hasNotifications = !_widgetForNotifId.isEmpty();
_ui->_headerLabel->setVisible( hasAccountsWithActivity );
_ui->_activityList->setVisible( hasAccountsWithActivity );
_ui->_notifyLabel->setVisible( hasNotifications );
_ui->_notifyScroll->setVisible( hasNotifications );
emit hideActivityTab(!hasAccountsWithActivity && !hasNotifications);
}
void ActivityWidget::slotOpenFile(QModelIndex indx)
{
qDebug() << indx.isValid() << indx.data(ActivityItemDelegate::PathRole).toString() << QFile::exists(indx.data(ActivityItemDelegate::PathRole).toString());
qDebug() << Q_FUNC_INFO << indx.isValid() << indx.data(ActivityItemDelegate::PathRole).toString() << QFile::exists(indx.data(ActivityItemDelegate::PathRole).toString());
if( indx.isValid() ) {
QString fullPath = indx.data(ActivityItemDelegate::PathRole).toString();
@@ -393,6 +239,268 @@ void ActivityWidget::slotOpenFile(QModelIndex indx)
}
}
// GUI: Display the notifications.
// All notifications in list are coming from the same account
// but in the _widgetForNotifId hash widgets for all accounts are
// collected.
void ActivityWidget::slotBuildNotificationDisplay(const ActivityList& list)
{
QHash<QString, int> accNotified;
QString listAccountName;
foreach( auto activity, list ) {
if( _blacklistedNotifications.contains(activity)) {
qDebug() << Q_FUNC_INFO << "Activity in blacklist, skip";
continue;
}
NotificationWidget *widget = 0;
if( _widgetForNotifId.contains( activity.ident()) ) {
widget = _widgetForNotifId[activity.ident()];
} else {
widget = new NotificationWidget(this);
connect(widget, SIGNAL(sendNotificationRequest(QString, QString, QByteArray)),
this, SLOT(slotSendNotificationRequest(QString, QString, QByteArray)));
connect(widget, SIGNAL(requestCleanupAndBlacklist(Activity)),
this, SLOT(slotRequestCleanupAndBlacklist(Activity)));
_notificationsLayout->addWidget(widget);
// _ui->_notifyScroll->setMinimumHeight( widget->height());
#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
_ui->_notifyScroll->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContentsOnFirstShow);
#endif
_widgetForNotifId[activity.ident()] = widget;
}
widget->setActivity( activity );
// remember the list account name for the strayCat handling below.
listAccountName = activity._accName;
// handle gui logs. In order to NOT annoy the user with every fetching of the
// notifications the notification id is stored in a Set. Only if an id
// is not in the set, it qualifies for guiLog.
// Important: The _guiLoggedNotifications set must be wiped regularly which
// will repeat the gui log.
// after one hour, clear the gui log notification store
if( _guiLogTimer.elapsed() > 60*60*1000 ) {
_guiLoggedNotifications.clear();
}
if( !_guiLoggedNotifications.contains(activity._id)) {
QString host = activity._accName;
// store the name of the account that sends the notification to be
// able to add it to the tray notification
// remove the user name from the account as that is not accurate here.
int indx = host.indexOf(QChar('@'));
if( indx>-1 ) {
host.remove(0, 1+indx);
}
if( !host.isEmpty() ) {
if( accNotified.contains(host)) {
accNotified[host] = accNotified[host]+1;
} else {
accNotified[host] = 1;
}
}
_guiLoggedNotifications.insert(activity._id);
}
}
// check if there are widgets that have no corresponding activity from
// the server any more. Collect them in a list
QList< Activity::Identifier > strayCats;
foreach( auto id, _widgetForNotifId.keys() ) {
NotificationWidget *widget = _widgetForNotifId[id];
bool found = false;
// do not mark widgets of other accounts to delete.
if( widget->activity()._accName != listAccountName ) {
continue;
}
foreach( auto activity, list ) {
if( activity.ident() == id ) {
// found an activity
found = true;
break;
}
}
if( ! found ) {
// the activity does not exist any more.
strayCats.append(id);
}
}
// .. and now delete all these stray cat widgets.
foreach( auto strayCatId, strayCats ) {
NotificationWidget *widgetToGo = _widgetForNotifId[strayCatId];
scheduleWidgetToRemove(widgetToGo, 0);
}
checkActivityTabVisibility();
int newGuiLogCount = accNotified.count();
if( newGuiLogCount > 0 ) {
// restart the gui log timer now that we show a notification
_guiLogTimer.restart();
// Assemble a tray notification
QString msg = tr("You received %n new notification(s) from %2.", "", accNotified[accNotified.keys().at(0)]).
arg(accNotified.keys().at(0));
if( newGuiLogCount >= 2 ) {
QString acc1 = accNotified.keys().at(0);
QString acc2 = accNotified.keys().at(1);
if( newGuiLogCount == 2 ) {
int notiCount = accNotified[ acc1 ] + accNotified[ acc2 ];
msg = tr("You received %n new notification(s) from %1 and %2.", "", notiCount).arg(acc1, acc2);
} else {
msg = tr("You received new notifications from %1, %2 and other accounts.").arg(acc1, acc2);
}
}
const QString log = tr("%1 Notifications - Action Required").arg(Theme::instance()->appNameGUI());
emit guiLog( log, msg);
}
}
void ActivityWidget::slotSendNotificationRequest(const QString& accountName, const QString& link, const QByteArray& verb)
{
qDebug() << Q_FUNC_INFO << "Server Notification Request " << verb << link << "on account" << accountName;
NotificationWidget *theSender = qobject_cast<NotificationWidget*>(sender());
const QStringList validVerbs = QStringList() << "GET" << "PUT" << "POST" << "DELETE";
if( validVerbs.contains(verb)) {
AccountStatePtr acc = AccountManager::instance()->account(accountName);
if( acc ) {
NotificationConfirmJob *job = new NotificationConfirmJob(acc->account());
QUrl l(link);
job->setLinkAndVerb(l, verb);
job->setWidget(theSender);
connect( job, SIGNAL( networkError(QNetworkReply*)),
this, SLOT(slotNotifyNetworkError(QNetworkReply*)));
connect( job, SIGNAL( jobFinished(QString, int)),
this, SLOT(slotNotifyServerFinished(QString, int)) );
job->start();
// count the number of running notification requests. If this member var
// is larger than zero, no new fetching of notifications is started
_notificationRequestsRunning++;
}
} else {
qDebug() << Q_FUNC_INFO << "Notification Links: Invalid verb:" << verb;
}
}
void ActivityWidget::endNotificationRequest( NotificationWidget *widget, int replyCode )
{
_notificationRequestsRunning--;
if( widget ) {
widget->slotNotificationRequestFinished(replyCode);
}
}
void ActivityWidget::slotNotifyNetworkError( QNetworkReply *reply)
{
NotificationConfirmJob *job = qobject_cast<NotificationConfirmJob*>(sender());
if( !job ) {
return;
}
int resultCode =0;
if( reply ) {
resultCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
}
endNotificationRequest(job->widget(), resultCode);
qDebug() << Q_FUNC_INFO << "Server notify job failed with code " << resultCode;
}
void ActivityWidget::slotNotifyServerFinished( const QString& reply, int replyCode )
{
NotificationConfirmJob *job = qobject_cast<NotificationConfirmJob*>(sender());
if( !job ) {
return;
}
endNotificationRequest(job->widget(), replyCode);
// FIXME: remove the widget after a couple of seconds
qDebug() << Q_FUNC_INFO << "Server Notification reply code"<< replyCode << reply;
// if the notification was successful start a timer that triggers
// removal of the done widgets in a few seconds
// Add 200 millisecs to the predefined value to make sure that the timer in
// widget's method readyToClose() has elapsed.
if( replyCode == OCS_SUCCESS_STATUS_CODE ) {
scheduleWidgetToRemove( job->widget() );
}
}
// blacklist the activity coming in here.
void ActivityWidget::slotRequestCleanupAndBlacklist(const Activity& blacklistActivity)
{
if ( ! _blacklistedNotifications.contains(blacklistActivity) ) {
_blacklistedNotifications.append(blacklistActivity);
}
NotificationWidget *widget = _widgetForNotifId[ blacklistActivity.ident() ];
scheduleWidgetToRemove(widget);
}
void ActivityWidget::scheduleWidgetToRemove(NotificationWidget *widget, int milliseconds)
{
if( !widget ) {
return;
}
// in five seconds from now, remove the widget.
QDateTime removeTime = QDateTime::currentDateTimeUtc().addMSecs(milliseconds);
QDateTime &it = _widgetsToRemove[widget];
if (!it.isValid() || it > removeTime) {
it = removeTime;
}
if( !_removeTimer.isActive() ) {
_removeTimer.start();
}
}
// Called every second to see if widgets need to be removed.
void ActivityWidget::slotCheckToCleanWidgets()
{
auto currentTime = QDateTime::currentDateTimeUtc();
auto it = _widgetsToRemove.begin();
while (it != _widgetsToRemove.end()) {
// loop over all widgets in the to-remove queue
QDateTime t = it.value();
NotificationWidget *widget = it.key();
if( currentTime > t ) {
// found one to remove!
Activity::Identifier id = widget->activity().ident();
_widgetForNotifId.remove(id);
widget->deleteLater();
it = _widgetsToRemove.erase(it);
} else {
++it;
}
}
if( _widgetsToRemove.isEmpty() ) {
_removeTimer.stop();
}
// check to see if the whole notification pane should be hidden
if( _widgetForNotifId.isEmpty() ) {
_ui->_notifyLabel->setHidden(true);
_ui->_notifyScroll->setHidden(true);
}
}
/* ==================================================================== */
ActivitySettings::ActivitySettings(QWidget *parent)
@@ -405,13 +513,16 @@ ActivitySettings::ActivitySettings(QWidget *parent)
_tab = new QTabWidget(this);
hbox->addWidget(_tab);
_activityWidget = new ActivityWidget(this);
_tab->addTab(_activityWidget, Theme::instance()->applicationIcon(), tr("Server Activity"));
_activityTabId = _tab->insertTab(0, _activityWidget, Theme::instance()->applicationIcon(), tr("Server Activity"));
connect(_activityWidget, SIGNAL(copyToClipboard()), this, SLOT(slotCopyToClipboard()));
connect(_activityWidget, SIGNAL(hideActivityTab(bool)), this, SLOT(setActivityTabHidden(bool)));
connect(_activityWidget, SIGNAL(guiLog(QString,QString)), this, SIGNAL(guiLog(QString,QString)));
_protocolWidget = new ProtocolWidget(this);
_tab->addTab(_protocolWidget, Theme::instance()->syncStateIcon(SyncResult::Success), tr("Sync Protocol"));
_tab->insertTab(1, _protocolWidget, Theme::instance()->syncStateIcon(SyncResult::Success), tr("Sync Protocol"));
connect(_protocolWidget, SIGNAL(copyToClipboard()), this, SLOT(slotCopyToClipboard()));
connect(_protocolWidget, SIGNAL(issueItemCountUpdated(int)),
this, SLOT(slotShowIssueItemCount(int)));
// Add the not-synced list into the tab
QWidget *w = new QWidget;
@@ -426,14 +537,49 @@ ActivitySettings::ActivitySettings(QWidget *parent)
connect(_copyBtn, SIGNAL(clicked()), this, SLOT(slotCopyToClipboard()));
w->setLayout(vbox2);
_tab->addTab(w, Theme::instance()->syncStateIcon(SyncResult::Problem), tr("Not Synced"));
_syncIssueTabId = _tab->insertTab(2, w, Theme::instance()->syncStateIcon(SyncResult::Problem), QString());
slotShowIssueItemCount(0); // to display the label.
// Add a progress indicator to spin if the acitivity list is updated.
_progressIndicator = new QProgressIndicator(this);
_tab->setCornerWidget(_progressIndicator);
connect(&_notificationCheckTimer, SIGNAL(timeout()),
this, SLOT(slotRegularNotificationCheck()));
// connect a model signal to stop the animation.
connect(_activityWidget, SIGNAL(rowsInserted()), _progressIndicator, SLOT(stopAnimation()));
// We want the protocol be the default
_tab->setCurrentIndex(1);
}
void ActivitySettings::setNotificationRefreshInterval( quint64 interval )
{
qDebug() << "Starting Notification refresh timer with " << interval/1000 << " sec interval";
_notificationCheckTimer.start(interval);
}
void ActivitySettings::setActivityTabHidden(bool hidden)
{
if( hidden && _activityTabId > -1 ) {
_tab->removeTab(_activityTabId);
_activityTabId = -1;
}
if( !hidden && _activityTabId == -1 ) {
_activityTabId = _tab->insertTab(0, _activityWidget, Theme::instance()->applicationIcon(), tr("Server Activity"));
}
}
void ActivitySettings::slotShowIssueItemCount(int cnt)
{
QString cntText = tr("Not Synced");
if( cnt ) {
//: %1 is the number of not synced files.
cntText = tr("Not Synced (%1)").arg(cnt);
}
_tab->setTabText(_syncIssueTabId, cntText);
}
void ActivitySettings::slotCopyToClipboard()
@@ -469,10 +615,32 @@ void ActivitySettings::slotRemoveAccount( AccountState *ptr )
void ActivitySettings::slotRefresh( AccountState* ptr )
{
if( ptr && ptr->isConnected() && isVisible()) {
qDebug() << "Refreshing Activity list for " << ptr->account()->displayName();
_progressIndicator->startAnimation();
_activityWidget->slotRefresh(ptr);
// QElapsedTimer isn't actually constructed as invalid.
if ( !_timeSinceLastCheck.contains(ptr) ) {
_timeSinceLastCheck[ptr].invalidate();
}
QElapsedTimer & timer = _timeSinceLastCheck[ptr];
// Fetch Activities only if visible and if last check is longer than 15 secs ago
if( timer.isValid() && timer.elapsed() < NOTIFICATION_REQUEST_FREE_PERIOD ) {
qDebug() << Q_FUNC_INFO << "do not check as last check is only secs ago: " << timer.elapsed() / 1000;
return;
}
if( ptr && ptr->isConnected() ) {
if( isVisible() || !timer.isValid() ) {
_progressIndicator->startAnimation();
_activityWidget->slotRefreshActivities( ptr);
}
_activityWidget->slotRefreshNotifications(ptr);
timer.start();
}
}
void ActivitySettings::slotRegularNotificationCheck()
{
AccountManager *am = AccountManager::instance();
foreach (AccountStatePtr a, am->accounts()) {
slotRefresh(a.data());
}
}
+51 -88
Ver Arquivo
@@ -22,6 +22,7 @@
#include "progressdispatcher.h"
#include "owncloudgui.h"
#include "account.h"
#include "activitydata.h"
#include "ui_activitywidget.h"
@@ -33,97 +34,15 @@ namespace OCC {
class Account;
class AccountStatusPtr;
class ProtocolWidget;
class JsonApiJob;
class NotificationWidget;
class ActivityListModel;
namespace Ui {
class ActivityWidget;
}
class Application;
/**
* @brief Activity Structure
* @ingroup gui
*
* contains all the information describing a single activity.
*/
class Activity
{
public:
qlonglong _id;
QString _subject;
QString _message;
QString _file;
QUrl _link;
QDateTime _dateTime;
QString _accName;
/**
* @brief Sort operator to sort the list youngest first.
* @param val
* @return
*/
bool operator<( const Activity& val ) const {
return _dateTime.toMSecsSinceEpoch() > val._dateTime.toMSecsSinceEpoch();
}
};
/**
* @brief The ActivityList
* @ingroup gui
*
* A QList based list of Activities
*/
class ActivityList:public QList<Activity>
{
public:
void setAccountName( const QString& name );
QString accountName() const;
private:
QString _accountName;
};
/**
* @brief The ActivityListModel
* @ingroup gui
*
* Simple list model to provide the list view with data.
*/
class ActivityListModel : public QAbstractListModel
{
Q_OBJECT
public:
explicit ActivityListModel(QWidget *parent=0);
QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE;
int rowCount(const QModelIndex& parent = QModelIndex()) const Q_DECL_OVERRIDE;
bool canFetchMore(const QModelIndex& ) const Q_DECL_OVERRIDE;
void fetchMore(const QModelIndex&) Q_DECL_OVERRIDE;
ActivityList activityList() { return _finalList; }
public slots:
void slotRefreshActivity(AccountState* ast);
void slotRemoveAccount( AccountState *ast );
private slots:
void slotActivitiesReceived(const QVariantMap& json, int statusCode);
signals:
void accountWithoutActivityApp(AccountState* ast);
private:
void startFetchJob(AccountState* s);
void combineActivityLists();
QMap<AccountState*, ActivityList> _activityLists;
ActivityList _finalList;
QSet<AccountState*> _currentlyFetching;
};
/**
* @brief The ActivityWidget class
* @ingroup gui
@@ -141,16 +60,37 @@ public:
QSize sizeHint() const Q_DECL_OVERRIDE { return ownCloudGui::settingsDialogSize(); }
void storeActivityList(QTextStream &ts);
/**
* Adjusts the activity tab's and some widgets' visibility
*
* Based on whether activities are enabled and whether notifications are
* available.
*/
void checkActivityTabVisibility();
public slots:
void slotOpenFile(QModelIndex indx);
void slotRefresh(AccountState* ptr);
void slotRefreshActivities(AccountState* ptr);
void slotRefreshNotifications(AccountState *ptr);
void slotRemoveAccount( AccountState *ptr );
void slotAccountWithoutActivityApp(AccountState *ast);
void slotAccountActivityStatus(AccountState *ast, int statusCode);
void slotRequestCleanupAndBlacklist(const Activity& blacklistActivity);
signals:
void guiLog(const QString&, const QString&);
void copyToClipboard();
void rowsInserted();
void hideActivityTab(bool);
void newNotificationList(const ActivityList& list);
private slots:
void slotBuildNotificationDisplay(const ActivityList& list);
void slotSendNotificationRequest(const QString &accountName, const QString& link, const QByteArray &verb);
void slotNotifyNetworkError( QNetworkReply* );
void slotNotifyServerFinished( const QString& reply, int replyCode );
void endNotificationRequest(NotificationWidget *widget , int replyCode);
void scheduleWidgetToRemove(NotificationWidget *widget, int milliseconds = 4500);
void slotCheckToCleanWidgets();
private:
void showLabels();
@@ -159,8 +99,21 @@ private:
QPushButton *_copyBtn;
QSet<QString> _accountsWithoutActivities;
QMap<Activity::Identifier, NotificationWidget*> _widgetForNotifId;
QElapsedTimer _guiLogTimer;
QSet<int> _guiLoggedNotifications;
ActivityList _blacklistedNotifications;
QHash<NotificationWidget*, QDateTime> _widgetsToRemove;
QTimer _removeTimer;
// number of currently running notification requests. If non zero,
// no query for notifications is started.
int _notificationRequestsRunning;
ActivityListModel *_model;
QVBoxLayout *_notificationsLayout;
};
@@ -183,7 +136,13 @@ public slots:
void slotRefresh( AccountState* ptr );
void slotRemoveAccount( AccountState *ptr );
void setNotificationRefreshInterval( quint64 interval );
private slots:
void slotCopyToClipboard();
void setActivityTabHidden(bool hidden);
void slotRegularNotificationCheck();
void slotShowIssueItemCount(int cnt);
signals:
void guiLog(const QString&, const QString&);
@@ -192,10 +151,14 @@ private:
bool event(QEvent* e) Q_DECL_OVERRIDE;
QTabWidget *_tab;
int _activityTabId;
int _syncIssueTabId;
ActivityWidget *_activityWidget;
ProtocolWidget *_protocolWidget;
QProgressIndicator *_progressIndicator;
QTimer _notificationCheckTimer;
QHash<AccountState*, QElapsedTimer> _timeSinceLastCheck;
};
}
+61 -3
Ver Arquivo
@@ -15,23 +15,81 @@
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="_headerLabel">
<widget class="QLabel" name="_notifyLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QListView" name="_activityList"/>
<widget class="QScrollArea" name="_notifyScroll">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<widget class="QWidget" name="_scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>677</width>
<height>70</height>
</rect>
</property>
</widget>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="_bottomLabel">
<widget class="QLabel" name="_headerLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QListView" name="_activityList">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="_bottomLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QDialogButtonBox" name="_dialogButtonBox"/>
</item>
</layout>
+11 -1
Ver Arquivo
@@ -182,7 +182,7 @@ Application::Application(int &argc, char **argv) :
// startup procedure.
connect(&_checkConnectionTimer, SIGNAL(timeout()), this, SLOT(slotCheckConnection()));
_checkConnectionTimer.setInterval(ConnectionValidator::defaultCallingIntervalMsec()); // check for connection every 32 seconds.
_checkConnectionTimer.setInterval(ConnectionValidator::DefaultCallingIntervalMsec); // check for connection every 32 seconds.
_checkConnectionTimer.start();
// Also check immediately
QTimer::singleShot( 0, this, SLOT( slotCheckConnection() ));
@@ -219,10 +219,14 @@ void Application::slotAccountStateRemoved(AccountState *accountState)
if (_gui) {
disconnect(accountState, SIGNAL(stateChanged(int)),
_gui, SLOT(slotAccountStateChanged()));
disconnect(accountState->account().data(), SIGNAL(serverVersionChanged(Account*,QString,QString)),
_gui, SLOT(slotTrayMessageIfServerUnsupported(Account*)));
}
if (_folderManager) {
disconnect(accountState, SIGNAL(stateChanged(int)),
_folderManager.data(), SLOT(slotAccountStateChanged()));
disconnect(accountState->account().data(), SIGNAL(serverVersionChanged(Account*,QString,QString)),
_folderManager.data(), SLOT(slotServerVersionChanged(Account*)));
}
// if there is no more account, show the wizard.
@@ -237,8 +241,14 @@ void Application::slotAccountStateAdded(AccountState *accountState)
{
connect(accountState, SIGNAL(stateChanged(int)),
_gui, SLOT(slotAccountStateChanged()));
connect(accountState->account().data(), SIGNAL(serverVersionChanged(Account*,QString,QString)),
_gui, SLOT(slotTrayMessageIfServerUnsupported(Account*)));
connect(accountState, SIGNAL(stateChanged(int)),
_folderManager.data(), SLOT(slotAccountStateChanged()));
connect(accountState->account().data(), SIGNAL(serverVersionChanged(Account*,QString,QString)),
_folderManager.data(), SLOT(slotServerVersionChanged(Account*)));
_gui->slotTrayMessageIfServerUnsupported(accountState->account().data());
}
void Application::slotCleanup()
+6 -1
Ver Arquivo
@@ -67,8 +67,13 @@ void ShibbolethCredentials::setAccount(Account* account)
{
AbstractCredentials::setAccount(account);
// This is for existing saved accounts.
if (_user.isEmpty()) {
_user = _account->credentialSetting(QLatin1String(userC)).toString();
}
// When constructed with a cookie (by the wizard), we usually don't know the
// user name yet. Request it now.
// user name yet. Request it now from the server.
if (_ready && _user.isEmpty()) {
QTimer::singleShot(1234, this, SLOT(slotFetchUser()));
}
+163 -342
Ver Arquivo
@@ -56,9 +56,10 @@ static void csyncLogCatcher(int /*verbosity*/,
Folder::Folder(const FolderDefinition& definition,
AccountState* accountState,
QObject* parent)
: QObject(parent)
, _accountState(0)
, _accountState(accountState)
, _definition(definition)
, _csyncError(false)
, _csyncUnavail(false)
@@ -69,8 +70,10 @@ Folder::Folder(const FolderDefinition& definition,
, _consecutiveFailingSyncs(0)
, _consecutiveFollowUpSyncs(0)
, _journal(definition.localPath)
, _csync_ctx(0)
{
qRegisterMetaType<SyncFileItemVector>("SyncFileItemVector");
qRegisterMetaType<SyncFileItem::Direction>("SyncFileItem::Direction");
qsrand(QTime::currentTime().msec());
_timeSinceLastSyncStart.start();
_timeSinceLastSyncDone.start();
@@ -85,68 +88,39 @@ Folder::Folder(const FolderDefinition& definition,
checkLocalPath();
_syncResult.setFolder(_definition.alias);
}
bool Folder::init()
{
// We need to reconstruct the url because the path needs to be fully decoded, as csync will re-encode the path:
// Remember that csync will just append the filename to the path and pass it to the vio plugin.
// csync_owncloud will then re-encode everything.
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
QUrl url = remoteUrl();
QString url_string = url.scheme() + QLatin1String("://") + url.authority(QUrl::EncodeDelimiters) + url.path(QUrl::FullyDecoded);
#else
// Qt4 was broken anyway as it did not encode the '#' as it should have done (it was actually a problem when parsing the path from QUrl::setPath
QString url_string = remoteUrl().toString();
#endif
url_string = Utility::toCSyncScheme(url_string);
_engine.reset(new SyncEngine(_accountState->account(), path(), remoteUrl(), remotePath(), &_journal));
// pass the setting if hidden files are to be ignored, will be read in csync_update
_engine->setIgnoreHiddenFiles(_definition.ignoreHiddenFiles);
QString localpath = path();
if (!setIgnoredFiles())
qWarning("Could not read system exclude file");
if( csync_create( &_csync_ctx, localpath.toUtf8().data(), url_string.toUtf8().data() ) < 0 ) {
qDebug() << "Unable to create csync-context!";
slotSyncError(tr("Unable to create csync-context"));
_csync_ctx = 0;
} else {
csync_set_log_callback( csyncLogCatcher );
csync_set_log_level( Logger::instance()->isNoop() ? 0 : 11 );
connect(_accountState.data(), SIGNAL(isConnectedChanged()), this, SIGNAL(canSyncChanged()));
connect(_engine.data(), SIGNAL(rootEtag(QString)), this, SLOT(etagRetreivedFromSyncEngine(QString)));
connect(_engine.data(), SIGNAL(treeWalkResult(const SyncFileItemVector&)),
this, SLOT(slotThreadTreeWalkResult(const SyncFileItemVector&)), Qt::QueuedConnection);
Q_ASSERT( _accountState );
connect(_engine.data(), SIGNAL(started()), SLOT(slotSyncStarted()), Qt::QueuedConnection);
connect(_engine.data(), SIGNAL(finished(bool)), SLOT(slotSyncFinished(bool)), Qt::QueuedConnection);
connect(_engine.data(), SIGNAL(csyncError(QString)), SLOT(slotSyncError(QString)), Qt::QueuedConnection);
connect(_engine.data(), SIGNAL(csyncUnavailable()), SLOT(slotCsyncUnavailable()), Qt::QueuedConnection);
if( csync_init( _csync_ctx ) < 0 ) {
qDebug() << "Could not initialize csync!" << csync_get_status(_csync_ctx) << csync_get_status_string(_csync_ctx);
QString errStr = SyncEngine::csyncErrorToString(CSYNC_STATUS(csync_get_status(_csync_ctx)));
const char *errMsg = csync_get_status_string(_csync_ctx);
if( errMsg ) {
errStr += QLatin1String("<br/>");
errStr += QString::fromUtf8(errMsg);
}
slotSyncError(errStr);
csync_destroy(_csync_ctx);
_csync_ctx = 0;
}
}
return _csync_ctx;
//direct connection so the message box is blocking the sync.
connect(_engine.data(), SIGNAL(aboutToRemoveAllFiles(SyncFileItem::Direction,bool*)),
SLOT(slotAboutToRemoveAllFiles(SyncFileItem::Direction,bool*)));
connect(_engine.data(), SIGNAL(aboutToRestoreBackup(bool*)),
SLOT(slotAboutToRestoreBackup(bool*)));
connect(_engine.data(), SIGNAL(folderDiscovered(bool,QString)), this, SLOT(slotFolderDiscovered(bool,QString)));
connect(_engine.data(), SIGNAL(transmissionProgress(ProgressInfo)), this, SLOT(slotTransmissionProgress(ProgressInfo)));
connect(_engine.data(), SIGNAL(itemCompleted(const SyncFileItem &, const PropagatorJob &)),
this, SLOT(slotItemCompleted(const SyncFileItem &, const PropagatorJob &)));
connect(_engine.data(), SIGNAL(newBigFolder(QString)), this, SLOT(slotNewBigFolderDiscovered(QString)));
connect(_engine.data(), SIGNAL(seenLockedFile(QString)), FolderMan::instance(), SLOT(slotSyncOnceFileUnlocks(QString)));
}
Folder::~Folder()
{
if( _engine ) {
_engine->abort();
_engine.reset(0);
}
// Destroy csync here.
csync_destroy(_csync_ctx);
}
void Folder::setAccountState( AccountState *account )
{
_accountState = account;
}
AccountState* Folder::accountState() const
{
return _accountState;
}
void Folder::checkLocalPath()
@@ -170,7 +144,7 @@ void Folder::checkLocalPath()
}
}
QString Folder::aliasGui() const
QString Folder::shortGuiRemotePathOrAppName() const
{
if (remotePath().length() > 0 && remotePath() != QLatin1String("/")) {
QString a = QFile(remotePath()).fileName();
@@ -197,7 +171,7 @@ QString Folder::path() const
return p;
}
QString Folder::shortGuiPath() const
QString Folder::shortGuiLocalPath() const
{
QString p = _definition.localPath;
QString home = QDir::homePath();
@@ -237,7 +211,7 @@ QString Folder::cleanPath()
bool Folder::isBusy() const
{
return !_engine.isNull();
return _engine->isSyncRunning();
}
QString Folder::remotePath() const
@@ -247,9 +221,6 @@ QString Folder::remotePath() const
QUrl Folder::remoteUrl() const
{
if (!_accountState) {
return QUrl("http://deleted-account");
}
return Account::concatUrlPath(_accountState->account()->davUrl(), remotePath());
}
@@ -265,19 +236,21 @@ bool Folder::canSync() const
void Folder::setSyncPaused( bool paused )
{
if (paused != _definition.paused) {
_definition.paused = paused;
saveToSettings();
if (paused == _definition.paused) {
return;
}
_definition.paused = paused;
saveToSettings();
if( !paused ) {
// qDebug() << "Syncing enabled on folder " << name();
setSyncState(SyncResult::NotYetStarted);
} else {
// do not stop or start the watcher here, that is done internally by
// folder class. Even if the watcher fires, the folder does not
// schedule itself because it checks the var. _enabled before.
setSyncState(SyncResult::Paused);
}
emit syncPausedChanged(this, paused);
emit syncStateChange();
emit canSyncChanged();
}
void Folder::setSyncState(SyncResult::Status state)
@@ -300,11 +273,6 @@ void Folder::slotRunEtagJob()
{
qDebug() << "* Trying to check" << remoteUrl().toString() << "for changes via ETag check. (time since last sync:" << (_timeSinceLastSyncDone.elapsed() / 1000) << "s)";
if (!_accountState) {
qDebug() << "Can't run EtagJob, account is deleted";
return;
}
AccountPtr account = _accountState->account();
if (!_requestEtagJob.isNull()) {
@@ -312,7 +280,7 @@ void Folder::slotRunEtagJob()
return;
}
if (_definition.paused || !_accountState->isConnected()) {
if (!canSync()) {
qDebug() << "Not syncing. :" << remoteUrl().toString() << _definition.paused << AccountState::stateString(_accountState->state());
return;
}
@@ -372,9 +340,7 @@ void Folder::etagRetreived(const QString& etag)
emit scheduleToSync(this);
}
if( _accountState ) {
_accountState->tagLastSuccessfullETagRequest();
}
_accountState->tagLastSuccessfullETagRequest();
}
void Folder::etagRetreivedFromSyncEngine(const QString& etag)
@@ -393,17 +359,19 @@ void Folder::bubbleUpSyncResult()
int updatedItems = 0;
int ignoredItems = 0;
int renamedItems = 0;
int conflictItems = 0;
int errorItems = 0;
SyncFileItemPtr firstItemNew;
SyncFileItemPtr firstItemDeleted;
SyncFileItemPtr firstItemUpdated;
SyncFileItemPtr firstItemRenamed;
SyncFileItemPtr firstConflictItem;
SyncFileItemPtr firstItemError;
SyncRunFileLog syncFileLog;
syncFileLog.start(path(), _engine ? _engine->stopWatch() : Utility::StopWatch() );
syncFileLog.start(path(), _engine->isSyncRunning() ? _engine->stopWatch() : Utility::StopWatch() );
QElapsedTimer timer;
timer.start();
@@ -414,6 +382,7 @@ void Folder::bubbleUpSyncResult()
// and process the item to the gui
if( item->_status == SyncFileItem::FatalError || item->_status == SyncFileItem::NormalError ) {
//: this displays an error string (%2) for a file %1
slotSyncError( tr("%1: %2").arg(item->_file, item->_errorString) );
errorItems++;
if (!firstItemError) {
@@ -422,6 +391,11 @@ void Folder::bubbleUpSyncResult()
} else if( item->_status == SyncFileItem::FileIgnored ) {
// ignored files don't show up in notifications
continue;
} else if( item->_status == SyncFileItem::Conflict ) {
conflictItems++;
if (!firstConflictItem) {
firstConflictItem = item;
}
} else {
// add new directories or remove gone away dirs to the watcher
if (item->_isDirectory && item->_instruction == CSYNC_INSTRUCTION_NEW ) {
@@ -444,7 +418,6 @@ void Folder::bubbleUpSyncResult()
if (!firstItemDeleted)
firstItemDeleted = item;
break;
case CSYNC_INSTRUCTION_CONFLICT:
case CSYNC_INSTRUCTION_SYNC:
if (!item->_isDirectory) {
updatedItems++;
@@ -478,32 +451,35 @@ void Folder::bubbleUpSyncResult()
_syncResult.setWarnCount(ignoredItems);
if( firstItemNew ) {
createGuiLog( firstItemNew->_file, SyncFileStatus::STATUS_NEW, newItems );
createGuiLog( firstItemNew->_file, LogStatusNew, newItems );
}
if( firstItemDeleted ) {
createGuiLog( firstItemDeleted->_file, SyncFileStatus::STATUS_REMOVE, removedItems );
createGuiLog( firstItemDeleted->_file, LogStatusRemove, removedItems );
}
if( firstItemUpdated ) {
createGuiLog( firstItemUpdated->_file, SyncFileStatus::STATUS_UPDATED, updatedItems );
createGuiLog( firstItemUpdated->_file, LogStatusUpdated, updatedItems );
}
if( firstItemRenamed ) {
SyncFileStatus status(SyncFileStatus::STATUS_RENAME);
LogStatus status(LogStatusRename);
// if the path changes it's rather a move
QDir renTarget = QFileInfo(firstItemRenamed->_renameTarget).dir();
QDir renSource = QFileInfo(firstItemRenamed->_file).dir();
if(renTarget != renSource) {
status.set(SyncFileStatus::STATUS_MOVE);
status = LogStatusMove;
}
createGuiLog( firstItemRenamed->_originalFile, status, renamedItems, firstItemRenamed->_renameTarget );
}
createGuiLog( firstItemError->_file, SyncFileStatus::STATUS_ERROR, errorItems );
if( firstConflictItem ) {
createGuiLog( firstConflictItem->_file, LogStatusConflict, conflictItems );
}
createGuiLog( firstItemError->_file, LogStatusError, errorItems );
qDebug() << "OO folder slotSyncFinished: result: " << int(_syncResult.status());
}
void Folder::createGuiLog( const QString& filename, SyncFileStatus status, int count,
void Folder::createGuiLog( const QString& filename, LogStatus status, int count,
const QString& renameTarget )
{
if(count > 0) {
@@ -512,53 +488,56 @@ void Folder::createGuiLog( const QString& filename, SyncFileStatus status, int c
QString file = QDir::toNativeSeparators(filename);
QString text;
// not all possible values of status are evaluated here because the others
// are not used in the calling function. Please check there.
switch (status.tag()) {
case SyncFileStatus::STATUS_REMOVE:
switch (status) {
case LogStatusRemove:
if( count > 1 ) {
text = tr("%1 and %2 other files have been removed.", "%1 names a file.").arg(file).arg(count-1);
text = tr("%1 and %n other file(s) have been removed.", "", count-1).arg(file);
} else {
text = tr("%1 has been removed.", "%1 names a file.").arg(file);
}
break;
case SyncFileStatus::STATUS_NEW:
case LogStatusNew:
if( count > 1 ) {
text = tr("%1 and %2 other files have been downloaded.", "%1 names a file.").arg(file).arg(count-1);
text = tr("%1 and %n other file(s) have been downloaded.", "", count-1).arg(file);
} else {
text = tr("%1 has been downloaded.", "%1 names a file.").arg(file);
}
break;
case SyncFileStatus::STATUS_UPDATED:
case LogStatusUpdated:
if( count > 1 ) {
text = tr("%1 and %2 other files have been updated.").arg(file).arg(count-1);
text = tr("%1 and %n other file(s) have been updated.", "", count-1).arg(file);
} else {
text = tr("%1 has been updated.", "%1 names a file.").arg(file);
}
break;
case SyncFileStatus::STATUS_RENAME:
case LogStatusRename:
if( count > 1 ) {
text = tr("%1 has been renamed to %2 and %3 other files have been renamed.").arg(file).arg(renameTarget).arg(count-1);
text = tr("%1 has been renamed to %2 and %n other file(s) have been renamed.", "", count-1).arg(file).arg(renameTarget);
} else {
text = tr("%1 has been renamed to %2.", "%1 and %2 name files.").arg(file).arg(renameTarget);
}
break;
case SyncFileStatus::STATUS_MOVE:
case LogStatusMove:
if( count > 1 ) {
text = tr("%1 has been moved to %2 and %3 other files have been moved.").arg(file).arg(renameTarget).arg(count-1);
text = tr("%1 has been moved to %2 and %n other file(s) have been moved.", "", count-1).arg(file).arg(renameTarget);
} else {
text = tr("%1 has been moved to %2.").arg(file).arg(renameTarget);
}
break;
case SyncFileStatus::STATUS_ERROR:
case LogStatusConflict:
if( count > 1 ) {
text = tr("%1 and %2 other files could not be synced due to errors. See the log for details.", "%1 names a file.").arg(file).arg(count-1);
text = tr("%1 has and %n other file(s) have sync conflicts.", "", count-1).arg(file);
} else {
text = tr("%1 has a sync conflict. Please check the conflict file!").arg(file);
}
break;
case LogStatusError:
if( count > 1 ) {
text = tr("%1 and %n other file(s) could not be synced due to errors. See the log for details.", "", count-1).arg(file);
} else {
text = tr("%1 could not be synced due to an error. See the log for details.").arg(file);
}
break;
default:
break;
}
if( !text.isEmpty() ) {
@@ -601,7 +580,8 @@ void Folder::slotWatchedPathChanged(const QString& path)
{
// When no sync is running or it's in the prepare phase, we can
// always schedule a new sync.
if (! _engine || _syncResult.status() == SyncResult::SyncPrepare) {
if (! _engine->isSyncRunning() || _syncResult.status() == SyncResult::SyncPrepare) {
emit watchedFileChangedExternally(path);
emit scheduleToSync(this);
return;
}
@@ -625,95 +605,19 @@ void Folder::slotWatchedPathChanged(const QString& path)
#endif
if (! ownChange) {
emit watchedFileChangedExternally(path);
emit scheduleToSync(this);
}
}
/**
* Whether this item should get an ERROR icon through the Socket API.
*
* The Socket API should only present serious, permanent errors to the user.
* In particular SoftErrors should just retain their 'needs to be synced'
* icon as the problem is most likely going to resolve itself quickly and
* automatically.
*/
static bool showErrorInSocketApi(const SyncFileItem& item)
{
const auto status = item._status;
return status == SyncFileItem::NormalError
|| status == SyncFileItem::FatalError;
}
static void addErroredSyncItemPathsToList(const SyncFileItemVector& items, QSet<QString>* set) {
foreach (const SyncFileItemPtr &item, items) {
if (showErrorInSocketApi(*item)) {
set->insert(item->_file);
}
}
}
void Folder::slotThreadTreeWalkResult(const SyncFileItemVector& items)
{
addErroredSyncItemPathsToList(items, &this->_stateLastSyncItemsWithErrorNew);
_syncResult.setSyncFileItemVector(items);
}
void Folder::slotAboutToPropagate(SyncFileItemVector& items)
{
// empty the tainted list since the status generation code will use the _syncedItems
// (which imply the folder) to generate the syncing state icon now.
_stateTaintedFolders.clear();
addErroredSyncItemPathsToList(items, &this->_stateLastSyncItemsWithErrorNew);
}
bool Folder::estimateState(QString fn, csync_ftw_type_e t, SyncFileStatus* s)
{
if (t == CSYNC_FTW_TYPE_DIR) {
if (Utility::doesSetContainPrefix(_stateLastSyncItemsWithError, fn)) {
qDebug() << Q_FUNC_INFO << "Folder has error" << fn;
s->set(SyncFileStatus::STATUS_ERROR);
return true;
}
// If sync is running, check _syncedItems, possibly give it STATUS_EVAL (=syncing down)
if (!_engine.isNull()) {
if (_engine->estimateState(fn, t, s)) {
return true;
}
}
if(!fn.endsWith(QLatin1Char('/'))) {
fn.append(QLatin1Char('/'));
}
if (Utility::doesSetContainPrefix(_stateTaintedFolders, fn)) {
qDebug() << Q_FUNC_INFO << "Folder is tainted, EVAL!" << fn;
s->set(SyncFileStatus::STATUS_EVAL);
return true;
}
return false;
} else if ( t== CSYNC_FTW_TYPE_FILE) {
// check if errorList has the directory/file
if (Utility::doesSetContainPrefix(_stateLastSyncItemsWithError, fn)) {
s->set(SyncFileStatus::STATUS_ERROR);
return true;
}
// If sync running: _syncedItems -> SyncingState
if (!_engine.isNull()) {
if (_engine->estimateState(fn, t, s)) {
return true;
}
}
}
return false;
}
void Folder::saveToSettings() const
{
if (!_accountState) {
qDebug() << "Can't save folder to settings, account is deleted";
return;
}
auto settings = _accountState->settings();
settings->beginGroup(QLatin1String("Folders"));
FolderDefinition::save(*settings, _definition);
@@ -724,11 +628,6 @@ void Folder::saveToSettings() const
void Folder::removeFromSettings() const
{
if (!_accountState) {
qDebug() << "Can't remove folder from settings, account is deleted";
return;
}
auto settings = _accountState->settings();
settings->beginGroup(QLatin1String("Folders"));
settings->remove(_definition.alias);
@@ -736,56 +635,19 @@ void Folder::removeFromSettings() const
bool Folder::isFileExcludedAbsolute(const QString& fullPath) const
{
if (!fullPath.startsWith(path())) {
// Mark paths we're not responsible for as excluded...
return true;
}
QString myFullPath = fullPath;
if (myFullPath.endsWith(QLatin1Char('/'))) {
myFullPath.chop(1);
}
QString relativePath = myFullPath.mid(path().size());
auto excl = ExcludedFiles::instance().isExcluded(myFullPath, relativePath, _definition.ignoreHiddenFiles);
return excl != CSYNC_NOT_EXCLUDED;
return _engine->excludedFiles().isExcluded(fullPath, path(), _definition.ignoreHiddenFiles);
}
bool Folder::isFileExcludedRelative(const QString& relativePath) const
{
return isFileExcludedAbsolute(path() + relativePath);
return _engine->excludedFiles().isExcluded(path() + relativePath, path(), _definition.ignoreHiddenFiles);
}
void Folder::watcherSlot(QString fn)
{
// FIXME: On OS X we could not do this "if" since on OS X the file watcher ignores events for ourselves
// however to have the same behaviour atm on all platforms, we don't do it
if (!_engine.isNull()) {
qDebug() << Q_FUNC_INFO << "Sync running, IGNORE event for " << fn;
return;
}
QFileInfo fi(fn);
if (fi.isFile()) {
fn = fi.path(); // depending on OS, file watcher might be for dir or file
}
// Make it a relative path depending on the folder
QString relativePath = fn.remove(0, path().length());
if( !relativePath.endsWith(QLatin1Char('/'))) {
relativePath.append(QLatin1Char('/'));
}
qDebug() << Q_FUNC_INFO << fi.canonicalFilePath() << fn << relativePath;
_stateTaintedFolders.insert(relativePath);
// Notify the SocketAPI?
}
void Folder::slotTerminateSync()
{
qDebug() << "folder " << alias() << " Terminating!";
if( _engine ) {
if( _engine->isSyncRunning() ) {
_engine->abort();
// Do not display an error message, user knows his own actions.
@@ -826,30 +688,25 @@ void Folder::wipe()
QFile::remove( stateDbFile + "-wal" );
QFile::remove( stateDbFile + "-journal" );
FolderMan::instance()->socketApi()->slotRegisterPath(alias());
if (canSync())
FolderMan::instance()->socketApi()->slotRegisterPath(alias());
}
bool Folder::setIgnoredFiles()
{
bool ok = false;
ConfigFile cfgFile;
csync_clear_exclude_list( _csync_ctx );
QString excludeList = cfgFile.excludeFile( ConfigFile::SystemScope );
if( !excludeList.isEmpty() ) {
qDebug() << "==== added system ignore list to csync:" << excludeList.toUtf8();
if (csync_add_exclude_list( _csync_ctx, excludeList.toUtf8() ) == 0) {
ok = true;
}
ConfigFile cfg;
QString systemList = cfg.excludeFile(ConfigFile::SystemScope);
if( QFile::exists(systemList) ) {
qDebug() << "==== adding system ignore list to csync:" << systemList;
_engine->excludedFiles().addExcludeFilePath(systemList);
}
excludeList = cfgFile.excludeFile( ConfigFile::UserScope );
if( !excludeList.isEmpty() ) {
qDebug() << "==== added user defined ignore list to csync:" << excludeList.toUtf8();
csync_add_exclude_list( _csync_ctx, excludeList.toUtf8() );
// reading the user exclude file is optional
QString userList = cfg.excludeFile(ConfigFile::UserScope);
if( QFile::exists(userList) ) {
qDebug() << "==== adding user defined ignore list to csync:" << userList;
_engine->excludedFiles().addExcludeFilePath(userList);
}
return ok;
return _engine->excludedFiles().reloadExcludes();
}
void Folder::setProxyDirty(bool value)
@@ -864,25 +721,11 @@ bool Folder::proxyDirty()
void Folder::startSync(const QStringList &pathList)
{
if (!_accountState) {
qDebug() << "Can't startSync, account is deleted";
return;
}
Q_UNUSED(pathList)
if (!_csync_ctx) {
// no _csync_ctx yet, initialize it.
init();
if (!_csync_ctx) {
qDebug() << Q_FUNC_INFO << "init failed.";
// the error should already be set
QMetaObject::invokeMethod(this, "slotSyncFinished", Qt::QueuedConnection, Q_ARG(bool, false));
return;
}
} else if (proxyDirty()) {
if (proxyDirty()) {
setProxyDirty(false);
}
csync_set_log_callback( csyncLogCatcher );
csync_set_log_level( Logger::instance()->isNoop() ? 0 : 11 );
if (isBusy()) {
@@ -902,42 +745,13 @@ void Folder::startSync(const QStringList &pathList)
qDebug() << "*** Start syncing " << remoteUrl().toString() << " - client version"
<< qPrintable(Theme::instance()->version());
if (! setIgnoredFiles())
if (!setIgnoredFiles())
{
slotSyncError(tr("Could not read system exclude file"));
QMetaObject::invokeMethod(this, "slotSyncFinished", Qt::QueuedConnection, Q_ARG(bool, false));
return;
}
// pass the setting if hidden files are to be ignored, will be read in csync_update
_csync_ctx->ignore_hidden_files = _definition.ignoreHiddenFiles;
_engine.reset(new SyncEngine( _accountState->account(), _csync_ctx, path(), remoteUrl().path(), remotePath(), &_journal));
qRegisterMetaType<SyncFileItemVector>("SyncFileItemVector");
qRegisterMetaType<SyncFileItem::Direction>("SyncFileItem::Direction");
connect(_engine.data(), SIGNAL(rootEtag(QString)), this, SLOT(etagRetreivedFromSyncEngine(QString)));
connect( _engine.data(), SIGNAL(treeWalkResult(const SyncFileItemVector&)),
this, SLOT(slotThreadTreeWalkResult(const SyncFileItemVector&)), Qt::QueuedConnection);
connect( _engine.data(), SIGNAL(aboutToPropagate(SyncFileItemVector&)),
this, SLOT(slotAboutToPropagate(SyncFileItemVector&)));
connect(_engine.data(), SIGNAL(started()), SLOT(slotSyncStarted()), Qt::QueuedConnection);
connect(_engine.data(), SIGNAL(finished(bool)), SLOT(slotSyncFinished(bool)), Qt::QueuedConnection);
connect(_engine.data(), SIGNAL(csyncError(QString)), SLOT(slotSyncError(QString)), Qt::QueuedConnection);
connect(_engine.data(), SIGNAL(csyncUnavailable()), SLOT(slotCsyncUnavailable()), Qt::QueuedConnection);
//direct connection so the message box is blocking the sync.
connect(_engine.data(), SIGNAL(aboutToRemoveAllFiles(SyncFileItem::Direction,bool*)),
SLOT(slotAboutToRemoveAllFiles(SyncFileItem::Direction,bool*)));
connect(_engine.data(), SIGNAL(folderDiscovered(bool,QString)), this, SLOT(slotFolderDiscovered(bool,QString)));
connect(_engine.data(), SIGNAL(transmissionProgress(ProgressInfo)), this, SLOT(slotTransmissionProgress(ProgressInfo)));
connect(_engine.data(), SIGNAL(itemCompleted(const SyncFileItem &, const PropagatorJob &)),
this, SLOT(slotItemCompleted(const SyncFileItem &, const PropagatorJob &)));
connect(_engine.data(), SIGNAL(syncItemDiscovered(const SyncFileItem &)), this, SLOT(slotSyncItemDiscovered(const SyncFileItem &)));
connect(_engine.data(), SIGNAL(newBigFolder(QString)), this, SLOT(slotNewBigFolderDiscovered(QString)));
setDirtyNetworkLimits();
ConfigFile cfgFile;
@@ -945,6 +759,8 @@ void Folder::startSync(const QStringList &pathList)
quint64 limit = newFolderLimit.first ? newFolderLimit.second * 1000 * 1000 : -1; // convert from MB to B
_engine->setNewBigFolderSizeLimit(limit);
_engine->setIgnoreHiddenFiles(_definition.ignoreHiddenFiles);
QMetaObject::invokeMethod(_engine.data(), "startSync", Qt::QueuedConnection);
// disable events until syncing is done
@@ -954,27 +770,24 @@ void Folder::startSync(const QStringList &pathList)
void Folder::setDirtyNetworkLimits()
{
if (_engine) {
ConfigFile cfg;
int downloadLimit = -75; // 75%
int useDownLimit = cfg.useDownloadLimit();
if (useDownLimit >= 1) {
downloadLimit = cfg.downloadLimit() * 1000;
} else if (useDownLimit == 0) {
downloadLimit = 0;
}
int uploadLimit = -75; // 75%
int useUpLimit = cfg.useUploadLimit();
if ( useUpLimit >= 1) {
uploadLimit = cfg.uploadLimit() * 1000;
} else if (useUpLimit == 0) {
uploadLimit = 0;
}
_engine->setNetworkLimits(uploadLimit, downloadLimit);
ConfigFile cfg;
int downloadLimit = -75; // 75%
int useDownLimit = cfg.useDownloadLimit();
if (useDownLimit >= 1) {
downloadLimit = cfg.downloadLimit() * 1000;
} else if (useDownLimit == 0) {
downloadLimit = 0;
}
int uploadLimit = -75; // 75%
int useUpLimit = cfg.useUploadLimit();
if ( useUpLimit >= 1) {
uploadLimit = cfg.uploadLimit() * 1000;
} else if (useUpLimit == 0) {
uploadLimit = 0;
}
_engine->setNetworkLimits(uploadLimit, downloadLimit);
}
@@ -1013,20 +826,11 @@ void Folder::slotSyncFinished(bool success)
}
bubbleUpSyncResult();
bool anotherSyncNeeded = false;
if (_engine) {
anotherSyncNeeded = _engine->isAnotherSyncNeeded();
_engine.reset(0);
}
bool anotherSyncNeeded = _engine->isAnotherSyncNeeded();
// _watcher->setEventsEnabledDelayed(2000);
// This is for sync state calculation
_stateLastSyncItemsWithError = _stateLastSyncItemsWithErrorNew;
_stateLastSyncItemsWithErrorNew.clear();
_stateTaintedFolders.clear(); // heuristic: assume the sync had been done, new file watches needed to taint dirs
if (_csyncError) {
_syncResult.setStatus(SyncResult::Error);
qDebug() << " ** error Strings: " << _errors;
@@ -1122,10 +926,6 @@ void Folder::slotTransmissionProgress(const ProgressInfo &pi)
// a item is completed: count the errors and forward to the ProgressDispatcher
void Folder::slotItemCompleted(const SyncFileItem &item, const PropagatorJob& job)
{
if (showErrorInSocketApi(item)) {
_stateLastSyncItemsWithErrorNew.insert(item._file);
}
if (Progress::isWarningKind(item._status)) {
// Count all error conditions.
_syncResult.setWarnCount(_syncResult.warnCount()+1);
@@ -1133,11 +933,6 @@ void Folder::slotItemCompleted(const SyncFileItem &item, const PropagatorJob& jo
emit ProgressDispatcher::instance()->itemCompleted(alias(), item, job);
}
void Folder::slotSyncItemDiscovered(const SyncFileItem & item)
{
emit ProgressDispatcher::instance()->syncItemDiscovered(alias(), item);
}
void Folder::slotNewBigFolderDiscovered(const QString &newF)
{
auto newFolder = newF;
@@ -1147,39 +942,46 @@ void Folder::slotNewBigFolderDiscovered(const QString &newF)
auto journal = journalDb();
// Add the entry to the blacklist if it is neither in the blacklist or whitelist already
auto blacklist = journal->getSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList);
auto whitelist = journal->getSelectiveSyncList(SyncJournalDb::SelectiveSyncWhiteList);
if (!blacklist.contains(newFolder) && !whitelist.contains(newFolder)) {
bool ok1, ok2;
auto blacklist = journal->getSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, &ok1);
auto whitelist = journal->getSelectiveSyncList(SyncJournalDb::SelectiveSyncWhiteList, &ok2);
if (ok1 && ok2 && !blacklist.contains(newFolder) && !whitelist.contains(newFolder)) {
blacklist.append(newFolder);
journal->setSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, blacklist);
}
// And add the entry to the undecided list and signal the UI
auto undecidedList = journal->getSelectiveSyncList(SyncJournalDb::SelectiveSyncUndecidedList);
if (!undecidedList.contains(newFolder)) {
undecidedList.append(newFolder);
journal->setSelectiveSyncList(SyncJournalDb::SelectiveSyncUndecidedList, undecidedList);
emit newBigFolderDiscovered(newFolder);
}
QString message = tr("A new folder larger than %1 MB has been added: %2.\n"
"Please go in the settings to select it if you wish to download it.")
auto undecidedList = journal->getSelectiveSyncList(SyncJournalDb::SelectiveSyncUndecidedList, &ok1);
if( ok1 ) {
if (!undecidedList.contains(newFolder)) {
undecidedList.append(newFolder);
journal->setSelectiveSyncList(SyncJournalDb::SelectiveSyncUndecidedList, undecidedList);
emit newBigFolderDiscovered(newFolder);
}
QString message = tr("A new folder larger than %1 MB has been added: %2.\n"
"Please go in the settings to select it if you wish to download it.")
.arg(ConfigFile().newBigFolderSizeLimit().second).arg(newF);
auto logger = Logger::instance();
logger->postOptionalGuiLog(Theme::instance()->appNameGUI(), message);
auto logger = Logger::instance();
logger->postOptionalGuiLog(Theme::instance()->appNameGUI(), message);
}
}
void Folder::slotAboutToRemoveAllFiles(SyncFileItem::Direction, bool *cancel)
{
ConfigFile cfgFile;
if (!cfgFile.promptDeleteFiles())
return;
QString msg =
tr("This sync would remove all the files in the sync folder '%1'.\n"
"This might be because the folder was silently reconfigured, or that all "
"the files were manually removed.\n"
"Are you sure you want to perform this operation?");
QMessageBox msgBox(QMessageBox::Warning, tr("Remove All Files?"),
msg.arg(alias()));
msg.arg(shortGuiLocalPath()));
msgBox.addButton(tr("Remove all files"), QMessageBox::DestructiveRole);
QPushButton* keepBtn = msgBox.addButton(tr("Keep files"), QMessageBox::AcceptRole);
if (msgBox.exec() == -1) {
@@ -1196,6 +998,25 @@ void Folder::slotAboutToRemoveAllFiles(SyncFileItem::Direction, bool *cancel)
}
}
void Folder::slotAboutToRestoreBackup(bool *restore)
{
QString msg =
tr("This sync would reset the files to an erlier time in the sync folder '%1'.\n"
"This might be because a backup was restored on the server.\n"
"Continuing the sync as normal will cause all your files to be overwritten by an older "
"file in an earlier state. "
"Do you want to keep your local most recent files as conflict files?");
QMessageBox msgBox(QMessageBox::Warning, tr("Backup detected"),
msg.arg(shortGuiLocalPath()));
msgBox.addButton(tr("Normal Synchronisation"), QMessageBox::DestructiveRole);
QPushButton* keepBtn = msgBox.addButton(tr("Keep Local Files as Conflict"), QMessageBox::AcceptRole);
if (msgBox.exec() == -1) {
*restore = true;
return;
}
*restore = msgBox.clickedButton() == keepBtn;
}
void FolderDefinition::save(QSettings& settings, const FolderDefinition& folder)
+28 -38
Ver Arquivo
@@ -21,21 +21,13 @@
#include "progressdispatcher.h"
#include "syncjournaldb.h"
#include "clientproxy.h"
#include "syncfilestatus.h"
#include "networkjobs.h"
#include <csync.h>
#include <QDir>
#include <QHash>
#include <QSet>
#include <QObject>
#include <QStringList>
#include <QDebug>
#include <QTimer>
#include <qelapsedtimer.h>
class QThread;
class QSettings;
@@ -87,7 +79,7 @@ class Folder : public QObject
Q_OBJECT
public:
Folder(const FolderDefinition& definition, QObject* parent = 0L);
Folder(const FolderDefinition& definition, AccountState* accountState, QObject* parent = 0L);
~Folder();
@@ -97,19 +89,18 @@ public:
/**
* The account the folder is configured on.
*/
void setAccountState( AccountState *account );
AccountState* accountState() const;
AccountState* accountState() const { return _accountState.data(); }
/**
* alias or nickname
*/
QString alias() const;
QString aliasGui() const; // since 2.0 we don't want to show aliases anymore, show the path instead
QString shortGuiRemotePathOrAppName() const; // since 2.0 we don't want to show aliases anymore, show the path instead
/**
* short path to display on the GUI (native separators)
* short local path to display on the GUI (native separators)
*/
QString shortGuiPath() const;
QString shortGuiLocalPath() const;
/**
* local folder path
@@ -133,8 +124,6 @@ public:
/**
* switch sync on or off
* If the sync is switched off, the startSync method is not going to
* be called.
*/
void setSyncPaused( bool );
@@ -142,8 +131,6 @@ public:
/**
* Returns true when the folder may sync.
*
* !syncPaused() && accountState->isConnected().
*/
bool canSync() const;
@@ -184,8 +171,7 @@ public:
// Used by the Socket API
SyncJournalDb *journalDb() { return &_journal; }
bool estimateState(QString fn, csync_ftw_type_e t, SyncFileStatus* s);
SyncEngine &syncEngine() { return *_engine; }
RequestEtagJob *etagJob() { return _requestEtagJob; }
qint64 msecSinceLastSync() const { return _timeSinceLastSyncDone.elapsed(); }
@@ -214,6 +200,14 @@ signals:
void scheduleToSync(Folder*);
void progressInfo(const ProgressInfo& progress);
void newBigFolderDiscovered(const QString &); // A new folder bigger than the threshold was discovered
void syncPausedChanged(Folder*, bool paused);
void canSyncChanged();
/**
* Fires for each change inside this folder that wasn't caused
* by sync activity.
*/
void watchedFileChangedExternally(const QString& path);
public slots:
@@ -222,7 +216,9 @@ public slots:
*/
void slotTerminateSync();
// connected to the corresponding signals in the SyncEngine
void slotAboutToRemoveAllFiles(SyncFileItem::Direction, bool*);
void slotAboutToRestoreBackup(bool*);
/**
@@ -256,33 +252,38 @@ private slots:
void slotFolderDiscovered(bool local, QString folderName);
void slotTransmissionProgress(const ProgressInfo& pi);
void slotItemCompleted(const SyncFileItem&, const PropagatorJob&);
void slotSyncItemDiscovered(const SyncFileItem & item);
void slotRunEtagJob();
void etagRetreived(const QString &);
void etagRetreivedFromSyncEngine(const QString &);
void slotAboutToPropagate(SyncFileItemVector& );
void slotThreadTreeWalkResult(const SyncFileItemVector& ); // after sync is done
void slotEmitFinishedDelayed();
void watcherSlot(QString);
void slotNewBigFolderDiscovered(const QString &);
private:
bool init();
bool setIgnoredFiles();
void bubbleUpSyncResult();
void checkLocalPath();
void createGuiLog(const QString& filename, SyncFileStatus status, int count,
enum LogStatus {
LogStatusRemove,
LogStatusRename,
LogStatusMove,
LogStatusNew,
LogStatusError,
LogStatusConflict,
LogStatusUpdated
};
void createGuiLog(const QString& filename, LogStatus status, int count,
const QString& renameTarget = QString::null );
QPointer<AccountState> _accountState;
AccountStatePtr _accountState;
FolderDefinition _definition;
SyncResult _syncResult;
@@ -307,20 +308,9 @@ private:
/// Reset when no follow-up is requested.
int _consecutiveFollowUpSyncs;
// SocketAPI: Cache files and folders that had errors so that they can
// get a red ERROR icon.
QSet<QString> _stateLastSyncItemsWithErrorNew; // gets moved to _stateLastSyncItemsWithError at end of sync
QSet<QString> _stateLastSyncItemsWithError;
// SocketAPI: A folder is tained if we got a file watcher notification
// for it. It's displayed as EVAL.
QSet<QString> _stateTaintedFolders;
SyncJournalDb _journal;
ClientProxy _clientProxy;
CSYNC *_csync_ctx;
};
}
+84 -45
Ver Arquivo
@@ -22,6 +22,7 @@
#include "accountstate.h"
#include "accountmanager.h"
#include "filesystem.h"
#include "lockwatcher.h"
#include <syncengine.h>
#ifdef Q_OS_MAC
@@ -32,7 +33,6 @@
#endif
#include <QMessageBox>
#include <QPointer>
#include <QtCore>
#include <QMutableSetIterator>
#include <QSet>
@@ -45,12 +45,13 @@ FolderMan::FolderMan(QObject *parent) :
QObject(parent),
_currentSyncFolder(0),
_syncEnabled( true ),
_lockWatcher(new LockWatcher),
_appRestartRequired(false)
{
Q_ASSERT(!_instance);
_instance = this;
_socketApi = new SocketApi(this);
_socketApi.reset(new SocketApi);
ConfigFile cfg;
int polltime = cfg.remotePollInterval();
@@ -65,6 +66,9 @@ FolderMan::FolderMan(QObject *parent) :
connect(AccountManager::instance(), SIGNAL(accountRemoved(AccountState*)),
SLOT(slotRemoveFoldersForAccount(AccountState*)));
connect(_lockWatcher.data(), SIGNAL(fileUnlocked(QString)),
SLOT(slotScheduleFolderOwningFile(QString)));
}
FolderMan *FolderMan::instance()
@@ -89,9 +93,7 @@ void FolderMan::unloadFolder( Folder *f )
return;
}
if( _socketApi ) {
_socketApi->slotUnregisterPath(f->alias());
}
_socketApi->slotUnregisterPath(f->alias());
if( _folderWatchers.contains(f->alias())) {
_folderWatchers.remove(f->alias());
@@ -106,6 +108,12 @@ void FolderMan::unloadFolder( Folder *f )
this, SLOT(slotFolderSyncFinished(SyncResult)));
disconnect(f, SIGNAL(syncStateChange()),
this, SLOT(slotForwardFolderSyncStateChange()));
disconnect(f, SIGNAL(syncPausedChanged(Folder*,bool)),
this, SLOT(slotFolderSyncPaused(Folder*,bool)));
disconnect(&f->syncEngine().syncFileStatusTracker(), SIGNAL(fileStatusChanged(const QString &, SyncFileStatus)),
_socketApi.data(), SLOT(slotFileStatusChanged(const QString &, SyncFileStatus)));
disconnect(f, SIGNAL(watchedFileChangedExternally(QString)),
&f->syncEngine().syncFileStatusTracker(), SLOT(slotPathTouched(QString)));
}
int FolderMan::unloadAndDeleteAllFolders()
@@ -144,16 +152,13 @@ void FolderMan::registerFolderMonitor( Folder *folder )
// to the signal mapper which maps to the folder alias. The changed path
// is lost this way, but we do not need it for the current implementation.
connect(fw, SIGNAL(pathChanged(QString)), folder, SLOT(slotWatchedPathChanged(QString)));
_folderWatchers.insert(folder->alias(), fw);
// This is at the moment only for the behaviour of the SocketApi.
connect(fw, SIGNAL(pathChanged(QString)), folder, SLOT(watcherSlot(QString)));
_folderWatchers.insert(folder->alias(), fw);
}
// register the folder with the socket API
if( _socketApi ) {
if (folder->canSync())
_socketApi->slotRegisterPath(folder->alias());
}
}
void FolderMan::addMonitorPath( const QString& alias, const QString& path )
@@ -204,9 +209,8 @@ int FolderMan::setupFolders()
foreach (const auto& folderAlias, settings->childGroups()) {
FolderDefinition folderDefinition;
if (FolderDefinition::load(*settings, folderAlias, &folderDefinition)) {
Folder* f = addFolderInternal(folderDefinition);
Folder* f = addFolderInternal(std::move(folderDefinition), account.data());
if (f) {
f->setAccountState( account.data() );
slotScheduleSync(f);
emit folderSyncStateChange(f);
}
@@ -305,7 +309,7 @@ QString FolderMan::escapeAlias( const QString& alias )
SocketApi *FolderMan::socketApi()
{
return this->_socketApi;
return this->_socketApi.data();
}
QString FolderMan::unescapeAlias( const QString& alias )
@@ -393,10 +397,8 @@ Folder* FolderMan::setupFolderFromOldConfigFile(const QString &file, AccountStat
folderDefinition.paused = paused;
folderDefinition.ignoreHiddenFiles = ignoreHiddenFiles();
folder = addFolderInternal(folderDefinition);
folder = addFolderInternal(folderDefinition, accountState);
if (folder) {
folder->setAccountState(accountState);
QStringList blackList = settings.value( QLatin1String("blackList")).toStringList();
if (!blackList.empty()) {
//migrate settings
@@ -411,21 +413,30 @@ Folder* FolderMan::setupFolderFromOldConfigFile(const QString &file, AccountStat
return folder;
}
void FolderMan::slotSetFolderPaused( Folder *f, bool paused )
void FolderMan::slotFolderSyncPaused( Folder *f, bool paused )
{
if( !f ) {
qWarning() << "!! slotSetFolderPaused called with empty folder";
qWarning() << "!! slotFolderSyncPaused called with empty folder";
return;
}
f->setSyncPaused(paused);
if (!paused) {
_disabledFolders.remove(f);
slotScheduleSync(f);
} else {
_disabledFolders.insert(f);
}
emit folderSyncStateChange(f);
}
void FolderMan::slotFolderCanSyncChanged()
{
Folder *f = qobject_cast<Folder*>(sender());
Q_ASSERT(f);
if (f->canSync()) {
_socketApi->slotRegisterPath(f->alias());
} else {
_socketApi->slotUnregisterPath(f->alias());
}
}
// this really terminates the current sync process
@@ -466,6 +477,11 @@ void FolderMan::slotScheduleAppRestart()
qDebug() << "## Application restart requested!";
}
void FolderMan::slotSyncOnceFileUnlocks(const QString& path)
{
_lockWatcher->addFile(path);
}
/*
* if a folder wants to be synced, it calls this slot and is added
* to the queue. The slot to actually start a sync is called afterwards.
@@ -478,21 +494,12 @@ void FolderMan::slotScheduleSync( Folder *f )
}
auto alias = f->alias();
if( _socketApi ) {
// We want the SocketAPI to already now update so that it can show the EVAL icon
// for files/folders. Only do this when not syncing, else we might get a lot
// of those notifications.
_socketApi->slotUpdateFolderView(f);
}
qDebug() << "Schedule folder " << alias << " to sync!";
if( ! _scheduleQueue.contains(f) ) {
if( !f->canSync() ) {
qDebug() << "Folder is not ready to sync, not scheduled!";
if( _socketApi ) {
_socketApi->slotUpdateFolderView(f);
}
_socketApi->slotUpdateFolderView(f);
return;
}
f->prepareToSync();
@@ -565,7 +572,7 @@ void FolderMan::slotAccountStateChanged()
}
}
} else {
qDebug() << "Account" << accountName << "disconnected, "
qDebug() << "Account" << accountName << "disconnected or paused, "
"terminating or descheduling sync folders";
if (_currentSyncFolder
@@ -744,6 +751,28 @@ void FolderMan::slotForwardFolderSyncStateChange()
}
}
void FolderMan::slotServerVersionChanged(Account *account)
{
// Pause folders if the server version is unsupported
if (account->serverVersionUnsupported()) {
qDebug() << "The server version is unsupported:" << account->serverVersion()
<< "pausing all folders on the account";
foreach (auto& f, _folderMap) {
if (f->accountState()->account().data() == account) {
f->setSyncPaused(true);
}
}
}
}
void FolderMan::slotScheduleFolderOwningFile(const QString& path)
{
if (Folder* f = folderForPath(path)) {
slotScheduleSync(f);
}
}
void FolderMan::slotFolderSyncStarted( )
{
qDebug() << ">===================================== sync started for " << _currentSyncFolder->remoteUrl().toString();
@@ -771,9 +800,8 @@ Folder* FolderMan::addFolder(AccountState* accountState, const FolderDefinition&
return 0;
}
auto folder = addFolderInternal(folderDefinition);
if(folder && accountState) {
folder->setAccountState(accountState);
auto folder = addFolderInternal(folderDefinition, accountState);
if(folder) {
folder->saveToSettings();
emit folderSyncStateChange(folder);
emit folderListChanged(_folderMap);
@@ -781,11 +809,18 @@ Folder* FolderMan::addFolder(AccountState* accountState, const FolderDefinition&
return folder;
}
Folder* FolderMan::addFolderInternal(const FolderDefinition& folderDefinition)
Folder* FolderMan::addFolderInternal(FolderDefinition folderDefinition, AccountState* accountState)
{
auto folder = new Folder(folderDefinition, this );
auto alias = folderDefinition.alias;
int count = 0;
while (folderDefinition.alias.isEmpty() || _folderMap.contains(folderDefinition.alias)) {
// There is already a folder configured with this name and folder names need to be unique
folderDefinition.alias = alias + QString::number(++count);
}
qDebug() << "Adding folder to Folder Map " << folder;
auto folder = new Folder(folderDefinition, accountState, this );
qDebug() << "Adding folder to Folder Map " << folder << folder->alias();
_folderMap[folder->alias()] = folder;
if (folder->syncPaused()) {
_disabledFolders.insert(folder);
@@ -796,6 +831,12 @@ Folder* FolderMan::addFolderInternal(const FolderDefinition& folderDefinition)
connect(folder, SIGNAL(syncStarted()), SLOT(slotFolderSyncStarted()));
connect(folder, SIGNAL(syncFinished(SyncResult)), SLOT(slotFolderSyncFinished(SyncResult)));
connect(folder, SIGNAL(syncStateChange()), SLOT(slotForwardFolderSyncStateChange()));
connect(folder, SIGNAL(syncPausedChanged(Folder*,bool)), SLOT(slotFolderSyncPaused(Folder*,bool)));
connect(folder, SIGNAL(canSyncChanged()), SLOT(slotFolderCanSyncChanged()));
connect(&folder->syncEngine().syncFileStatusTracker(), SIGNAL(fileStatusChanged(const QString &, SyncFileStatus)),
_socketApi.data(), SLOT(slotFileStatusChanged(const QString &, SyncFileStatus)));
connect(folder, SIGNAL(watchedFileChangedExternally(QString)),
&folder->syncEngine().syncFileStatusTracker(), SLOT(slotPathTouched(QString)));
registerFolderMonitor(folder);
return folder;
@@ -919,15 +960,13 @@ bool FolderMan::startFromScratch( const QString& localFolder )
}
// Disconnect the socket api from the database to avoid that locking of the
// db file does not allow to move this dir.
if( _socketApi ) {
Folder *f = folderForPath(localFolder);
if(f) {
if( localFolder.startsWith(f->path()) ) {
_socketApi->slotUnregisterPath(f->alias());
}
f->journalDb()->close();
f->slotTerminateSync(); // Normally it should not be running, but viel hilft viel
Folder *f = folderForPath(localFolder);
if(f) {
if( localFolder.startsWith(f->path()) ) {
_socketApi->slotUnregisterPath(f->alias());
}
f->journalDb()->close();
f->slotTerminateSync(); // Normally it should not be running, but viel hilft viel
}
// Make a backup of the folder/file.

Alguns arquivos não foram exibidos porque demasiados arquivos foram alterados neste diff Mostrar Mais