Compare commits
362
Commits
ssh
...
v1.0.0-rc1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
496f5c256f | ||
|
|
6617d07e1e | ||
|
|
99837f0b8f | ||
|
|
6ed27b7b83 | ||
|
|
3247fd77d6 | ||
|
|
f9b9679b5e | ||
|
|
685d264ab7 | ||
|
|
2355f0cb54 | ||
|
|
68674fb1c2 | ||
|
|
0e1a942ecd | ||
|
|
d01ce11e47 | ||
|
|
eee9e1cbd4 | ||
|
|
54e2da3dc4 | ||
|
|
6887850eae | ||
|
|
ab16885112 | ||
|
|
5f3ff06403 | ||
|
|
81f872fdaf | ||
|
|
0ec739a2b7 | ||
|
|
752bc51452 | ||
|
|
3a23c62070 | ||
|
|
4512b4c998 | ||
|
|
684d2abcdc | ||
|
|
db3eb8872c | ||
|
|
885f388201 | ||
|
|
9431f7f2a8 | ||
|
|
f7752fd2b7 | ||
|
|
e4b2c2262e | ||
|
|
9a31b6259d | ||
|
|
995abe9401 | ||
|
|
a13b39907b | ||
|
|
c64fec22ee | ||
|
|
dd1d835dfe | ||
|
|
5a78becc6f | ||
|
|
f598e62d1c | ||
|
|
b8ae4d0fc5 | ||
|
|
5403c16f48 | ||
|
|
3d162032f4 | ||
|
|
c591a7a91e | ||
|
|
fd2c476ae5 | ||
|
|
42bf58a0b5 | ||
|
|
4d09e1b5a5 | ||
|
|
d825b065f2 | ||
|
|
13428b5ed6 | ||
|
|
521d1789bc | ||
|
|
f64257ae86 | ||
|
|
d228ca1c4b | ||
|
|
690f21e9ae | ||
|
|
743710a9f0 | ||
|
|
ba29971e20 | ||
|
|
f49e492a92 | ||
|
|
1c62abaec0 | ||
|
|
1940a03a61 | ||
|
|
9bee2f443f | ||
|
|
1cf331f83c | ||
|
|
685b835886 | ||
|
|
ab2494a6ba | ||
|
|
3ae0d61bd5 | ||
|
|
f4e11b623f | ||
|
|
938479e43f | ||
|
|
31ec650659 | ||
|
|
87c13ff74a | ||
|
|
2c820daae4 | ||
|
|
3b0cdc0caa | ||
|
|
85570dd61d | ||
|
|
400d3670be | ||
|
|
45c93a7f3e | ||
|
|
6ca3f492fe | ||
|
|
dee3768d34 | ||
|
|
d2570f9c44 | ||
|
|
a630fa9f98 | ||
|
|
875d4248f5 | ||
|
|
9523ff2472 | ||
|
|
cf92bda8db | ||
|
|
c73b5ed7dc | ||
|
|
ceb32f63ad | ||
|
|
4065193dd8 | ||
|
|
b4d7ae2d3e | ||
|
|
140dce8dd2 | ||
|
|
e80fd77299 | ||
|
|
7e552a4db0 | ||
|
|
7d28eec8d0 | ||
|
|
c29f8bf2f4 | ||
|
|
f76433729c | ||
|
|
db630fc549 | ||
|
|
4eb13e84cc | ||
|
|
16b8c8efea | ||
|
|
9b2c72e4a2 | ||
|
|
b3b9c52144 | ||
|
|
4bc319e0bb | ||
|
|
50071a2980 | ||
|
|
2036ce4ba1 | ||
|
|
f2624fea03 | ||
|
|
950bc3daaa | ||
|
|
39b6779a8c | ||
|
|
743486d88b | ||
|
|
50b0e90495 | ||
|
|
a8b049686a | ||
|
|
23f133dbbe | ||
|
|
b033375491 | ||
|
|
ca4060473d | ||
|
|
472b6a8c46 | ||
|
|
0947bac63c | ||
|
|
1a414a2723 | ||
|
|
d7bbf37cbb | ||
|
|
6063ff90eb | ||
|
|
20dd2eb564 | ||
|
|
4a80853be6 | ||
|
|
b6cd30e22d | ||
|
|
ea59ca59d3 | ||
|
|
c67d8a942b | ||
|
|
244d94b17d | ||
|
|
b05b348198 | ||
|
|
38a8595d15 | ||
|
|
f2226c92b3 | ||
|
|
466709df23 | ||
|
|
702459918b | ||
|
|
4efbd8b90a | ||
|
|
655bfcba0a | ||
|
|
041356ebac | ||
|
|
32e9ceb741 | ||
|
|
1aeb0e42dc | ||
|
|
9c419b1f2e | ||
|
|
0786489924 | ||
|
|
2835aa1685 | ||
|
|
9cc34df185 | ||
|
|
d5adcdf988 | ||
|
|
837c0e1a17 | ||
|
|
d458f43b63 | ||
|
|
1cfb1b6287 | ||
|
|
6f5124616a | ||
|
|
3ba2f8ae93 | ||
|
|
b6d87141f2 | ||
|
|
993e9355bb | ||
|
|
be655902b4 | ||
|
|
f9332befb3 | ||
|
|
66728df978 | ||
|
|
60dc85c2fc | ||
|
|
4b4eb623b9 | ||
|
|
d2c98bf559 | ||
|
|
9a31503996 | ||
|
|
cb4622f61f | ||
|
|
1ce1091eca | ||
|
|
4546eef1e3 | ||
|
|
1d8f24ced2 | ||
|
|
19a61ba517 | ||
|
|
2b377e35f1 | ||
|
|
f5011630a3 | ||
|
|
8967a84f12 | ||
|
|
38bed5e4eb | ||
|
|
5370ecedd8 | ||
|
|
0e8cd5cefc | ||
|
|
e3c3649b0f | ||
|
|
19f953fa54 | ||
|
|
ad1b5e35c7 | ||
|
|
ff2eb0fef6 | ||
|
|
d356c14f87 | ||
|
|
829cbf890b | ||
|
|
5ae52d4b4f | ||
|
|
4608c6b7ee | ||
|
|
7c7a818ca0 | ||
|
|
08c6d44185 | ||
|
|
dc8495b147 | ||
|
|
e2f5765a87 | ||
|
|
7ebc059430 | ||
|
|
0d3fda185f | ||
|
|
38df06261b | ||
|
|
0086df54f0 | ||
|
|
ddc0c910c8 | ||
|
|
31f74baef7 | ||
|
|
8cd4f50304 | ||
|
|
48f6abb361 | ||
|
|
27626665a1 | ||
|
|
47039d8685 | ||
|
|
bc9aded3b7 | ||
|
|
f919544d1e | ||
|
|
ea49d0508a | ||
|
|
894f508f6c | ||
|
|
2f2d3d584e | ||
|
|
67ae8a614d | ||
|
|
cbba0e639d | ||
|
|
8f3fdf3d1d | ||
|
|
650fdd60cc | ||
|
|
8b114b5122 | ||
|
|
bc96addce5 | ||
|
|
bac2c9d5ec | ||
|
|
94e0c37cf8 | ||
|
|
42d6181724 | ||
|
|
4c5f2e99cb | ||
|
|
95e2fef4a6 | ||
|
|
4d8e3e61a1 | ||
|
|
c227798a4b | ||
|
|
fc5a6b9ac6 | ||
|
|
b820f17b3c | ||
|
|
079ec51dac | ||
|
|
88dee956f2 | ||
|
|
53945eb095 | ||
|
|
578447ecc3 | ||
|
|
0cecad49b3 | ||
|
|
652df93690 | ||
|
|
efa81c11c5 | ||
|
|
4af070b5b8 | ||
|
|
95faf9332e | ||
|
|
7e87befd3d | ||
|
|
d6155f4c35 | ||
|
|
33c89eac63 | ||
|
|
335adc6bdb | ||
|
|
a107084745 | ||
|
|
67f60b19ea | ||
|
|
734b17d0e7 | ||
|
|
7739c92064 | ||
|
|
0ae5f12024 | ||
|
|
e05dd9d1e9 | ||
|
|
ecbbeaeeca | ||
|
|
ea164e82a7 | ||
|
|
bfa1e68ce0 | ||
|
|
dc010092ab | ||
|
|
82c6bd6452 | ||
|
|
49a1251091 | ||
|
|
97ffad1178 | ||
|
|
592883af29 | ||
|
|
51a38c5607 | ||
|
|
5a645015a2 | ||
|
|
064094489f | ||
|
|
218310c23c | ||
|
|
7c48a9e28d | ||
|
|
8ecffcbddb | ||
|
|
c31f8d8275 | ||
|
|
6db3fe11b9 | ||
|
|
ae7735f6bd | ||
|
|
22dc364f7e | ||
|
|
f11f22f6bb | ||
|
|
2e07f1a9cb | ||
|
|
62bc7e5ff7 | ||
|
|
6b9dd95b26 | ||
|
|
0f3fd3ace1 | ||
|
|
10b9e91c8e | ||
|
|
f813bc42ed | ||
|
|
1c713c0e7e | ||
|
|
6b36671ad7 | ||
|
|
720ee7981b | ||
|
|
dbed9b88a4 | ||
|
|
d5afee08af | ||
|
|
5ae70fcf29 | ||
|
|
16621d9ce1 | ||
|
|
32bfb590a8 | ||
|
|
0e2f9b427f | ||
|
|
1f8abd1be7 | ||
|
|
f7e24ba46c | ||
|
|
d3b993d67e | ||
|
|
eddd6269e7 | ||
|
|
ad14cf6a94 | ||
|
|
53f92d91e7 | ||
|
|
58e8c708c4 | ||
|
|
3b7f1f9cce | ||
|
|
d921975e9f | ||
|
|
9db35b6597 | ||
|
|
c6c5bd179b | ||
|
|
dd1d3ebdc4 | ||
|
|
d5110c4ac8 | ||
|
|
e08775f18a | ||
|
|
a1e273a147 | ||
|
|
79a7d163c4 | ||
|
|
4fd1a633e7 | ||
|
|
ce126b0f1d | ||
|
|
1ccde63c31 | ||
|
|
713972aed0 | ||
|
|
a1abb11ec4 | ||
|
|
2f86b628f9 | ||
|
|
23994758dd | ||
|
|
1c5b6a722a | ||
|
|
a62d826005 | ||
|
|
c98d72f0cd | ||
|
|
55d59c1944 | ||
|
|
064b0b5ee1 | ||
|
|
ebf03afa75 | ||
|
|
116b2efd5f | ||
|
|
dfa66b8113 | ||
|
|
620b0ff59a | ||
|
|
bc01acfef0 | ||
|
|
f8199eaa79 | ||
|
|
bb7408979a | ||
|
|
93a42e752b | ||
|
|
8959bcb912 | ||
|
|
d533e3bf8e | ||
|
|
fbdd73ec58 | ||
|
|
3c5e570e04 | ||
|
|
263c02eaab | ||
|
|
44ad01b3ad | ||
|
|
a37736eed6 | ||
|
|
a3c09047e6 | ||
|
|
dc30c28c02 | ||
|
|
d17f2e6eb9 | ||
|
|
fdabd03924 | ||
|
|
a8a04186ed | ||
|
|
164dd1d7a5 | ||
|
|
4fc487348a | ||
|
|
0bb566869e | ||
|
|
9fb495d785 | ||
|
|
df86e7c8ec | ||
|
|
1e6f4d8052 | ||
|
|
bd470b1330 | ||
|
|
60b3494207 | ||
|
|
475a7fc44c | ||
|
|
f19c5a0dc0 | ||
|
|
3e35f1dea3 | ||
|
|
d8197dd00e | ||
|
|
3205248519 | ||
|
|
224801dcd9 | ||
|
|
e8c0de07de | ||
|
|
cf7e061c7a | ||
|
|
a75becd6c5 | ||
|
|
e411bbe3c4 | ||
|
|
c8ca4bc945 | ||
|
|
ffd9d13d79 | ||
|
|
4addeb5835 | ||
|
|
c086c9ab83 | ||
|
|
44e30e4520 | ||
|
|
8f93cd7dd6 | ||
|
|
a4390d348b | ||
|
|
bd72766414 | ||
|
|
866828c1ba | ||
|
|
8cedcc5d55 | ||
|
|
860cee5f12 | ||
|
|
a75972cf3b | ||
|
|
03e3592443 | ||
|
|
c113244319 | ||
|
|
3a64e1310b | ||
|
|
306b759b74 | ||
|
|
5976884b0f | ||
|
|
ee907c3321 | ||
|
|
86307a724f | ||
|
|
997aa44654 | ||
|
|
feb1d277e9 | ||
|
|
7e1dac26f1 | ||
|
|
a7b95d4caf | ||
|
|
bb042fe70b | ||
|
|
e50ee8fbee | ||
|
|
55d8a6ff76 | ||
|
|
576fc07b5b | ||
|
|
b4978269a5 | ||
|
|
24e9a74a6c | ||
|
|
61e79155b4 | ||
|
|
d23d9a2e7a | ||
|
|
ae13e30494 | ||
|
|
292c664941 | ||
|
|
c78845571e | ||
|
|
a0d5c67e48 | ||
|
|
059a3dcc37 | ||
|
|
d543bb4539 | ||
|
|
3e8f800682 | ||
|
|
368f2abe0d | ||
|
|
1e8af399e7 | ||
|
|
72f669e189 | ||
|
|
c787b54e7c | ||
|
|
3512be30cf | ||
|
|
0bb279eec8 | ||
|
|
75331d9025 | ||
|
|
99a46eafc8 | ||
|
|
ac9300f5be | ||
|
|
4e4e5154f4 | ||
|
|
b54c096762 | ||
|
|
5d409ec1de |
@@ -1,30 +1,143 @@
|
||||
0.9.0-RC6 / February 13, 2016
|
||||
|
||||
Latest Version:
|
||||
Robomongo 1.0-RC1 (1 Feb, 2017)
|
||||
http://blog.robomongo.org/robomongo-1-rc1
|
||||
|
||||
Robomongo 1.x:
|
||||
Major Features:
|
||||
- Replica set support.
|
||||
- New operation logging for almost all operations.
|
||||
|
||||
Major Improvements:
|
||||
- Improved error handling (no double error windows) for almost all operations.
|
||||
|
||||
* What's new in 1.0-RC1 (1 Feb, 2017)
|
||||
New Features:
|
||||
- Replica set support.
|
||||
- New 'Attributes' column in connections list window showing connection properties such as 'Replica Set', 'SSL' and 'SSH'.
|
||||
- New operation logging for almost all operations.
|
||||
|
||||
Improvements:
|
||||
- Improved error handling (no double error windows) for all fail and success conditions for the following operations:
|
||||
. Create, Drop Database
|
||||
. Create, Edit User
|
||||
. Create, Drop, Duplicate, Rename Collection
|
||||
. Insert Document
|
||||
. Remove document, Delete document, Remove all documents (triggered both from explorer and from result widget)
|
||||
. Refresh Users
|
||||
. Refresh Functions
|
||||
. Refresh Collections
|
||||
. Connection establish & refresh server fail and success cases.
|
||||
- Windows size is saved and restored for Edit, View and Insert Document dialogs.
|
||||
- Connections are automatically imported from the latest Robomongo version.
|
||||
- Better error reporting/handling and logging have been added for the operations below:
|
||||
. Create, Drop Function
|
||||
. Drop User
|
||||
- More informative and adjusted log level prefix strings haven added for log window. (Error, Warn, Info etc..).
|
||||
- After a new connection is created, now the focus goes to the newly created connection (end of list) instead of the
|
||||
beginning of connections list. And when Robomongo is opened, the focus goes to the end of the connection list instead
|
||||
of beginning.
|
||||
- Error messages added and error handling for 'remove document', 'remove all documents' operations triggered explorer
|
||||
and single or multi 'delete document' from results window have been re-implemented and improved.
|
||||
- Relevant errors are shown for the following cases now:
|
||||
. Drop attempt of non-existing collection.
|
||||
. Duplicating a collection with a collection name which already exists finishes silently.
|
||||
|
||||
Fixes:
|
||||
- Fix for the problem where the connections list comes empty if the Robomongo config file or it's directory do not exist.
|
||||
- Fix for the problem where 'in-progress bar' widget was not centered on main window for "Update Documents/Remove Documents"
|
||||
operations. https://github.com/paralect/robomongo/issues/1256
|
||||
- Fix for two problems where the operations 'insert or update documents' in one tab was causing auto reloading of other
|
||||
unrelated tabs and also multiple error windows to popup. https://github.com/paralect/robomongo/issues/1236
|
||||
- Fix for the problem where the operations 'remove document', 'remove all documents' triggered explorer and single or
|
||||
multi 'delete document' from results window were causing auto reloading of other unrelated tabs.
|
||||
|
||||
|
||||
Robomongo 0.9:
|
||||
Major Features:
|
||||
- Support for MongoDB 3.x and WiredTiger engine. Robomongo now embeds MongoDB 3.2 shell.
|
||||
(Ability to use the latest shell functionality)
|
||||
- SSH tunnel support for connections added.
|
||||
- SSL tunnel support added.
|
||||
- MongoDB Atlas (Cloud MongoDB) support added.
|
||||
- Both MONGODB-CR and SCRAM-SHA-1 authentication mechanisms are supported.
|
||||
- Allow direct connections to Replica Set members and support IPv6 protocol.
|
||||
- Loading of huge list of collections is now faster.
|
||||
- Create Collection dialog is now expandable to support all advanced options.
|
||||
(Supporting all major mongoDB versions 3.2, 3.0, 2.6 and all storage engine types (WiredTiger
|
||||
and MMAPV1) is added.)
|
||||
- Windows High DPI support.
|
||||
- Full size editor with dockable output window.
|
||||
|
||||
Fixed:
|
||||
- Incorrect representation of floating-point numbers (fixed since RC3). This bug was a reason of a big number of issues:
|
||||
#910, #748, #697, #699, #760, #815, #870, #892,
|
||||
#894, #905, #945, #974, #978, #990, #831, #842
|
||||
- SSH connection failure to servers on Ubuntu and OS X with the following error:
|
||||
"Resource temporarily unavailable. Error when starting up SSH session: -8" (#1090)
|
||||
|
||||
|
||||
|
||||
What's new in 0.9.0 Final (30 Sep, 2016)
|
||||
http://blog.robomongo.org/robomongo-0-9-0-final/
|
||||
|
||||
New Features:
|
||||
- Full size editor with dockable output window.
|
||||
- New JSON object context menu items: "Copy Name" and "Copy Path" are added.
|
||||
- New database context menu items: "Current Operations" and "Kill Operation" are added.
|
||||
- Minimize to Tray Icon for Windows.
|
||||
|
||||
Other Changes
|
||||
- Cloud platforms Compose, mLab and Amazon EC2 were tested and added as supported platforms.
|
||||
- Maximize/Restore result button behavior and icon have been improved.
|
||||
- For better code quality and code reviews, Robomongo Code Quality and Robomongo Code Review pages have been created.
|
||||
https://github.com/paralect/robomongo/wiki/Robomongo-Code-Review
|
||||
https://github.com/paralect/robomongo/wiki/Robomongo-Code-Quality
|
||||
|
||||
Most important changes: support for MongoDB 3.x and WiredTiger engine. Robomongo
|
||||
now embeds MongoDB 3.2 shell. It means that you can now use the latest
|
||||
shell functionality. Both MONGODB-CR and SCRAM-SHA-1 authentication mechanisms are
|
||||
supprted. Also, we now allow direct connections to Replica Set members and
|
||||
support IPv6 protocol. Loading of huge list of collections is now faster.
|
||||
What's new in RC10 (Aug 8, 2016)
|
||||
http://blog.robomongo.org/robomongo-rc10/
|
||||
|
||||
New Features:
|
||||
- SSL connections with advanced options are now supported.
|
||||
- MongoDB Atlas (Cloud MongoDB) is supported.
|
||||
- Cross Platform High DPI enhancements.
|
||||
|
||||
Enhancements:
|
||||
- For better security, OpenSSL has been upgraded to OpenSSL 1.0.1p.
|
||||
- Qt has been updated from Qt 5.5 to latest Qt 5.7. With this update there are many
|
||||
enhancements including cross platform High DPI support.
|
||||
|
||||
Fixed incorrect representation of floating-point numbers (fixed since RC3).
|
||||
This bug was a reason of a big number of issues:
|
||||
|
||||
#910, #748, #697, #699, #760, #815, #870, #892,
|
||||
#894, #905, #945, #974, #978, #990, #831, #842
|
||||
|
||||
What's new in RC7 (March 4, 2016 )
|
||||
What's new in RC9 (May 30, 2016)
|
||||
http://blog.robomongo.org/robomongo-rc9/
|
||||
|
||||
New Features:
|
||||
- Create Collection dialog with advanced options.
|
||||
- New feature has been added to save/restore window sizes. (#1102)
|
||||
- Connection settings from previous versions of Robomongo are
|
||||
now automatically imported starting from Robomongo 0.9.0 RC9. (#1086)
|
||||
- Support for querying documents by UUIDs is added, both in legacy and new encoding. (#432)
|
||||
|
||||
Fixed:
|
||||
- SSH connection failure to servers on Ubuntu and OS X with the following error:
|
||||
"Resource temporarily unavailable. Error when starting up SSH session: -8" (#1090)
|
||||
- Correct handling of create/drop of databases and collections. (#1083)
|
||||
|
||||
What's new in RC8 (April 14, 2016)
|
||||
http://blog.robomongo.org/robomongo-rc8/
|
||||
|
||||
What's new in RC7 (March 4, 2016)
|
||||
http://blog.robomongo.org/robomongo-rc7
|
||||
|
||||
What's new in RC6 (February 13, 2016 )
|
||||
What's new in RC6 (February 13, 2016)
|
||||
http://blog.robomongo.org/ipv6-protocol
|
||||
|
||||
What's new in RC5 (February 8, 2016 )
|
||||
What's new in RC5 (February 8, 2016)
|
||||
http://blog.robomongo.org/campaign-is-over-robomongo-is-not/
|
||||
|
||||
What's new in RC4 (February 1, 2016 )
|
||||
What's new in RC4 (February 1, 2016)
|
||||
http://blog.robomongo.org/robomongo-rc4
|
||||
|
||||
What's new in RC3 (January 29, 2016 )
|
||||
What's new in RC3 (January 29, 2016)
|
||||
http://blog.robomongo.org/robomongo-0-9-0-rc3-released
|
||||
|
||||
What's new in RC2 (January 26, 2016)
|
||||
@@ -86,7 +199,7 @@
|
||||
* [Andres Kievsky]
|
||||
- Added new app icon in a variety of png and iDraw formats (#693)
|
||||
* [Michael Steinacher]
|
||||
- Retina text display support (#724)
|
||||
- Retina text display support (#724)
|
||||
|
||||
0.8.4 / November 27, 2013
|
||||
|
||||
|
||||
+4
-4
@@ -5,10 +5,10 @@ project(Robomongo)
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
|
||||
|
||||
# Project version
|
||||
set(PROJECT_VERSION_MAJOR "0")
|
||||
set(PROJECT_VERSION_MINOR "9")
|
||||
set(PROJECT_VERSION_MAJOR "1")
|
||||
set(PROJECT_VERSION_MINOR "0")
|
||||
set(PROJECT_VERSION_PATCH "0")
|
||||
set(PROJECT_VERSION_BUILD "-RC7")
|
||||
set(PROJECT_VERSION_BUILD "-RC1")
|
||||
set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}${PROJECT_VERSION_BUILD}")
|
||||
|
||||
# Modules
|
||||
@@ -28,13 +28,13 @@ find_package(Qt5Gui REQUIRED)
|
||||
find_package(Qt5Widgets REQUIRED)
|
||||
find_package(Qt5PrintSupport REQUIRED)
|
||||
find_package(MongoDB REQUIRED)
|
||||
find_package(OpenSSL REQUIRED)
|
||||
|
||||
if (SYSTEM_MACOSX)
|
||||
find_package(Qt5MacExtras REQUIRED)
|
||||
endif()
|
||||
|
||||
# Projects
|
||||
add_subdirectory(src/third-party/openssl)
|
||||
add_subdirectory(src/third-party/libssh2)
|
||||
add_subdirectory(src/robomongo/ssh)
|
||||
add_subdirectory(src/third-party/qjson)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
About Robomongo
|
||||
About Robomongo [](https://gitter.im/paralect/robomongo?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
===============
|
||||
|
||||
[Robomongo](http://www.robomongo.org) is a shell-centric cross-platform MongoDB management tool. Unlike most other MongoDB admin UI tools, Robomongo embeds the actual `mongo` shell in a tabbed interface with access to a shell command line as well as GUI interaction.
|
||||
@@ -7,13 +7,74 @@ Starting from version 0.9, Robomongo is compatibile with MongoDB 3.x (including
|
||||
|
||||
Robomongo 0.8.x embeds the **MongoDB 2.4.0** shell.
|
||||
|
||||
What's new in latest Robomongo 1.0-RC1?
|
||||
====================================
|
||||
Latest release of Robomongo 1.0-RC1 includes:
|
||||
- Replica Set Support
|
||||
- Improved error handling (no double error windows) and new operation logging for almost all operations.
|
||||
- New 'Attributes' column in connections list window showing connection properties such as 'Replica Set', 'SSL' and 'SSH'.
|
||||
- Windows size is saved/restored for Edit, View and Insert Document dialogs.
|
||||
|
||||
Central feature of this release is Replica Set feature. With this new feature, Robomongo can be used to work with replica set clusters with SSL, hosted on localhost, LAN or in the cloud. Even when primary is unreachable, it is possible to work on a secondary with a couple of clicks.
|
||||
|
||||
Blog: http://blog.robomongo.org/robomongo-1-rc1/
|
||||
Download: https://robomongo.org/download
|
||||
Watch: [Robomongo youtube channel](https://www.youtube.com/channel/UCM_7WAseRWeeiBikExppstA)
|
||||
Follow: https://twitter.com/Robomongo
|
||||
|
||||
What's Planned for the Next Releases?
|
||||
====================================
|
||||
|
||||
We are currently working towards [Robomongo 1.0 milestone](https://github.com/paralect/robomongo/milestone/20).
|
||||
|
||||
Currently in progress:
|
||||
- Robomongo 1.0 Planning (ETA: 22 Feb 2017)
|
||||
|
||||
Planned for Next Release:
|
||||
- Robomongo 1.0 Planning
|
||||
|
||||
Plans for Future:
|
||||
- User Roles
|
||||
- Support execution of multiple simultaneous queries [#1161](https://github.com/paralect/robomongo/issues/1161)
|
||||
- Selectable mongodb engines [#1249](https://github.com/paralect/robomongo/issues/1249)
|
||||
- Enhancements for stability, running without crashes ([Stability Milestone](https://github.com/paralect/robomongo/milestone/15))
|
||||
- More Documentation
|
||||
- Unit Tests
|
||||
- Refactoring Debug Log module
|
||||
|
||||
Supported Platforms
|
||||
===============
|
||||
|
||||
| MongoDB Versions |
|
||||
| :-------------------- |
|
||||
| 3.2 |
|
||||
| 3.0 |
|
||||
| 2.6 |
|
||||
|
||||
| MongoDB Cloud Platforms|
|
||||
| :------------ |
|
||||
| MongoDB Atlas |
|
||||
| Compose |
|
||||
| mLab |
|
||||
| ObjectRocket |
|
||||
| Amazon EC2 |
|
||||
|
||||
| Windows | Mac | Linux |
|
||||
|:---------------------- | :---------------| :---------------------------|
|
||||
| Windows 64-bit 10 | Mac OS X 10.11 | Linux Ubuntu 16.04 64-bit* |
|
||||
Windows 64-bit 8.1 | Mac OS X 10.10 | Linux Ubuntu 14.04 64-bit* |
|
||||
| Windows 64-bit 7 | | Linux CentOS 7 64-bit* |
|
||||
| | | Linux CentOS 6 64-bit* |
|
||||
|
||||
\* latest stable build
|
||||
|
||||
|
||||
Download
|
||||
========
|
||||
|
||||
You can download tested install packages for OS X, Windows, and Linux from our site: [www.robomongo.org](http://www.robomongo.org).
|
||||
|
||||
The latest stable release is currently **Robomongo 0.8.5**.
|
||||
The latest stable release is currently [**Robomongo 0.9.0**](http://blog.robomongo.org/robomongo-0-9-0-final/).
|
||||
|
||||
Support
|
||||
=======
|
||||
@@ -24,39 +85,30 @@ You can:
|
||||
|
||||
- [Create a new issue in the Github issue queue](https://github.com/paralect/robomongo/issues)
|
||||
|
||||
- [Join developer discussion on Gitter](https://gitter.im/paralect/robomongo) [](https://gitter.im/paralect/robomongo?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
- [Join developer discussion on Gitter](https://gitter.im/paralect/robomongo)
|
||||
|
||||
Build
|
||||
=====
|
||||
|
||||
The wiki contains prerequisites and instructions to [Build Robomongo](https://github.com/paralect/robomongo/wiki/Build-Robomongo).
|
||||
The wiki contains prerequisites and instructions to [Build Robomongo](https://github.com/paralect/robomongo/wiki).
|
||||
|
||||
If you want to compile from source yourself, you should be able to do so cleanly from a [release branch](https://github.com/paralect/robomongo/releases).
|
||||
|
||||
What's Planned for the Next Release?
|
||||
====================================
|
||||
|
||||
We are currently working toward a [0.9.0 release milestone](https://github.com/paralect/robomongo/milestones/Robomongo%200.9.0)
|
||||
with some major features targeted including compatibility with MongoDB 3.0
|
||||
(updated shell, SCRAM-SHA-1 auth, support for WiredTiger storage engine)
|
||||
and full support for replica sets.
|
||||
|
||||
Contribute!
|
||||
===========
|
||||
|
||||
### Vote for Features
|
||||
|
||||
Please view our [Magic Backlog](http://app.robomongo.org/backlog) to see features prioritised by donation. The small amount of donations we currently receive are put toward hosting costs.
|
||||
|
||||
### Suggest Features
|
||||
|
||||
New feature suggestions or UI improvements are always welcome.
|
||||
[Create a new feature request on github](https://github.com/paralect/robomongo/issues/new)
|
||||
|
||||
This project is powered by open source volunteers, so we have a limited amount of development resource to address all requests. We will certainly make best efforts to progress (particularly for those with strong community upvotes).
|
||||
|
||||
### Code Contributions
|
||||
|
||||
Code contributions are always welcome! Just try to follow our coding style: [Robomongo Coding Style](https://github.com/paralect/robomongo/wiki/Robomongo-Coding-Style).
|
||||
Code contributions are always welcome! Just try to follow our pre-commit checks and coding style:
|
||||
- [Robomongo Code Quality](https://github.com/paralect/robomongo/wiki/Robomongo-Code-Quality)
|
||||
- [Robomongo Coding Style](https://github.com/paralect/robomongo/wiki/Robomongo-Coding-Style)
|
||||
|
||||
If you plan to contribute, please create a Github issue (or comment on the relevant existing issue) so we can help coordinate with upcoming release plans.
|
||||
|
||||
@@ -81,7 +133,7 @@ There are a number of issues we could use help with reproducing. They are marked
|
||||
License
|
||||
=======
|
||||
|
||||
Copyright (C) 2013-2015 [Paralect, Inc](http://www.paralect.com)
|
||||
Copyright (C) 2013-2017 [Paralect, Inc](http://www.paralect.com)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 3 as
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
BIN_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" # Get directory of this file
|
||||
PROJECT_DIR=$(dirname $BIN_DIR) # Get root directory of project
|
||||
|
||||
VERSION="$(echo $1 | tr '[A-Z]' '[a-z]')"
|
||||
if [ -z "$VERSION" ]; then
|
||||
echo "Usage: tag <name>"
|
||||
echo "Example: tag 0.9.0-rc9"
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ "$(git rev-parse --abbrev-ref HEAD)" != "master" ]; then
|
||||
echo "Error: You are not on master branch"
|
||||
exit
|
||||
fi
|
||||
|
||||
cd $PROJECT_DIR
|
||||
git tag -a "v${VERSION}" -m "${VERSION}"
|
||||
git push --tags
|
||||
@@ -0,0 +1,138 @@
|
||||
##################################################
|
||||
# Find and Add OpenSSL shared (dynamic) library
|
||||
#
|
||||
# - This module defines the following variables:
|
||||
# OpenSSL_DIR
|
||||
# OPENSSL_VERSION
|
||||
#
|
||||
# - Imported shared(dynamic) libraries "ssl" and "crypto" are created.
|
||||
#
|
||||
# - It is assumed that OpenSSL has been built and path to OpenSSL build directory is
|
||||
# added into system(environment) variable ROBOMONGO_CMAKE_PREFIX_PATH.
|
||||
#
|
||||
##################################################
|
||||
|
||||
# Try to find OpenSSL directory (uses CMAKE_PREFIX_PATH locations)
|
||||
#-------------------------------------------
|
||||
|
||||
find_path(
|
||||
OpenSSL_DIR include/openssl/ssl.h
|
||||
DOC "Path to OpenSSL (github.com/openssl/openssl) root directory"
|
||||
)
|
||||
|
||||
# Find OpenSSL version
|
||||
#-------------------------------------------
|
||||
|
||||
if(SYSTEM_WINDOWS)
|
||||
set (OPENSSL_INCLUDE_DIR "${OpenSSL_DIR}/inc32")
|
||||
else()
|
||||
set (OPENSSL_INCLUDE_DIR "${OpenSSL_DIR}/include")
|
||||
endif()
|
||||
|
||||
function(from_hex HEX DEC)
|
||||
string(TOUPPER "${HEX}" HEX)
|
||||
set(_res 0)
|
||||
string(LENGTH "${HEX}" _strlen)
|
||||
|
||||
while (_strlen GREATER 0)
|
||||
math(EXPR _res "${_res} * 16")
|
||||
string(SUBSTRING "${HEX}" 0 1 NIBBLE)
|
||||
string(SUBSTRING "${HEX}" 1 -1 HEX)
|
||||
if (NIBBLE STREQUAL "A")
|
||||
math(EXPR _res "${_res} + 10")
|
||||
elseif (NIBBLE STREQUAL "B")
|
||||
math(EXPR _res "${_res} + 11")
|
||||
elseif (NIBBLE STREQUAL "C")
|
||||
math(EXPR _res "${_res} + 12")
|
||||
elseif (NIBBLE STREQUAL "D")
|
||||
math(EXPR _res "${_res} + 13")
|
||||
elseif (NIBBLE STREQUAL "E")
|
||||
math(EXPR _res "${_res} + 14")
|
||||
elseif (NIBBLE STREQUAL "F")
|
||||
math(EXPR _res "${_res} + 15")
|
||||
else()
|
||||
math(EXPR _res "${_res} + ${NIBBLE}")
|
||||
endif()
|
||||
|
||||
string(LENGTH "${HEX}" _strlen)
|
||||
endwhile()
|
||||
|
||||
set(${DEC} ${_res} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
if (OPENSSL_INCLUDE_DIR)
|
||||
if(OPENSSL_INCLUDE_DIR AND EXISTS "${OPENSSL_INCLUDE_DIR}/openssl/opensslv.h")
|
||||
file(STRINGS "${OPENSSL_INCLUDE_DIR}/openssl/opensslv.h" openssl_version_str
|
||||
REGEX "^#[\t ]*define[\t ]+OPENSSL_VERSION_NUMBER[\t ]+0x([0-9a-fA-F])+.*")
|
||||
|
||||
# The version number is encoded as 0xMNNFFPPS: major minor fix patch status
|
||||
# The status gives if this is a developer or prerelease and is ignored here.
|
||||
# Major, minor, and fix directly translate into the version numbers shown in
|
||||
# the string. The patch field translates to the single character suffix that
|
||||
# indicates the bug fix state, which 00 -> nothing, 01 -> a, 02 -> b and so
|
||||
# on.
|
||||
|
||||
string(REGEX REPLACE "^.*OPENSSL_VERSION_NUMBER[\t ]+0x([0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F]).*$"
|
||||
"\\1;\\2;\\3;\\4;\\5" OPENSSL_VERSION_LIST "${openssl_version_str}")
|
||||
list(GET OPENSSL_VERSION_LIST 0 OPENSSL_VERSION_MAJOR)
|
||||
list(GET OPENSSL_VERSION_LIST 1 OPENSSL_VERSION_MINOR)
|
||||
from_hex("${OPENSSL_VERSION_MINOR}" OPENSSL_VERSION_MINOR)
|
||||
list(GET OPENSSL_VERSION_LIST 2 OPENSSL_VERSION_FIX)
|
||||
from_hex("${OPENSSL_VERSION_FIX}" OPENSSL_VERSION_FIX)
|
||||
list(GET OPENSSL_VERSION_LIST 3 OPENSSL_VERSION_PATCH)
|
||||
|
||||
if (NOT OPENSSL_VERSION_PATCH STREQUAL "00")
|
||||
from_hex("${OPENSSL_VERSION_PATCH}" _tmp)
|
||||
# 96 is the ASCII code of 'a' minus 1
|
||||
math(EXPR OPENSSL_VERSION_PATCH_ASCII "${_tmp} + 96")
|
||||
unset(_tmp)
|
||||
# Once anyone knows how OpenSSL would call the patch versions beyond 'z'
|
||||
# this should be updated to handle that, too. This has not happened yet
|
||||
# so it is simply ignored here for now.
|
||||
string(ASCII "${OPENSSL_VERSION_PATCH_ASCII}" OPENSSL_VERSION_PATCH_STRING)
|
||||
endif ()
|
||||
|
||||
set(OPENSSL_VERSION "${OPENSSL_VERSION_MAJOR}.${OPENSSL_VERSION_MINOR}.${OPENSSL_VERSION_FIX}${OPENSSL_VERSION_PATCH_STRING}")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
|
||||
# Add imported ssl and crypto libraries
|
||||
#-------------------------------------------
|
||||
|
||||
# Add imported target ssl (ssleay32)
|
||||
add_library(ssl SHARED IMPORTED)
|
||||
|
||||
# Add imported target for crypto (libeay32)
|
||||
add_library(crypto SHARED IMPORTED)
|
||||
|
||||
if(SYSTEM_WINDOWS)
|
||||
set_target_properties(ssl PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${OpenSSL_DIR}/inc32"
|
||||
IMPORTED_IMPLIB "${OpenSSL_DIR}/out32dll/ssleay32.lib"
|
||||
)
|
||||
set_target_properties(crypto PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${OpenSSL_DIR}/inc32"
|
||||
IMPORTED_IMPLIB "${OpenSSL_DIR}/out32dll/libeay32.lib"
|
||||
)
|
||||
elseif(SYSTEM_MACOSX)
|
||||
set_target_properties(ssl PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${OpenSSL_DIR}/include"
|
||||
IMPORTED_LOCATION "${OpenSSL_DIR}/libssl.dylib"
|
||||
)
|
||||
set_target_properties(crypto PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${OpenSSL_DIR}/include"
|
||||
IMPORTED_LOCATION "${OpenSSL_DIR}/libcrypto.dylib"
|
||||
)
|
||||
elseif(SYSTEM_LINUX)
|
||||
set_target_properties(ssl PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${OpenSSL_DIR}/include"
|
||||
IMPORTED_LOCATION "${OpenSSL_DIR}/libssl.so"
|
||||
)
|
||||
set_target_properties(crypto PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${OpenSSL_DIR}/include"
|
||||
IMPORTED_LOCATION "${OpenSSL_DIR}/libcrypto.so"
|
||||
)
|
||||
endif()
|
||||
|
||||
# End of file
|
||||
@@ -8,7 +8,8 @@ message("| Qt5 Core: ${Qt5Core_VERSION} ${Qt5Core_DIR}")
|
||||
message("| Qt5 Gui: ${Qt5Gui_VERSION} ${Qt5Gui_DIR}")
|
||||
message("| Qt5 Widgets: ${Qt5Widgets_VERSION} ${Qt5Widgets_DIR}")
|
||||
message("| Qt5 PrintSupport: ${Qt5Widgets_VERSION} ${Qt5PrintSupport_DIR}")
|
||||
message("| MongoDB: ${MongoDB_VERSION} ${MongoDB_BUILD_DIR}")
|
||||
message("| MongoDB: ${MongoDB_VERSION} ${MongoDB_BUILD_DIR}")
|
||||
message("| OpenSSL: ${OPENSSL_VERSION} ${OpenSSL_DIR}")
|
||||
message("|")
|
||||
message("|-- Summary:")
|
||||
message("|")
|
||||
|
||||
@@ -52,6 +52,18 @@ elseif("${CMAKE_BUILD_TYPE}" MATCHES "MinSizeRel")
|
||||
set(BUILD_MINSIZEREL TRUE)
|
||||
endif()
|
||||
|
||||
# Compiler checks
|
||||
# We check Clang using MATCH instead of strict equality, because as of CMake 3.0.0 the
|
||||
# CMAKE_<LANG>_COMPILER_ID value for Apple-provided Clang is AppleClang. To test
|
||||
# for both the Apple-provided Clang and the regular Clang we use MATCH.
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
set(COMPILER_CLANG TRUE)
|
||||
elseif(CMAKE_CXX_COMPILER_ID MATCHES "^GNU$")
|
||||
set(COMPILER_GCC TRUE)
|
||||
elseif(CMAKE_CXX_COMPILER_ID MATCHES "^MSVC$")
|
||||
set(COMPILER_MSVC TRUE)
|
||||
endif()
|
||||
|
||||
# Link "groups" and "whole-archive" options
|
||||
if(SYSTEM_LINUX OR SYSTEM_FREEBSD OR SYSTEM_OPENBSD)
|
||||
set(LINK_LIBGROUP_START -Wl,--start-group)
|
||||
|
||||
@@ -56,6 +56,27 @@ install(
|
||||
FILES "${CMAKE_BINARY_DIR}/qt.conf"
|
||||
DESTINATION "${qt_conf_dir}")
|
||||
|
||||
# Install OpenSSL dynamic lib files
|
||||
if(SYSTEM_WINDOWS)
|
||||
install(
|
||||
FILES
|
||||
"${OpenSSL_DIR}/out32dll/ssleay32.dll"
|
||||
"${OpenSSL_DIR}/out32dll/libeay32.dll"
|
||||
DESTINATION ${bin_dir})
|
||||
elseif(SYSTEM_MACOSX)
|
||||
install(
|
||||
FILES
|
||||
"${OpenSSL_DIR}/libssl.1.0.0.dylib"
|
||||
"${OpenSSL_DIR}/libcrypto.1.0.0.dylib"
|
||||
DESTINATION ${lib_dir}/lib)
|
||||
elseif(SYSTEM_LINUX)
|
||||
install(
|
||||
FILES
|
||||
"${OpenSSL_DIR}/libssl.so.1.0.0"
|
||||
"${OpenSSL_DIR}/libcrypto.so.1.0.0"
|
||||
DESTINATION ${lib_dir})
|
||||
endif()
|
||||
|
||||
# Install binary
|
||||
install(
|
||||
TARGETS robomongo
|
||||
@@ -79,7 +100,6 @@ install_icu_libs()
|
||||
if(SYSTEM_LINUX)
|
||||
install_qt_lib(XcbQpa DBus)
|
||||
install_qt_plugins(
|
||||
QGtk2ThemePlugin
|
||||
QXcbIntegrationPlugin)
|
||||
|
||||
elseif(SYSTEM_MACOSX)
|
||||
@@ -105,4 +125,3 @@ elseif(SYSTEM_WINDOWS)
|
||||
set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION .)
|
||||
include(InstallRequiredSystemLibraries)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ elseif(SYSTEM_WINDOWS)
|
||||
# The /MP option can reduce the total time to compile the source files. This option
|
||||
# causes the compiler to create one or more copies of itself, each in a separate process.
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS} /MP")
|
||||
|
||||
set(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_CXX_STANDARD_LIBRARIES} psapi.lib bcrypt.lib Iphlpapi.lib dbghelp.lib ws2_32.lib winmm.lib version.lib")
|
||||
endif()
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -6,7 +6,12 @@ Robomongo uses [modified version](https://github.com/paralect/robomongo-shell/tr
|
||||
Build for Mac OS X or Linux
|
||||
---------------------------
|
||||
|
||||
1. Clone Robomongo Shell and checkout to `roboshell-v3.2` branch:
|
||||
#### Prerequisites
|
||||
|
||||
1. Install [Scons](http://scons.org/tag/releases.html) (2.4 or later)
|
||||
|
||||
|
||||
2. Clone Robomongo Shell and checkout to `roboshell-v3.2` branch:
|
||||
|
||||
```sh
|
||||
$ git clone https://github.com/paralect/robomongo-shell.git
|
||||
@@ -14,7 +19,7 @@ Build for Mac OS X or Linux
|
||||
$ git checkout roboshell-v3.2
|
||||
```
|
||||
|
||||
2. Build Robomongo Shell:
|
||||
3. Build Robomongo Shell:
|
||||
|
||||
```sh
|
||||
$ bin/build
|
||||
|
||||
+99
-20
@@ -1,28 +1,104 @@
|
||||
Building Robomongo
|
||||
Building Robomongo (Mac OS X and Linux)
|
||||
==================
|
||||
|
||||
#### Step 1.
|
||||
A. Prerequisites
|
||||
-------------
|
||||
|
||||
Download and build Robomongo Shell (our fork of MongoDB).
|
||||
Refer to [Building Robomongo Shell](BuildingMongoDB.md) document.
|
||||
1. Install CMake (3.2 or later)
|
||||
2. Install [Scons](http://scons.org/tag/releases.html) (2.4 or later)
|
||||
3. Install Qt 5.7
|
||||
|
||||
#### Step 2.
|
||||
```sh
|
||||
Example installation for MAC OSX and Ubuntu:
|
||||
Go to http://download.qt.io/archive/qt/5.7/5.7.0/
|
||||
Download, run and install
|
||||
qt-opensource-mac-x64-clang-5.7.0.dmg (for MAC OSX)
|
||||
qt-opensource-linux-x64-5.7.0.run (for Linux)
|
||||
After successful installation you should have
|
||||
/path/to/qt-5.7.0/5.7/clang_64 (for MAC OSX)
|
||||
/path/to/qt-5.7.0/5.7/gcc_64 (for Linux)
|
||||
|
||||
More information for installing on Ubuntu:
|
||||
https://wiki.qt.io/Install_Qt_5_on_Ubuntu
|
||||
```
|
||||
|
||||
4. Download and Build OpenSSL (1.0.1p) - (explained below in section B)
|
||||
|
||||
B. Building Robomongo and Dependencies
|
||||
-------------
|
||||
|
||||
#### Step 1. Build OpenSSL (1.0.1p)
|
||||
|
||||
Steps to build OpenSSL on MAC:
|
||||
|
||||
```sh
|
||||
Download openssl-1.0.1p (ftp://ftp.openssl.org/source/old/1.0.1/openssl-1.0.1p.tar.gz)
|
||||
tar -xvzf openssl-1.0.1p.tar.gz
|
||||
cd /Users/<user>/Downloads/openssl-1.0.1p
|
||||
./Configure darwin64-x86_64-cc shared --openssldir="@rpath"
|
||||
make (or sudo make)
|
||||
mkdir lib
|
||||
cp libssl.1.0.0.dylib libcrypto.1.0.0.dylib lib/
|
||||
```
|
||||
Helper Commands
|
||||
```sh
|
||||
// to start fresh build
|
||||
make clean
|
||||
```
|
||||
|
||||
|
||||
Steps to build OpenSSL on Linux:
|
||||
|
||||
```sh
|
||||
Download openssl-1.0.1p (ftp://ftp.openssl.org/source/old/1.0.1/openssl-1.0.1p.tar.gz)
|
||||
tar -xvzf openssl-1.0.1p.tar.gz
|
||||
cd /home/<user>/Downloads/openssl-1.0.1p
|
||||
./config shared
|
||||
make
|
||||
mkdir lib
|
||||
cp libssl* libcrypto* lib/
|
||||
```
|
||||
|
||||
#### Step 2. Build Robomongo Shell (fork of MongoDB)
|
||||
|
||||
Clone Robomongo Shell and checkout to `roboshell-v3.2` branch:
|
||||
|
||||
```sh
|
||||
$ git clone https://github.com/paralect/robomongo-shell.git
|
||||
$ cd robomongo-shell
|
||||
$ git checkout roboshell-v3.2
|
||||
```
|
||||
|
||||
Set special environment variable `ROBOMONGO_CMAKE_PREFIX_PATH` to point to a set of
|
||||
directories:
|
||||
|
||||
1. Location of Qt 5 SDK
|
||||
2. Location of Robomongo Shell
|
||||
3. Location of OpenSSL
|
||||
|
||||
Separate directories by semicolon `;` (not colon):
|
||||
|
||||
// MAC OSX example:
|
||||
$ export ROBOMONGO_CMAKE_PREFIX_PATH="/path/to/qt-5.7.0/5.7/clang_64;/path/to/robomongo-shell;/path/to/openssl-1.0.1p"
|
||||
// Ubuntu example:
|
||||
$ export ROBOMONGO_CMAKE_PREFIX_PATH="/path/to/qt-5.7.0/5.7/gcc_64;/path/to/robomongo-shell;/path/to/openssl-1.0.1p"
|
||||
|
||||
|
||||
Build Robomongo Shell:
|
||||
|
||||
```sh
|
||||
$ bin/build
|
||||
```
|
||||
|
||||
For more information refer to [Building Robomongo Shell](BuildingMongoDB.md)
|
||||
|
||||
#### Step 3. Build Robomongo
|
||||
|
||||
Download Robomongo:
|
||||
|
||||
$ git clone https://github.com/paralect/robomongo
|
||||
$ cd robomongo
|
||||
|
||||
Set special environment variable `ROBOMONGO_CMAKE_PREFIX_PATH` to point to a set of
|
||||
directories:
|
||||
|
||||
1. Location of Qt 5 SDK
|
||||
2. Location of Robomongo Shell (built in "Step 1")
|
||||
|
||||
Separate directories by semicolon `;` (not colon):
|
||||
|
||||
$ export ROBOMONGO_CMAKE_PREFIX_PATH="/path/to/qt-5.5/5.5/clang_64;/path/to/robomongo-shell"
|
||||
|
||||
Run configuration step:
|
||||
|
||||
$ bin/configure
|
||||
@@ -30,14 +106,14 @@ Run configuration step:
|
||||
And finally, build Robomongo:
|
||||
|
||||
$ bin/build
|
||||
|
||||
<br>
|
||||
## Helper commands
|
||||
|
||||
Execute just built Robomongo:
|
||||
To run robomongo:
|
||||
|
||||
$ bin/run
|
||||
|
||||
|
||||
**Helper commands**
|
||||
|
||||
Clean build files (in order to start build from scratch):
|
||||
|
||||
$ bin/clean
|
||||
@@ -49,4 +125,7 @@ Install Robomongo to `build/release/install` folder:
|
||||
Create package for your OS in `build/release/package` folder:
|
||||
|
||||
$ bin/pack
|
||||
|
||||
|
||||
**Notes**
|
||||
- For Ubuntu 16.04 builds, it has been reported that Robomongo /bin/configure step might fail and installing mesa-common-dev package solves it. Details: https://github.com/paralect/robomongo/issues/1268
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Building Robomongo for Windows
|
||||
Building Robomongo (Windows)
|
||||
==============================
|
||||
|
||||
Prerequisites
|
||||
A. Prerequisites
|
||||
-------------
|
||||
|
||||
#### 0. Git
|
||||
@@ -64,25 +64,70 @@ Prerequisites
|
||||
command will be available from Command Prompt. For this set "Add CMake to the system
|
||||
PATH for current user" in the installer wizard page.
|
||||
|
||||
#### 5. Qt 5.5
|
||||
#### 5. Qt 5.7
|
||||
|
||||
Download Qt from http://www.qt.io/download-open-source/ (we use Qt Online installer)
|
||||
|
||||
When installing Qt, only one component is required by Robomongo:
|
||||
|
||||
Qt 5.5
|
||||
Qt 5.7
|
||||
[x] msvc2013 64-bit.
|
||||
|
||||
This components contains prebuilt 64-bit binaries for Visual Studio 2013.
|
||||
|
||||
You may uncheck all other components to download as little as possible.
|
||||
|
||||
#### 6. OpenSSL (1.0.1p)
|
||||
Download openssl-1.0.1p (ftp://ftp.openssl.org/source/old/1.0.1/openssl-1.0.1p.tar.gz)
|
||||
|
||||
|
||||
|
||||
B. Building Robomongo and Dependencies
|
||||
-------------
|
||||
|
||||
## Build Robomongo Shell (fork of MongoDB)
|
||||
#### 1. Build OpenSSL (1.0.1p)
|
||||
|
||||
Steps to build OpenSSL on windows:
|
||||
```sh
|
||||
Open Visual Studio tool x64 Cross Tools Command prompt
|
||||
cd to the directory where you have openssl sources cd c:\myPath\openssl
|
||||
perl Configure VC-WIN64A
|
||||
ms\do_win64a
|
||||
nmake -f ms\ntdll.mak
|
||||
```
|
||||
|
||||
**Check Point:**
|
||||
After successful build, newly created sub directory out32dll should contain dynamic lib files libeay32.lib (and libeay32.dll) and ssleay32.lib (and ssleay32.dll); and associated include files will be in newly created folder "inc32".
|
||||
|
||||
Helper Commands:
|
||||
```sh
|
||||
// clean to start fresh build
|
||||
nmake -f ms\ntdll.mak clean
|
||||
```
|
||||
|
||||
Refer to OpenSSL documentation for more information:
|
||||
https://wiki.openssl.org/index.php/Compilation_and_Installation#W64
|
||||
|
||||
#### 2. Build Robomongo Shell (fork of MongoDB)
|
||||
|
||||
Clone Robomongo Shell and checkout to roboshell-v3.2 branch:
|
||||
|
||||
```sh
|
||||
$ git clone https://github.com/paralect/robomongo-shell.git
|
||||
$ cd robomongo-shell
|
||||
$ git checkout roboshell-v3.2
|
||||
```
|
||||
|
||||
Set environment variable `ROBOMONGO_CMAKE_PREFIX_PATH`, required by Robomongo-Shell and Robomongo build scripts, needs to be set according to the following directories:
|
||||
|
||||
1. Location of Qt SDK
|
||||
2. Location of Robomongo Shell
|
||||
3. Location of OpenSSL
|
||||
|
||||
Separate directories by semicolon `;` (not colon). You can do this in Command Prompt:
|
||||
|
||||
> setx ROBOMONGO_CMAKE_PREFIX_PATH "d:\Qt-5\5.7\msvc2013_64;d:\Projects\robomongo-shell;c:\myPath\openssl-1.0.1p"
|
||||
|
||||
Refer to MongoDB documentation for additional information:
|
||||
https://docs.mongodb.org/manual/contributors/tutorial/build-mongodb-from-source/#windows-specific-instructions
|
||||
|
||||
Open VS2013 x64 Native Tools Command Prompt and navigate to `robomongo-shell` folder.
|
||||
|
||||
@@ -96,20 +141,19 @@ Clean build files:
|
||||
|
||||
> bin\clean
|
||||
|
||||
Refer to MongoDB documentation for additional information:
|
||||
https://docs.mongodb.org/manual/contributors/tutorial/build-mongodb-from-source/#windows-specific-instructions
|
||||
|
||||
## Build Robomongo
|
||||
|
||||
Set special environment variable `ROBOMONGO_CMAKE_PREFIX_PATH` to point to set of
|
||||
directories:
|
||||
#### 3. Build Robomongo
|
||||
|
||||
1. Location of Qt SDK
|
||||
2. Location of Robomongo Shell
|
||||
Clone Robomongo:
|
||||
|
||||
Separate directories by semicolon `;` (not colon). You can do this in Command Prompt:
|
||||
|
||||
> setx ROBOMONGO_CMAKE_PREFIX_PATH "d:\Qt-5\5.5\msvc2013_64;d:\Projects\robomongo-shell"
|
||||
|
||||
Reopen your Command Prompt to make this variable available.
|
||||
```sh
|
||||
$ git clone https://github.com/paralect/robomongo.git
|
||||
```
|
||||
|
||||
Open VS2013 x64 Native Tools Command Prompt and navigate to `robomongo` folder.
|
||||
|
||||
Run configuration step:
|
||||
|
||||
@@ -118,15 +162,23 @@ Run configuration step:
|
||||
And finally, build Robomongo:
|
||||
|
||||
> bin\build
|
||||
|
||||
|
||||
#### Helper commands
|
||||
|
||||
|
||||
**Run Robomongo**
|
||||
|
||||
Install Robomongo to `build\Release\install` folder:
|
||||
|
||||
> bin\install
|
||||
|
||||
And run Robomongo
|
||||
|
||||
> \robomongo\build\Release\install\Robomongo.exe
|
||||
|
||||
**Helper commands**
|
||||
|
||||
Clean build files (in order to start build from scratch):
|
||||
|
||||
> bin\clean
|
||||
|
||||
Install Robomongo to `build\Release\install` folder:
|
||||
|
||||
> bin\install
|
||||
|
||||
|
||||
@@ -5,6 +5,10 @@ set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
|
||||
#set(sanitize "-fsanitize=address")
|
||||
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${sanitize}")
|
||||
#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${sanitize}")
|
||||
|
||||
set(SOURCES
|
||||
|
||||
# Isolated Scope #1
|
||||
@@ -49,8 +53,11 @@ set(SOURCES
|
||||
core/domain/App.cpp
|
||||
core/mongodb/MongoClient.cpp
|
||||
core/mongodb/MongoWorker.cpp
|
||||
core/mongodb/ReplicaSet.cpp
|
||||
core/settings/SettingsManager.cpp
|
||||
core/AppRegistry.cpp
|
||||
utils/string_operations.cpp
|
||||
utils/common.cpp
|
||||
|
||||
# Isolated Scope #3
|
||||
gui/GuiRegistry.cpp
|
||||
@@ -62,6 +69,7 @@ set(SOURCES
|
||||
gui/dialogs/ConnectionDialog.cpp
|
||||
gui/dialogs/CopyCollectionDialog.cpp
|
||||
gui/widgets/workarea/IndicatorLabel.cpp
|
||||
gui/dialogs/CreateCollectionDialog.cpp
|
||||
gui/dialogs/CreateDatabaseDialog.cpp
|
||||
gui/dialogs/CreateUserDialog.cpp
|
||||
gui/utils/ComboBoxUtils.cpp
|
||||
@@ -70,6 +78,8 @@ set(SOURCES
|
||||
# Isolated scope #4
|
||||
gui/dialogs/PreferencesDialog.cpp
|
||||
gui/dialogs/ConnectionsDialog.cpp
|
||||
gui/dialogs/CreateConnectionDialog.cpp
|
||||
gui/dialogs/ExportDialog.cpp
|
||||
|
||||
# Isolated scope #5
|
||||
gui/editors/PlainJavaScriptEditor.cpp
|
||||
@@ -81,6 +91,8 @@ set(SOURCES
|
||||
# Isolated scope #6
|
||||
gui/widgets/explorer/ExplorerCollectionTreeItem.cpp
|
||||
gui/widgets/explorer/ExplorerTreeItem.cpp
|
||||
gui/widgets/explorer/ExplorerReplicaSetFolderItem.cpp
|
||||
gui/widgets/explorer/ExplorerReplicaSetTreeItem.cpp
|
||||
gui/widgets/explorer/ExplorerDatabaseTreeItem.cpp
|
||||
gui/widgets/explorer/ExplorerDatabaseCategoryTreeItem.cpp
|
||||
gui/widgets/explorer/ExplorerUserTreeItem.cpp
|
||||
@@ -117,7 +129,10 @@ set(SOURCES
|
||||
gui/MainWindow.cpp
|
||||
|
||||
gui/dialogs/SSHTunnelTab.cpp
|
||||
gui/dialogs/SSLTab.cpp
|
||||
core/settings/SshSettings.cpp
|
||||
core/settings/SslSettings.cpp
|
||||
core/settings/ReplicaSetSettings.cpp
|
||||
core/mongodb/SshTunnelWorker.cpp
|
||||
|
||||
gui/resources/gui.qrc
|
||||
|
||||
+15
-18
@@ -7,14 +7,14 @@
|
||||
// Header "mongo/platform/basic" is required by "sock.h" under Windows
|
||||
#include <mongo/platform/basic.h>
|
||||
#include <mongo/util/net/sock.h>
|
||||
|
||||
#include <mongo/base/initializer.h>
|
||||
#include <mongo/util/net/ssl_options.h>
|
||||
|
||||
#include "robomongo/gui/MainWindow.h"
|
||||
#include "robomongo/gui/AppStyle.h"
|
||||
|
||||
#include "robomongo/ssh/ssh.h"
|
||||
|
||||
|
||||
int main(int argc, char *argv[], char** envp)
|
||||
{
|
||||
if (rbm_ssh_init()) {
|
||||
@@ -29,6 +29,12 @@ int main(int argc, char *argv[], char** envp)
|
||||
// Support for IPv6 is disabled by default. Enable it.
|
||||
mongo::enableIPv6(true);
|
||||
|
||||
// Perform SSL-enabled mongo initialization
|
||||
mongo::sslGlobalParams.sslMode.store(mongo::SSLParams::SSLMode_allowSSL);
|
||||
|
||||
// Cross Platform High DPI support - Qt 5.7
|
||||
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||
|
||||
// Initialization routine for MongoDB shell
|
||||
mongo::runGlobalInitializersOrDie(argc, argv, envp);
|
||||
|
||||
@@ -39,30 +45,21 @@ int main(int argc, char *argv[], char** envp)
|
||||
// This can cause a conflict when using POSIX functions, for instance, when
|
||||
// converting between data types such as floats and strings, since the notation
|
||||
// may differ between locales. To get around this problem, call the POSIX
|
||||
// function setlocale(LC_NUMERIC,"C") right after initializing QApplication or
|
||||
// function setlocale(LC_NUMERIC, "C") right after initializing QApplication or
|
||||
// QCoreApplication to reset the locale that is used for number formatting to "C"-locale.
|
||||
// (http://doc.qt.io/qt-5/qcoreapplication.html#locale-settings)
|
||||
setlocale(LC_NUMERIC, "C");
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
app.setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||
#endif
|
||||
|
||||
// Init GUI style
|
||||
Robomongo::AppStyleUtils::initStyle();
|
||||
|
||||
// Application main window
|
||||
Robomongo::MainWindow win;
|
||||
|
||||
// Resize main window. We are trying to keep it "almost" maximized.
|
||||
QRect screenGeometry = QApplication::desktop()->availableGeometry();
|
||||
int horizontalMargin = (int)(screenGeometry.width() * 0.1);
|
||||
int verticalMargin = (int)(screenGeometry.height() * 0.1);
|
||||
int width = screenGeometry.width() - horizontalMargin;
|
||||
int height = screenGeometry.height() - verticalMargin;
|
||||
win.resize(QSize(width, height));
|
||||
|
||||
// Center main window
|
||||
int x = (screenGeometry.width() - win.width()) / 2;
|
||||
int y = (screenGeometry.height() - win.height()) / 2;
|
||||
win.move(x, y);
|
||||
win.show();
|
||||
Robomongo::MainWindow mainWindow;
|
||||
mainWindow.show();
|
||||
|
||||
int rc = app.exec();
|
||||
rbm_ssh_cleanup();
|
||||
|
||||
@@ -33,12 +33,19 @@ void precisionAssert(const std::string &text, double d) {
|
||||
std::stringstream s;
|
||||
s.precision(std::numeric_limits<double>::digits10);
|
||||
s << d;
|
||||
if (d == (long long)d)
|
||||
s << ".0";
|
||||
std::cout << "Checking " << text << " - ";
|
||||
assert(text == s.str());
|
||||
std::cout << "Correct. " << std::endl;
|
||||
}
|
||||
|
||||
void testPrecision() {
|
||||
precisionAssert("-9.987654321", -9.987654321);
|
||||
precisionAssert("-1.0", -1.0);
|
||||
precisionAssert("-0.0", -0.0);
|
||||
precisionAssert("0.0", 0.0);
|
||||
precisionAssert("9.0", 9.0);
|
||||
precisionAssert("9.8", 9.8);
|
||||
precisionAssert("9.9", 9.9);
|
||||
precisionAssert("9.98", 9.98);
|
||||
|
||||
@@ -30,4 +30,20 @@ namespace Robomongo
|
||||
|
||||
class MongoDocument;
|
||||
typedef boost::shared_ptr<MongoDocument> MongoDocumentPtr;
|
||||
|
||||
enum ConnectionType {
|
||||
// This type of connection is shown in Explorer and also opens SSH tunnel for secondary
|
||||
// connections (if needed)
|
||||
ConnectionPrimary = 0,
|
||||
|
||||
// Never shown in Explorer and uses SSH tunnel from primary connection (if needed)
|
||||
ConnectionSecondary = 1,
|
||||
|
||||
// The same as Primary, but is specifically for testing connections.
|
||||
ConnectionTest = 2,
|
||||
|
||||
// Never shown in Explorer and can be used to refresh (via reconnecting) current connection view
|
||||
// (i.e. db version, storage engine, current replica set primary, status of replica set etc...)
|
||||
ConnectionRefresh = 3
|
||||
};
|
||||
}
|
||||
|
||||
@@ -10,9 +10,9 @@ namespace
|
||||
template<typename type, int size>
|
||||
inline type findTypeInArray(const char *(&arr)[size], const char *text)
|
||||
{
|
||||
for (int i=0; i < size;++i )
|
||||
for (int i = 0; i < size; ++i )
|
||||
{
|
||||
if (strcmp(text,arr[i])==0)
|
||||
if (strcmp(text, arr[i]) == 0)
|
||||
{
|
||||
return static_cast<type>(i);
|
||||
}
|
||||
|
||||
@@ -1,19 +1,35 @@
|
||||
#include "robomongo/core/EventError.h"
|
||||
|
||||
#include "robomongo/utils/string_operations.h"
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
EventError::EventError() :
|
||||
_isNull(true) {}
|
||||
|
||||
EventError::EventError(const std::string &errorMessage) :
|
||||
_errorMessage(errorMessage),
|
||||
EventError::EventError(const std::string &errorMessage, ErrorCode errorCode /*= Unknown*/,
|
||||
bool showErrorWindow /* = true */) :
|
||||
_errorMessage(captilizeFirstChar(errorMessage)),
|
||||
_errorCode(errorCode),
|
||||
_showErrorWindow(showErrorWindow),
|
||||
_isNull(false) {}
|
||||
|
||||
bool EventError::isNull() const {
|
||||
EventError::EventError(const std::string &errorMessage, ReplicaSet const& replicaSetInfo,
|
||||
bool showErrorWindow /* = true */ ) :
|
||||
_errorMessage(errorMessage),
|
||||
_errorCode(SetPrimaryUnreachable),
|
||||
_replicaSetInfo(replicaSetInfo),
|
||||
_showErrorWindow(showErrorWindow),
|
||||
_isNull(false) {}
|
||||
|
||||
bool EventError::isNull() const
|
||||
{
|
||||
return _isNull;
|
||||
}
|
||||
|
||||
const std::string &EventError::errorMessage() const {
|
||||
const std::string &EventError::errorMessage() const
|
||||
{
|
||||
return _errorMessage;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
|
||||
#include "robomongo/core/mongodb/ReplicaSet.h"
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
/**
|
||||
@@ -10,7 +12,11 @@ namespace Robomongo
|
||||
*/
|
||||
class EventError
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
enum ErrorCode { Unknown, SetPrimaryUnreachable };
|
||||
|
||||
/**
|
||||
* @brief Creates "null" (or, in other words, empty) error.
|
||||
* Subsequent call of isNull() on this object will return true.
|
||||
@@ -21,7 +27,14 @@ namespace Robomongo
|
||||
* @brief Creates error object with specified error message.
|
||||
* Subsequent call of isNull() on this object will return false.
|
||||
*/
|
||||
explicit EventError(const std::string &errorMessage);
|
||||
explicit EventError(const std::string &errorMessage, ErrorCode errorCode = Unknown,
|
||||
bool showErrorWindow = true);
|
||||
|
||||
/**
|
||||
* @brief Creates error object only for replica set connection failures
|
||||
*/
|
||||
explicit EventError(const std::string &errorMessage, ReplicaSet const& replicaSetInfo,
|
||||
bool showErrorWindow = true);
|
||||
|
||||
/**
|
||||
* @brief Tests whether error is "null" (or, in other words, empty).
|
||||
@@ -35,13 +48,19 @@ namespace Robomongo
|
||||
* @brief Returns error message that describes this error.
|
||||
*/
|
||||
const std::string &errorMessage() const;
|
||||
|
||||
ErrorCode errorCode() const { return _errorCode; }
|
||||
ReplicaSet replicaSetInfo() const { return _replicaSetInfo; }
|
||||
bool showErrorWindow() const { return _showErrorWindow; }
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Error message
|
||||
*/
|
||||
const std::string _errorMessage;
|
||||
|
||||
bool _isNull;
|
||||
std::string const _errorMessage;
|
||||
ErrorCode const _errorCode = Unknown;
|
||||
ReplicaSet const _replicaSetInfo;
|
||||
bool const _showErrorWindow = true;
|
||||
bool const _isNull;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace Robomongo
|
||||
char *data = new char[bytes];
|
||||
|
||||
const char *p = s.c_str();
|
||||
for( size_t i = 0; i < bytes; i++ ) {
|
||||
for (size_t i = 0; i < bytes; i++) {
|
||||
data[i] = mongo::fromHex(p);
|
||||
p += 2;
|
||||
}
|
||||
@@ -159,7 +159,7 @@ namespace Robomongo
|
||||
mongo::BinDataType binType = element.binDataType();
|
||||
|
||||
if (binType != mongo::newUUID && binType != mongo::bdtUUID)
|
||||
throw new std::invalid_argument("Binary subtype should be 3 (bdtUUID) or 4 (newUUID)");
|
||||
throw std::invalid_argument("Binary subtype should be 3 (bdtUUID) or 4 (newUUID)");
|
||||
|
||||
int len;
|
||||
const char *data = element.binData(len);
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
#include "robomongo/core/domain/App.h"
|
||||
#include <QHash>
|
||||
#include <QInputDialog>
|
||||
|
||||
#include "robomongo/core/domain/MongoServer.h"
|
||||
#include "robomongo/core/domain/MongoShell.h"
|
||||
#include "robomongo/core/domain/MongoCollection.h"
|
||||
#include "robomongo/core/settings/ConnectionSettings.h"
|
||||
#include "robomongo/core/settings/ReplicaSetSettings.h"
|
||||
#include "robomongo/core/settings/SshSettings.h"
|
||||
#include "robomongo/core/settings/SslSettings.h"
|
||||
#include "robomongo/core/mongodb/SshTunnelWorker.h"
|
||||
#include "robomongo/core/EventBus.h"
|
||||
#include "robomongo/core/utils/QtUtils.h"
|
||||
@@ -33,7 +36,7 @@ namespace Robomongo
|
||||
}
|
||||
|
||||
App::App(EventBus *const bus) : QObject(),
|
||||
_bus(bus) {
|
||||
_bus(bus), _lastServerHandle(0) {
|
||||
_bus->subscribe(this, EstablishSshConnectionResponse::Type);
|
||||
_bus->subscribe(this, ListenSshConnectionResponse::Type);
|
||||
_bus->subscribe(this, LogEvent::Type);
|
||||
@@ -45,29 +48,84 @@ namespace Robomongo
|
||||
std::for_each(_servers.begin(), _servers.end(), stdutils::default_delete<MongoServer*>());
|
||||
}
|
||||
|
||||
bool App::openServer(ConnectionSettings *connection, ConnectionType type) {
|
||||
|
||||
SshSettings *ssh = connection->sshSettings();
|
||||
|
||||
if (ssh->enabled() && ssh->askPassword() &&
|
||||
(type == ConnectionPrimary || type == ConnectionTest)) {
|
||||
bool ok = false;
|
||||
|
||||
bool isByKey = ssh->authMethod() == "publickey";
|
||||
std::string passText = isByKey ? "passphrase" : "password";
|
||||
|
||||
std::stringstream s;
|
||||
s << "In order to continue, please provide the " << passText;
|
||||
|
||||
if (isByKey)
|
||||
s << " for the key file";
|
||||
|
||||
s << "." << std::endl << std::endl;
|
||||
|
||||
if (ssh->authMethod() == "publickey")
|
||||
s << "Private Key: " << ssh->privateKeyFile() << std::endl;
|
||||
|
||||
s << "Server: " << ssh->host() << std::endl;
|
||||
s << "User: " << ssh->userName() << std::endl;
|
||||
|
||||
|
||||
s << std::endl << "Enter your " << passText << " that will never be stored:";
|
||||
|
||||
QString userInput = QInputDialog::getText(NULL, tr("SSH Authentication"),
|
||||
QtUtils::toQString(s.str()),
|
||||
QLineEdit::Password, "", &ok);
|
||||
|
||||
if (!ok)
|
||||
return false;
|
||||
|
||||
ssh->setAskedPassword(QtUtils::toStdString(userInput));
|
||||
}
|
||||
|
||||
SslSettings *sslSettings = connection->sslSettings();
|
||||
|
||||
if (sslSettings->sslEnabled() && sslSettings->usePemFile() && sslSettings->askPassphrase()
|
||||
&& (type == ConnectionPrimary || type == ConnectionTest))
|
||||
{
|
||||
if (!askSslPassphrasePromptDialog(connection))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
openServerInternal(connection, type);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and opens new server connection.
|
||||
* @param connection: ConnectionSettings, that will be owned by MongoServer.
|
||||
* @param visible: should this server be visible in UI (explorer) or not.
|
||||
*/
|
||||
MongoServer *App::openServer(ConnectionSettings *connection, bool visible) {
|
||||
if (visible)
|
||||
MongoServer *App::openServerInternal(ConnectionSettings* connSettings, ConnectionType type) {
|
||||
++_lastServerHandle;
|
||||
|
||||
if (type == ConnectionPrimary)
|
||||
_bus->publish(new ConnectingEvent(this));
|
||||
|
||||
// When connection is not "visible" or do not have
|
||||
// When connection is SECONDARY or do not have
|
||||
// ssh settings enabled, continue without SSH Tunnel
|
||||
if (!visible || !connection->sshSettings()->enabled()) {
|
||||
return continueOpenServer(connection, visible);
|
||||
if (type == ConnectionSecondary || !connSettings->sshSettings()->enabled()) {
|
||||
return continueOpenServer(_lastServerHandle, connSettings, type);
|
||||
}
|
||||
|
||||
// Open SSH channel and only after that open connection
|
||||
LOG_MSG(QString("Creating SSH tunnel to %1:%2...")
|
||||
.arg(QtUtils::toQString(connection->sshSettings()->host()))
|
||||
.arg(connection->sshSettings()->port()), mongo::logger::LogSeverity::Info());
|
||||
.arg(QtUtils::toQString(connSettings->sshSettings()->host()))
|
||||
.arg(connSettings->sshSettings()->port()), mongo::logger::LogSeverity::Info());
|
||||
|
||||
ConnectionSettings* settingsCopy = connection->clone();
|
||||
ConnectionSettings* settingsCopy = connSettings->clone();
|
||||
SshTunnelWorker* sshWorker = new SshTunnelWorker(settingsCopy);
|
||||
_bus->send(sshWorker, new EstablishSshConnectionRequest(this, sshWorker, settingsCopy, visible));
|
||||
_bus->send(sshWorker, new EstablishSshConnectionRequest(this, _lastServerHandle, sshWorker, settingsCopy, type));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -77,46 +135,54 @@ namespace Robomongo
|
||||
*/
|
||||
void App::closeServer(MongoServer *server)
|
||||
{
|
||||
_servers.erase(std::remove_if(_servers.begin(), _servers.end(), stdutils::RemoveIfFound<MongoServer*>(server)), _servers.end());
|
||||
_servers.erase(std::remove_if(_servers.begin(), _servers.end(), stdutils::RemoveIfFound<MongoServer*>(server)),
|
||||
_servers.end());
|
||||
}
|
||||
|
||||
MongoShell *App::openShell(MongoCollection *collection,const QString &filePathToSave)
|
||||
void App::openShell(MongoCollection *collection, const QString &filePathToSave)
|
||||
{
|
||||
ConnectionSettings *connection = collection->database()->server()->connectionRecord();
|
||||
connection->setDefaultDatabase(collection->database()->name());
|
||||
QString script = detail::buildCollectionQuery(collection->name(), "find({})");
|
||||
return openShell(connection, ScriptInfo(script, true, CursorPosition(0, -2), QtUtils::toQString(collection->database()->name()),filePathToSave));
|
||||
openShell(collection->database()->server(), connection, ScriptInfo(script, true, CursorPosition(0, -2),
|
||||
QtUtils::toQString(collection->database()->name()), filePathToSave));
|
||||
}
|
||||
|
||||
MongoShell *App::openShell(MongoServer *server,const QString &script, const std::string &dbName,
|
||||
void App::openShell(MongoServer *server, const QString &script, const std::string &dbName,
|
||||
bool execute, const QString &shellName,
|
||||
const CursorPosition &cursorPosition,const QString &filePathToSave)
|
||||
const CursorPosition &cursorPosition, const QString &filePathToSave)
|
||||
{
|
||||
ConnectionSettings *connection = server->connectionRecord();
|
||||
|
||||
if (!dbName.empty())
|
||||
connection->setDefaultDatabase(dbName);
|
||||
|
||||
return openShell(connection, ScriptInfo(script, execute, cursorPosition, shellName,filePathToSave));
|
||||
openShell(server, connection, ScriptInfo(script, execute, cursorPosition, shellName, filePathToSave));
|
||||
}
|
||||
|
||||
MongoShell *App::openShell(MongoDatabase *database, const QString &script,
|
||||
void App::openShell(MongoDatabase *database, const QString &script,
|
||||
bool execute, const QString &shellName,
|
||||
const CursorPosition &cursorPosition,const QString &filePathToSave)
|
||||
const CursorPosition &cursorPosition, const QString &filePathToSave)
|
||||
{
|
||||
ConnectionSettings *connection = database->server()->connectionRecord();
|
||||
connection->setDefaultDatabase(database->name());
|
||||
return openShell(connection, ScriptInfo(script, execute, cursorPosition, shellName,filePathToSave));
|
||||
openShell(database->server(), connection, ScriptInfo(script, execute, cursorPosition,
|
||||
shellName, filePathToSave));
|
||||
}
|
||||
|
||||
MongoShell *App::openShell(ConnectionSettings *connection, const ScriptInfo &scriptInfo)
|
||||
void App::openShell(MongoServer* server, ConnectionSettings* connection, const ScriptInfo &scriptInfo)
|
||||
{
|
||||
MongoServer *server = openServer(connection, false);
|
||||
MongoShell *shell = new MongoShell(server,scriptInfo);
|
||||
MongoServer *serverClone = openServerInternal(connection, ConnectionSecondary);
|
||||
if (!serverClone || !server)
|
||||
return;
|
||||
|
||||
MongoShell *shell = new MongoShell(serverClone, scriptInfo);
|
||||
// Connection between explorer's server and tab's MongoShells
|
||||
_bus->subscribe(server, ReplicaSetRefreshed::Type, shell);
|
||||
_shells.push_back(shell);
|
||||
_bus->publish(new OpeningShellEvent(this, shell));
|
||||
shell->execute();
|
||||
return shell;
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,7 +192,7 @@ namespace Robomongo
|
||||
void App::closeShell(MongoShell *shell)
|
||||
{
|
||||
// Do nothing, if this shell not owned by this App.
|
||||
MongoShellsContainerType::iterator it = std::find_if(_shells.begin(),_shells.end(),std::bind1st(std::equal_to<MongoShell *>(),shell));
|
||||
MongoShellsContainerType::iterator it = std::find_if(_shells.begin(), _shells.end(), std::bind1st(std::equal_to<MongoShell *>(), shell));
|
||||
if (it == _shells.end())
|
||||
return;
|
||||
|
||||
@@ -135,57 +201,103 @@ namespace Robomongo
|
||||
delete shell;
|
||||
}
|
||||
|
||||
MongoServer* App::continueOpenServer(ConnectionSettings *connection, bool visible, int localport) {
|
||||
ConnectionSettings* settings = connection->clone();
|
||||
MongoServer *App::continueOpenServer(int serverHandle, ConnectionSettings* connSettings, ConnectionType type,
|
||||
int localport)
|
||||
{
|
||||
ConnectionSettings* connSettingsClone = connSettings->clone();
|
||||
|
||||
// Modify connection settings when SSH tunnel is used
|
||||
if (visible && settings->sshSettings()->enabled()) {
|
||||
settings->setServerHost("127.0.0.1");
|
||||
settings->setServerPort(localport);
|
||||
if ((type == ConnectionPrimary || type == ConnectionTest)
|
||||
&& connSettingsClone->sshSettings()->enabled()) {
|
||||
connSettingsClone->setServerHost("127.0.0.1");
|
||||
connSettingsClone->setServerPort(localport);
|
||||
}
|
||||
|
||||
MongoServer *server = new MongoServer(settings, visible);
|
||||
MongoServer *server = new MongoServer(serverHandle, connSettingsClone, type);
|
||||
_servers.push_back(server);
|
||||
|
||||
server->runWorkerThread();
|
||||
|
||||
LOG_MSG(QString("Connecting to %1...").arg(QtUtils::toQString(server->connectionRecord()->getFullAddress())), mongo::logger::LogSeverity::Info());
|
||||
auto replicaSetStr = QString::fromStdString(connSettings->connectionName()) + " [Replica Set]";
|
||||
replicaSetStr = (connSettings->replicaSetSettings()->members().size() > 0)
|
||||
? replicaSetStr + QString::fromStdString(connSettings->replicaSetSettings()->members()[0])
|
||||
: replicaSetStr + "";
|
||||
|
||||
QString serverAddress = connSettings->isReplicaSet()
|
||||
? replicaSetStr
|
||||
: QString::fromStdString(connSettings->getFullAddress());
|
||||
|
||||
LOG_MSG(QString("Connecting to %1...").arg(serverAddress), mongo::logger::LogSeverity::Info());
|
||||
server->tryConnect();
|
||||
return server;
|
||||
}
|
||||
|
||||
void App::handle(EstablishSshConnectionResponse *event) {
|
||||
if (event->isError()) {
|
||||
_bus->publish(new ConnectionFailedEvent(this, event->error().errorMessage()));
|
||||
_bus->publish(new ConnectionFailedEvent(
|
||||
this, event->serverHandle, event->connectionType, event->error().errorMessage(),
|
||||
ConnectionFailedEvent::SshConnection));
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_MSG(QString("SSH tunnel created successfully"), mongo::logger::LogSeverity::Info());
|
||||
|
||||
_bus->send(event->worker, new ListenSshConnectionRequest(this));
|
||||
continueOpenServer(event->settings, event->visible, event->localport);
|
||||
|
||||
// record event->worker and delete when needed
|
||||
continueOpenServer(event->serverHandle, event->settings, event->connectionType, event->localport);
|
||||
_bus->send(event->worker, new ListenSshConnectionRequest(this, event->serverHandle, event->connectionType));
|
||||
}
|
||||
|
||||
void App::handle(LogEvent *event) {
|
||||
if (event->level == LogEvent::ERROR) {
|
||||
if (event->level == LogEvent::RBM_ERROR) {
|
||||
LOG_MSG(event->message, mongo::logger::LogSeverity::Error());
|
||||
} else if (event->level == LogEvent::WARN) {
|
||||
} else if (event->level == LogEvent::RBM_WARN) {
|
||||
LOG_MSG(event->message, mongo::logger::LogSeverity::Warning());
|
||||
} else if (event->level == LogEvent::INFO) {
|
||||
} else if (event->level == LogEvent::RBM_INFO) {
|
||||
LOG_MSG(event->message, mongo::logger::LogSeverity::Info());
|
||||
} else if (event->level == LogEvent::DEBUG) {
|
||||
} else if (event->level == LogEvent::RBM_DEBUG) {
|
||||
LOG_MSG(event->message, mongo::logger::LogSeverity::Log());
|
||||
}
|
||||
}
|
||||
|
||||
void App::handle(ListenSshConnectionResponse *event) {
|
||||
if (event->isError()) {
|
||||
_bus->publish(new ConnectionFailedEvent(this, event->error().errorMessage()));
|
||||
_bus->publish(new ConnectionFailedEvent(this, event->serverHandle, event->connectionType, event->error().errorMessage(),
|
||||
ConnectionFailedEvent::SshChannel));
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_MSG(QString("SSH tunnel closed."), mongo::logger::LogSeverity::Error());
|
||||
}
|
||||
|
||||
void App::fireConnectionFailedEvent(int serverHandle, ConnectionType type, std::string errormsg,
|
||||
ConnectionFailedEvent::Reason reason) {
|
||||
_bus->publish(new ConnectionFailedEvent(this, serverHandle, type, errormsg, reason));
|
||||
}
|
||||
|
||||
bool App::askSslPassphrasePromptDialog(ConnectionSettings *connSettings) const
|
||||
{
|
||||
auto sslSettings = connSettings->sslSettings();
|
||||
bool ok = false;
|
||||
|
||||
std::stringstream s;
|
||||
s << "In order to continue, please provide the passphrase";
|
||||
s << "." << std::endl << std::endl;
|
||||
|
||||
s << "Server: " << connSettings->serverHost() << ":" << connSettings->serverPort() << std::endl;
|
||||
s << "PEM file: " << sslSettings->pemKeyFile() << std::endl;
|
||||
|
||||
s << std::endl << "Enter your PEM key passphrase (will never be stored):";
|
||||
|
||||
QString userInput = QInputDialog::getText(NULL, tr("SSL Authentication"),
|
||||
QtUtils::toQString(s.str()),
|
||||
QLineEdit::Password, "", &ok);
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
sslSettings->setPemPassPhrase(QtUtils::toStdString(userInput));
|
||||
return ok;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -47,8 +47,9 @@ namespace Robomongo
|
||||
* @brief Creates and opens new server connection.
|
||||
* @param connection: ConnectionSettings, that will be owned by MongoServer.
|
||||
* @param visible: should this server be visible in UI (explorer) or not.
|
||||
* @return Succeeded or not
|
||||
*/
|
||||
MongoServer *openServer(ConnectionSettings *connection, bool visible);
|
||||
bool openServer(ConnectionSettings *connection, ConnectionType type);
|
||||
|
||||
/**
|
||||
* @brief Closes MongoServer connection and frees all resources, owned
|
||||
@@ -59,18 +60,22 @@ namespace Robomongo
|
||||
/**
|
||||
* @brief Open new shell based on specified collection
|
||||
*/
|
||||
MongoShell *openShell(MongoCollection *collection,const QString &filePathToSave=QString());
|
||||
void openShell(MongoCollection *collection, const QString &filePathToSave = QString());
|
||||
|
||||
MongoShell *openShell(MongoServer *server, const QString &script, const std::string &dbName = std::string(),
|
||||
void openShell(MongoServer *server, const QString &script, const std::string &dbName = std::string(),
|
||||
bool execute = true, const QString &shellName = QString(),
|
||||
const CursorPosition &cursorPosition = CursorPosition(), const QString &file = QString());
|
||||
|
||||
MongoShell *openShell(MongoDatabase *database, const QString &script,
|
||||
void openShell(MongoDatabase *database, const QString &script,
|
||||
bool execute = true, const QString &shellName = QString(),
|
||||
const CursorPosition &cursorPosition = CursorPosition(), const QString &filePathToSave = QString());
|
||||
|
||||
MongoShell *openShell(ConnectionSettings *connection, const ScriptInfo &scriptInfo);
|
||||
MongoServersContainerType getServers() const {return _servers; };
|
||||
/**
|
||||
* @brief Open new shell using explorer's MongoServer (ExplorerServerTreeItem's _server)
|
||||
*/
|
||||
void openShell(MongoServer* server, ConnectionSettings* connSettings, const ScriptInfo &scriptInfo);
|
||||
|
||||
MongoServersContainerType getServers() const { return _servers; };
|
||||
|
||||
/**
|
||||
* @brief Closes MongoShell and frees all resources, owned by specified MongoShell.
|
||||
@@ -78,13 +83,25 @@ namespace Robomongo
|
||||
*/
|
||||
void closeShell(MongoShell *shell);
|
||||
|
||||
void fireConnectionFailedEvent(int serverHandle, ConnectionType type, std::string errormsg, ConnectionFailedEvent::Reason reason);
|
||||
|
||||
int getLastServerHandle() const { return _lastServerHandle; };
|
||||
|
||||
public Q_SLOTS:
|
||||
void handle(EstablishSshConnectionResponse *event);
|
||||
void handle(ListenSshConnectionResponse *event);
|
||||
void handle(LogEvent *event);
|
||||
|
||||
private:
|
||||
MongoServer* continueOpenServer(ConnectionSettings *connection, bool visible, int localport = 0);
|
||||
MongoServer *openServerInternal(ConnectionSettings* connSettings, ConnectionType type);
|
||||
MongoServer *continueOpenServer(int serverHandle, ConnectionSettings* connSettings, ConnectionType type, int localport = 0);
|
||||
|
||||
/**
|
||||
* @brief Create prompt dialog to enter SSL PEM key passphrase and save passphrase into SSL settings
|
||||
* @param connection Pointer to active connection settings
|
||||
* @return true on success, false otherwise
|
||||
*/
|
||||
bool askSslPassphrasePromptDialog(ConnectionSettings *connSettings) const;
|
||||
|
||||
/**
|
||||
* MongoServers, owned by this App.
|
||||
@@ -97,5 +114,9 @@ namespace Robomongo
|
||||
MongoShellsContainerType _shells;
|
||||
|
||||
EventBus *const _bus;
|
||||
|
||||
// Increase monotonically when new MongoServer is created
|
||||
// Never decreases.
|
||||
int _lastServerHandle;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include "robomongo/core/mongodb/MongoWorker.h"
|
||||
#include "robomongo/core/AppRegistry.h"
|
||||
#include "robomongo/core/EventBus.h"
|
||||
#include "robomongo/core/utils/Logger.h"
|
||||
#include "robomongo/utils/common.h"
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
@@ -15,6 +17,13 @@ namespace Robomongo
|
||||
R_REGISTER_EVENT(MongoDatabaseFunctionsLoadingEvent)
|
||||
R_REGISTER_EVENT(MongoDatabaseCollectionsLoadingEvent)
|
||||
|
||||
const std::string MongoDatabase::StorageEngineType::WIRED_TIGER = "wiredTiger";
|
||||
const std::string MongoDatabase::StorageEngineType::MMAPV1 = "mmapv1";
|
||||
|
||||
const float MongoDatabase::DBVersion::MONGODB_2_6 = 2.6f;
|
||||
const float MongoDatabase::DBVersion::MONGODB_3_0 = 3.0f;
|
||||
const float MongoDatabase::DBVersion::MONGODB_3_2 = 3.2f;
|
||||
|
||||
MongoDatabase::MongoDatabase(MongoServer *server, const std::string &name) :
|
||||
QObject(),
|
||||
_system(name == "admin" || name == "local"),
|
||||
@@ -30,113 +39,148 @@ namespace Robomongo
|
||||
void MongoDatabase::loadCollections()
|
||||
{
|
||||
_bus->publish(new MongoDatabaseCollectionsLoadingEvent(this));
|
||||
_bus->send(_server->client(), new LoadCollectionNamesRequest(this, _name));
|
||||
_bus->send(_server->worker(), new LoadCollectionNamesRequest(this, _name));
|
||||
}
|
||||
|
||||
void MongoDatabase::loadUsers()
|
||||
{
|
||||
_bus->publish(new MongoDatabaseUsersLoadingEvent(this));
|
||||
_bus->send(_server->client(), new LoadUsersRequest(this, _name));
|
||||
_bus->send(_server->worker(), new LoadUsersRequest(this, _name));
|
||||
}
|
||||
|
||||
void MongoDatabase::loadFunctions()
|
||||
{
|
||||
_bus->publish(new MongoDatabaseFunctionsLoadingEvent(this));
|
||||
_bus->send(_server->client(), new LoadFunctionsRequest(this, _name));
|
||||
_bus->send(_server->worker(), new LoadFunctionsRequest(this, _name));
|
||||
}
|
||||
void MongoDatabase::createCollection(const std::string &collection)
|
||||
|
||||
void MongoDatabase::createCollection(const std::string &collection, long long size, bool capped, int maxDocNum, const mongo::BSONObj& extraOptions)
|
||||
{
|
||||
_bus->send(_server->client(), new CreateCollectionRequest(this, MongoNamespace(_name, collection)));
|
||||
_bus->send(_server->worker(),
|
||||
new CreateCollectionRequest(this, MongoNamespace(_name, collection), extraOptions, size, capped, maxDocNum));
|
||||
}
|
||||
|
||||
void MongoDatabase::dropCollection(const std::string &collection)
|
||||
{
|
||||
_bus->send(_server->client(), new DropCollectionRequest(this, MongoNamespace(_name, collection)));
|
||||
_bus->send(_server->worker(), new DropCollectionRequest(this, MongoNamespace(_name, collection)));
|
||||
}
|
||||
|
||||
void MongoDatabase::renameCollection(const std::string &collection, const std::string &newCollection)
|
||||
{
|
||||
_bus->send(_server->client(), new RenameCollectionRequest(this, MongoNamespace(_name, collection), newCollection));
|
||||
_bus->send(_server->worker(), new RenameCollectionRequest(this, MongoNamespace(_name, collection),
|
||||
newCollection));
|
||||
}
|
||||
|
||||
void MongoDatabase::duplicateCollection(const std::string &collection, const std::string &newCollection)
|
||||
{
|
||||
_bus->send(_server->client(), new DuplicateCollectionRequest(this, MongoNamespace(_name, collection), newCollection));
|
||||
_bus->send(_server->worker(), new DuplicateCollectionRequest(this, MongoNamespace(_name, collection), newCollection));
|
||||
}
|
||||
|
||||
void MongoDatabase::copyCollection(MongoServer *server, const std::string &sourceDatabase, const std::string &collection)
|
||||
{
|
||||
_bus->send(_server->client(), new CopyCollectionToDiffServerRequest(this, server->client(), sourceDatabase, collection, _name));
|
||||
_bus->send(_server->worker(), new CopyCollectionToDiffServerRequest(this, server->worker(), sourceDatabase, collection, _name));
|
||||
}
|
||||
|
||||
void MongoDatabase::createUser(const MongoUser &user, bool overwrite)
|
||||
{
|
||||
_bus->send(_server->client(), new CreateUserRequest(this, _name, user, overwrite));
|
||||
_bus->send(_server->worker(), new CreateUserRequest(this, _name, user, overwrite));
|
||||
}
|
||||
|
||||
void MongoDatabase::dropUser(const mongo::OID &id)
|
||||
void MongoDatabase::dropUser(const mongo::OID &id, std::string const& userName)
|
||||
{
|
||||
_bus->send(_server->client(), new DropUserRequest(this, _name, id));
|
||||
_bus->send(_server->worker(), new DropUserRequest(this, _name, id, userName));
|
||||
}
|
||||
|
||||
void MongoDatabase::createFunction(const MongoFunction &fun)
|
||||
{
|
||||
_bus->send(_server->client(), new CreateFunctionRequest(this, _name, fun));
|
||||
_bus->send(_server->worker(), new CreateFunctionRequest(this, _name, fun));
|
||||
}
|
||||
|
||||
void MongoDatabase::updateFunction(const std::string &name, const MongoFunction &fun)
|
||||
{
|
||||
_bus->send(_server->client(), new CreateFunctionRequest(this, _name, fun, name));
|
||||
_bus->send(_server->worker(), new CreateFunctionRequest(this, _name, fun, name));
|
||||
}
|
||||
|
||||
void MongoDatabase::dropFunction(const std::string &name)
|
||||
{
|
||||
_bus->send(_server->client(), new DropFunctionRequest(this, _name, name));
|
||||
_bus->send(_server->worker(), new DropFunctionRequest(this, _name, name));
|
||||
}
|
||||
|
||||
void MongoDatabase::handle(LoadCollectionNamesResponse *loaded)
|
||||
void MongoDatabase::handle(LoadCollectionNamesResponse *event)
|
||||
{
|
||||
if (loaded->isError()) {
|
||||
_bus->publish(new MongoDatabaseCollectionListLoadedEvent(this, loaded->error()));
|
||||
if (event->isError()) {
|
||||
if (_server->connectionRecord()->isReplicaSet()) // replica set
|
||||
handleIfReplicaSetUnreachable(event);
|
||||
else // single server
|
||||
_bus->publish(new MongoDatabaseCollectionListLoadedEvent(this, event->error()));
|
||||
|
||||
genericEventErrorHandler(event, "Failed to refresh 'Collections'.", _bus, this);
|
||||
return;
|
||||
}
|
||||
|
||||
clearCollections();
|
||||
const std::vector<MongoCollectionInfo> &colectionsInfos = loaded->collectionInfos();
|
||||
for (std::vector<MongoCollectionInfo>::const_iterator it = colectionsInfos.begin(); it != colectionsInfos.end(); ++it) {
|
||||
const MongoCollectionInfo &info = *it;
|
||||
MongoCollection *collection = new MongoCollection(this, info);
|
||||
addCollection(collection);
|
||||
}
|
||||
|
||||
for (auto const& collectionInfo : event->collectionInfos())
|
||||
addCollection(new MongoCollection(this, collectionInfo));
|
||||
|
||||
_bus->publish(new MongoDatabaseCollectionListLoadedEvent(this, _collections));
|
||||
LOG_MSG("'Collections' refreshed.", mongo::logger::LogSeverity::Info());
|
||||
}
|
||||
|
||||
void MongoDatabase::handle(CreateFunctionResponse *event)
|
||||
{
|
||||
if (event->isError()) {
|
||||
handleIfReplicaSetUnreachable(event);
|
||||
genericEventErrorHandler(event, "Failed to create function \'" + event->functionName + "\'.", _bus, this);
|
||||
}
|
||||
else {
|
||||
loadFunctions();
|
||||
LOG_MSG("Function \'" + event->functionName + "\' created.", mongo::logger::LogSeverity::Info());
|
||||
}
|
||||
}
|
||||
|
||||
void MongoDatabase::handle(CreateUserResponse *event)
|
||||
{
|
||||
if (event->isError())
|
||||
return;
|
||||
|
||||
if (event->isError()) {
|
||||
handleIfReplicaSetUnreachable(event);
|
||||
genericEventErrorHandler(event, "Failed to create user \'" + event->userName + "\'.", _bus, this);
|
||||
}
|
||||
else {
|
||||
loadUsers();
|
||||
LOG_MSG("User \'" + event->userName + "\' created.", mongo::logger::LogSeverity::Info());
|
||||
}
|
||||
}
|
||||
|
||||
void MongoDatabase::handle(LoadUsersResponse *event)
|
||||
{
|
||||
if (event->isError()) {
|
||||
_bus->publish(new MongoDatabaseUsersLoadedEvent(this, event->error()));
|
||||
if (_server->connectionRecord()->isReplicaSet()) // replica set
|
||||
handleIfReplicaSetUnreachable(event);
|
||||
else // single server
|
||||
_bus->publish(new MongoDatabaseUsersLoadedEvent(this, event->error()));
|
||||
|
||||
genericEventErrorHandler(event, "Failed to refresh 'Users'.", _bus, this);
|
||||
return;
|
||||
}
|
||||
|
||||
_bus->publish(new MongoDatabaseUsersLoadedEvent(this, this, event->users()));
|
||||
LOG_MSG("'Users' refreshed.", mongo::logger::LogSeverity::Info());
|
||||
}
|
||||
|
||||
void MongoDatabase::handle(LoadFunctionsResponse *event)
|
||||
{
|
||||
if (event->isError()) {
|
||||
_bus->publish(new MongoDatabaseFunctionsLoadedEvent(this, event->error()));
|
||||
if (_server->connectionRecord()->isReplicaSet()) // replica set
|
||||
handleIfReplicaSetUnreachable(event);
|
||||
else // single server
|
||||
_bus->publish(new MongoDatabaseFunctionsLoadedEvent(this, event->error()));
|
||||
|
||||
genericEventErrorHandler(event, "Failed to refresh 'Functions'.", _bus, this);
|
||||
return;
|
||||
}
|
||||
|
||||
_bus->publish(new MongoDatabaseFunctionsLoadedEvent(this, this, event->functions()));
|
||||
LOG_MSG("'Functions' refreshed.", mongo::logger::LogSeverity::Info());
|
||||
}
|
||||
|
||||
void MongoDatabase::clearCollections()
|
||||
@@ -149,4 +193,92 @@ namespace Robomongo
|
||||
{
|
||||
_collections.push_back(collection);
|
||||
}
|
||||
|
||||
void MongoDatabase::handle(CreateCollectionResponse *event)
|
||||
{
|
||||
if (event->isError()) {
|
||||
handleIfReplicaSetUnreachable(event);
|
||||
genericEventErrorHandler(event, "Failed to create collection \'" + event->collection + "\'.", _bus, this);
|
||||
}
|
||||
else {
|
||||
loadCollections();
|
||||
LOG_MSG("Collection \'" + event->collection + "\' created.", mongo::logger::LogSeverity::Info());
|
||||
}
|
||||
}
|
||||
|
||||
void MongoDatabase::handle(DropCollectionResponse *event)
|
||||
{
|
||||
if (event->isError()) {
|
||||
handleIfReplicaSetUnreachable(event);
|
||||
genericEventErrorHandler(event, "Failed to drop collection \'" + event->collection + "\'.", _bus, this);
|
||||
}
|
||||
else {
|
||||
loadCollections();
|
||||
LOG_MSG("Collection \'" + event->collection + "\' dropped", mongo::logger::LogSeverity::Info());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MongoDatabase::handle(DropFunctionResponse *event)
|
||||
{
|
||||
if (event->isError()) {
|
||||
handleIfReplicaSetUnreachable(event);
|
||||
genericEventErrorHandler(event, "Failed to remove function \'" + event->functionName + "\'.", _bus, this);
|
||||
}
|
||||
else {
|
||||
loadFunctions();
|
||||
LOG_MSG("Function \'" + event->functionName + "\' removed", mongo::logger::LogSeverity::Info());
|
||||
}
|
||||
}
|
||||
|
||||
void MongoDatabase::handle(DropUserResponse *event)
|
||||
{
|
||||
if (event->isError()) {
|
||||
handleIfReplicaSetUnreachable(event);
|
||||
genericEventErrorHandler(event, "Failed to drop user \'" + event->username + "\'.", _bus, this);
|
||||
}
|
||||
else {
|
||||
loadUsers();
|
||||
LOG_MSG("User \'" + event->username + "\' dropped", mongo::logger::LogSeverity::Info());
|
||||
}
|
||||
}
|
||||
|
||||
void MongoDatabase::handle(RenameCollectionResponse *event)
|
||||
{
|
||||
if (event->isError()) {
|
||||
handleIfReplicaSetUnreachable(event);
|
||||
genericEventErrorHandler(event, "Failed to rename collection.", _bus, this);
|
||||
}
|
||||
else {
|
||||
loadCollections();
|
||||
LOG_MSG("Collection \'" + event->oldCollection + "\' renamed to \'" +
|
||||
event->newCollection +"\'." , mongo::logger::LogSeverity::Info());
|
||||
}
|
||||
}
|
||||
|
||||
void MongoDatabase::handle(DuplicateCollectionResponse *event)
|
||||
{
|
||||
if (event->isError()) {
|
||||
handleIfReplicaSetUnreachable(event);
|
||||
genericEventErrorHandler(event, "Failed to duplicate collection \'" + event->sourceCollection + "\'.",
|
||||
_bus, this);
|
||||
}
|
||||
else {
|
||||
loadCollections();
|
||||
LOG_MSG("Collection \'" + event->sourceCollection + "\' duplicated as \'" +
|
||||
event->duplicateCollection + "\'.", mongo::logger::LogSeverity::Info());
|
||||
}
|
||||
}
|
||||
|
||||
void MongoDatabase::handleIfReplicaSetUnreachable(Event *event)
|
||||
{
|
||||
if (!_server->connectionRecord()->isReplicaSet())
|
||||
return;
|
||||
|
||||
if (EventError::SetPrimaryUnreachable == event->error().errorCode()) {
|
||||
auto refreshEvent = ReplicaSetRefreshed(this, event->error(), event->error().replicaSetInfo());
|
||||
_server->handle(&refreshEvent);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,6 +18,25 @@ namespace Robomongo
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Database storage engine type
|
||||
*/
|
||||
struct StorageEngineType
|
||||
{
|
||||
static const std::string WIRED_TIGER;
|
||||
static const std::string MMAPV1;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief MongoDB version
|
||||
*/
|
||||
struct DBVersion
|
||||
{
|
||||
static const float MONGODB_2_6;
|
||||
static const float MONGODB_3_0;
|
||||
static const float MONGODB_3_2;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief MongoDatabase
|
||||
* @param server: pointer to parent MongoServer
|
||||
@@ -37,14 +56,14 @@ namespace Robomongo
|
||||
|
||||
void loadFunctions();
|
||||
|
||||
void createCollection(const std::string &collection);
|
||||
void createCollection(const std::string &collection, long long size, bool capped, int maxDocNum, const mongo::BSONObj& extraOptions);
|
||||
void dropCollection(const std::string &collection);
|
||||
void renameCollection(const std::string &collection, const std::string &newCollection);
|
||||
void duplicateCollection(const std::string &collection, const std::string &newCollection);
|
||||
void copyCollection(MongoServer *server, const std::string &sourceDatabase, const std::string &collection);
|
||||
|
||||
void createUser(const MongoUser &user, bool overwrite);
|
||||
void dropUser(const mongo::OID &id);
|
||||
void dropUser(const mongo::OID &id, std::string const& userName);
|
||||
|
||||
void createFunction(const MongoFunction &fun);
|
||||
void updateFunction(const std::string &name, const MongoFunction &fun);
|
||||
@@ -64,11 +83,19 @@ namespace Robomongo
|
||||
void handle(LoadCollectionNamesResponse *event);
|
||||
void handle(LoadUsersResponse *event);
|
||||
void handle(LoadFunctionsResponse *event);
|
||||
void handle(CreateFunctionResponse *event);
|
||||
void handle(CreateUserResponse *event);
|
||||
void handle(CreateCollectionResponse *event);
|
||||
void handle(DropCollectionResponse *event);
|
||||
void handle(DropFunctionResponse *event);
|
||||
void handle(DropUserResponse *event);
|
||||
void handle(RenameCollectionResponse *event);
|
||||
void handle(DuplicateCollectionResponse *event);
|
||||
|
||||
private:
|
||||
void clearCollections();
|
||||
void addCollection(MongoCollection *collection);
|
||||
void handleIfReplicaSetUnreachable(Event *event);
|
||||
|
||||
private:
|
||||
MongoServer *_server;
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace Robomongo
|
||||
std::vector<MongoDocumentPtr> MongoDocument::fromBsonObj(const std::vector<mongo::BSONObj> &bsonObjs)
|
||||
{
|
||||
std::vector<MongoDocumentPtr> list;
|
||||
for (std::vector<mongo::BSONObj>::const_iterator it=bsonObjs.begin(); it!=bsonObjs.end(); ++it) {
|
||||
for (std::vector<mongo::BSONObj>::const_iterator it = bsonObjs.begin(); it != bsonObjs.end(); ++it) {
|
||||
list.push_back(fromBsonObj(*it));
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Robomongo
|
||||
{
|
||||
MongoFunction::MongoFunction(const mongo::BSONObj &obj)
|
||||
{
|
||||
_name = BsonUtils::getField<mongo::String>(obj,"_id");
|
||||
_name = BsonUtils::getField<mongo::String>(obj, "_id");
|
||||
_code = obj.getField("value")._asCode();
|
||||
}
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ namespace Robomongo
|
||||
std::string prepareServerAddress(const std::string &address)
|
||||
{
|
||||
size_t pos = address.find_first_of("{");
|
||||
if (pos!=std::string::npos){
|
||||
return address.substr(0,pos);
|
||||
if (pos != std::string::npos) {
|
||||
return address.substr(0, pos);
|
||||
}
|
||||
return address;
|
||||
}
|
||||
@@ -20,7 +20,7 @@ namespace Robomongo
|
||||
|
||||
CollectionInfo::CollectionInfo(const std::string &server, const std::string &database, const std::string &collection)
|
||||
:_serverAddress(server),
|
||||
_ns(database,collection)
|
||||
_ns(database, collection)
|
||||
{}
|
||||
|
||||
bool CollectionInfo::isValid() const
|
||||
|
||||
@@ -4,55 +4,82 @@
|
||||
#include "robomongo/core/settings/ConnectionSettings.h"
|
||||
#include "robomongo/core/settings/SshSettings.h"
|
||||
#include "robomongo/core/settings/SettingsManager.h"
|
||||
#include "robomongo/core/settings/ReplicaSetSettings.h"
|
||||
#include "robomongo/core/mongodb/MongoWorker.h"
|
||||
#include "robomongo/core/mongodb/SshTunnelWorker.h"
|
||||
#include "robomongo/core/AppRegistry.h"
|
||||
#include "robomongo/core/domain/App.h"
|
||||
#include "robomongo/core/EventBus.h"
|
||||
#include "robomongo/core/events/MongoEventsInfo.h"
|
||||
#include "robomongo/core/utils/Logger.h"
|
||||
#include "robomongo/core/utils/QtUtils.h"
|
||||
#include "robomongo/utils/common.h"
|
||||
#include "robomongo/utils/string_operations.h"
|
||||
|
||||
namespace Robomongo {
|
||||
R_REGISTER_EVENT(MongoServerLoadingDatabasesEvent)
|
||||
|
||||
MongoServer::MongoServer(ConnectionSettings *settings, bool visible) : QObject(),
|
||||
MongoServer::MongoServer(int handle, ConnectionSettings *settings, ConnectionType connectionType) : QObject(),
|
||||
_version(0.0f),
|
||||
_visible(visible),
|
||||
_client(NULL),
|
||||
_connectionType(connectionType),
|
||||
_worker(nullptr),
|
||||
_isConnected(false),
|
||||
_settings(settings),
|
||||
_bus(AppRegistry::instance().bus()) { }
|
||||
_connSettings(settings),
|
||||
_handle(handle),
|
||||
_bus(AppRegistry::instance().bus()),
|
||||
_app(AppRegistry::instance().app()),
|
||||
_replicaSetInfo(nullptr)
|
||||
{}
|
||||
|
||||
bool MongoServer::isConnected() const {
|
||||
return _isConnected;
|
||||
}
|
||||
|
||||
ConnectionSettings *MongoServer::connectionRecord() const {
|
||||
return _settings;
|
||||
return _connSettings.get();
|
||||
}
|
||||
|
||||
MongoServer::~MongoServer() {
|
||||
clearDatabases();
|
||||
|
||||
if (_client != NULL) {
|
||||
_client->stopAndDelete();
|
||||
if (_worker) {
|
||||
_worker->stopAndDelete();
|
||||
}
|
||||
|
||||
// MongoWorker "_client" does not deleted here, because it is now owned by
|
||||
// MongoWorker "_worker" is not deleted here, because it is now owned by
|
||||
// another thread (call to moveToThread() made in MongoWorker constructor).
|
||||
// It will be deleted by this thread by means of "deleteLater()", which
|
||||
// is also specified in MongoWorker constructor.
|
||||
|
||||
delete _settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Try to connect to MongoDB server.
|
||||
* @throws MongoException, if fails
|
||||
*/
|
||||
void MongoServer::tryConnect() {
|
||||
_bus->send(_client, new EstablishConnectionRequest(this));
|
||||
void MongoServer::tryConnect()
|
||||
{
|
||||
_bus->send(_worker, new EstablishConnectionRequest(this, _connectionType, _connSettings->uniqueId()));
|
||||
}
|
||||
|
||||
QStringList MongoServer::getDatabasesNames() const {
|
||||
void MongoServer::tryRefresh()
|
||||
{
|
||||
_bus->send(_worker, new EstablishConnectionRequest(this, ConnectionRefresh, _connSettings->uniqueId()));
|
||||
}
|
||||
|
||||
void MongoServer::tryRefreshReplicaSetConnection()
|
||||
{
|
||||
_bus->send(_worker, new EstablishConnectionRequest(this, ConnectionRefresh, _connSettings->uniqueId()));
|
||||
}
|
||||
|
||||
void MongoServer::tryRefreshReplicaSetFolder(bool expanded, bool showLoading /*= true*/)
|
||||
{
|
||||
if (!_connSettings->isReplicaSet())
|
||||
return;
|
||||
|
||||
if (showLoading)
|
||||
_bus->publish(new ReplicaSetFolderLoading(this));
|
||||
|
||||
_bus->send(_worker, new RefreshReplicaSetFolderRequest(this, expanded));
|
||||
}
|
||||
|
||||
QStringList MongoServer::getDatabasesNames() const
|
||||
{
|
||||
QStringList result;
|
||||
for (QList<MongoDatabase *>::const_iterator it = _databases.begin(); it != _databases.end(); ++it) {
|
||||
MongoDatabase *datab = *it;
|
||||
@@ -61,11 +88,17 @@ namespace Robomongo {
|
||||
return result;
|
||||
}
|
||||
|
||||
void MongoServer::createDatabase(const std::string &dbName) {
|
||||
_bus->send(_client,new CreateDatabaseRequest(this, dbName));
|
||||
void MongoServer::addDatabase(MongoDatabase *database) {
|
||||
_databases.append(database);
|
||||
}
|
||||
|
||||
MongoDatabase *MongoServer::findDatabaseByName(const std::string &dbName) const {
|
||||
void MongoServer::createDatabase(const std::string &dbName)
|
||||
{
|
||||
_bus->send(_worker, new CreateDatabaseRequest(this, dbName));
|
||||
}
|
||||
|
||||
MongoDatabase *MongoServer::findDatabaseByName(const std::string &dbName) const
|
||||
{
|
||||
for (QList<MongoDatabase *>::const_iterator it = _databases.begin(); it != _databases.end(); ++it) {
|
||||
MongoDatabase *datab = *it;
|
||||
if (datab->name() == dbName) {
|
||||
@@ -76,7 +109,7 @@ namespace Robomongo {
|
||||
}
|
||||
|
||||
void MongoServer::dropDatabase(const std::string &dbName) {
|
||||
_bus->send(_client,new DropDatabaseRequest(this, dbName));
|
||||
_bus->send(_worker, new DropDatabaseRequest(this, dbName));
|
||||
}
|
||||
|
||||
void MongoServer::insertDocuments(const std::vector<mongo::BSONObj> &objCont,
|
||||
@@ -87,94 +120,327 @@ namespace Robomongo {
|
||||
}
|
||||
|
||||
void MongoServer::insertDocument(const mongo::BSONObj &obj, const MongoNamespace &ns) {
|
||||
_bus->send(_client,new InsertDocumentRequest(this, obj, ns));
|
||||
_bus->send(_worker, new InsertDocumentRequest(this, obj, ns));
|
||||
}
|
||||
|
||||
void MongoServer::saveDocuments(const std::vector<mongo::BSONObj> &objCont, const MongoNamespace &ns) {
|
||||
for (std::vector<mongo::BSONObj>::const_iterator it = objCont.begin(); it != objCont.end(); it++) {
|
||||
saveDocument(*it,ns);
|
||||
saveDocument(*it, ns);
|
||||
}
|
||||
}
|
||||
|
||||
void MongoServer::saveDocument(const mongo::BSONObj &obj, const MongoNamespace &ns) {
|
||||
_bus->send(_client, new InsertDocumentRequest(this, obj, ns, true));
|
||||
_bus->send(_worker, new InsertDocumentRequest(this, obj, ns, true));
|
||||
}
|
||||
|
||||
void MongoServer::removeDocuments(mongo::Query query, const MongoNamespace &ns, bool justOne) {
|
||||
_bus->send(_client, new RemoveDocumentRequest(this, query, ns, justOne));
|
||||
void MongoServer::removeDocuments(mongo::Query query, const MongoNamespace &ns,
|
||||
RemoveDocumentCount removeCount, int index)
|
||||
{
|
||||
_bus->send(_worker, new RemoveDocumentRequest(this, query, ns, removeCount, index));
|
||||
}
|
||||
|
||||
void MongoServer::loadDatabases() {
|
||||
void MongoServer::loadDatabases()
|
||||
{
|
||||
_bus->publish(new MongoServerLoadingDatabasesEvent(this));
|
||||
_bus->send(_client, new LoadDatabaseNamesRequest(this));
|
||||
if (_connSettings->isReplicaSet()) {
|
||||
tryRefreshReplicaSetConnection();
|
||||
}
|
||||
else { // single server
|
||||
_bus->send(_worker, new LoadDatabaseNamesRequest(this));
|
||||
}
|
||||
}
|
||||
|
||||
void MongoServer::clearDatabases() {
|
||||
void MongoServer::clearDatabases()
|
||||
{
|
||||
qDeleteAll(_databases);
|
||||
_databases.clear();
|
||||
}
|
||||
|
||||
void MongoServer::addDatabase(MongoDatabase *database) {
|
||||
_databases.append(database);
|
||||
}
|
||||
void MongoServer::handle(EstablishConnectionResponse *event)
|
||||
{
|
||||
_connectionType = event->connectionType;
|
||||
|
||||
void MongoServer::handle(EstablishConnectionResponse *event) {
|
||||
// In any case, replica set info must be updated, there might be reachable secondary(ies).
|
||||
// Also cached set name must be updated or cleared for failed connections.
|
||||
updateReplicaSetSettings(event);
|
||||
|
||||
// --- Connection Failed
|
||||
if (event->isError()) {
|
||||
_isConnected = false;
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "Cannot connect to the MongoDB at " << connectionRecord()->getFullAddress()
|
||||
<< ".\n\nError:\n" << event->error().errorMessage();
|
||||
|
||||
_bus->publish(new ConnectionFailedEvent(this, ss.str()));
|
||||
handleConnectionFailure(event);
|
||||
return;
|
||||
}
|
||||
|
||||
const ConnectionInfo &info = event->info();
|
||||
// --- Connections Successful
|
||||
// Save various information after successful connection
|
||||
const ConnectionInfo &info = event->info;
|
||||
_version = info._version;
|
||||
_storageEngineType = info._storageEngineType;
|
||||
_isConnected = true;
|
||||
|
||||
// Do nothing if this is not "visible" connection
|
||||
if (!_visible) {
|
||||
return;
|
||||
// ConnectionRefresh is used just to update connection view (_version, _storageEngineType, _repPrimary etc..)
|
||||
// So we return here after updating(refreshing) information related to connection view.
|
||||
if (ConnectionRefresh == event->connectionType) {
|
||||
if (_connSettings->isReplicaSet()) {
|
||||
// If it is replica set connection, do not return yet.
|
||||
LOG_MSG("Replica set refreshed. [" + _connSettings->connectionName() + ']',
|
||||
mongo::logger::LogSeverity::Info());
|
||||
}
|
||||
else { // single server
|
||||
LOG_MSG("Server refreshed.", mongo::logger::LogSeverity::Info());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_bus->publish(new ConnectionEstablishedEvent(this));
|
||||
// Only for single servers
|
||||
if (!_connSettings->isReplicaSet()) {
|
||||
_bus->publish(new ConnectionEstablishedEvent(this, _connectionType, info));
|
||||
// Do nothing if this is not a "primary" connection
|
||||
if (ConnectionPrimary != _connectionType)
|
||||
return;
|
||||
}
|
||||
|
||||
if (ConnectionPrimary == _connectionType)
|
||||
LOG_MSG("Establish connection successful. Connection: " + _connSettings->connectionName(),
|
||||
mongo::logger::LogSeverity::Info());
|
||||
|
||||
clearDatabases();
|
||||
for(std::vector<std::string>::const_iterator it = info._databases.begin(); it != info._databases.end(); ++it) {
|
||||
const std::string &name = *it;
|
||||
MongoDatabase *db = new MongoDatabase(this, name);
|
||||
addDatabase(db);
|
||||
for (auto const& dbname : info._databases) {
|
||||
MongoDatabase *db = new MongoDatabase(this, dbname);
|
||||
addDatabase(db); // todo: serverClones for replica sets should not do this
|
||||
}
|
||||
|
||||
if (_connSettings->isReplicaSet()) {
|
||||
_bus->publish(new ConnectionEstablishedEvent(this, event->connectionType, info));
|
||||
// In order to do first connection much faster, time consuming refresh
|
||||
// "repSetMonitor->startOrContinueRefresh(). refreshAll()" is being requested after
|
||||
// successful connection.
|
||||
if (ConnectionPrimary == event->connectionType)
|
||||
_bus->send(_worker, new RefreshReplicaSetFolderRequest(this, false));
|
||||
}
|
||||
}
|
||||
|
||||
void MongoServer::handle(LoadDatabaseNamesResponse *event) {
|
||||
void MongoServer::handle(RefreshReplicaSetFolderResponse *event)
|
||||
{
|
||||
handleReplicaSetRefreshEvents(event->isError(), event->error(), event->replicaSet, event->expanded);
|
||||
}
|
||||
|
||||
void MongoServer::handle(LoadDatabaseNamesResponse *event)
|
||||
{
|
||||
if (event->isError()) {
|
||||
_bus->publish(new DatabaseListLoadedEvent(this, event->error()));
|
||||
return;
|
||||
}
|
||||
|
||||
clearDatabases();
|
||||
for(std::vector<std::string>::iterator it = event->databaseNames.begin(); it != event->databaseNames.end(); ++it) {
|
||||
const std::string &name = *it;
|
||||
MongoDatabase *db = new MongoDatabase(this, name);
|
||||
|
||||
for (auto const& dbname : event->databaseNames) {
|
||||
MongoDatabase *db = new MongoDatabase(this, dbname);
|
||||
addDatabase(db);
|
||||
}
|
||||
|
||||
_bus->publish(new DatabaseListLoadedEvent(this, _databases));
|
||||
LOG_MSG("Database list refreshed. Connection: " + _connSettings->connectionName(),
|
||||
mongo::logger::LogSeverity::Info());
|
||||
}
|
||||
|
||||
void MongoServer::handle(InsertDocumentResponse *event) {
|
||||
_bus->publish(new InsertDocumentResponse(event->sender(), event->error()));
|
||||
void MongoServer::handle(InsertDocumentResponse *event)
|
||||
{
|
||||
if (event->isError()) {
|
||||
if (_connSettings->isReplicaSet()) {
|
||||
if (ConnectionPrimary == _connectionType) { // Insert document from explorer context menu
|
||||
if (EventError::SetPrimaryUnreachable == event->error().errorCode()) {
|
||||
auto refreshEvent = ReplicaSetRefreshed(this, event->error(), event->error().replicaSetInfo());
|
||||
handle(&refreshEvent);
|
||||
}
|
||||
}
|
||||
else { // Insert document from tab results window (Notifier, OutputWindow widget)
|
||||
_bus->publish(new InsertDocumentResponse(this, event->error()));
|
||||
}
|
||||
}
|
||||
genericEventErrorHandler(event, "Failed to insert document.", _bus, this);
|
||||
}
|
||||
else {
|
||||
_bus->publish(new InsertDocumentResponse(this, event->error()));
|
||||
LOG_MSG("Document inserted.", mongo::logger::LogSeverity::Info());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MongoServer::handle(RemoveDocumentResponse *event) {
|
||||
_bus->publish(new RemoveDocumentResponse(event->sender(), event->error()));
|
||||
void MongoServer::handle(RemoveDocumentResponse *event)
|
||||
{
|
||||
if (event->removeCount == RemoveDocumentCount::MULTI && event->index > 0)
|
||||
return;
|
||||
|
||||
std::string subStr;
|
||||
switch (event->removeCount) {
|
||||
case RemoveDocumentCount::ONE: subStr = "document."; break;
|
||||
case RemoveDocumentCount::MULTI: subStr = "documents."; break;
|
||||
case RemoveDocumentCount::ALL: subStr = "all documents."; break;
|
||||
default: subStr = "(logic error)."; break;
|
||||
}
|
||||
|
||||
if (event->isError()) {
|
||||
if (_connSettings->isReplicaSet() &&
|
||||
EventError::SetPrimaryUnreachable == event->error().errorCode()) {
|
||||
auto refreshEvent = ReplicaSetRefreshed(this, event->error(), event->error().replicaSetInfo());
|
||||
handle(&refreshEvent);
|
||||
}
|
||||
genericEventErrorHandler(event, "Failed to remove " + subStr, _bus, this);
|
||||
}
|
||||
else { // success
|
||||
_bus->publish(new RemoveDocumentResponse(this, event->error(), event->removeCount, event->index));
|
||||
LOG_MSG("Removed " + subStr, mongo::logger::LogSeverity::Info());
|
||||
}
|
||||
}
|
||||
|
||||
void MongoServer::runWorkerThread() {
|
||||
_client = new MongoWorker(_settings->clone(),
|
||||
AppRegistry::instance().settingsManager()->loadMongoRcJs(),
|
||||
AppRegistry::instance().settingsManager()->batchSize());
|
||||
void MongoServer::runWorkerThread()
|
||||
{
|
||||
_worker = new MongoWorker(_connSettings->clone(),
|
||||
AppRegistry::instance().settingsManager()->loadMongoRcJs(),
|
||||
AppRegistry::instance().settingsManager()->batchSize(),
|
||||
AppRegistry::instance().settingsManager()->mongoTimeoutSec(),
|
||||
AppRegistry::instance().settingsManager()->shellTimeoutSec());
|
||||
}
|
||||
}
|
||||
|
||||
void MongoServer::handle(CreateDatabaseResponse *event)
|
||||
{
|
||||
if (event->isError()) {
|
||||
if (_connSettings->isReplicaSet() &&
|
||||
EventError::SetPrimaryUnreachable == event->error().errorCode()) {
|
||||
auto refreshEvent = ReplicaSetRefreshed(this, event->error(), event->error().replicaSetInfo());
|
||||
handle(&refreshEvent);
|
||||
}
|
||||
genericEventErrorHandler(event, "Failed to create database \'" + event->database + "\'.", _bus, this);
|
||||
}
|
||||
else {
|
||||
loadDatabases();
|
||||
LOG_MSG("Database \'" + event->database + "\' created.", mongo::logger::LogSeverity::Info());
|
||||
}
|
||||
}
|
||||
|
||||
void MongoServer::handle(DropDatabaseResponse *event)
|
||||
{
|
||||
if (event->isError()) {
|
||||
if (_connSettings->isReplicaSet() &&
|
||||
EventError::SetPrimaryUnreachable == event->error().errorCode()) {
|
||||
auto refreshEvent = ReplicaSetRefreshed(this, event->error(), event->error().replicaSetInfo());
|
||||
handle(&refreshEvent);
|
||||
}
|
||||
genericEventErrorHandler(event, "Failed to drop database \'" + event->database + "\'.", _bus, this);
|
||||
}
|
||||
else {
|
||||
loadDatabases();
|
||||
LOG_MSG("Database \'" + event->database + "\' dropped.", mongo::logger::LogSeverity::Info());
|
||||
}
|
||||
}
|
||||
|
||||
void MongoServer::handle(ReplicaSetRefreshed *event)
|
||||
{
|
||||
handleReplicaSetRefreshEvents(event->isError(), event->error(), event->replicaSet, false);
|
||||
}
|
||||
|
||||
void MongoServer::handleReplicaSetRefreshEvents(bool isError, EventError eventError,
|
||||
ReplicaSet const& replicaSet, bool expanded)
|
||||
{
|
||||
if (isError) { // Primary is unreachable
|
||||
_replicaSetInfo.reset(new ReplicaSet(replicaSet));
|
||||
LOG_MSG("Replica set folder refreshed with error. " + eventError.errorMessage() +
|
||||
". Connection: " + _connSettings->connectionName(), mongo::logger::LogSeverity::Error());
|
||||
_bus->publish(new ReplicaSetFolderRefreshed(this, eventError, true));
|
||||
return;
|
||||
}
|
||||
|
||||
// Primary is reachable
|
||||
// Update replica set settings and mongo server _replicaSetInfo
|
||||
_replicaSetInfo.reset(new ReplicaSet(replicaSet));
|
||||
_connSettings->setServerHost(_replicaSetInfo->primary.host());
|
||||
_connSettings->setServerPort(_replicaSetInfo->primary.port());
|
||||
_connSettings->replicaSetSettings()->setSetName(_replicaSetInfo->setName);
|
||||
|
||||
LOG_MSG("Replica set folder refreshed. Connection: " + _connSettings->connectionName(),
|
||||
mongo::logger::LogSeverity::Info());
|
||||
_bus->publish(new ReplicaSetFolderRefreshed(this, expanded));
|
||||
}
|
||||
|
||||
void MongoServer::updateReplicaSetSettings(EstablishConnectionResponse* event)
|
||||
{
|
||||
if (!_connSettings->isReplicaSet())
|
||||
return;
|
||||
|
||||
_replicaSetInfo.reset(new ReplicaSet(event->replicaSet));
|
||||
_connSettings->setServerHost(_replicaSetInfo->primary.host());
|
||||
_connSettings->setServerPort(_replicaSetInfo->primary.port());
|
||||
_connSettings->replicaSetSettings()->setSetName(_replicaSetInfo->setName);
|
||||
|
||||
// Cache replica set name for 2 times faster first connection
|
||||
if (ConnectionPrimary == _connectionType) {
|
||||
ConnectionSettings* originalConnSettings = AppRegistry::instance().settingsManager()
|
||||
->getConnectionSettings(event->info._originalConnectionSettingsId);
|
||||
if (originalConnSettings) {
|
||||
auto setName = event->isError() ? "" : _replicaSetInfo->setName;
|
||||
originalConnSettings->replicaSetSettings()->setSetName(setName);
|
||||
AppRegistry::instance().settingsManager()->save();
|
||||
LOG_MSG("Replica set name cached as \"" + setName + "\".", mongo::logger::LogSeverity::Info());
|
||||
}
|
||||
else
|
||||
LOG_MSG("Failed to cache the replica set name.", mongo::logger::LogSeverity::Warning());
|
||||
}
|
||||
}
|
||||
|
||||
void MongoServer::handleConnectionFailure(EstablishConnectionResponse* event)
|
||||
{
|
||||
_isConnected = false;
|
||||
|
||||
std::stringstream ss("Unknown error");
|
||||
auto eventErrorReason = event->errorReason;
|
||||
|
||||
if (_connSettings->isReplicaSet())
|
||||
{
|
||||
ss.clear();
|
||||
std::string server = "";
|
||||
if (_connSettings->replicaSetSettings()->members().size() > 0)
|
||||
server = "[" + _connSettings->replicaSetSettings()->members().front() + "]";
|
||||
|
||||
ss << "Cannot connect to replica set \"" << _connSettings->connectionName() << "\"" << server
|
||||
<< ". \nSet's primary is unreachable.\n\nReason:\n" << event->error().errorMessage();
|
||||
|
||||
_bus->publish(new ConnectionFailedEvent(this, _handle, event->connectionType, ss.str(),
|
||||
ConnectionFailedEvent::MongoConnection));
|
||||
_app->fireConnectionFailedEvent(_handle, event->connectionType, ss.str(),
|
||||
ConnectionFailedEvent::MongoConnection);
|
||||
LOG_MSG("Establish connection failed. " + event->error().errorMessage() + ". Connection: "
|
||||
+ _connSettings->connectionName(), mongo::logger::LogSeverity::Error());
|
||||
}
|
||||
else // single server
|
||||
{
|
||||
if (EstablishConnectionResponse::ErrorReason::MongoSslConnection == eventErrorReason)
|
||||
{
|
||||
auto reason = ConnectionFailedEvent::SslConnection;
|
||||
ss.clear();
|
||||
ss << "Cannot connect to the MongoDB at " << connectionRecord()->getFullAddress()
|
||||
<< ".\n\nError:\n" << "SSL connection failure: " << event->error().errorMessage();
|
||||
_app->fireConnectionFailedEvent(_handle, _connectionType, ss.str(), reason);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto reason = (EstablishConnectionResponse::ErrorReason::MongoAuth == eventErrorReason) ?
|
||||
ConnectionFailedEvent::MongoAuth : ConnectionFailedEvent::MongoConnection;
|
||||
ss.clear();
|
||||
ss << "Cannot connect to the MongoDB at " << connectionRecord()->getFullAddress()
|
||||
<< ".\n\nError:\n" << event->error().errorMessage();
|
||||
_app->fireConnectionFailedEvent(_handle, _connectionType, ss.str(), reason);
|
||||
}
|
||||
|
||||
// When connection cannot be established, we should cleanup this instance of MongoServer if it wasn't
|
||||
// shown in UI (i.e. it is not a Secondary connection that is used for shells tab)
|
||||
if (_connectionType == ConnectionPrimary || _connectionType == ConnectionTest)
|
||||
{
|
||||
LOG_MSG("Establish connection failed. " + event->error().errorMessage() +
|
||||
". Connection: " + _connSettings->connectionName(),
|
||||
mongo::logger::LogSeverity::Error());
|
||||
_app->closeServer(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Robomongo
|
||||
|
||||
@@ -8,10 +8,16 @@ namespace Robomongo
|
||||
{
|
||||
class MongoWorker;
|
||||
class MongoDatabase;
|
||||
class EstablishConnectionResponse;
|
||||
class EventBus;
|
||||
class App;
|
||||
|
||||
// Messages
|
||||
struct EstablishConnectionResponse;
|
||||
struct RefreshReplicaSetFolderResponse;
|
||||
class LoadDatabaseNamesResponse;
|
||||
class InsertDocumentResponse;
|
||||
class EventBus;
|
||||
struct CreateDatabaseResponse;
|
||||
struct DropDatabaseResponse;
|
||||
|
||||
/**
|
||||
* @brief MongoServer represents active connection to MongoDB server.
|
||||
@@ -29,30 +35,60 @@ namespace Robomongo
|
||||
* @param visible
|
||||
* @param defaultDatabase
|
||||
*/
|
||||
MongoServer(ConnectionSettings *connectionRecord, bool visible);
|
||||
MongoServer(int handle, ConnectionSettings *connectionRecord, ConnectionType connectionType);
|
||||
~MongoServer();
|
||||
|
||||
void runWorkerThread();
|
||||
|
||||
/**
|
||||
* @brief Try to connect to MongoDB server.
|
||||
* @brief Try to connect to MongoDB single server or replica set.
|
||||
* @throws MongoException, if fails
|
||||
*/
|
||||
void tryConnect();
|
||||
bool isConnected()const;
|
||||
|
||||
/**
|
||||
* @brief Try to re-connect to MongoDB server in order to refresh connection view.
|
||||
* Never shown in Explorer and can be used to refresh (via reconnecting) current connection view.
|
||||
* (i.e. db version, storage engine etc...)
|
||||
* @throws MongoException, if fails
|
||||
*/
|
||||
void tryRefresh();
|
||||
|
||||
/**
|
||||
* @brief Try to re-connect to MongoDB replica set in order to refresh connection view.
|
||||
* It is used to refresh (via reconnecting) current connection view.
|
||||
* (i.e. db version, storage engine, current replica set primary, status of replica set etc...)
|
||||
* It will update Explorer widgets depending on replica set status (online if primary reachable and
|
||||
* offline otherwise)
|
||||
*/
|
||||
void tryRefreshReplicaSetConnection();
|
||||
|
||||
/**
|
||||
* @brief Try to re-connect to MongoDB replica set in order to refresh connection view.
|
||||
* It is used to refresh (via reconnecting) current connection view.
|
||||
* (i.e. db version, storage engine, current replica set primary, status of replica set etc...)
|
||||
* It will update only 'Replica Set' folder widgets depending on replica set status
|
||||
* (online if primary reachable and offline otherwise)
|
||||
*/
|
||||
void tryRefreshReplicaSetFolder(bool expanded, bool showLoading = true);
|
||||
|
||||
bool isConnected() const;
|
||||
|
||||
void addDatabase(MongoDatabase *database);
|
||||
void createDatabase(const std::string &dbName);
|
||||
void dropDatabase(const std::string &dbName);
|
||||
QStringList getDatabasesNames() const;
|
||||
QList<MongoDatabase*> const& databases() const { return _databases; };
|
||||
MongoDatabase *findDatabaseByName(const std::string &dbName) const;
|
||||
|
||||
void insertDocuments(const std::vector<mongo::BSONObj> &objCont, const MongoNamespace &ns);
|
||||
void insertDocument(const mongo::BSONObj &obj, const MongoNamespace &ns);
|
||||
void saveDocuments(const std::vector<mongo::BSONObj> &objCont, const MongoNamespace &ns);
|
||||
void saveDocument(const mongo::BSONObj &obj, const MongoNamespace &ns);
|
||||
void removeDocuments(mongo::Query query, const MongoNamespace &ns, bool justOne = true);
|
||||
void removeDocuments(mongo::Query query, const MongoNamespace &ns, RemoveDocumentCount removeCount,
|
||||
int index = 0);
|
||||
float version() const{ return _version; }
|
||||
|
||||
const std::string& getStorageEngineType() const { return _storageEngineType; }
|
||||
|
||||
/**
|
||||
* @brief Returns associated connection record
|
||||
@@ -63,28 +99,41 @@ namespace Robomongo
|
||||
* @brief Loads databases of this server asynchronously.
|
||||
*/
|
||||
void loadDatabases();
|
||||
bool visible() const { return _visible; }
|
||||
MongoWorker *const client() const { return _client; }
|
||||
MongoWorker *const worker() const { return _worker; }
|
||||
|
||||
ReplicaSet* replicaSetInfo() const { return _replicaSetInfo.get(); }
|
||||
|
||||
void handle(ReplicaSetRefreshed *event);
|
||||
|
||||
protected Q_SLOTS:
|
||||
void handle(EstablishConnectionResponse *event);
|
||||
void handle(RefreshReplicaSetFolderResponse *event);
|
||||
void handle(LoadDatabaseNamesResponse *event);
|
||||
void handle(InsertDocumentResponse *event);
|
||||
void handle(RemoveDocumentResponse *event);
|
||||
void handle(CreateDatabaseResponse *event);
|
||||
void handle(DropDatabaseResponse *event);
|
||||
|
||||
private:
|
||||
private:
|
||||
void clearDatabases();
|
||||
void addDatabase(MongoDatabase *database);
|
||||
void handleReplicaSetRefreshEvents(bool isError, EventError eventError, ReplicaSet const& replicaSet,
|
||||
bool expanded);
|
||||
void updateReplicaSetSettings(EstablishConnectionResponse* event);
|
||||
void handleConnectionFailure(EstablishConnectionResponse* event);
|
||||
|
||||
MongoWorker* _client;
|
||||
ConnectionSettings* _settings;
|
||||
EventBus* _bus;
|
||||
MongoWorker *_worker;
|
||||
std::unique_ptr<ConnectionSettings> _connSettings;
|
||||
EventBus *_bus;
|
||||
App *_app;
|
||||
|
||||
float _version;
|
||||
bool _visible;
|
||||
std::string _storageEngineType;
|
||||
ConnectionType _connectionType;
|
||||
bool _isConnected;
|
||||
int _handle;
|
||||
|
||||
QList<MongoDatabase *> _databases;
|
||||
std::unique_ptr<ReplicaSet> _replicaSetInfo;
|
||||
};
|
||||
|
||||
class MongoServerLoadingDatabasesEvent : public Event
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include "robomongo/core/settings/SettingsManager.h"
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
{
|
||||
|
||||
MongoShell::MongoShell(MongoServer *server, const ScriptInfo &scriptInfo) :
|
||||
QObject(),
|
||||
@@ -24,7 +24,7 @@ namespace Robomongo
|
||||
{
|
||||
AppRegistry::instance().bus()->publish(new ScriptExecutingEvent(this));
|
||||
_scriptInfo.setScript(QtUtils::toQString(script));
|
||||
AppRegistry::instance().bus()->send(_server->client(), new ExecuteScriptRequest(this, query(), dbName));
|
||||
AppRegistry::instance().bus()->send(_server->worker(), new ExecuteScriptRequest(this, query(), dbName));
|
||||
LOG_MSG(_scriptInfo.script(), mongo::logger::LogSeverity::Info());
|
||||
}
|
||||
|
||||
@@ -37,19 +37,19 @@ namespace Robomongo
|
||||
{
|
||||
if (_scriptInfo.execute()) {
|
||||
AppRegistry::instance().bus()->publish(new ScriptExecutingEvent(this));
|
||||
AppRegistry::instance().bus()->send(_server->client(), new ExecuteScriptRequest(this, query(), dbName));
|
||||
if(!_scriptInfo.script().isEmpty())
|
||||
AppRegistry::instance().bus()->send(_server->worker(), new ExecuteScriptRequest(this, query(), dbName));
|
||||
if (!_scriptInfo.script().isEmpty())
|
||||
LOG_MSG(_scriptInfo.script(), mongo::logger::LogSeverity::Info());
|
||||
} else {
|
||||
AppRegistry::instance().bus()->publish(new ScriptExecutingEvent(this));
|
||||
_scriptInfo.setScript("");
|
||||
AppRegistry::instance().bus()->send(_server->client(), new ExecuteScriptRequest(this,query() , dbName));
|
||||
AppRegistry::instance().bus()->send(_server->worker(), new ExecuteScriptRequest(this, query() , dbName));
|
||||
}
|
||||
}
|
||||
|
||||
void MongoShell::query(int resultIndex, const MongoQueryInfo &info)
|
||||
{
|
||||
AppRegistry::instance().bus()->send(_server->client(), new ExecuteQueryRequest(this, resultIndex, info));
|
||||
AppRegistry::instance().bus()->send(_server->worker(), new ExecuteQueryRequest(this, resultIndex, info));
|
||||
}
|
||||
|
||||
void MongoShell::autocomplete(const std::string &prefix)
|
||||
@@ -57,12 +57,12 @@ namespace Robomongo
|
||||
AutocompletionMode autocompletionMode = AppRegistry::instance().settingsManager()->autocompletionMode();
|
||||
if (autocompletionMode == AutocompleteNone)
|
||||
return;
|
||||
AppRegistry::instance().bus()->send(_server->client(), new AutocompleteRequest(this, prefix, autocompletionMode));
|
||||
AppRegistry::instance().bus()->send(_server->worker(), new AutocompleteRequest(this, prefix, autocompletionMode));
|
||||
}
|
||||
|
||||
void MongoShell::stop()
|
||||
{
|
||||
// _server->client()->interrupt();
|
||||
// _server->worker()->interrupt();
|
||||
// mongo::Scope::setInterruptFlag(true);
|
||||
}
|
||||
|
||||
@@ -94,8 +94,17 @@ namespace Robomongo
|
||||
void MongoShell::handle(ExecuteScriptResponse *event)
|
||||
{
|
||||
if (event->isError()) {
|
||||
AppRegistry::instance().bus()->publish(new ScriptExecutedEvent(this, event->error()));
|
||||
return;
|
||||
if (_server->connectionRecord()->isReplicaSet()) {
|
||||
AppRegistry::instance().bus()->publish(
|
||||
new ReplicaSetRefreshed(this, event->error(), event->error().replicaSetInfo()));
|
||||
|
||||
AppRegistry::instance().bus()->publish(new ScriptExecutedEvent(this, event->error()));
|
||||
return;
|
||||
}
|
||||
else { // single server
|
||||
AppRegistry::instance().bus()->publish(new ScriptExecutedEvent(this, event->error()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
AppRegistry::instance().bus()->publish(new ScriptExecutedEvent(this, event->result, event->empty));
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Robomongo
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MongoShell(MongoServer *server,const ScriptInfo &scriptInfo);
|
||||
MongoShell(MongoServer *server, const ScriptInfo &scriptInfo);
|
||||
|
||||
void open(const std::string &script, const std::string &dbName = std::string());
|
||||
void query(int resultIndex, const MongoQueryInfo &info);
|
||||
@@ -19,7 +19,7 @@ namespace Robomongo
|
||||
MongoServer *server() const { return _server; }
|
||||
std::string query() const;
|
||||
void execute(const std::string &dbName = std::string());
|
||||
bool isExecutable() const {return _scriptInfo.execute(); }
|
||||
bool isExecutable() const { return _scriptInfo.execute(); }
|
||||
const QString &title() const { return _scriptInfo.title(); }
|
||||
const CursorPosition &cursor() const { return _scriptInfo.cursor(); }
|
||||
void setScript(const QString &script) { return _scriptInfo.setScript(script); }
|
||||
|
||||
@@ -29,15 +29,22 @@ namespace Robomongo
|
||||
{
|
||||
public:
|
||||
MongoShellExecResult() { }
|
||||
|
||||
MongoShellExecResult(const std::vector<MongoShellResult> &results,
|
||||
const std::string ¤tServer, bool isCurrentServerValid,
|
||||
const std::string ¤tDatabase, bool isCurrentDatabaseValid);
|
||||
|
||||
MongoShellExecResult(bool error, std::string const& errorMsg = "") :
|
||||
_error(error), _errorMessage(errorMsg) { }
|
||||
|
||||
std::vector<MongoShellResult> results() const { return _results; }
|
||||
std::string currentServer() const { return _currentServer; }
|
||||
std::string currentDatabase() const { return _currentDatabase; }
|
||||
void setCurrentServer(std::string const& server) { _currentServer = server; }
|
||||
std::string currentDatabase() const { return _currentDatabase; }
|
||||
bool isCurrentServerValid() const { return _isCurrentServerValid; }
|
||||
bool isCurrentDatabaseValid() const { return _isCurrentDatabaseValid; }
|
||||
std::string errorMessage() const { return _errorMessage; }
|
||||
bool error() const { return _error; }
|
||||
|
||||
private:
|
||||
std::vector<MongoShellResult> _results;
|
||||
@@ -45,5 +52,7 @@ namespace Robomongo
|
||||
std::string _currentDatabase;
|
||||
bool _isCurrentServerValid;
|
||||
bool _isCurrentDatabaseValid;
|
||||
std::string _errorMessage;
|
||||
bool _error = false;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -12,15 +12,15 @@ namespace Robomongo
|
||||
_version(version), _readOnly(false)
|
||||
{
|
||||
_id = obj.getField("_id").OID();
|
||||
_name = BsonUtils::getField<mongo::String>(obj,"user");
|
||||
_passwordHash = BsonUtils::getField<mongo::String>(obj,"pwd");
|
||||
_name = BsonUtils::getField<mongo::String>(obj, "user");
|
||||
_passwordHash = BsonUtils::getField<mongo::String>(obj, "pwd");
|
||||
if (_version<minimumSupportedVersion) {
|
||||
_readOnly = BsonUtils::getField<mongo::Bool>(obj,"readOnly");
|
||||
_readOnly = BsonUtils::getField<mongo::Bool>(obj, "readOnly");
|
||||
}
|
||||
else {
|
||||
_userSource = BsonUtils::getField<mongo::String>(obj,"userSource");
|
||||
_userSource = BsonUtils::getField<mongo::String>(obj, "userSource");
|
||||
std::vector<mongo::BSONElement> roles = BsonUtils::getField<mongo::Array>(obj, "roles");
|
||||
for (std::vector<mongo::BSONElement>::const_iterator it = roles.begin(); it!= roles.end(); ++it)
|
||||
for (std::vector<mongo::BSONElement>::const_iterator it = roles.begin(); it != roles.end(); ++it)
|
||||
{
|
||||
_role.push_back(BsonUtils::bsonelement_cast<std::string>(*it));
|
||||
}
|
||||
@@ -28,7 +28,7 @@ namespace Robomongo
|
||||
}
|
||||
|
||||
MongoUser::MongoUser(const float version) :
|
||||
_version(version),_readOnly(false),_role() {}
|
||||
_version(version), _readOnly(false), _role() {}
|
||||
|
||||
mongo::BSONObj MongoUser::toBson() const
|
||||
{
|
||||
@@ -50,7 +50,7 @@ namespace Robomongo
|
||||
builder.append("userSource", _userSource);
|
||||
|
||||
mongo::BSONArrayBuilder builderA;
|
||||
for (RoleType::const_iterator it = _role.begin(); it!=_role.end(); ++it)
|
||||
for (RoleType::const_iterator it = _role.begin(); it != _role.end(); ++it)
|
||||
{
|
||||
builderA << *it;
|
||||
}
|
||||
|
||||
@@ -25,10 +25,10 @@ namespace Robomongo
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
bool isSimpleType(Robomongo::BsonTreeItem *item)
|
||||
bool isSimpleType(Robomongo::BsonTreeItem const *item)
|
||||
{
|
||||
return Robomongo::BsonUtils::isSimpleType(item->type())
|
||||
|| Robomongo::BsonUtils::isUuidType(item->type(), item->binType());
|
||||
return BsonUtils::isSimpleType(item->type()) ||
|
||||
BsonUtils::isUuidType(item->type(), item->binType());
|
||||
}
|
||||
|
||||
bool isObjectIdType(Robomongo::BsonTreeItem *item)
|
||||
@@ -38,12 +38,22 @@ namespace Robomongo
|
||||
|
||||
bool isMultiSelection(const QModelIndexList &indexes)
|
||||
{
|
||||
return indexes.count()>1;
|
||||
return indexes.count() > 1;
|
||||
}
|
||||
|
||||
bool isDocumentType(BsonTreeItem *item)
|
||||
bool isDocumentType(BsonTreeItem const *item)
|
||||
{
|
||||
return Robomongo::BsonUtils::isDocument(item->type());
|
||||
return BsonUtils::isDocument(item->type());
|
||||
}
|
||||
|
||||
bool isArrayChild(BsonTreeItem const *item)
|
||||
{
|
||||
return BsonUtils::isArray(dynamic_cast<BsonTreeItem*>(item->parent())->type());
|
||||
}
|
||||
|
||||
bool isDocumentRoot(BsonTreeItem const *item)
|
||||
{
|
||||
return ( item == item->superParent() );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -55,20 +65,20 @@ namespace Robomongo
|
||||
QModelIndexList uniqueRows(QModelIndexList indexes, bool returnSuperParents)
|
||||
{
|
||||
QModelIndexList result;
|
||||
for (QModelIndexList::const_iterator it = indexes.begin(); it!=indexes.end(); ++it)
|
||||
for (QModelIndexList::const_iterator it = indexes.begin(); it != indexes.end(); ++it)
|
||||
{
|
||||
QModelIndex isUnique = *it;
|
||||
Robomongo::BsonTreeItem *item = Robomongo::QtUtils::item<Robomongo::BsonTreeItem*>(isUnique);
|
||||
if(item){
|
||||
for (QModelIndexList::const_iterator jt = result.begin(); jt!=result.end(); ++jt)
|
||||
if (item) {
|
||||
for (QModelIndexList::const_iterator jt = result.begin(); jt != result.end(); ++jt)
|
||||
{
|
||||
Robomongo::BsonTreeItem *jItem = Robomongo::QtUtils::item<Robomongo::BsonTreeItem*>(*jt);
|
||||
if (jItem && jItem->superParent() == item->superParent()){
|
||||
if (jItem && jItem->superParent() == item->superParent()) {
|
||||
isUnique = QModelIndex();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isUnique.isValid()){
|
||||
if (isUnique.isValid()) {
|
||||
if (returnSuperParents) {
|
||||
// Move index onto "super parent" element before pushing it into result set
|
||||
QModelIndex parent = isUnique.parent();
|
||||
@@ -92,8 +102,8 @@ namespace Robomongo
|
||||
_queryInfo(queryInfo)
|
||||
{
|
||||
QWidget *wid = dynamic_cast<QWidget*>(_observer);
|
||||
AppRegistry::instance().bus()->subscribe(this, InsertDocumentResponse::Type);
|
||||
AppRegistry::instance().bus()->subscribe(this, RemoveDocumentResponse::Type);
|
||||
AppRegistry::instance().bus()->subscribe(this, InsertDocumentResponse::Type, _shell->server());
|
||||
AppRegistry::instance().bus()->subscribe(this, RemoveDocumentResponse::Type, _shell->server());
|
||||
|
||||
_deleteDocumentAction = new QAction("Delete Document...", wid);
|
||||
VERIFY(connect(_deleteDocumentAction, SIGNAL(triggered()), SLOT(onDeleteDocument())));
|
||||
@@ -113,6 +123,12 @@ namespace Robomongo
|
||||
_copyValueAction = new QAction("Copy Value", wid);
|
||||
VERIFY(connect(_copyValueAction, SIGNAL(triggered()), SLOT(onCopyDocument())));
|
||||
|
||||
_copyValueNameAction = new QAction("Copy Name", wid);
|
||||
VERIFY(connect(_copyValueNameAction, SIGNAL(triggered()), SLOT(onCopyNameDocument())));
|
||||
|
||||
_copyValuePathAction = new QAction("Copy Path", wid);
|
||||
VERIFY(connect(_copyValuePathAction, SIGNAL(triggered()), SLOT(onCopyPathDocument())));
|
||||
|
||||
_copyTimestampAction = new QAction("Copy Timestamp from ObjectId", wid);
|
||||
VERIFY(connect(_copyTimestampAction, SIGNAL(triggered()), SLOT(onCopyTimestamp())));
|
||||
|
||||
@@ -128,20 +144,29 @@ namespace Robomongo
|
||||
bool isSimple = false;
|
||||
bool isDocument = false;
|
||||
bool isObjectId = false;
|
||||
bool isNotArrayChild = false;
|
||||
bool isRoot = false;
|
||||
|
||||
if (item) {
|
||||
isSimple = detail::isSimpleType(item);
|
||||
isDocument = detail::isDocumentType(item);
|
||||
isObjectId = detail::isObjectIdType(item);
|
||||
isNotArrayChild = !detail::isArrayChild(item);
|
||||
isRoot = detail::isDocumentRoot(item);
|
||||
}
|
||||
|
||||
if (onItem && isEditable) menu->addAction(_editDocumentAction);
|
||||
if (onItem) menu->addAction(_viewDocumentAction);
|
||||
if (isEditable) menu->addAction(_insertDocumentAction);
|
||||
|
||||
if (onItem && (isSimple || isDocument)) menu->addSeparator();
|
||||
|
||||
if (onItem && isSimple) menu->addAction(_copyValueAction);
|
||||
|
||||
if (onItem && (isSimple || isDocument) && isNotArrayChild && !isRoot)
|
||||
menu->addAction(_copyValueNameAction);
|
||||
|
||||
if (onItem && (isSimple || isDocument) && !isRoot)
|
||||
menu->addAction(_copyValuePathAction);
|
||||
|
||||
if (onItem && isObjectId) menu->addAction(_copyTimestampAction);
|
||||
if (onItem && isDocument) menu->addAction(_copyJsonAction);
|
||||
if (onItem && isEditable) menu->addSeparator();
|
||||
@@ -156,11 +181,12 @@ namespace Robomongo
|
||||
if (isEditable) menu->addAction(_deleteDocumentsAction);
|
||||
}
|
||||
|
||||
void Notifier::deleteDocuments(std::vector<BsonTreeItem*> items, bool force)
|
||||
void Notifier::deleteDocuments(std::vector<BsonTreeItem*> const& items, bool force)
|
||||
{
|
||||
bool isNeededRefresh = false;
|
||||
for (std::vector<BsonTreeItem*>::const_iterator it = items.begin(); it != items.end(); ++it) {
|
||||
BsonTreeItem * documentItem = *it;
|
||||
|
||||
int index = 0;
|
||||
for (auto const * const documentItem : items) {
|
||||
if (!documentItem)
|
||||
break;
|
||||
|
||||
@@ -168,7 +194,8 @@ namespace Robomongo
|
||||
mongo::BSONElement id = obj.getField("_id");
|
||||
|
||||
if (id.eoo()) {
|
||||
QMessageBox::warning(dynamic_cast<QWidget*>(_observer), "Cannot delete", "Selected document doesn't have _id field. \n"
|
||||
QMessageBox::warning(dynamic_cast<QWidget*>(_observer), "Cannot delete",
|
||||
"Selected document doesn't have _id field. \n"
|
||||
"Maybe this is a system document that should be managed in a special way?");
|
||||
break;
|
||||
}
|
||||
@@ -187,45 +214,98 @@ namespace Robomongo
|
||||
break;
|
||||
}
|
||||
|
||||
isNeededRefresh=true;
|
||||
_shell->server()->removeDocuments(query, _queryInfo._info._ns);
|
||||
}
|
||||
isNeededRefresh = true;
|
||||
|
||||
if (isNeededRefresh)
|
||||
_shell->query(0, _queryInfo);
|
||||
RemoveDocumentCount removeCount = items.size() == 1 ? RemoveDocumentCount::ONE :
|
||||
RemoveDocumentCount::MULTI;
|
||||
_shell->server()->removeDocuments(query, _queryInfo._info._ns, removeCount, index);
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
void Notifier::handle(InsertDocumentResponse *event)
|
||||
{
|
||||
if (event->isError()) {
|
||||
QMessageBox::warning(NULL, "Database Error", QString::fromStdString(event->error().errorMessage()));
|
||||
if (event->isError()) { // Error
|
||||
if (_shell->server()->connectionRecord()->isReplicaSet()) {
|
||||
// Insert document from tab results window (Notifier, OutputWindow widget)
|
||||
if (EventError::SetPrimaryUnreachable == event->error().errorCode()) {
|
||||
AppRegistry::instance().bus()->publish(
|
||||
new ReplicaSetRefreshed(_shell, event->error(), event->error().replicaSetInfo()));
|
||||
}
|
||||
}
|
||||
else // single server
|
||||
QMessageBox::warning(NULL, "Database Error", QString::fromStdString(event->error().errorMessage()));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Success
|
||||
_shell->query(0, _queryInfo);
|
||||
}
|
||||
|
||||
void Notifier::handle(RemoveDocumentResponse *event)
|
||||
{
|
||||
if (event->isError()) {
|
||||
// Commented because when you'll delete multiple documents,
|
||||
// we'll receive this event as many times as many selected
|
||||
// documents you have. This is incorrect and remove of
|
||||
// multiple documents should be a single command, instead
|
||||
// of many. Error message should be printed in the logs.
|
||||
if (event->isError()) {
|
||||
if (!(event->removeCount == RemoveDocumentCount::MULTI && event->index > 0))
|
||||
QMessageBox::warning(NULL, "Database Error", QString::fromStdString(event->error().errorMessage()));
|
||||
}
|
||||
else // Success
|
||||
_shell->query(0, _queryInfo);
|
||||
}
|
||||
|
||||
// QMessageBox::warning(NULL, "Database Error", QString::fromStdString(event->error().errorMessage()));
|
||||
void Notifier::onCopyNameDocument()
|
||||
{
|
||||
QModelIndex const& selectedInd = _observer->selectedIndex();
|
||||
if (!selectedInd.isValid())
|
||||
return;
|
||||
|
||||
BsonTreeItem const *documentItem = QtUtils::item<BsonTreeItem*>(selectedInd);
|
||||
if (!documentItem)
|
||||
return;
|
||||
|
||||
if (!(detail::isSimpleType(documentItem) || detail::isDocumentType(documentItem) ||
|
||||
!detail::isArrayChild(documentItem) || !detail::isDocumentRoot(documentItem)))
|
||||
return;
|
||||
|
||||
QClipboard *clipboard = QApplication::clipboard();
|
||||
clipboard->setText(QString::fromStdString(documentItem->fieldName()));
|
||||
}
|
||||
|
||||
void Notifier::onCopyPathDocument()
|
||||
{
|
||||
QModelIndex const& selectedInd = _observer->selectedIndex();
|
||||
if (!selectedInd.isValid())
|
||||
return;
|
||||
|
||||
BsonTreeItem const *documentItem = QtUtils::item<BsonTreeItem*>(selectedInd);
|
||||
if (!documentItem)
|
||||
return;
|
||||
|
||||
if (!(detail::isSimpleType(documentItem) || detail::isDocumentType(documentItem) ||
|
||||
!detail::isDocumentRoot(documentItem)))
|
||||
return;
|
||||
|
||||
QStringList namesList;
|
||||
BsonTreeItem const *documentItemHelper = documentItem;
|
||||
|
||||
while (!detail::isDocumentRoot(documentItemHelper)) {
|
||||
if (!detail::isArrayChild(documentItemHelper)) {
|
||||
namesList.push_front(QString::fromStdString(documentItemHelper->fieldName()));
|
||||
}
|
||||
|
||||
documentItemHelper = dynamic_cast<BsonTreeItem*>(documentItemHelper->parent());
|
||||
}
|
||||
|
||||
QClipboard *clipboard = QApplication::clipboard();
|
||||
clipboard->setText(namesList.join("."));
|
||||
}
|
||||
|
||||
void Notifier::handleDeleteCommand()
|
||||
{
|
||||
if (_observer->selectedIndexes().count() > 1) {
|
||||
if (_observer->selectedIndexes().count() > 1)
|
||||
onDeleteDocuments();
|
||||
} else {
|
||||
onDeleteDocument();
|
||||
}
|
||||
else
|
||||
onDeleteDocument();
|
||||
}
|
||||
|
||||
void Notifier::onDeleteDocuments()
|
||||
@@ -237,14 +317,16 @@ namespace Robomongo
|
||||
if (!detail::isMultiSelection(selectedIndexes))
|
||||
return;
|
||||
|
||||
int answer = QMessageBox::question(dynamic_cast<QWidget*>(_observer), "Delete", QString("Do you want to delete %1 selected documents?").arg(selectedIndexes.count()));
|
||||
if (answer == QMessageBox::Yes) {
|
||||
int const answer = QMessageBox::question(dynamic_cast<QWidget*>(_observer), "Delete",
|
||||
QString("Do you want to delete %1 selected documents?").
|
||||
arg(selectedIndexes.count()));
|
||||
|
||||
if (QMessageBox::Yes == answer) {
|
||||
std::vector<BsonTreeItem*> items;
|
||||
for (QModelIndexList::const_iterator it = selectedIndexes.begin(); it!= selectedIndexes.end(); ++it) {
|
||||
BsonTreeItem *item = QtUtils::item<BsonTreeItem*>(*it);
|
||||
items.push_back(item);
|
||||
}
|
||||
deleteDocuments(items,true);
|
||||
for (auto index : selectedIndexes)
|
||||
items.push_back(QtUtils::item<BsonTreeItem*>(index));
|
||||
|
||||
deleteDocuments(items, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,7 +342,7 @@ namespace Robomongo
|
||||
BsonTreeItem *documentItem = QtUtils::item<BsonTreeItem*>(selectedIndex);
|
||||
std::vector<BsonTreeItem*> vec;
|
||||
vec.push_back(documentItem);
|
||||
return deleteDocuments(vec,false);
|
||||
return deleteDocuments(vec, false);
|
||||
}
|
||||
|
||||
void Notifier::onEditDocument()
|
||||
@@ -317,7 +399,7 @@ namespace Robomongo
|
||||
json, true, dynamic_cast<QWidget*>(_observer));
|
||||
|
||||
editor->setWindowTitle("View Document");
|
||||
editor->show();
|
||||
editor->exec();
|
||||
}
|
||||
|
||||
void Notifier::onInsertDocument()
|
||||
@@ -339,8 +421,6 @@ namespace Robomongo
|
||||
for (DocumentTextEditor::ReturnType::const_iterator it = obj.begin(); it != obj.end(); ++it) {
|
||||
_shell->server()->insertDocument(*it, _queryInfo._info._ns);
|
||||
}
|
||||
|
||||
_shell->query(0, _queryInfo);
|
||||
}
|
||||
|
||||
void Notifier::onCopyDocument()
|
||||
@@ -376,19 +456,19 @@ namespace Robomongo
|
||||
QClipboard *clipboard = QApplication::clipboard();
|
||||
|
||||
// new Date(parseInt(this.valueOf().slice(0,8), 16)*1000);
|
||||
QString hexTimestamp = documentItem->value().mid(10,8);
|
||||
QString hexTimestamp = documentItem->value().mid(10, 8);
|
||||
bool ok;
|
||||
long long milliTimestamp = (long long)hexTimestamp.toLongLong(&ok,16)*1000;
|
||||
long long milliTimestamp = (long long)hexTimestamp.toLongLong(&ok, 16)*1000;
|
||||
|
||||
bool isSupportedDate = (miutil::minDate < milliTimestamp) && (milliTimestamp < miutil::maxDate);
|
||||
|
||||
boost::posix_time::ptime epoch(boost::gregorian::date(1970,1,1));
|
||||
boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1));
|
||||
boost::posix_time::time_duration diff = boost::posix_time::millisec(milliTimestamp);
|
||||
boost::posix_time::ptime time = epoch + diff;
|
||||
|
||||
if(isSupportedDate)
|
||||
if (isSupportedDate)
|
||||
{
|
||||
std::string date = miutil::isotimeString(time,false,false);
|
||||
std::string date = miutil::isotimeString(time, false, false);
|
||||
clipboard->setText("ISODate(\""+QString::fromStdString(date)+"\")");
|
||||
}
|
||||
else {
|
||||
@@ -411,13 +491,13 @@ namespace Robomongo
|
||||
|
||||
QClipboard *clipboard = QApplication::clipboard();
|
||||
mongo::BSONObj obj = documentItem->root();
|
||||
if (documentItem != documentItem->superParent()){
|
||||
obj = obj[QtUtils::toStdString(documentItem->key())].Obj();
|
||||
if (documentItem != documentItem->superParent()) {
|
||||
obj = obj[documentItem->fieldName()].Obj();
|
||||
}
|
||||
bool isArray = BsonUtils::isArray(documentItem->type());
|
||||
std::string str = BsonUtils::jsonString(obj, mongo::TenGen, 1,
|
||||
AppRegistry::instance().settingsManager()->uuidEncoding(),
|
||||
AppRegistry::instance().settingsManager()->timeZone(),isArray);
|
||||
AppRegistry::instance().settingsManager()->timeZone(), isArray);
|
||||
|
||||
const QString &json = QtUtils::toQString(str);
|
||||
clipboard->setText(json);
|
||||
|
||||
@@ -14,14 +14,14 @@ namespace Robomongo
|
||||
class MongoShell;
|
||||
class BsonTreeItem;
|
||||
class InsertDocumentResponse;
|
||||
class RemoveDocumentResponse;
|
||||
struct RemoveDocumentResponse;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
bool isSimpleType(BsonTreeItem *item);
|
||||
bool isSimpleType(BsonTreeItem const *item);
|
||||
bool isObjectIdType(BsonTreeItem *item);
|
||||
bool isMultiSelection(const QModelIndexList &indexes);
|
||||
bool isDocumentType(BsonTreeItem *item);
|
||||
bool isDocumentType(BsonTreeItem const *item);
|
||||
QModelIndexList uniqueRows(QModelIndexList indexes, bool returnSuperParents = false);
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace Robomongo
|
||||
void initMenu(QMenu *const menu, BsonTreeItem *const item);
|
||||
void initMultiSelectionMenu(QMenu *const menu);
|
||||
|
||||
void deleteDocuments(std::vector<BsonTreeItem*> items, bool force);
|
||||
void deleteDocuments(std::vector<BsonTreeItem*> const& items, bool force);
|
||||
void handleDeleteCommand();
|
||||
|
||||
public Q_SLOTS:
|
||||
@@ -60,6 +60,10 @@ namespace Robomongo
|
||||
void handle(InsertDocumentResponse *event);
|
||||
void handle(RemoveDocumentResponse *event);
|
||||
|
||||
private Q_SLOTS:
|
||||
void onCopyNameDocument();
|
||||
void onCopyPathDocument();
|
||||
|
||||
private:
|
||||
QAction *_deleteDocumentAction;
|
||||
QAction *_deleteDocumentsAction;
|
||||
@@ -67,6 +71,8 @@ namespace Robomongo
|
||||
QAction *_viewDocumentAction;
|
||||
QAction *_insertDocumentAction;
|
||||
QAction *_copyValueAction;
|
||||
QAction *_copyValueNameAction;
|
||||
QAction *_copyValuePathAction;
|
||||
QAction *_copyTimestampAction;
|
||||
QAction *_copyJsonAction;
|
||||
const MongoQueryInfo _queryInfo;
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
ScriptInfo::ScriptInfo(const QString &script, bool execute,const CursorPosition &position,
|
||||
ScriptInfo::ScriptInfo(const QString &script, bool execute, const CursorPosition &position,
|
||||
const QString &title, const QString &filePath) :
|
||||
_script(script),
|
||||
_execute(execute),
|
||||
@@ -73,10 +73,10 @@ namespace Robomongo
|
||||
bool ScriptInfo::loadFromFile(const QString &filePath)
|
||||
{
|
||||
bool result = false;
|
||||
QString filepath = QFileDialog::getOpenFileName(QApplication::activeWindow(), filePath,QString(), filterForScripts);
|
||||
QString filepath = QFileDialog::getOpenFileName(QApplication::activeWindow(), filePath, QString(), filterForScripts);
|
||||
if (!filepath.isEmpty()) {
|
||||
QString out;
|
||||
if (loadFromFileText(filepath,out)) {
|
||||
if (loadFromFileText(filepath, out)) {
|
||||
_script = out;
|
||||
_filePath = filepath;
|
||||
result = true;
|
||||
@@ -95,7 +95,7 @@ namespace Robomongo
|
||||
QString filepath = QFileDialog::getSaveFileName(QApplication::activeWindow(),
|
||||
QObject::tr("Save As"), _filePath, filterForScripts);
|
||||
|
||||
if (saveToFileText(filepath,_script)) {
|
||||
if (saveToFileText(filepath, _script)) {
|
||||
_filePath = filepath;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -55,34 +55,30 @@ namespace mongo {
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
ScriptEngine::ScriptEngine(ConnectionSettings *connection) :
|
||||
ScriptEngine::ScriptEngine(ConnectionSettings *connection, int timeoutSec) :
|
||||
_connection(connection),
|
||||
_scope(NULL),
|
||||
_scope(nullptr),
|
||||
_engine(NULL),
|
||||
_timeoutSec(timeoutSec),
|
||||
_initialized(false),
|
||||
_mutex(QMutex::Recursive) { }
|
||||
|
||||
ScriptEngine::~ScriptEngine()
|
||||
{
|
||||
QMutexLocker lock(&_mutex);
|
||||
|
||||
delete _scope;
|
||||
_scope = NULL;
|
||||
|
||||
// delete _engine;
|
||||
// _engine = NULL;
|
||||
}
|
||||
|
||||
void ScriptEngine::init(bool isLoadMongoRcJs)
|
||||
void ScriptEngine::init(bool isLoadMongoRcJs, const std::string& serverAddr, const std::string& dbName)
|
||||
{
|
||||
QMutexLocker lock(&_mutex);
|
||||
|
||||
std::string connectDatabase = "test";
|
||||
std::string connectDatabase = dbName.empty() ? "test" : dbName;
|
||||
|
||||
if (_connection->hasEnabledPrimaryCredential())
|
||||
connectDatabase = _connection->primaryCredential()->databaseName();
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "db = connect('" << _connection->info().toString() << "/" << connectDatabase;
|
||||
auto hostAndPort = serverAddr.empty() ? _connection->hostAndPort().toString() : serverAddr;
|
||||
ss << "db = connect('" << hostAndPort << "/" << connectDatabase;
|
||||
|
||||
// v0.9
|
||||
// ss << "db = connect('" << _connection->serverHost() << ":" << _connection->serverPort() << _connection->sslInfo() << _connection->sshInfo() << "/" << connectDatabase;
|
||||
@@ -106,49 +102,63 @@ namespace Robomongo
|
||||
mongo::globalScriptEngine->setScopeInitCallback(mongo::shell_utils::initScope);
|
||||
mongo::globalScriptEngine->enableJIT(true);
|
||||
|
||||
mongo::Scope *scope = mongo::globalScriptEngine->newScope();
|
||||
_scope = scope;
|
||||
_scope.reset(mongo::globalScriptEngine->newScope());
|
||||
_engine = mongo::globalScriptEngine;
|
||||
|
||||
// Load '.mongorc.js' from user's home directory
|
||||
if (isLoadMongoRcJs) {
|
||||
QString mongorcPath = QString("%1/.mongorc.js").arg(QDir::homePath());
|
||||
if (QFile::exists(mongorcPath)) {
|
||||
scope->execFile(QtUtils::toStdString(mongorcPath), false, false);
|
||||
_scope->execFile(QtUtils::toStdString(mongorcPath), false, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Load '.robomongorc.js'
|
||||
QString robomongorcPath = QString("%1/.robomongorc.js").arg(QDir::homePath());
|
||||
if (QFile::exists(robomongorcPath)) {
|
||||
scope->execFile(QtUtils::toStdString(robomongorcPath), false, false);
|
||||
_scope->execFile(QtUtils::toStdString(robomongorcPath), false, false);
|
||||
}
|
||||
_failedScope = false;
|
||||
}
|
||||
|
||||
// Esprima ECMAScript parser: http://esprima.org/
|
||||
QFile file(":/robomongo/scripts/esprima.js");
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
throw std::runtime_error("Unable to read esprima.js ");
|
||||
std::string esprima = loadFile(":/robomongo/scripts/esprima.js", true);
|
||||
_scope->exec(esprima, "(esprima)", false, true, true);
|
||||
|
||||
QTextStream in(&file);
|
||||
QString esprima = in.readAll();
|
||||
|
||||
// Inject Esprima into Javascript scope
|
||||
_scope->exec(QtUtils::toStdString(esprima), "(esprima)", true, true, true);
|
||||
// UUID helpers
|
||||
std::string uuidhelpers = loadFile(":/robomongo/scripts/uuidhelpers.js", true);
|
||||
_scope->exec(uuidhelpers, "(uuidhelpers)", false, true, true);
|
||||
|
||||
// Enable verbose shell reporting
|
||||
_scope->exec("_verboseShell = true;", "(verboseShell)", false, false, false);
|
||||
|
||||
// Save original autocomplete function so it can be restored if overwritten by user preference
|
||||
_scope->exec("DB.autocompleteOriginal = DB.autocomplete;", "(saveOriginalAutocomplete)", false, false, false);
|
||||
|
||||
// Cache result of original "DB.autocomplete"
|
||||
// Cache invalidated by the invalidateDbCollectionsCache() method.
|
||||
std::string cacheAutocompletion =
|
||||
"__robomongoAutocompletionCache = null;"
|
||||
"DB.autocompleteCached = function(obj) { "
|
||||
" if (__robomongoAutocompletionCache == null) {"
|
||||
" __robomongoAutocompletionCache = DB.autocompleteOriginal(obj);"
|
||||
" }"
|
||||
" return __robomongoAutocompletionCache;"
|
||||
"}";
|
||||
|
||||
_scope->exec(cacheAutocompletion, "", false, false, false);
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
MongoShellExecResult ScriptEngine::exec(const std::string &originalScript, const std::string &dbName)
|
||||
{
|
||||
QMutexLocker lock(&_mutex);
|
||||
|
||||
if(!_scope)
|
||||
return MongoShellExecResult();
|
||||
if (!_scope) {
|
||||
_failedScope = true;
|
||||
return MongoShellExecResult(true, "Connection error. Uninitialized mongo scope.");
|
||||
}
|
||||
|
||||
/*
|
||||
* Replace all commands ('show dbs', 'use db' etc.) with call
|
||||
@@ -177,7 +187,7 @@ namespace Robomongo
|
||||
|
||||
use(dbName);
|
||||
|
||||
for(std::vector<std::string>::const_iterator it = statements.begin(); it != statements.end(); ++it)
|
||||
for (std::vector<std::string>::const_iterator it = statements.begin(); it != statements.end(); ++it)
|
||||
{
|
||||
std::string statement = *it;
|
||||
// clear global objects
|
||||
@@ -188,10 +198,15 @@ namespace Robomongo
|
||||
|
||||
if (true /* ! wascmd */) {
|
||||
try {
|
||||
bool failed = false;
|
||||
QElapsedTimer timer;
|
||||
timer.start();
|
||||
if ( _scope->exec( statement , "(shell)" , false , true , false, 0 ) ) {
|
||||
_scope->exec( "__robomongoLastRes = __lastres__; shellPrintHelper( __lastres__ );" , "(shell2)" , true , true , false, 0);
|
||||
if ( _scope->exec( statement , "(shell)" , false , true , false, _timeoutSec * 1000) ) {
|
||||
_scope->exec( "__robomongoLastRes = __lastres__; shellPrintHelper( __lastres__ );",
|
||||
"(shell2)" , true , true , false, _timeoutSec * 1000);
|
||||
}
|
||||
else { // failed to run script
|
||||
failed = true;
|
||||
}
|
||||
|
||||
qint64 elapsed = timer.elapsed();
|
||||
@@ -199,6 +214,10 @@ namespace Robomongo
|
||||
std::string logs = __logs.str();
|
||||
std::string answer = logs.c_str();
|
||||
std::string type = __type.c_str();
|
||||
|
||||
if (failed)
|
||||
return MongoShellExecResult(true, answer);
|
||||
|
||||
std::vector<MongoDocumentPtr> docs = MongoDocument::fromBsonObj(__objects);
|
||||
|
||||
if (!answer.empty() || docs.size() > 0)
|
||||
@@ -251,8 +270,11 @@ namespace Robomongo
|
||||
|
||||
void ScriptEngine::ping()
|
||||
{
|
||||
if (!_scope)
|
||||
return;
|
||||
|
||||
QMutexLocker lock(&_mutex);
|
||||
_scope->exec("if (db) { db.runCommand({ping:1}); }", "(ping)", false, false, false);
|
||||
_scope->exec("if (db) { db.runCommand({ping:1}); }", "(ping)", false, false, false, 3000);
|
||||
}
|
||||
|
||||
QStringList ScriptEngine::complete(const std::string &prefix, const AutocompletionMode mode)
|
||||
@@ -262,7 +284,7 @@ namespace Robomongo
|
||||
|
||||
try {
|
||||
if (mode == AutocompleteAll)
|
||||
_scope->exec("DB.autocomplete = DB.autocompleteOriginal;", "", false, false, false);
|
||||
_scope->exec("DB.autocomplete = DB.autocompleteCached;", "", false, false, false);
|
||||
else if (mode == AutocompleteNoCollectionNames)
|
||||
_scope->exec("DB.autocomplete = function(obj){return [];}", "", false, false, false);
|
||||
|
||||
@@ -413,4 +435,25 @@ namespace Robomongo
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScriptEngine::invalidateDbCollectionsCache() {
|
||||
if (!_initialized)
|
||||
return;
|
||||
|
||||
_scope->exec("__robomongoAutocompletionCache = null;", "", false, false, false);
|
||||
}
|
||||
|
||||
std::string ScriptEngine::loadFile(const QString &path, bool throwOnError) {
|
||||
QFile file(path);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
if (throwOnError)
|
||||
throw std::runtime_error("Unable to load file");
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
QTextStream in(&file);
|
||||
QString content = in.readAll();
|
||||
return QtUtils::toStdString(content);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,10 @@ namespace Robomongo
|
||||
{
|
||||
|
||||
public:
|
||||
ScriptEngine(ConnectionSettings *connection);
|
||||
ScriptEngine(ConnectionSettings *connection, int timeoutSec);
|
||||
~ScriptEngine();
|
||||
|
||||
void init(bool isLoadMongoJs);
|
||||
void init(bool isLoadMongoJs, const std::string& serverAddr = "", const std::string& dbName = "");
|
||||
MongoShellExecResult exec(const std::string &script, const std::string &dbName = std::string());
|
||||
void interrupt();
|
||||
|
||||
@@ -27,20 +27,26 @@ namespace Robomongo
|
||||
void ping();
|
||||
QStringList complete(const std::string &prefix, const AutocompletionMode mode);
|
||||
|
||||
void invalidateDbCollectionsCache();
|
||||
|
||||
bool failedScope() const { return _failedScope; }
|
||||
|
||||
private:
|
||||
ConnectionSettings *_connection;
|
||||
|
||||
MongoShellResult prepareResult(const std::string &type, const std::string &output, const std::vector<MongoDocumentPtr> &objects, qint64 elapsedms);
|
||||
MongoShellExecResult prepareExecResult(const std::vector<MongoShellResult> &results);
|
||||
std::string loadFile(const QString &path, bool throwOnError);
|
||||
|
||||
std::string getString(const char *fieldName);
|
||||
|
||||
bool statementize(const std::string &script, std::vector<std::string> &outList, std::string &outError);
|
||||
std::vector<std::string> statementize2(const std::string &script);
|
||||
// void parseTree(JSParseNode * root, int indent, const std::string &script, std::vector<std::string> &list, bool topList);
|
||||
|
||||
int _timeoutSec;
|
||||
mongo::ScriptEngine *_engine;
|
||||
mongo::Scope *_scope;
|
||||
std::unique_ptr<mongo::Scope> _scope;
|
||||
bool _failedScope = false;
|
||||
QMutex _mutex;
|
||||
bool _initialized;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,6 +5,11 @@ namespace Robomongo
|
||||
{
|
||||
R_REGISTER_EVENT(EstablishConnectionRequest)
|
||||
R_REGISTER_EVENT(EstablishConnectionResponse)
|
||||
R_REGISTER_EVENT(ReplicaSetRefreshed)
|
||||
R_REGISTER_EVENT(RefreshReplicaSetFolderRequest)
|
||||
R_REGISTER_EVENT(RefreshReplicaSetFolderResponse)
|
||||
R_REGISTER_EVENT(ReplicaSetFolderLoading)
|
||||
R_REGISTER_EVENT(ReplicaSetFolderRefreshed)
|
||||
R_REGISTER_EVENT(LoadDatabaseNamesRequest)
|
||||
R_REGISTER_EVENT(LoadDatabaseNamesResponse)
|
||||
R_REGISTER_EVENT(LoadCollectionNamesRequest)
|
||||
@@ -66,4 +71,5 @@ namespace Robomongo
|
||||
R_REGISTER_EVENT(ListenSshConnectionResponse)
|
||||
R_REGISTER_EVENT(LogEvent)
|
||||
R_REGISTER_EVENT(StopScriptRequest)
|
||||
R_REGISTER_EVENT(OperationFailedEvent)
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "robomongo/core/events/MongoEventsInfo.h"
|
||||
#include "robomongo/core/Event.h"
|
||||
#include "robomongo/core/Enums.h"
|
||||
#include "robomongo/core/mongodb/ReplicaSet.h"
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
@@ -26,32 +27,120 @@ namespace Robomongo
|
||||
* @brief EstablishConnection
|
||||
*/
|
||||
|
||||
class EstablishConnectionRequest : public Event
|
||||
struct EstablishConnectionRequest : public Event
|
||||
{
|
||||
R_EVENT
|
||||
|
||||
EstablishConnectionRequest(QObject *sender) :
|
||||
EstablishConnectionRequest(QObject *sender, ConnectionType connectionType,
|
||||
int const originalConnectionSettingsId) :
|
||||
Event(sender),
|
||||
connectionType(connectionType),
|
||||
originalConnectionSettingsId(originalConnectionSettingsId) {}
|
||||
|
||||
ConnectionType const connectionType;
|
||||
int const originalConnectionSettingsId = -1;
|
||||
};
|
||||
|
||||
struct EstablishConnectionResponse : public Event
|
||||
{
|
||||
R_EVENT
|
||||
|
||||
enum ErrorReason {
|
||||
NoError = 0,
|
||||
MongoConnection = 1,
|
||||
MongoAuth = 2,
|
||||
MongoSslConnection = 3
|
||||
};
|
||||
|
||||
EstablishConnectionResponse(QObject *sender, const ConnectionInfo &info, ConnectionType connectionType,
|
||||
const ReplicaSet& replicaSet) :
|
||||
Event(sender),
|
||||
info(info),
|
||||
connectionType(connectionType),
|
||||
replicaSet(replicaSet)
|
||||
{}
|
||||
|
||||
EstablishConnectionResponse(QObject *sender, const EventError &error, ConnectionType connectionType,
|
||||
ConnectionInfo const& info, const ReplicaSet& replicaSet, ErrorReason errorReason) :
|
||||
Event(sender, error),
|
||||
info(info),
|
||||
connectionType(connectionType),
|
||||
errorReason(errorReason),
|
||||
replicaSet(replicaSet)
|
||||
{}
|
||||
|
||||
ConnectionInfo const info;
|
||||
ConnectionType const connectionType;
|
||||
ErrorReason const errorReason = NoError;
|
||||
ReplicaSet const replicaSet;
|
||||
};
|
||||
|
||||
struct ReplicaSetRefreshed : public Event
|
||||
{
|
||||
R_EVENT
|
||||
|
||||
ReplicaSetRefreshed(QObject *sender) :
|
||||
Event(sender) {}
|
||||
|
||||
ReplicaSetRefreshed(QObject *sender, const EventError &error, ReplicaSet const& replicaSet) :
|
||||
Event(sender, error), replicaSet(replicaSet) {}
|
||||
|
||||
ReplicaSet const replicaSet;
|
||||
};
|
||||
|
||||
struct RefreshReplicaSetFolderRequest : public Event
|
||||
{
|
||||
R_EVENT
|
||||
|
||||
RefreshReplicaSetFolderRequest(QObject *sender, bool expanded) :
|
||||
Event(sender), expanded(expanded) {}
|
||||
|
||||
bool const expanded = false;
|
||||
};
|
||||
|
||||
struct RefreshReplicaSetFolderResponse : public Event
|
||||
{
|
||||
R_EVENT
|
||||
|
||||
// Primary is reachable
|
||||
RefreshReplicaSetFolderResponse(QObject *sender, ReplicaSet const& replicaSet, bool expanded) :
|
||||
Event(sender), replicaSet(replicaSet), expanded(expanded) {}
|
||||
|
||||
// Primary is unreachable, secondary(ies) might be reachable
|
||||
RefreshReplicaSetFolderResponse(QObject *sender, ReplicaSet const& replicaSet, bool expanded,
|
||||
const EventError &error) :
|
||||
Event(sender, error), replicaSet(replicaSet), expanded(expanded) {}
|
||||
|
||||
ReplicaSet const replicaSet;
|
||||
bool const expanded = false;
|
||||
};
|
||||
|
||||
struct ReplicaSetFolderLoading : public Event
|
||||
{
|
||||
R_EVENT
|
||||
|
||||
ReplicaSetFolderLoading(QObject *sender) :
|
||||
Event(sender) {}
|
||||
};
|
||||
|
||||
class EstablishConnectionResponse : public Event
|
||||
struct ReplicaSetFolderRefreshed : public Event
|
||||
{
|
||||
R_EVENT
|
||||
|
||||
EstablishConnectionResponse(QObject *sender, const ConnectionInfo &info) :
|
||||
Event(sender),
|
||||
_info(info) {}
|
||||
ReplicaSetFolderRefreshed(QObject *sender, bool expanded) :
|
||||
Event(sender), expanded(expanded) {}
|
||||
|
||||
EstablishConnectionResponse(QObject *sender, const EventError &error) :
|
||||
Event(sender, error),_info() {}
|
||||
ReplicaSetFolderRefreshed(QObject *sender, const EventError &error, bool expanded) :
|
||||
Event(sender, error), expanded(expanded) {}
|
||||
|
||||
const ConnectionInfo &info() const{
|
||||
return _info;
|
||||
}
|
||||
const ConnectionInfo _info;
|
||||
ReplicaSetFolderRefreshed(QObject *sender, const EventError &error, ReplicaSet const& replicaSet,
|
||||
bool expanded) :
|
||||
Event(sender, error), replicaSet(replicaSet), expanded(expanded) {}
|
||||
|
||||
ReplicaSet const replicaSet;
|
||||
bool const expanded = false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief LoadDatabaseNames
|
||||
*/
|
||||
@@ -109,7 +198,7 @@ namespace Robomongo
|
||||
_databaseName(databaseName),
|
||||
_collectionInfos(collectionInfos) { }
|
||||
|
||||
LoadCollectionNamesResponse(QObject *sender,const EventError &error) :
|
||||
LoadCollectionNamesResponse(QObject *sender, const EventError &error) :
|
||||
Event(sender, error) {}
|
||||
|
||||
std::string databaseName() const { return _databaseName; }
|
||||
@@ -125,7 +214,7 @@ namespace Robomongo
|
||||
R_EVENT
|
||||
public:
|
||||
LoadCollectionIndexesRequest(QObject *sender, const MongoCollectionInfo &collection) :
|
||||
Event(sender),_collection(collection) {}
|
||||
Event(sender), _collection(collection) {}
|
||||
MongoCollectionInfo collection() const { return _collection; }
|
||||
private:
|
||||
const MongoCollectionInfo _collection;
|
||||
@@ -148,7 +237,7 @@ namespace Robomongo
|
||||
class EnsureIndexRequest : public Event
|
||||
{
|
||||
R_EVENT
|
||||
EnsureIndexRequest(QObject *sender,const EnsureIndexInfo &oldInfo,const EnsureIndexInfo &newInfo) : Robomongo::Event(sender),oldInfo_(oldInfo),newInfo_(newInfo) {}
|
||||
EnsureIndexRequest(QObject *sender, const EnsureIndexInfo &oldInfo, const EnsureIndexInfo &newInfo) : Robomongo::Event(sender), oldInfo_(oldInfo), newInfo_(newInfo) {}
|
||||
const EnsureIndexInfo &oldInfo() const
|
||||
{
|
||||
return oldInfo_;
|
||||
@@ -172,7 +261,7 @@ namespace Robomongo
|
||||
_name(name) {}
|
||||
|
||||
MongoCollectionInfo collection() const { return _collection; }
|
||||
std::string name() const {return _name;}
|
||||
std::string name() const { return _name; }
|
||||
private:
|
||||
const MongoCollectionInfo _collection;
|
||||
std::string _name;
|
||||
@@ -182,14 +271,14 @@ namespace Robomongo
|
||||
{
|
||||
R_EVENT
|
||||
public:
|
||||
DeleteCollectionIndexResponse(QObject *sender, const MongoCollectionInfo &collection,const std::string &index) :
|
||||
Event(sender),_collection(collection),_index(index) {}
|
||||
DeleteCollectionIndexResponse(QObject *sender, const MongoCollectionInfo &collection, const std::string &index) :
|
||||
Event(sender), _collection(collection), _index(index) {}
|
||||
|
||||
DeleteCollectionIndexResponse(QObject *sender, const EventError &error) :
|
||||
Event(sender, error) {}
|
||||
|
||||
MongoCollectionInfo collection() const { return _collection; }
|
||||
std::string index() const {return _index;}
|
||||
std::string index() const { return _index; }
|
||||
private:
|
||||
MongoCollectionInfo _collection;
|
||||
std::string _index;
|
||||
@@ -199,11 +288,11 @@ namespace Robomongo
|
||||
{
|
||||
R_EVENT
|
||||
public:
|
||||
EditIndexRequest(QObject *sender, const MongoCollectionInfo &collection,const std::string &oldIndex,const std::string &newIndex) :
|
||||
Event(sender),_collection(collection),_oldIndex(oldIndex),_newIndex(newIndex) {}
|
||||
EditIndexRequest(QObject *sender, const MongoCollectionInfo &collection, const std::string &oldIndex, const std::string &newIndex) :
|
||||
Event(sender), _collection(collection), _oldIndex(oldIndex), _newIndex(newIndex) {}
|
||||
MongoCollectionInfo collection() const { return _collection; }
|
||||
std::string oldIndex() const {return _oldIndex;}
|
||||
std::string newIndex() const {return _newIndex;}
|
||||
std::string oldIndex() const { return _oldIndex; }
|
||||
std::string newIndex() const { return _newIndex; }
|
||||
private:
|
||||
const MongoCollectionInfo _collection;
|
||||
std::string _oldIndex;
|
||||
@@ -240,7 +329,7 @@ namespace Robomongo
|
||||
_databaseName(databaseName),
|
||||
_users(users) { }
|
||||
|
||||
LoadUsersResponse(QObject *sender,const EventError &error) :
|
||||
LoadUsersResponse(QObject *sender, const EventError &error) :
|
||||
Event(sender, error) {}
|
||||
|
||||
std::string databaseName() const { return _databaseName; }
|
||||
@@ -282,7 +371,7 @@ namespace Robomongo
|
||||
_databaseName(databaseName),
|
||||
_functions(functions) { }
|
||||
|
||||
LoadFunctionsResponse(QObject *sender,const EventError &error) :
|
||||
LoadFunctionsResponse(QObject *sender, const EventError &error) :
|
||||
Event(sender, error) {}
|
||||
|
||||
std::string databaseName() const { return _databaseName; }
|
||||
@@ -326,7 +415,7 @@ namespace Robomongo
|
||||
InsertDocumentResponse(QObject *sender) :
|
||||
Event(sender) {}
|
||||
|
||||
InsertDocumentResponse(QObject *sender, const EventError &error) :
|
||||
InsertDocumentResponse(QObject *sender, EventError const& error) :
|
||||
Event(sender, error) {}
|
||||
};
|
||||
|
||||
@@ -334,37 +423,46 @@ namespace Robomongo
|
||||
* @brief Remove Document
|
||||
*/
|
||||
|
||||
enum class RemoveDocumentCount { ONE, MULTI, ALL };
|
||||
|
||||
class RemoveDocumentRequest : public Event
|
||||
{
|
||||
R_EVENT
|
||||
|
||||
public:
|
||||
RemoveDocumentRequest(QObject *sender, mongo::Query query, const MongoNamespace &ns, bool justOne = true) :
|
||||
RemoveDocumentRequest(QObject *sender, mongo::Query query, const MongoNamespace &ns,
|
||||
RemoveDocumentCount removeCount, int index) :
|
||||
Event(sender),
|
||||
_query(query),
|
||||
_ns(ns),
|
||||
_justOne(justOne) {}
|
||||
_removeCount(removeCount),
|
||||
_index(index) {}
|
||||
|
||||
mongo::Query query() const { return _query; }
|
||||
MongoNamespace ns() const { return _ns; }
|
||||
bool justOne() const { return _justOne; }
|
||||
RemoveDocumentCount removeCount() const { return _removeCount; }
|
||||
int index() const { return _index; }
|
||||
|
||||
private:
|
||||
mongo::Query _query;
|
||||
const MongoNamespace _ns;
|
||||
bool _justOne;
|
||||
mongo::Query const _query;
|
||||
MongoNamespace const _ns;
|
||||
RemoveDocumentCount const _removeCount;
|
||||
// if this is a multi remove, this is the index of current document deleted. 0 for first document.
|
||||
int const _index;
|
||||
};
|
||||
|
||||
class RemoveDocumentResponse : public Event
|
||||
struct RemoveDocumentResponse : public Event
|
||||
{
|
||||
R_EVENT
|
||||
|
||||
public:
|
||||
RemoveDocumentResponse(QObject *sender) :
|
||||
Event(sender) {}
|
||||
RemoveDocumentResponse(QObject *sender, RemoveDocumentCount removeCount, int index) :
|
||||
Event(sender), removeCount(removeCount), index(index) {}
|
||||
|
||||
RemoveDocumentResponse(QObject *sender, const EventError &error) :
|
||||
Event(sender, error) {}
|
||||
RemoveDocumentResponse(QObject *sender, const EventError &error, RemoveDocumentCount removeCount, int index) :
|
||||
Event(sender, error), removeCount(removeCount), index(index) {}
|
||||
|
||||
RemoveDocumentCount const removeCount;
|
||||
int const index;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -383,52 +481,49 @@ namespace Robomongo
|
||||
std::string database() const { return _database; }
|
||||
|
||||
private:
|
||||
std::string _database;
|
||||
std::string const _database;
|
||||
};
|
||||
|
||||
class CreateDatabaseResponse : public Event
|
||||
struct CreateDatabaseResponse : public Event
|
||||
{
|
||||
R_EVENT
|
||||
|
||||
public:
|
||||
CreateDatabaseResponse(QObject *sender) :
|
||||
Event(sender) {}
|
||||
CreateDatabaseResponse(QObject *sender, const std::string &database) :
|
||||
Event(sender), database(database) {}
|
||||
|
||||
CreateDatabaseResponse(QObject *sender, const EventError &error) :
|
||||
Event(sender, error) {}
|
||||
CreateDatabaseResponse(QObject *sender, const std::string &database, const EventError &error) :
|
||||
Event(sender, error), database(database) {}
|
||||
|
||||
std::string const database;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Drop Database
|
||||
*/
|
||||
|
||||
class DropDatabaseRequest : public Event
|
||||
struct DropDatabaseRequest : public Event
|
||||
{
|
||||
R_EVENT
|
||||
|
||||
public:
|
||||
DropDatabaseRequest(QObject *sender, const std::string &database) :
|
||||
Event(sender),
|
||||
_database(database) {}
|
||||
Event(sender), database(database) {}
|
||||
|
||||
std::string database() const { return _database; }
|
||||
|
||||
private:
|
||||
std::string _database;
|
||||
std::string const database;
|
||||
};
|
||||
|
||||
class DropDatabaseResponse : public Event
|
||||
struct DropDatabaseResponse : public Event
|
||||
{
|
||||
R_EVENT
|
||||
|
||||
public:
|
||||
DropDatabaseResponse(QObject *sender) :
|
||||
Event(sender) {}
|
||||
DropDatabaseResponse(QObject *sender, const std::string &database) :
|
||||
Event(sender), database(database) {}
|
||||
|
||||
DropDatabaseResponse(QObject *sender, const EventError &error) :
|
||||
Event(sender, error) {}
|
||||
DropDatabaseResponse(QObject *sender, const std::string &database, const EventError &error) :
|
||||
Event(sender, error), database(database) {}
|
||||
|
||||
std::string const database;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -440,26 +535,40 @@ namespace Robomongo
|
||||
R_EVENT
|
||||
|
||||
public:
|
||||
CreateCollectionRequest(QObject *sender, const MongoNamespace &ns) :
|
||||
Event(sender),
|
||||
_ns(ns) {}
|
||||
CreateCollectionRequest(QObject *sender, const MongoNamespace &ns,
|
||||
const mongo::BSONObj& extraOptions,
|
||||
long long size = 0,
|
||||
bool capped = false,
|
||||
int maxDocNum = 0
|
||||
) :
|
||||
Event(sender), _ns(ns), _extraOptions(extraOptions),
|
||||
_size(size), _capped(capped), _maxDocNum(maxDocNum) {}
|
||||
|
||||
MongoNamespace ns() const { return _ns; }
|
||||
long long getSize() const { return _size; }
|
||||
bool getCapped() const { return _capped; }
|
||||
int getMaxDocNum() const { return _maxDocNum; }
|
||||
const mongo::BSONObj getExtraOptions() const { return _extraOptions; }
|
||||
|
||||
private:
|
||||
const MongoNamespace _ns;
|
||||
MongoNamespace const _ns;
|
||||
long long const _size;
|
||||
bool const _capped;
|
||||
int const _maxDocNum;
|
||||
mongo::BSONObj const _extraOptions;
|
||||
};
|
||||
|
||||
class CreateCollectionResponse : public Event
|
||||
struct CreateCollectionResponse : public Event
|
||||
{
|
||||
R_EVENT
|
||||
|
||||
public:
|
||||
CreateCollectionResponse(QObject *sender) :
|
||||
Event(sender) {}
|
||||
CreateCollectionResponse(QObject *sender, std::string const& collection) :
|
||||
Event(sender), collection(collection) {}
|
||||
|
||||
CreateCollectionResponse(QObject *sender, const EventError &error) :
|
||||
Event(sender, error) {}
|
||||
CreateCollectionResponse(QObject *sender, std::string const& collection, const EventError &error) :
|
||||
Event(sender, error), collection(collection) {}
|
||||
|
||||
std::string const collection;
|
||||
};
|
||||
|
||||
|
||||
@@ -483,16 +592,18 @@ namespace Robomongo
|
||||
MongoNamespace _ns;
|
||||
};
|
||||
|
||||
class DropCollectionResponse : public Event
|
||||
struct DropCollectionResponse : public Event
|
||||
{
|
||||
R_EVENT
|
||||
|
||||
public:
|
||||
DropCollectionResponse(QObject *sender) :
|
||||
Event(sender) {}
|
||||
DropCollectionResponse(QObject *sender, std::string const& collection) :
|
||||
Event(sender), collection(collection) {}
|
||||
|
||||
DropCollectionResponse(QObject *sender, const EventError &error) :
|
||||
Event(sender, error) {}
|
||||
DropCollectionResponse(QObject *sender, std::string const& collection, const EventError &error) :
|
||||
Event(sender, error), collection(collection) {}
|
||||
|
||||
std::string const collection;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -513,20 +624,23 @@ namespace Robomongo
|
||||
std::string newCollection() const { return _newCollection; }
|
||||
|
||||
private:
|
||||
MongoNamespace _ns;
|
||||
std::string _newCollection;
|
||||
MongoNamespace const _ns;
|
||||
std::string const _newCollection;
|
||||
};
|
||||
|
||||
class RenameCollectionResponse : public Event
|
||||
struct RenameCollectionResponse : public Event
|
||||
{
|
||||
R_EVENT
|
||||
|
||||
public:
|
||||
RenameCollectionResponse(QObject *sender) :
|
||||
Event(sender) {}
|
||||
RenameCollectionResponse(QObject *sender, std::string const& oldCollection,
|
||||
std::string const& newCollection) :
|
||||
Event(sender), oldCollection(oldCollection), newCollection(newCollection) {}
|
||||
|
||||
RenameCollectionResponse(QObject *sender, const EventError &error) :
|
||||
Event(sender, error) {}
|
||||
|
||||
std::string const oldCollection;
|
||||
std::string const newCollection;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -547,20 +661,24 @@ namespace Robomongo
|
||||
std::string newCollection() const { return _newCollection; }
|
||||
|
||||
private:
|
||||
MongoNamespace _ns;
|
||||
std::string _newCollection;
|
||||
MongoNamespace const _ns;
|
||||
std::string const _newCollection;
|
||||
};
|
||||
|
||||
class DuplicateCollectionResponse : public Event
|
||||
struct DuplicateCollectionResponse : public Event
|
||||
{
|
||||
R_EVENT
|
||||
|
||||
public:
|
||||
DuplicateCollectionResponse(QObject *sender) :
|
||||
Event(sender) {}
|
||||
DuplicateCollectionResponse(QObject *sender, std::string const& sourceCollection,
|
||||
std::string const& duplicateCollection) :
|
||||
Event(sender), sourceCollection(sourceCollection), duplicateCollection(duplicateCollection) {}
|
||||
|
||||
DuplicateCollectionResponse(QObject *sender, const EventError &error) :
|
||||
Event(sender, error) {}
|
||||
DuplicateCollectionResponse(QObject *sender, std::string const& sourceCollection, const EventError &error) :
|
||||
Event(sender, error), sourceCollection(sourceCollection) {}
|
||||
|
||||
std::string const sourceCollection;
|
||||
std::string const duplicateCollection;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -580,7 +698,7 @@ namespace Robomongo
|
||||
_to(databaseTo, collection) {}
|
||||
|
||||
MongoWorker *worker() const { return _worker; }
|
||||
MongoNamespace from() const { return _from;}
|
||||
MongoNamespace from() const { return _from; }
|
||||
MongoNamespace to() const { return _to; }
|
||||
private:
|
||||
MongoWorker *_worker;
|
||||
@@ -625,16 +743,17 @@ namespace Robomongo
|
||||
bool _overwrite;
|
||||
};
|
||||
|
||||
class CreateUserResponse : public Event
|
||||
struct CreateUserResponse : public Event
|
||||
{
|
||||
R_EVENT
|
||||
|
||||
public:
|
||||
CreateUserResponse(QObject *sender) :
|
||||
Event(sender) {}
|
||||
CreateUserResponse(QObject *sender, std::string const& userName) :
|
||||
Event(sender), userName(userName) {}
|
||||
|
||||
CreateUserResponse(QObject *sender, const EventError &error) :
|
||||
Event(sender, error) {}
|
||||
CreateUserResponse(QObject *sender, std::string const& userName, const EventError &error) :
|
||||
Event(sender, error), userName(userName) {}
|
||||
|
||||
std::string const userName;
|
||||
};
|
||||
|
||||
|
||||
@@ -647,29 +766,31 @@ namespace Robomongo
|
||||
R_EVENT
|
||||
|
||||
public:
|
||||
DropUserRequest(QObject *sender, const std::string &database, const mongo::OID &id) :
|
||||
Event(sender),
|
||||
_database(database),
|
||||
_id(id) {}
|
||||
DropUserRequest(QObject *sender, const std::string &database, const mongo::OID &id,
|
||||
std::string const& username) :
|
||||
Event(sender), _database(database), _id(id), _username(username) {}
|
||||
|
||||
std::string database() const { return _database; }
|
||||
mongo::OID id() const { return _id; }
|
||||
std::string username() const { return _username; }
|
||||
|
||||
private:
|
||||
std::string _database;
|
||||
mongo::OID _id;
|
||||
std::string const _username;
|
||||
};
|
||||
|
||||
class DropUserResponse : public Event
|
||||
struct DropUserResponse : public Event
|
||||
{
|
||||
R_EVENT
|
||||
|
||||
public:
|
||||
DropUserResponse(QObject *sender) :
|
||||
Event(sender) {}
|
||||
DropUserResponse(QObject *sender, std::string const& username) :
|
||||
Event(sender), username(username) {}
|
||||
|
||||
DropUserResponse(QObject *sender, const EventError &error) :
|
||||
Event(sender, error) {}
|
||||
DropUserResponse(QObject *sender, std::string const& username, const EventError &error) :
|
||||
Event(sender, error), username(username) {}
|
||||
|
||||
std::string const username;
|
||||
};
|
||||
|
||||
|
||||
@@ -702,19 +823,21 @@ namespace Robomongo
|
||||
bool _overwrite;
|
||||
};
|
||||
|
||||
class CreateFunctionResponse : public Event
|
||||
struct CreateFunctionResponse : public Event
|
||||
{
|
||||
R_EVENT
|
||||
|
||||
enum Operation { NewFunction, NameUpdate, CodeUpdate }; // todo
|
||||
|
||||
public:
|
||||
CreateFunctionResponse(QObject *sender) :
|
||||
Event(sender) {}
|
||||
CreateFunctionResponse(QObject *sender, std::string const& functionName) :
|
||||
Event(sender), functionName(functionName) {}
|
||||
|
||||
CreateFunctionResponse(QObject *sender, const EventError &error) :
|
||||
Event(sender, error) {}
|
||||
CreateFunctionResponse(QObject *sender, std::string const& functionName, const EventError &error) :
|
||||
Event(sender, error), functionName(functionName) {}
|
||||
|
||||
std::string const functionName;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Drop Function
|
||||
*/
|
||||
@@ -724,29 +847,30 @@ namespace Robomongo
|
||||
R_EVENT
|
||||
|
||||
public:
|
||||
DropFunctionRequest(QObject *sender, const std::string &database, const std::string &name) :
|
||||
DropFunctionRequest(QObject *sender, const std::string &database, const std::string &funcName) :
|
||||
Event(sender),
|
||||
_database(database),
|
||||
_name(name) {}
|
||||
_functionName(funcName) {}
|
||||
|
||||
std::string database() const { return _database; }
|
||||
std::string name() const { return _name; }
|
||||
std::string functionName() const { return _functionName; }
|
||||
|
||||
private:
|
||||
std::string _database;
|
||||
std::string _name;
|
||||
std::string _functionName;
|
||||
};
|
||||
|
||||
class DropFunctionResponse : public Event
|
||||
struct DropFunctionResponse : public Event
|
||||
{
|
||||
R_EVENT
|
||||
|
||||
public:
|
||||
DropFunctionResponse(QObject *sender) :
|
||||
Event(sender) {}
|
||||
DropFunctionResponse(QObject *sender, const std::string &functionName) :
|
||||
Event(sender), functionName(functionName) {}
|
||||
|
||||
DropFunctionResponse(QObject *sender, const EventError &error) :
|
||||
Event(sender, error) {}
|
||||
DropFunctionResponse(QObject *sender, const std::string &functionName, const EventError &error) :
|
||||
Event(sender, error), functionName(functionName) {}
|
||||
|
||||
std::string const functionName;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -806,7 +930,7 @@ namespace Robomongo
|
||||
{
|
||||
R_EVENT
|
||||
|
||||
AutocompleteResponse(QObject *sender,const QStringList &list, const std::string &prefix) :
|
||||
AutocompleteResponse(QObject *sender, const QStringList &list, const std::string &prefix) :
|
||||
Event(sender),
|
||||
list(list),
|
||||
prefix(prefix) {}
|
||||
@@ -879,14 +1003,26 @@ namespace Robomongo
|
||||
{
|
||||
R_EVENT
|
||||
|
||||
ConnectionFailedEvent(QObject *sender, const std::string& message) :
|
||||
Event(sender),
|
||||
message(message) {}
|
||||
enum Reason {
|
||||
MongoConnection = 1,
|
||||
MongoAuth = 2,
|
||||
SshConnection = 3,
|
||||
SshChannel = 4,
|
||||
SslConnection = 5
|
||||
};
|
||||
|
||||
ConnectionFailedEvent(QObject *sender, const EventError &error) :
|
||||
Event(sender, error) {}
|
||||
ConnectionFailedEvent(QObject *sender, int serverHandle, ConnectionType connectionType,
|
||||
const std::string& message, Reason reason) :
|
||||
Event(sender),
|
||||
message(message),
|
||||
reason(reason),
|
||||
serverHandle(serverHandle),
|
||||
connectionType(connectionType) {}
|
||||
|
||||
std::string message;
|
||||
Reason reason;
|
||||
ConnectionType connectionType;
|
||||
int serverHandle;
|
||||
};
|
||||
|
||||
// class ScriptExecute
|
||||
@@ -895,11 +1031,15 @@ namespace Robomongo
|
||||
{
|
||||
R_EVENT
|
||||
|
||||
ConnectionEstablishedEvent(MongoServer *server) :
|
||||
ConnectionEstablishedEvent(MongoServer *server, ConnectionType type, ConnectionInfo connInfo) :
|
||||
Event((QObject *)server),
|
||||
server(server) { }
|
||||
server(server),
|
||||
connectionType(type),
|
||||
connInfo(connInfo) { }
|
||||
|
||||
MongoServer *server;
|
||||
ConnectionType const connectionType;
|
||||
ConnectionInfo const connInfo;
|
||||
};
|
||||
|
||||
class DatabaseListLoadedEvent : public Event
|
||||
@@ -973,6 +1113,20 @@ namespace Robomongo
|
||||
Event(sender) { }
|
||||
};
|
||||
|
||||
class OperationFailedEvent : public Event
|
||||
{
|
||||
R_EVENT
|
||||
|
||||
public:
|
||||
OperationFailedEvent(QObject *sender, const std::string &technicalErrorMessage, const std::string &userFriendlyErrorMessage) :
|
||||
technicalErrorMessage(technicalErrorMessage),
|
||||
userFriendlyErrorMessage(userFriendlyErrorMessage),
|
||||
Event(sender) { }
|
||||
|
||||
std::string technicalErrorMessage;
|
||||
std::string userFriendlyErrorMessage;
|
||||
};
|
||||
|
||||
class QueryWidgetUpdatedEvent : public Event
|
||||
{
|
||||
R_EVENT
|
||||
@@ -996,38 +1150,43 @@ namespace Robomongo
|
||||
{
|
||||
R_EVENT
|
||||
|
||||
EstablishSshConnectionRequest(QObject *sender, SshTunnelWorker* worker, ConnectionSettings* settings, bool visible) :
|
||||
EstablishSshConnectionRequest(QObject *sender, int serverHandle, SshTunnelWorker* worker, ConnectionSettings* settings, ConnectionType connectionType) :
|
||||
Event(sender),
|
||||
worker(worker),
|
||||
settings(settings),
|
||||
visible(visible) {}
|
||||
serverHandle(serverHandle),
|
||||
connectionType(connectionType) {}
|
||||
|
||||
ConnectionSettings* settings;
|
||||
bool visible;
|
||||
ConnectionType connectionType;
|
||||
SshTunnelWorker* worker;
|
||||
int serverHandle;
|
||||
};
|
||||
|
||||
class EstablishSshConnectionResponse : public Event
|
||||
{
|
||||
R_EVENT
|
||||
|
||||
EstablishSshConnectionResponse(QObject *sender, SshTunnelWorker* worker, ConnectionSettings* settings, bool visible, int localport) :
|
||||
EstablishSshConnectionResponse(QObject *sender, int serverHandle, SshTunnelWorker* worker, ConnectionSettings* settings, ConnectionType connectionType, int localport) :
|
||||
Event(sender),
|
||||
worker(worker),
|
||||
settings(settings),
|
||||
visible(visible),
|
||||
connectionType(connectionType),
|
||||
serverHandle(serverHandle),
|
||||
localport(localport) {}
|
||||
|
||||
EstablishSshConnectionResponse(QObject *sender, const EventError &error, SshTunnelWorker* worker, ConnectionSettings* settings, bool visible) :
|
||||
EstablishSshConnectionResponse(QObject *sender, int serverHandle, const EventError &error, SshTunnelWorker* worker, ConnectionSettings* settings, ConnectionType connectionType) :
|
||||
Event(sender, error),
|
||||
worker(worker),
|
||||
settings(settings),
|
||||
visible(visible) {}
|
||||
serverHandle(serverHandle),
|
||||
connectionType(connectionType) {}
|
||||
|
||||
ConnectionSettings* settings;
|
||||
bool visible;
|
||||
ConnectionType connectionType;
|
||||
SshTunnelWorker* worker;
|
||||
int localport;
|
||||
int serverHandle;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1038,23 +1197,34 @@ namespace Robomongo
|
||||
{
|
||||
R_EVENT
|
||||
|
||||
ListenSshConnectionRequest(QObject *sender) :
|
||||
Event(sender) {}
|
||||
ListenSshConnectionRequest(QObject *sender, int serverHandle, ConnectionType connectionType) :
|
||||
Event(sender),
|
||||
serverHandle(serverHandle),
|
||||
connectionType(connectionType) {}
|
||||
|
||||
ConnectionType connectionType;
|
||||
int serverHandle;
|
||||
};
|
||||
|
||||
class ListenSshConnectionResponse : public Event
|
||||
{
|
||||
R_EVENT
|
||||
|
||||
ListenSshConnectionResponse(QObject *sender, ConnectionSettings* settings) :
|
||||
ListenSshConnectionResponse(QObject *sender, int serverHandle, ConnectionSettings* settings, ConnectionType connectionType) :
|
||||
Event(sender),
|
||||
settings(settings) {}
|
||||
settings(settings),
|
||||
serverHandle(serverHandle),
|
||||
connectionType(connectionType) {}
|
||||
|
||||
ListenSshConnectionResponse(QObject *sender, const EventError &error, ConnectionSettings* settings) :
|
||||
ListenSshConnectionResponse(QObject *sender, const EventError &error, int serverHandle, ConnectionSettings* settings, ConnectionType connectionType) :
|
||||
Event(sender, error),
|
||||
settings(settings) {}
|
||||
settings(settings),
|
||||
serverHandle(serverHandle),
|
||||
connectionType(connectionType) {}
|
||||
|
||||
ConnectionSettings* settings;
|
||||
ConnectionType connectionType;
|
||||
int serverHandle;
|
||||
};
|
||||
|
||||
class LogEvent : public Event
|
||||
@@ -1062,10 +1232,10 @@ namespace Robomongo
|
||||
R_EVENT
|
||||
|
||||
enum LogLevel {
|
||||
ERROR = 1,
|
||||
WARN = 2,
|
||||
INFO = 3,
|
||||
DEBUG = 100 // log as much as possible
|
||||
RBM_ERROR = 1,
|
||||
RBM_WARN = 2,
|
||||
RBM_INFO = 3,
|
||||
RBM_DEBUG = 100 // log as much as possible
|
||||
};
|
||||
|
||||
LogEvent(QObject *sender, const std::string& message, LogLevel level) :
|
||||
|
||||
@@ -17,13 +17,19 @@ namespace Robomongo
|
||||
_languageOverride(languageOverride),
|
||||
_textWeights(textWeights) {}
|
||||
|
||||
ConnectionInfo::ConnectionInfo() :
|
||||
ConnectionInfo::ConnectionInfo(int originalConnectionSettingsId) :
|
||||
_address(),
|
||||
_databases(),
|
||||
_version(0.0f) {}
|
||||
_version(0.0f),
|
||||
_originalConnectionSettingsId(originalConnectionSettingsId)
|
||||
{}
|
||||
|
||||
ConnectionInfo::ConnectionInfo(const std::string &address, const std::vector<std::string> &databases, float version) :
|
||||
ConnectionInfo::ConnectionInfo(const std::string &address, const std::vector<std::string> &databases,
|
||||
float version, const std::string& storageEngine, int originalConnectionSettingsId) :
|
||||
_address(address),
|
||||
_databases(databases),
|
||||
_version(version) {}
|
||||
_version(version),
|
||||
_storageEngineType(storageEngine),
|
||||
_originalConnectionSettingsId(originalConnectionSettingsId)
|
||||
{}
|
||||
}
|
||||
|
||||
@@ -34,10 +34,13 @@ namespace Robomongo
|
||||
|
||||
struct ConnectionInfo
|
||||
{
|
||||
ConnectionInfo();
|
||||
ConnectionInfo(const std::string &address, const std::vector<std::string> &databases, float version);
|
||||
ConnectionInfo(int originalConnectionSettingsId);
|
||||
ConnectionInfo(const std::string &address, const std::vector<std::string> &databases, float version,
|
||||
const std::string& storageEngine, int originalConnectionSettingsId);
|
||||
const std::string _address;
|
||||
const std::vector<std::string> _databases;
|
||||
const float _version;
|
||||
const std::string _storageEngineType;
|
||||
const int _originalConnectionSettingsId = -1;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "robomongo/core/mongodb/MongoClient.h"
|
||||
|
||||
#include "mongo/db/namespace_string.h"
|
||||
|
||||
#include "robomongo/core/domain/MongoDocument.h"
|
||||
#include "robomongo/core/utils/BsonUtils.h"
|
||||
#include "robomongo/shell/bson/json.h"
|
||||
@@ -13,7 +15,7 @@ namespace
|
||||
using namespace Robomongo::BsonUtils;
|
||||
Robomongo::EnsureIndexInfo info(collection);
|
||||
info._name = getField<mongo::String>(obj, "name");
|
||||
mongo::BSONObj keyObj = getField<mongo::Object>(obj,"key");
|
||||
mongo::BSONObj keyObj = getField<mongo::Object>(obj, "key");
|
||||
if (keyObj.isValid()) {
|
||||
info._request = jsonString(keyObj, mongo::TenGen, 1, Robomongo::DefaultEncoding, Robomongo::Utc);
|
||||
}
|
||||
@@ -56,11 +58,18 @@ namespace Robomongo
|
||||
float result = 0.0f;
|
||||
mongo::BSONObj resultObj;
|
||||
_dbclient->runCommand("db", BSON("buildInfo" << "1"), resultObj);
|
||||
std::string resultStr = BsonUtils::getField<mongo::String>(resultObj,"version");
|
||||
std::string resultStr = BsonUtils::getField<mongo::String>(resultObj, "version");
|
||||
result = atof(resultStr.c_str());
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string MongoClient::getStorageEngineType() const
|
||||
{
|
||||
mongo::BSONObj resultObj;
|
||||
_dbclient->runCommand("db", BSON("serverStatus" << "1"), resultObj);
|
||||
return resultObj.getObjectField("storageEngine").getStringField("name");
|
||||
}
|
||||
|
||||
std::vector<std::string> MongoClient::getDatabaseNames() const
|
||||
{
|
||||
typedef std::list<std::string> cont_string_t;
|
||||
@@ -87,7 +96,7 @@ namespace Robomongo
|
||||
float ver = getVersion();
|
||||
while (cursor->more()) {
|
||||
mongo::BSONObj bsonObj = cursor->next();
|
||||
MongoUser user(ver,bsonObj);
|
||||
MongoUser user(ver, bsonObj);
|
||||
users.push_back(user);
|
||||
}
|
||||
|
||||
@@ -99,9 +108,10 @@ namespace Robomongo
|
||||
MongoNamespace ns(dbName, "system.users");
|
||||
mongo::BSONObj obj = user.toBson();
|
||||
|
||||
if (!overwrite) {
|
||||
if (!overwrite) { // create new user
|
||||
_dbclient->insert(ns.toString(), obj);
|
||||
} else {
|
||||
}
|
||||
else { // update existing user
|
||||
mongo::BSONElement id = obj.getField("_id");
|
||||
mongo::BSONObjBuilder builder;
|
||||
builder.append(id);
|
||||
@@ -110,6 +120,10 @@ namespace Robomongo
|
||||
|
||||
_dbclient->update(ns.toString(), query, obj, true, false);
|
||||
}
|
||||
|
||||
std::string errorStr = _dbclient->getLastError();
|
||||
if (!errorStr.empty())
|
||||
throw mongo::DBException(errorStr, 0);
|
||||
}
|
||||
|
||||
void MongoClient::dropUser(const std::string &dbName, const mongo::OID &id)
|
||||
@@ -122,6 +136,10 @@ namespace Robomongo
|
||||
mongo::Query query(bsonQuery);
|
||||
|
||||
_dbclient->remove(ns.toString(), query, true);
|
||||
|
||||
std::string errorStr = _dbclient->getLastError();
|
||||
if (!errorStr.empty())
|
||||
throw mongo::DBException(errorStr, 0);
|
||||
}
|
||||
|
||||
std::vector<MongoFunction> MongoClient::getFunctions(const std::string &dbName)
|
||||
@@ -157,13 +175,13 @@ namespace Robomongo
|
||||
|
||||
for (std::list<mongo::BSONObj>::iterator it = indexes.begin(); it != indexes.end(); ++it) {
|
||||
mongo::BSONObj bsonObj = *it;
|
||||
result.push_back(makeEnsureIndexInfoFromBsonObj(collection,bsonObj));
|
||||
result.push_back(makeEnsureIndexInfoFromBsonObj(collection, bsonObj));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void MongoClient::ensureIndex(const EnsureIndexInfo &oldInfo,const EnsureIndexInfo &newInfo) const
|
||||
void MongoClient::ensureIndex(const EnsureIndexInfo &oldInfo, const EnsureIndexInfo &newInfo) const
|
||||
{
|
||||
std::string ns = newInfo._collection.ns().toString();
|
||||
|
||||
@@ -172,8 +190,8 @@ namespace Robomongo
|
||||
|
||||
mongo::BSONObj keys = mongo::Robomongo::fromjson(newInfo._request);
|
||||
mongo::BSONObjBuilder toSave;
|
||||
bool cache=true;
|
||||
int version =-1;
|
||||
bool cache = true;
|
||||
int version = -1;
|
||||
|
||||
toSave.append( "ns" , ns );
|
||||
toSave.append( "key" , keys );
|
||||
@@ -284,36 +302,46 @@ namespace Robomongo
|
||||
_dbclient->dropIndex(collection.ns().toString(), indexName);
|
||||
}
|
||||
|
||||
void MongoClient::createFunction(const std::string &dbName, const MongoFunction &fun, const std::string &existingFunctionName /* = QString() */)
|
||||
void MongoClient::createFunction(const std::string &dbName, const MongoFunction &fun,
|
||||
const std::string &existingFunctionName /* = QString() */)
|
||||
{
|
||||
MongoNamespace ns(dbName, "system.js");
|
||||
mongo::BSONObj obj = fun.toBson();
|
||||
|
||||
if (existingFunctionName.empty()) { // this is insert
|
||||
if (existingFunctionName.empty()) { // create new function
|
||||
_dbclient->insert(ns.toString(), obj);
|
||||
std::string errorStr = _dbclient->getLastError();
|
||||
if (!errorStr.empty())
|
||||
throw mongo::DBException(errorStr, 0);
|
||||
} else { // this is update
|
||||
|
||||
std::string name = fun.name();
|
||||
|
||||
if (existingFunctionName == name) {
|
||||
if (existingFunctionName == name) { // update existing function code
|
||||
mongo::BSONObjBuilder builder;
|
||||
builder.append("_id", name);
|
||||
mongo::BSONObj bsonQuery = builder.obj();
|
||||
mongo::Query query(bsonQuery);
|
||||
|
||||
_dbclient->update(ns.toString(), query, obj, true, false);
|
||||
} else {
|
||||
std::string errorStr = _dbclient->getLastError();
|
||||
if (!errorStr.empty())
|
||||
throw mongo::DBException(errorStr, 0);
|
||||
} else { // update function name (remove & insert)
|
||||
_dbclient->insert(ns.toString(), obj);
|
||||
std::string res = _dbclient->getLastError();
|
||||
std::string errorStr = _dbclient->getLastError();
|
||||
|
||||
// if no errors
|
||||
if (res.empty()) {
|
||||
if (errorStr.empty()) {
|
||||
mongo::BSONObjBuilder builder;
|
||||
builder.append("_id", existingFunctionName);
|
||||
mongo::BSONObj bsonQuery = builder.obj();
|
||||
mongo::Query query(bsonQuery);
|
||||
_dbclient->remove(ns.toString(), query, true);
|
||||
}
|
||||
else {
|
||||
throw mongo::DBException(errorStr, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -328,6 +356,9 @@ namespace Robomongo
|
||||
mongo::Query query(bsonQuery);
|
||||
|
||||
_dbclient->remove(ns.toString(), query, true);
|
||||
std::string errorStr = _dbclient->getLastError();
|
||||
if (!errorStr.empty())
|
||||
throw mongo::DBException(errorStr, 0);
|
||||
}
|
||||
|
||||
void MongoClient::createDatabase(const std::string &dbName)
|
||||
@@ -342,7 +373,7 @@ namespace Robomongo
|
||||
|
||||
// If <dbName>.temp already exists, stop.
|
||||
if (_dbclient->exists(ns.toString()))
|
||||
return;
|
||||
throw mongo::DBException(dbName + ".temp already exists.", 0);
|
||||
|
||||
// Building { _id : "temp" } document
|
||||
mongo::BSONObjBuilder builder;
|
||||
@@ -351,6 +382,9 @@ namespace Robomongo
|
||||
|
||||
// Insert this document
|
||||
_dbclient->insert(ns.toString(), obj);
|
||||
std::string errorStr = _dbclient->getLastError();
|
||||
if (!errorStr.empty())
|
||||
throw mongo::DBException(errorStr, 0);
|
||||
|
||||
// Drop temp collection
|
||||
_dbclient->dropCollection(ns.toString());
|
||||
@@ -358,12 +392,50 @@ namespace Robomongo
|
||||
|
||||
void MongoClient::dropDatabase(const std::string &dbName)
|
||||
{
|
||||
_dbclient->dropDatabase(dbName);
|
||||
mongo::BSONObj result;
|
||||
if (!_dbclient->dropDatabase(dbName, &result)) {
|
||||
std::string errStr = result.getStringField("errmsg");
|
||||
if (errStr.empty())
|
||||
errStr = "Failed to get error message.";
|
||||
|
||||
throw mongo::DBException(errStr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void MongoClient::createCollection(const MongoNamespace &ns)
|
||||
void MongoClient::createCollection(const std::string& ns, long long size, bool capped, int max,
|
||||
const mongo::BSONObj& extraOptions, mongo::BSONObj* info)
|
||||
{
|
||||
_dbclient->createCollection(ns.toString());
|
||||
verify(!capped || size);
|
||||
mongo::BSONObj o;
|
||||
if (info == 0)
|
||||
info = &o;
|
||||
mongo::BSONObjBuilder b;
|
||||
std::string db = mongo::nsToDatabase(ns);
|
||||
b.append("create", ns.c_str() + db.length() + 1);
|
||||
if (size) {
|
||||
b.append("size", size);
|
||||
}
|
||||
if (capped) {
|
||||
b.append("capped", true);
|
||||
}
|
||||
if (max) {
|
||||
b.append("max", max);
|
||||
}
|
||||
b.appendElements(extraOptions);
|
||||
|
||||
if (!_dbclient->exists(ns)) {
|
||||
mongo::BSONObj result;
|
||||
if (!_dbclient->runCommand(db.c_str(), b.done(), result)) {
|
||||
std::string errStr = result.getStringField("errmsg");
|
||||
if (errStr.empty())
|
||||
errStr = "Failed to get error message.";
|
||||
|
||||
throw mongo::DBException(errStr, 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw mongo::DBException("Collection with same name already exists.", 0);
|
||||
}
|
||||
}
|
||||
|
||||
void MongoClient::renameCollection(const MongoNamespace &ns, const std::string &newCollectionName)
|
||||
@@ -377,18 +449,38 @@ namespace Robomongo
|
||||
command.append("to", to.toString());
|
||||
|
||||
mongo::BSONObj result;
|
||||
_dbclient->runCommand("admin", command.obj(), result); // this command should be run against "admin" db
|
||||
if (!_dbclient->runCommand("admin", command.obj(), result)) { // this command should be run against "admin" db
|
||||
std::string errStr = result.getStringField("errmsg");
|
||||
if (errStr.empty())
|
||||
errStr = "Failed to get error message.";
|
||||
|
||||
throw mongo::DBException(errStr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void MongoClient::duplicateCollection(const MongoNamespace &ns, const std::string &newCollectionName)
|
||||
{
|
||||
MongoNamespace from(ns);
|
||||
MongoNamespace to(ns.databaseName(), newCollectionName);
|
||||
MongoNamespace sourceCollection(ns);
|
||||
MongoNamespace newCollection(ns.databaseName(), newCollectionName);
|
||||
|
||||
if (!_dbclient->exists(to.toString()))
|
||||
_dbclient->createCollection(to.toString());
|
||||
if (!_dbclient->exists(newCollection.toString())) {
|
||||
mongo::BSONObj result;
|
||||
// todo: Issue #1258 : Duplicate Collection should support advanced collection options.
|
||||
// _dbclient->createCollection() should be called with properties of source collection
|
||||
// not with default parameters as below.
|
||||
if (!_dbclient->createCollection(newCollection.toString(), 0, false, 0, &result)) {
|
||||
std::string errStr = result.getStringField("errmsg");
|
||||
if (errStr.empty())
|
||||
errStr = "Failed to get error message.";
|
||||
|
||||
std::unique_ptr<mongo::DBClientCursor> cursor(_dbclient->query(from.toString(), mongo::Query()));
|
||||
throw mongo::DBException(errStr, 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw mongo::DBException("Collection with same name already exists.", 0);
|
||||
}
|
||||
|
||||
std::unique_ptr<mongo::DBClientCursor> cursor(_dbclient->query(sourceCollection.toString(), mongo::Query()));
|
||||
|
||||
// Cursor may be NULL, it means we have connectivity problem
|
||||
if (!cursor)
|
||||
@@ -396,11 +488,12 @@ namespace Robomongo
|
||||
|
||||
while (cursor->more()) {
|
||||
mongo::BSONObj bsonObj = cursor->next();
|
||||
_dbclient->insert(to.toString(), bsonObj);
|
||||
_dbclient->insert(newCollection.toString(), bsonObj);
|
||||
}
|
||||
}
|
||||
|
||||
void MongoClient::copyCollectionToDiffServer(mongo::DBClientBase *const fromServ,const MongoNamespace &from, const MongoNamespace &to)
|
||||
void MongoClient::copyCollectionToDiffServer(mongo::DBClientBase *const fromServ, const MongoNamespace &from,
|
||||
const MongoNamespace &to)
|
||||
{
|
||||
if (!_dbclient->exists(to.toString()))
|
||||
_dbclient->createCollection(to.toString());
|
||||
@@ -419,7 +512,19 @@ namespace Robomongo
|
||||
|
||||
void MongoClient::dropCollection(const MongoNamespace &ns)
|
||||
{
|
||||
_dbclient->dropCollection(ns.toString());
|
||||
if (_dbclient->exists(ns.toString())) {
|
||||
mongo::BSONObj result;
|
||||
if (!_dbclient->dropCollection(ns.toString(), &result)) {
|
||||
std::string errStr = result.getStringField("errmsg");
|
||||
if (errStr.empty())
|
||||
errStr = "Failed to get error message.";
|
||||
|
||||
throw mongo::DBException(errStr, 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw mongo::DBException("Collection does not exist.", 0);
|
||||
}
|
||||
}
|
||||
|
||||
void MongoClient::insertDocument(const mongo::BSONObj &obj, const MongoNamespace &ns)
|
||||
@@ -497,11 +602,10 @@ namespace Robomongo
|
||||
std::vector<MongoCollectionInfo> MongoClient::runCollStatsCommand(const std::vector<std::string> &namespaces)
|
||||
{
|
||||
std::vector<MongoCollectionInfo> infos;
|
||||
for (std::vector<std::string>::const_iterator it = namespaces.begin(); it!=namespaces.end(); ++it) {
|
||||
MongoCollectionInfo info = runCollStatsCommand(*it);
|
||||
if (info.ns().isValid()){
|
||||
for (auto const& ns : namespaces) {
|
||||
MongoCollectionInfo info = runCollStatsCommand(ns);
|
||||
if (info.ns().isValid())
|
||||
infos.push_back(info);
|
||||
}
|
||||
}
|
||||
return infos;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <mongo/client/dbclientinterface.h>
|
||||
#include <mongo/bson/bsonobj.h>
|
||||
|
||||
#include "robomongo/core/Core.h"
|
||||
#include "robomongo/core/domain/MongoQueryInfo.h"
|
||||
#include "robomongo/core/domain/MongoUser.h"
|
||||
@@ -17,6 +19,7 @@ namespace Robomongo
|
||||
std::vector<std::string> getCollectionNames(const std::string &dbname) const;
|
||||
std::vector<std::string> getDatabaseNames() const;
|
||||
float getVersion() const;
|
||||
std::string getStorageEngineType() const;
|
||||
|
||||
std::vector<MongoUser> getUsers(const std::string &dbName);
|
||||
void createUser(const std::string &dbName, const MongoUser &user, bool overwrite);
|
||||
@@ -37,11 +40,11 @@ namespace Robomongo
|
||||
void createDatabase(const std::string &dbName);
|
||||
void dropDatabase(const std::string &dbName);
|
||||
|
||||
void createCollection(const MongoNamespace &ns);
|
||||
void createCollection(const std::string &ns, long long size, bool capped, int max, const mongo::BSONObj& extraOptions, mongo::BSONObj* info = nullptr);
|
||||
void renameCollection(const MongoNamespace &ns, const std::string &newCollectionName);
|
||||
void duplicateCollection(const MongoNamespace &ns, const std::string &newCollectionName);
|
||||
void dropCollection(const MongoNamespace &ns);
|
||||
void copyCollectionToDiffServer(mongo::DBClientBase *const,const MongoNamespace &from, const MongoNamespace &to);
|
||||
void copyCollectionToDiffServer(mongo::DBClientBase *const, const MongoNamespace &from, const MongoNamespace &to);
|
||||
|
||||
void insertDocument(const mongo::BSONObj &obj, const MongoNamespace &ns);
|
||||
void saveDocument(const mongo::BSONObj &obj, const MongoNamespace &ns);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,8 @@
|
||||
#include <QMutex>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <mongo/client/dbclient_rs.h>
|
||||
|
||||
#include "robomongo/core/events/MongoEvents.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
@@ -22,15 +24,23 @@ namespace Robomongo
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
typedef std::vector<std::string> DatabasesContainerType;
|
||||
explicit MongoWorker(ConnectionSettings *connection, bool isLoadMongoRcJs, int batchSize, QObject *parent = NULL);
|
||||
~MongoWorker();
|
||||
enum { pingTimeMs = 60 * 1000 };
|
||||
|
||||
typedef std::vector<std::string> DatabasesContainerType;
|
||||
using DBClientReplicaSet = std::unique_ptr<mongo::DBClientReplicaSet>;
|
||||
using DBClientConnection = std::unique_ptr<mongo::DBClientConnection>;
|
||||
|
||||
explicit MongoWorker(ConnectionSettings *connection, bool isLoadMongoRcJs, int batchSize,
|
||||
int mongoTimeoutSec, int shellTimeoutSec, QObject *parent = NULL);
|
||||
|
||||
~MongoWorker();
|
||||
void interrupt();
|
||||
void stopAndDelete();
|
||||
|
||||
protected Q_SLOTS: // handlers:
|
||||
protected Q_SLOTS:
|
||||
|
||||
void init();
|
||||
|
||||
/**
|
||||
* @brief Every minute we are issuing { ping : 1 } command to every used connection
|
||||
* in order to avoid dropped connections.
|
||||
@@ -40,7 +50,13 @@ namespace Robomongo
|
||||
/**
|
||||
* @brief Initiate connection to MongoDB
|
||||
*/
|
||||
void handle(EstablishConnectionRequest *event);
|
||||
bool handle(EstablishConnectionRequest *event);
|
||||
|
||||
/**
|
||||
* @brief Try to re-connect to MongoDB replica set in order to refresh connection view.
|
||||
* For more details, see MongoServer::tryRefreshReplicaSetFolder()
|
||||
*/
|
||||
void handle(RefreshReplicaSetFolderRequest *event);
|
||||
|
||||
/**
|
||||
* @brief Load list of all database names
|
||||
@@ -127,33 +143,75 @@ namespace Robomongo
|
||||
* @brief Send event to this MongoWorker
|
||||
*/
|
||||
void send(Event *event);
|
||||
|
||||
DatabasesContainerType getDatabaseNamesSafe();
|
||||
std::string getAuthBase() const;
|
||||
|
||||
mongo::DBClientBase *_dbclient;
|
||||
mongo::DBClientBase *getConnection();
|
||||
mongo::DBClientBase *getConnection(bool mayReturnNull = false);
|
||||
MongoClient *getClient();
|
||||
|
||||
/**
|
||||
*@brief Reset and update global mongo SSL settings (mongo::sslGlobalParams)
|
||||
*/
|
||||
void configureSSL();
|
||||
|
||||
/**
|
||||
*@brief Update global mongo SSL settings (mongo::sslGlobalParams) according to active connection
|
||||
* request's SSL settings.
|
||||
*/
|
||||
void updateGlobalSSLparams() const;
|
||||
|
||||
/**
|
||||
*@brief Reset global mongo SSL settings (mongo::sslGlobalParams) into default zero state
|
||||
*/
|
||||
void resetGlobalSSLparams() const;
|
||||
|
||||
/**
|
||||
*@brief Update Replica Set related parameters/settings.
|
||||
* Warning: Using parameter 'refresh' as false might return incorrect cached values for status
|
||||
* of secondary members.
|
||||
* @param refresh : If true it will be deep scan of replica set status which
|
||||
* might take quite some time. If false, it will be quick scan
|
||||
* which might return incorrect cached values for status of secondary members.
|
||||
* Use false only to achieve quick first connection, in all other cases true
|
||||
* should be used.
|
||||
*/
|
||||
ReplicaSet getReplicaSetInfo(bool refresh = true) const;
|
||||
|
||||
std::string connectAndGetReplicaSetName() const;
|
||||
|
||||
/**
|
||||
* @brief Send reply event to object 'obj'
|
||||
*/
|
||||
void reply(QObject *receiver, Event *event);
|
||||
|
||||
/**
|
||||
* @brief Send hear beat messages to database in order to keep connection alive
|
||||
*/
|
||||
void pingDatabase(mongo::DBClientBase *dbclient) const;
|
||||
|
||||
QThread *_thread;
|
||||
QMutex _firstConnectionMutex;
|
||||
|
||||
ScriptEngine *_scriptEngine;
|
||||
std::unique_ptr<ScriptEngine> _scriptEngine;
|
||||
|
||||
bool _isAdmin;
|
||||
const bool _isLoadMongoRcJs;
|
||||
const int _batchSize;
|
||||
int _timerId;
|
||||
int _dbAutocompleteCacheTimerId;
|
||||
int _mongoTimeoutSec;
|
||||
int _shellTimeoutSec;
|
||||
QAtomicInteger<int> _isQuiting;
|
||||
|
||||
ConnectionSettings *_connection;
|
||||
std::unique_ptr<mongo::DBClientConnection> _dbclient;
|
||||
std::unique_ptr<mongo::DBClientReplicaSet> _dbclientRepSet;
|
||||
|
||||
ConnectionSettings *_connSettings;
|
||||
|
||||
// Collection of created databases.
|
||||
// Starting from 3.0, MongoDB drops empty databases.
|
||||
// It means, we do not find a way to create "empty" database.
|
||||
// It means, we did not find a way to create "empty" database.
|
||||
// We save all created databases in this collection and merge with
|
||||
// list of real databases returned from MongoDB server.
|
||||
std::unordered_set<std::string> _createdDbs;
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
#include "robomongo/core/mongodb/ReplicaSet.h"
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
ReplicaSet::ReplicaSet(std::string const& setName, const mongo::HostAndPort primary,
|
||||
std::vector<std::pair<std::string, bool>> const& membersAndHealths,
|
||||
std::string const& errorStr)
|
||||
: setName(setName), primary(primary), membersAndHealths(membersAndHealths),
|
||||
errorStr(errorStr)
|
||||
{}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <mongo/util/net/hostandport.h>
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
/**
|
||||
* @brief Struct to represent live information of a replica set.
|
||||
*/
|
||||
struct ReplicaSet
|
||||
{
|
||||
ReplicaSet(std::string const& setName, const mongo::HostAndPort primary,
|
||||
std::vector<std::pair<std::string, bool>> const& membersAndHealths, std::string const& errorStr = "");
|
||||
|
||||
ReplicaSet() {};
|
||||
|
||||
std::string const setName;
|
||||
mongo::HostAndPort const primary;
|
||||
std::vector<std::pair<std::string, bool>> const membersAndHealths; // pair: {HostNameAndPort, Health}
|
||||
std::string const errorStr;
|
||||
};
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "robomongo/core/mongodb/SshTunnelWorker.h"
|
||||
|
||||
#include <QThread>
|
||||
#include <QElapsedTimer>
|
||||
|
||||
#include "robomongo/core/utils/QtUtils.h"
|
||||
#include "robomongo/core/utils/Logger.h"
|
||||
#include "robomongo/core/settings/ConnectionSettings.h"
|
||||
@@ -15,50 +17,23 @@ namespace Robomongo
|
||||
{
|
||||
SshTunnelWorker::SshTunnelWorker(ConnectionSettings *settings) : QObject(),
|
||||
_settings(settings),
|
||||
_sshConfig(NULL),
|
||||
_sshSession(NULL)
|
||||
_sshSession(NULL),
|
||||
_configCreator(settings)
|
||||
{
|
||||
_thread = new QThread();
|
||||
moveToThread(_thread);
|
||||
VERIFY(connect(_thread, SIGNAL(started()), this, SLOT(init())));
|
||||
VERIFY(connect(_thread, SIGNAL(finished()), _thread, SLOT(deleteLater())));
|
||||
VERIFY(connect(_thread, SIGNAL(finished()), this, SLOT(deleteLater())));
|
||||
_thread->start();
|
||||
}
|
||||
|
||||
void SshTunnelWorker::init()
|
||||
{
|
||||
try {
|
||||
SshSettings *ssh = _settings->sshSettings();
|
||||
|
||||
// We are going to access raw char* buffers, so prepare copies
|
||||
// of strings (and rest of the things for symmetry)
|
||||
_sshhost = ssh->host();
|
||||
_sshport = ssh->port();
|
||||
_remotehost = _settings->serverHost();
|
||||
_remoteport = _settings->serverPort();
|
||||
_localip = "127.0.0.1";
|
||||
_localport = 27040;
|
||||
|
||||
_userName = ssh->userName();
|
||||
_userPassword = ssh->userPassword();
|
||||
_privateKeyFile = ssh->privateKeyFile();
|
||||
_publicKeyFile = ssh->publicKeyFile();
|
||||
_passphrase = ssh->passphrase();
|
||||
_authMethod = ssh->authMethod(); // "password" or "publickey"
|
||||
}
|
||||
catch (const std::exception &ex) {
|
||||
LOG_MSG(ex.what(), mongo::logger::LogSeverity::Error());
|
||||
}
|
||||
}
|
||||
|
||||
SshTunnelWorker::~SshTunnelWorker()
|
||||
{
|
||||
// QThread "_thread" and MongoWorker itself will be deleted later
|
||||
// (see MongoWorker() constructor)
|
||||
|
||||
delete _settings;
|
||||
delete _sshConfig;
|
||||
printf("SSH tunnel closed.\n");
|
||||
}
|
||||
|
||||
void SshTunnelWorker::stopAndDelete() {
|
||||
@@ -71,30 +46,12 @@ namespace Robomongo
|
||||
if (_isQuiting)
|
||||
return;
|
||||
|
||||
_sshConfig = new rbm_ssh_tunnel_config;
|
||||
_sshConfig->sshserverip = const_cast<char*>(_sshhost.c_str());
|
||||
_sshConfig->sshserverport = static_cast<unsigned int>(_sshport);
|
||||
_sshConfig->remotehost = const_cast<char*>(_remotehost.c_str());
|
||||
_sshConfig->remoteport = static_cast<unsigned int>(_remoteport);
|
||||
_sshConfig->localip = const_cast<char*>(_localip.c_str());
|
||||
_sshConfig->localport = static_cast<unsigned int>(_localport);
|
||||
|
||||
_sshConfig->username = const_cast<char*>(_userName.c_str());
|
||||
_sshConfig->password = const_cast<char*>(_userPassword.c_str());
|
||||
_sshConfig->privatekeyfile = const_cast<char*>(_privateKeyFile.c_str());
|
||||
_sshConfig->publickeyfile = _publicKeyFile.empty() ? NULL : const_cast<char*>(_publicKeyFile.c_str());
|
||||
_sshConfig->passphrase = const_cast<char*>(_passphrase.c_str());
|
||||
_sshConfig->authtype = (_authMethod == "publickey") ? RBM_SSH_AUTH_TYPE_PUBLICKEY : RBM_SSH_AUTH_TYPE_PASSWORD;
|
||||
|
||||
_sshConfig->logcontext = this;
|
||||
_sshConfig->logcallback = &SshTunnelWorker::logCallbackHandler;
|
||||
_sshConfig->loglevel = (rbm_ssh_log_type) _settings->sshSettings()->logLevel(); // RBM_SSH_LOG_TYPE_DEBUG;
|
||||
|
||||
if ((_sshSession = rbm_ssh_session_create(_sshConfig)) == 0) {
|
||||
// Cleanup config structure
|
||||
delete _sshConfig;
|
||||
_sshConfig = NULL;
|
||||
// Additionally configure "rbm_ssh_tunnel_config"
|
||||
_configCreator.config()->logcontext = this;
|
||||
_configCreator.config()->logcallback = &SshTunnelWorker::logCallbackHandler;
|
||||
_configCreator.config()->loglevel = (rbm_ssh_log_type) _settings->sshSettings()->logLevel(); // RBM_SSH_LOG_TYPE_DEBUG;
|
||||
|
||||
if ((_sshSession = rbm_ssh_session_create(_configCreator.config())) == 0) {
|
||||
// Not much we can say about this error
|
||||
throw std::runtime_error("Failed to create SSH session");
|
||||
}
|
||||
@@ -113,20 +70,16 @@ namespace Robomongo
|
||||
rbm_ssh_session_close(_sshSession);
|
||||
_sshSession = NULL;
|
||||
|
||||
// Cleanup config
|
||||
delete _sshConfig;
|
||||
_sshConfig = NULL;
|
||||
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
|
||||
reply(event->sender(), new EstablishSshConnectionResponse(
|
||||
this, event->worker, event->settings, event->visible, _sshConfig->localport));
|
||||
this, event->serverHandle, event->worker, event->settings, event->connectionType, _configCreator.config()->localport));
|
||||
|
||||
} catch (const std::exception& ex) {
|
||||
reply(event->sender(),
|
||||
new EstablishSshConnectionResponse(this, EventError(ex.what()),
|
||||
event->worker, event->settings, event->visible));
|
||||
new EstablishSshConnectionResponse(this, event->serverHandle, EventError(ex.what()),
|
||||
event->worker, event->settings, event->connectionType));
|
||||
|
||||
// In case of error in connection, we should cleanup SshTunnelWorker
|
||||
stopAndDelete();
|
||||
@@ -134,28 +87,50 @@ namespace Robomongo
|
||||
}
|
||||
|
||||
void SshTunnelWorker::handle(ListenSshConnectionRequest *event) {
|
||||
|
||||
// do it here, i.e. cycle
|
||||
|
||||
try {
|
||||
if (_isQuiting)
|
||||
return;
|
||||
|
||||
if (_sshSession == NULL || _sshConfig == NULL)
|
||||
if (_sshSession == NULL)
|
||||
return;
|
||||
|
||||
// We are running this timer in order to distinguish between two
|
||||
// types of errors:
|
||||
// 1) SSH tunnel wasn't successfully created
|
||||
// 2) SSH tunnel was disconnected
|
||||
// This is used now only for UI error message, that we show to user.
|
||||
QElapsedTimer timer;
|
||||
timer.start();
|
||||
|
||||
// This function will block until all TCP connections disconnects.
|
||||
// Initially, it will wait for at least one such connection.
|
||||
if (rbm_ssh_open_tunnel(_sshSession) != 0) {
|
||||
|
||||
qint64 elapsed = timer.elapsed();
|
||||
bool wasDisconnected = elapsed > 20000; // More than 20 seconds passed
|
||||
|
||||
// Prepare copy of error message (if any)
|
||||
std::string error(_sshSession->lasterror);
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "You are disconnected from SSH tunnel ("
|
||||
<< _settings->sshSettings()->host() << ":"
|
||||
<< _settings->sshSettings()->port() << "). "
|
||||
<< "Please initiate a new connection and reopen all tabs.\n\nError:\n"
|
||||
<< error;
|
||||
|
||||
if (wasDisconnected) {
|
||||
ss << "You are disconnected from SSH tunnel ("
|
||||
<< _settings->sshSettings()->host() << ":"
|
||||
<< _settings->sshSettings()->port() << "). "
|
||||
<< "Please initiate a new connection and reopen all tabs.\n\nError:\n"
|
||||
<< error;
|
||||
} else {
|
||||
ss << "Cannot establish SSH tunnel ("
|
||||
<< _settings->sshSettings()->host() << ":"
|
||||
<< _settings->sshSettings()->port() << "). "
|
||||
<< "\n\nError:\n"
|
||||
<< error;
|
||||
}
|
||||
|
||||
// Cleanup session
|
||||
rbm_ssh_session_close(_sshSession);
|
||||
_sshSession = NULL;
|
||||
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
@@ -164,7 +139,7 @@ namespace Robomongo
|
||||
|
||||
} catch (const std::exception& ex) {
|
||||
reply(event->sender(),
|
||||
new ListenSshConnectionResponse(this, EventError(ex.what()), _settings));
|
||||
new ListenSshConnectionResponse(this, EventError(ex.what()), event->serverHandle, _settings, event->connectionType));
|
||||
}
|
||||
|
||||
// When we done with SSH (i.e. rbm_ssh_open_tunnel exits) regardless
|
||||
@@ -195,4 +170,60 @@ namespace Robomongo
|
||||
void SshTunnelWorker::logCallbackHandler(void *context, char *message, int level) {
|
||||
static_cast<SshTunnelWorker*>(context)->log(message, level);
|
||||
}
|
||||
|
||||
/*
|
||||
* SshTunnelConfigCreator
|
||||
*/
|
||||
SshTunnelConfigCreator::SshTunnelConfigCreator(ConnectionSettings *settings) {
|
||||
SshSettings *ssh = settings->sshSettings();
|
||||
|
||||
// We are going to access raw char* buffers, so prepare copies
|
||||
// of strings (and rest of the things for symmetry)
|
||||
_sshhost = ssh->host();
|
||||
_sshport = ssh->port();
|
||||
_remotehost = settings->serverHost();
|
||||
_remoteport = settings->serverPort();
|
||||
_localip = "127.0.0.1";
|
||||
_localport = 27040;
|
||||
|
||||
_userName = ssh->userName();
|
||||
_userPassword = ssh->userPassword();
|
||||
_privateKeyFile = ssh->privateKeyFile();
|
||||
_publicKeyFile = ssh->publicKeyFile();
|
||||
_passphrase = ssh->passphrase();
|
||||
_authMethod = ssh->authMethod(); // "password" or "publickey"
|
||||
|
||||
// Use "askedPassword" for both passphrase and password if required
|
||||
if (ssh->askPassword()) {
|
||||
_passphrase = ssh->askedPassword();
|
||||
_userPassword = ssh->askedPassword();
|
||||
}
|
||||
|
||||
_sshConfig = new rbm_ssh_tunnel_config;
|
||||
_sshConfig->sshserverhost = const_cast<char*>(_sshhost.c_str());
|
||||
_sshConfig->sshserverport = static_cast<unsigned int>(_sshport);
|
||||
_sshConfig->remotehost = const_cast<char*>(_remotehost.c_str());
|
||||
_sshConfig->remoteport = static_cast<unsigned int>(_remoteport);
|
||||
_sshConfig->localip = const_cast<char*>(_localip.c_str());
|
||||
_sshConfig->localport = static_cast<unsigned int>(_localport);
|
||||
|
||||
_sshConfig->username = const_cast<char*>(_userName.c_str());
|
||||
_sshConfig->password = const_cast<char*>(_userPassword.c_str());
|
||||
_sshConfig->privatekeyfile = const_cast<char*>(_privateKeyFile.c_str());
|
||||
_sshConfig->publickeyfile = _publicKeyFile.empty() ? NULL : const_cast<char*>(_publicKeyFile.c_str());
|
||||
_sshConfig->passphrase = const_cast<char*>(_passphrase.c_str());
|
||||
_sshConfig->authtype = (_authMethod == "publickey") ? RBM_SSH_AUTH_TYPE_PUBLICKEY : RBM_SSH_AUTH_TYPE_PASSWORD;
|
||||
|
||||
// Default settings
|
||||
_sshConfig->logcontext = NULL;
|
||||
_sshConfig->logcallback = NULL;
|
||||
_sshConfig->loglevel = RBM_SSH_LOG_TYPE_ERROR;
|
||||
|
||||
}
|
||||
|
||||
SshTunnelConfigCreator::~SshTunnelConfigCreator() {
|
||||
delete _sshConfig;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <robomongo/ssh/ssh.h>
|
||||
#include "robomongo/core/events/MongoEvents.h"
|
||||
|
||||
@@ -17,6 +18,34 @@ namespace Robomongo
|
||||
class EstablishSshConnectionRequest;
|
||||
class ListenSshConnectionRequest;
|
||||
|
||||
/*
|
||||
* Helper class for creating rbm_ssh_tunnel_config from ConnectionSettings
|
||||
* Automatically deletes allocated structure
|
||||
*/
|
||||
class SshTunnelConfigCreator {
|
||||
public:
|
||||
explicit SshTunnelConfigCreator(ConnectionSettings *settings);
|
||||
~SshTunnelConfigCreator();
|
||||
rbm_ssh_tunnel_config *config() { return _sshConfig; }
|
||||
|
||||
private:
|
||||
std::string _sshhost;
|
||||
int _sshport;
|
||||
std::string _remotehost;
|
||||
int _remoteport;
|
||||
std::string _localip;
|
||||
int _localport;
|
||||
|
||||
std::string _userName;
|
||||
std::string _userPassword;
|
||||
std::string _privateKeyFile;
|
||||
std::string _publicKeyFile;
|
||||
std::string _passphrase;
|
||||
std::string _authMethod; // "password" or "publickey"
|
||||
|
||||
rbm_ssh_tunnel_config* _sshConfig;
|
||||
};
|
||||
|
||||
class SshTunnelWorker : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -31,7 +60,6 @@ namespace Robomongo
|
||||
void stopAndDelete();
|
||||
|
||||
protected Q_SLOTS: // handlers:
|
||||
void init();
|
||||
void handle(EstablishSshConnectionRequest *event);
|
||||
void handle(ListenSshConnectionRequest *event);
|
||||
|
||||
@@ -42,21 +70,9 @@ namespace Robomongo
|
||||
QThread *_thread;
|
||||
QAtomicInteger<int> _isQuiting;
|
||||
ConnectionSettings* _settings;
|
||||
rbm_ssh_tunnel_config* _sshConfig;
|
||||
rbm_ssh_session* _sshSession;
|
||||
|
||||
std::string _sshhost;
|
||||
int _sshport;
|
||||
std::string _remotehost;
|
||||
int _remoteport;
|
||||
std::string _localip;
|
||||
int _localport;
|
||||
|
||||
std::string _userName;
|
||||
std::string _userPassword;
|
||||
std::string _privateKeyFile;
|
||||
std::string _publicKeyFile;
|
||||
std::string _passphrase;
|
||||
std::string _authMethod; // "password" or "publickey"
|
||||
SshTunnelConfigCreator _configCreator;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -3,36 +3,82 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "robomongo/core/settings/CredentialSettings.h"
|
||||
#include "robomongo/core/settings/ReplicaSetSettings.h"
|
||||
#include "robomongo/core/settings/SshSettings.h"
|
||||
#include "robomongo/core/settings/SslSettings.h"
|
||||
#include "robomongo/core/utils/QtUtils.h"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
namespace
|
||||
{
|
||||
const unsigned port = 27017;
|
||||
const char *defaultServerHost = "localhost";
|
||||
const char *defaultNameConnection = "New Connection";
|
||||
|
||||
const int maxLength = 300;
|
||||
}
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
|
||||
/**
|
||||
* Creates ConnectionSettings with default values
|
||||
*/
|
||||
ConnectionSettings::ConnectionSettings() : QObject(),
|
||||
ConnectionSettings::ConnectionSettings(bool isClone) : QObject(),
|
||||
_connectionName(defaultNameConnection),
|
||||
_host(defaultServerHost),
|
||||
_port(port),
|
||||
_sshSettings(new SshSettings()) { }
|
||||
_imported(false),
|
||||
_sshSettings(new SshSettings()),
|
||||
_sslSettings(new SslSettings()),
|
||||
_isReplicaSet(false),
|
||||
_replicaSetSettings(new ReplicaSetSettings()),
|
||||
_clone(isClone)
|
||||
{ }
|
||||
|
||||
// todo: remove or add clone support
|
||||
ConnectionSettings::ConnectionSettings(const mongo::MongoURI& uri, bool isClone)
|
||||
: _connectionName(defaultNameConnection),
|
||||
_host(defaultServerHost),
|
||||
_port(port),
|
||||
_imported(false),
|
||||
_sshSettings(new SshSettings()),
|
||||
_sslSettings(new SslSettings()),
|
||||
_isReplicaSet((uri.type() == mongo::ConnectionString::ConnectionType::SET)),
|
||||
_replicaSetSettings(new ReplicaSetSettings(uri)),
|
||||
_clone(isClone)
|
||||
{
|
||||
if (!uri.getServers().empty()) {
|
||||
_host = uri.getServers().front().host();
|
||||
_port = uri.getServers().front().port();
|
||||
}
|
||||
|
||||
auto str = std::string(uri.getOptions().getStringField("ssl"));
|
||||
auto sslEnabled = ("true" == str);
|
||||
if (sslEnabled) {
|
||||
_sslSettings->enableSSL(true);
|
||||
_sslSettings->setAllowInvalidCertificates(true);
|
||||
}
|
||||
|
||||
auto credential = new CredentialSettings();
|
||||
credential->setUserName(uri.getUser());
|
||||
credential->setUserPassword(uri.getPassword());
|
||||
credential->setDatabaseName(uri.getDatabase());
|
||||
if (!credential->userName().empty() && !credential->userPassword().empty()) { // todo:
|
||||
credential->setEnabled(true);
|
||||
}
|
||||
addCredential(credential);
|
||||
}
|
||||
|
||||
void ConnectionSettings::fromVariant(const QVariantMap &map) {
|
||||
setConnectionName(QtUtils::toStdString(map.value("connectionName").toString()));
|
||||
setServerHost(QtUtils::toStdString(map.value("serverHost").toString()));
|
||||
setServerHost(QtUtils::toStdString(map.value("serverHost").toString().left(maxLength)));
|
||||
setServerPort(map.value("serverPort").toInt());
|
||||
setDefaultDatabase(QtUtils::toStdString(map.value("defaultDatabase").toString()));
|
||||
|
||||
setReplicaSet(map.value("isReplicaSet").toBool());
|
||||
|
||||
QVariantList list = map.value("credentials").toList();
|
||||
for(QVariantList::const_iterator it = list.begin(); it != list.end(); ++it) {
|
||||
for (QVariantList::const_iterator it = list.begin(); it != list.end(); ++it) {
|
||||
QVariant var = *it;
|
||||
CredentialSettings *credential = new CredentialSettings(var.toMap());
|
||||
addCredential(credential);
|
||||
@@ -42,6 +88,14 @@ namespace Robomongo
|
||||
_sshSettings->fromVariant(map.value("ssh").toMap());
|
||||
}
|
||||
|
||||
if (map.contains("ssl")) {
|
||||
_sslSettings->fromVariant(map.value("ssl").toMap());
|
||||
}
|
||||
|
||||
if (isReplicaSet()) {
|
||||
_replicaSetSettings->fromVariant(map.value("replicaSet").toMap());
|
||||
}
|
||||
|
||||
//#ifdef MONGO_SSL
|
||||
// ,SSLInfo(map.value("sslEnabled").toBool(),QtUtils::toStdString(map.value("sslPemKeyFile").toString()))
|
||||
//#endif
|
||||
@@ -53,7 +107,6 @@ namespace Robomongo
|
||||
ConnectionSettings::~ConnectionSettings()
|
||||
{
|
||||
clearCredentials();
|
||||
delete _sshSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,9 +114,10 @@ namespace Robomongo
|
||||
*/
|
||||
ConnectionSettings *ConnectionSettings::clone() const
|
||||
{
|
||||
ConnectionSettings *record = new ConnectionSettings();
|
||||
record->apply(this);
|
||||
return record;
|
||||
auto settings = new ConnectionSettings(true);
|
||||
settings->setUniqueId(_uniqueId);
|
||||
settings->apply(this);
|
||||
return settings;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,6 +129,8 @@ namespace Robomongo
|
||||
setServerHost(source->serverHost());
|
||||
setServerPort(source->serverPort());
|
||||
setDefaultDatabase(source->defaultDatabase());
|
||||
setImported(source->imported());
|
||||
setReplicaSet(source->isReplicaSet());
|
||||
|
||||
clearCredentials();
|
||||
QList<CredentialSettings *> cred = source->credentials();
|
||||
@@ -82,8 +138,9 @@ namespace Robomongo
|
||||
addCredential((*it)->clone());
|
||||
}
|
||||
|
||||
// Clone SSH settings
|
||||
_sshSettings = source->sshSettings()->clone();
|
||||
_sshSettings.reset(source->sshSettings()->clone());
|
||||
_sslSettings.reset(source->sslSettings()->clone());
|
||||
_replicaSetSettings.reset(source->_replicaSetSettings->clone());
|
||||
|
||||
//#ifdef MONGO_SSL
|
||||
// setSslInfo(source->sslInfo());
|
||||
@@ -100,27 +157,33 @@ namespace Robomongo
|
||||
map.insert("serverHost", QtUtils::toQString(serverHost()));
|
||||
map.insert("serverPort", serverPort());
|
||||
map.insert("defaultDatabase", QtUtils::toQString(defaultDatabase()));
|
||||
map.insert("isReplicaSet", isReplicaSet());
|
||||
if (isReplicaSet()) {
|
||||
map.insert("replicaSet", _replicaSetSettings->toVariant());
|
||||
}
|
||||
|
||||
#ifdef MONGO_SSL
|
||||
SSLInfo infl = _info.sslInfo();
|
||||
map.insert("sslEnabled", infl._sslSupport);
|
||||
map.insert("sslPemKeyFile", QtUtils::toQString(infl._sslPEMKeyFile));
|
||||
#endif
|
||||
QVariantList list;
|
||||
for(QList<CredentialSettings *>::const_iterator it = _credentials.begin(); it != _credentials.end(); ++it) {
|
||||
for (QList<CredentialSettings *>::const_iterator it = _credentials.begin(); it != _credentials.end(); ++it) {
|
||||
CredentialSettings *credential = *it;
|
||||
list.append(credential->toVariant());
|
||||
}
|
||||
map.insert("credentials", list);
|
||||
|
||||
map.insert("ssh", _sshSettings->toVariant());
|
||||
map.insert("ssl", _sslSettings->toVariant());
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
CredentialSettings *ConnectionSettings::findCredential(const std::string &databaseName) const
|
||||
{
|
||||
CredentialSettings *result = NULL;
|
||||
for(QList<CredentialSettings *>::const_iterator it = _credentials.begin(); it != _credentials.end(); ++it) {
|
||||
CredentialSettings *result = nullptr;
|
||||
for (QList<CredentialSettings *>::const_iterator it = _credentials.begin(); it != _credentials.end(); ++it) {
|
||||
CredentialSettings *cred = *it;
|
||||
if (cred->databaseName() == databaseName) {
|
||||
result = cred;
|
||||
@@ -143,7 +206,7 @@ namespace Robomongo
|
||||
* @brief Checks whether this connection has primary credential
|
||||
* which is also enabled.
|
||||
*/
|
||||
bool ConnectionSettings::hasEnabledPrimaryCredential()
|
||||
bool ConnectionSettings::hasEnabledPrimaryCredential() const
|
||||
{
|
||||
if (_credentials.count() == 0)
|
||||
return false;
|
||||
@@ -152,12 +215,13 @@ namespace Robomongo
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns primary credential, or NULL if no credentials exists.
|
||||
* @brief Returns primary credential
|
||||
*/
|
||||
CredentialSettings *ConnectionSettings::primaryCredential()
|
||||
CredentialSettings *ConnectionSettings::primaryCredential() const
|
||||
{
|
||||
if (_credentials.count() == 0)
|
||||
return NULL;
|
||||
if (_credentials.count() == 0) {
|
||||
_credentials.append(new CredentialSettings());
|
||||
}
|
||||
|
||||
return _credentials.at(0);
|
||||
}
|
||||
@@ -173,9 +237,26 @@ namespace Robomongo
|
||||
|
||||
std::string ConnectionSettings::getFullAddress() const
|
||||
{
|
||||
return info().toString();
|
||||
return hostAndPort().toString();
|
||||
}
|
||||
|
||||
mongo::HostAndPort ConnectionSettings::hostAndPort() const
|
||||
{
|
||||
// If it doesn't look like IPv6 address,
|
||||
// treat it like IPv4 or literal hostname
|
||||
if (_host.find(':') == std::string::npos) {
|
||||
return mongo::HostAndPort(_host, _port);
|
||||
}
|
||||
|
||||
// The following code assumes, that it is IPv6 address
|
||||
// If address contains square brackets ("["), remove them:
|
||||
std::string hostCopy = _host;
|
||||
if (_host.find('[') != std::string::npos) {
|
||||
boost::erase_all(hostCopy, "[");
|
||||
boost::erase_all(hostCopy, "]");
|
||||
}
|
||||
|
||||
return mongo::HostAndPort(hostCopy, _port);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,13 +3,18 @@
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
#include <QVariantMap>
|
||||
|
||||
#include <mongo/client/dbclientinterface.h>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <mongo/client/mongo_uri.h>
|
||||
#include <mongo/util/net/hostandport.h>
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
class CredentialSettings;
|
||||
class SshSettings;
|
||||
class SslSettings;
|
||||
class ReplicaSetSettings;
|
||||
|
||||
/**
|
||||
* @brief Represents connection record
|
||||
*/
|
||||
@@ -21,9 +26,14 @@ namespace Robomongo
|
||||
/**
|
||||
* @brief Creates ConnectionSettings with default values
|
||||
*/
|
||||
ConnectionSettings();
|
||||
ConnectionSettings(bool isClone);
|
||||
|
||||
/**
|
||||
* @brief Creates ConnectionSettings from mongo connection string URI
|
||||
*/
|
||||
ConnectionSettings(const mongo::MongoURI& uri, bool isClone);
|
||||
|
||||
explicit ConnectionSettings(QVariantMap map);
|
||||
explicit ConnectionSettings(QVariantMap map, bool isClone);
|
||||
|
||||
/**
|
||||
* @brief Cleanup used resources
|
||||
@@ -61,7 +71,7 @@ namespace Robomongo
|
||||
/**
|
||||
* @brief Port of server
|
||||
*/
|
||||
unsigned short serverPort() const { return _port; }
|
||||
int serverPort() const { return _port; }
|
||||
void setServerPort(const int port) { _port = port; }
|
||||
|
||||
/**
|
||||
@@ -70,6 +80,12 @@ namespace Robomongo
|
||||
std::string defaultDatabase() const { return _defaultDatabase; }
|
||||
void setDefaultDatabase(const std::string &defaultDatabase) { _defaultDatabase = defaultDatabase; }
|
||||
|
||||
/**
|
||||
* Was this connection imported from somewhere?
|
||||
*/
|
||||
bool imported() const { return _imported; }
|
||||
void setImported(bool imported) { _imported = imported; }
|
||||
|
||||
/**
|
||||
* @brief Adds credential to this connection
|
||||
*/
|
||||
@@ -84,12 +100,12 @@ namespace Robomongo
|
||||
* @brief Checks whether this connection has primary credential
|
||||
* which is also enabled.
|
||||
*/
|
||||
bool hasEnabledPrimaryCredential();
|
||||
bool hasEnabledPrimaryCredential() const;
|
||||
|
||||
/**
|
||||
* @brief Returns primary credential, or NULL if no credentials exists.
|
||||
* @brief Returns primary credential
|
||||
*/
|
||||
CredentialSettings *primaryCredential();
|
||||
CredentialSettings *primaryCredential() const;
|
||||
|
||||
/**
|
||||
* @brief Returns number of credentials
|
||||
@@ -124,34 +140,42 @@ namespace Robomongo
|
||||
return _connectionName;
|
||||
}
|
||||
|
||||
mongo::HostAndPort info() const {
|
||||
// If it doesn't look like IPv6 address,
|
||||
// treat it like IPv4 or literal hostname
|
||||
if (_host.find(':') == std::string::npos) {
|
||||
return mongo::HostAndPort(_host, _port);
|
||||
}
|
||||
mongo::HostAndPort hostAndPort() const;
|
||||
SshSettings *sshSettings() const { return _sshSettings.get(); }
|
||||
SslSettings *sslSettings() const { return _sslSettings.get(); }
|
||||
ReplicaSetSettings *replicaSetSettings() const { return _replicaSetSettings.get(); }
|
||||
|
||||
// The following code assumes, that it is IPv6 address
|
||||
// If address contains square brackets ("["), remove them:
|
||||
std::string hostCopy = _host;
|
||||
if (_host.find('[') != std::string::npos) {
|
||||
boost::erase_all(hostCopy, "[");
|
||||
boost::erase_all(hostCopy, "]");
|
||||
}
|
||||
bool isReplicaSet() const { return _isReplicaSet; }
|
||||
void setReplicaSet(bool flag) { _isReplicaSet = flag; }
|
||||
|
||||
return mongo::HostAndPort(hostCopy, _port);
|
||||
}
|
||||
|
||||
SshSettings *sshSettings() const { return _sshSettings; }
|
||||
void setUniqueId(int id) { _uniqueId = id; }
|
||||
int uniqueId() { return _uniqueId; }
|
||||
|
||||
private:
|
||||
CredentialSettings *findCredential(const std::string &databaseName) const;
|
||||
|
||||
std::string _connectionName;
|
||||
std::string _host;
|
||||
int _port;
|
||||
std::string _defaultDatabase;
|
||||
QList<CredentialSettings *> _credentials;
|
||||
SshSettings *_sshSettings;
|
||||
mutable QList<CredentialSettings *> _credentials;
|
||||
std::unique_ptr<SshSettings> _sshSettings;
|
||||
std::unique_ptr<SslSettings> _sslSettings;
|
||||
bool _isReplicaSet;
|
||||
std::unique_ptr<ReplicaSetSettings> _replicaSetSettings;
|
||||
|
||||
// Was this connection imported from somewhere?
|
||||
bool _imported;
|
||||
|
||||
// Flag to check if this is a clone(copy) or original ConnectionSettings
|
||||
// Note: If this is not a clone connection settings, this object is original connection
|
||||
// ConnectionSettings object which is loaded/saved into Robomongo config. file.
|
||||
bool _clone = false;
|
||||
|
||||
// If this is a clone connection settings, unique ID will be used to identify from which
|
||||
// original connection settings this object is cloned.
|
||||
// -1 for invalid(uninitialized) unique ID which should not be seen in theory
|
||||
int _uniqueId = -1;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace Robomongo
|
||||
std::string _userName;
|
||||
std::string _userPassword;
|
||||
std::string _databaseName;
|
||||
std::string _mechanism; // authentication mechanism
|
||||
std::string _mechanism; // authentication mechanism (MONGODB-CR or SCRAM-SHA-1)
|
||||
|
||||
/**
|
||||
* @brief Flag, indecating whether we should use this
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
#include "robomongo/core/settings/ReplicaSetSettings.h"
|
||||
|
||||
#include "robomongo/core/utils/QtUtils.h"
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
|
||||
ReplicaSetSettings::ReplicaSetSettings()
|
||||
: _setName(""), _members(), _readPreference(ReadPreference::PRIMARY)
|
||||
{}
|
||||
|
||||
ReplicaSetSettings::ReplicaSetSettings(const mongo::MongoURI& uri)
|
||||
: ReplicaSetSettings()
|
||||
{
|
||||
_setName = uri.getSetName();
|
||||
for (auto const& server : uri.getServers()) {
|
||||
_members.push_back(server.host() + ":" + std::to_string(server.port()));
|
||||
}
|
||||
}
|
||||
|
||||
ReplicaSetSettings *ReplicaSetSettings::clone() const
|
||||
{
|
||||
ReplicaSetSettings *cloned = new ReplicaSetSettings(*this);
|
||||
return cloned;
|
||||
}
|
||||
|
||||
QVariant ReplicaSetSettings::toVariant() const
|
||||
{
|
||||
QVariantMap map;
|
||||
map.insert("setName", QString::fromStdString(_setName));
|
||||
int idx = 0;
|
||||
for (std::string const& str : _members) {
|
||||
map.insert(QString::number(idx), QtUtils::toQString(str));
|
||||
++idx;
|
||||
}
|
||||
map.insert("readPreference", static_cast<int>(readPreference()));
|
||||
return map;
|
||||
}
|
||||
|
||||
void ReplicaSetSettings::fromVariant(const QVariantMap &map)
|
||||
{
|
||||
setSetName(map.value("setName").toString().toStdString());
|
||||
// Extract and set replica members
|
||||
std::vector<std::string> vec;
|
||||
auto itr = map.begin();
|
||||
int idx = 0;
|
||||
do
|
||||
{
|
||||
itr = map.find(QString::number(idx));
|
||||
if (map.end() == itr) break;
|
||||
vec.push_back(itr->toString().toStdString());
|
||||
++idx;
|
||||
} while (map.end() != itr);
|
||||
setMembers(vec);
|
||||
// Extract and set read reference
|
||||
setReadPreference(static_cast<ReadPreference>(map.value("readPreference").toInt()));
|
||||
}
|
||||
|
||||
void ReplicaSetSettings::setMembers(const std::vector<std::string>& members)
|
||||
{
|
||||
_members.clear();
|
||||
for (auto const& member : members)
|
||||
_members.push_back(member);
|
||||
}
|
||||
|
||||
void ReplicaSetSettings::setMembers(const std::vector<std::pair<std::string, bool>>& membersAndHealts)
|
||||
{
|
||||
_members.clear();
|
||||
|
||||
for (auto const& memberAndHealth : membersAndHealts)
|
||||
_members.push_back(memberAndHealth.first);
|
||||
}
|
||||
|
||||
std::vector<mongo::HostAndPort> ReplicaSetSettings::membersToHostAndPort() const
|
||||
{
|
||||
std::vector<mongo::HostAndPort> membersHostAndPort;
|
||||
for (auto const& member : _members)
|
||||
membersHostAndPort.push_back(mongo::HostAndPort(member));
|
||||
|
||||
return membersHostAndPort;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
#include <QVariantMap>
|
||||
|
||||
#include <mongo/util/net/hostandport.h>
|
||||
#include <mongo/client/mongo_uri.h>
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
class ReplicaSetSettings
|
||||
{
|
||||
public:
|
||||
enum class ReadPreference
|
||||
{
|
||||
PRIMARY = 0,
|
||||
PRIMARY_PREFERRED = 1
|
||||
// todo: Add all supported read preferences
|
||||
};
|
||||
|
||||
ReplicaSetSettings();
|
||||
|
||||
ReplicaSetSettings(const mongo::MongoURI& uri);
|
||||
|
||||
/**
|
||||
* Clones Replica Set settings.
|
||||
*/
|
||||
ReplicaSetSettings* clone() const;
|
||||
|
||||
/**
|
||||
* Converts to QVariantMap
|
||||
*/
|
||||
QVariant toVariant() const;
|
||||
void fromVariant(const QVariantMap &map);
|
||||
|
||||
// Getters
|
||||
std::string const& setName() { return _setName; }
|
||||
std::vector<std::string> const& members() const { return _members; }
|
||||
std::vector<mongo::HostAndPort> membersToHostAndPort() const;
|
||||
|
||||
std::set<mongo::HostAndPort> membersToHostAndPortAsSet() const
|
||||
{
|
||||
auto const& members = membersToHostAndPort();
|
||||
return std::set<mongo::HostAndPort>(members.begin(), members.end());
|
||||
}
|
||||
|
||||
ReadPreference readPreference() const { return _readPreference; }
|
||||
|
||||
// Setters
|
||||
void setSetName(const std::string& setName) { _setName = setName; }
|
||||
void setMembers(const std::vector<std::string>& members);
|
||||
void setMembers(const std::vector<std::pair<std::string,bool>>& membersAndHealts);
|
||||
void deleteAllMembers() { _members.clear(); }
|
||||
void setReadPreference(ReadPreference readPreference) { _readPreference = readPreference; }
|
||||
|
||||
|
||||
private:
|
||||
std::string _setName;
|
||||
std::vector<std::string> _members;
|
||||
ReadPreference _readPreference = ReadPreference::PRIMARY;
|
||||
};
|
||||
}
|
||||
@@ -7,7 +7,11 @@
|
||||
#include <serializer.h>
|
||||
|
||||
#include "robomongo/core/settings/ConnectionSettings.h"
|
||||
#include "robomongo/core/settings/CredentialSettings.h"
|
||||
#include "robomongo/core/settings/SshSettings.h"
|
||||
#include "robomongo/core/settings/SslSettings.h"
|
||||
#include "robomongo/core/utils/Logger.h"
|
||||
#include "robomongo/core/utils/QtUtils.h"
|
||||
#include "robomongo/core/utils/StdUtils.h"
|
||||
#include "robomongo/gui/AppStyle.h"
|
||||
|
||||
@@ -22,17 +26,45 @@ namespace
|
||||
* @brief Config file absolute path
|
||||
* (usually: /home/user/.config/robomongo/robomongo.json)
|
||||
*/
|
||||
const QString _configPath = QString("%1/.config/robomongo/0.9/robomongo.json").arg(QDir::homePath());
|
||||
const auto _configPath = QString("%1/.config/robomongo/1.0/robomongo.json").arg(QDir::homePath());
|
||||
|
||||
/**
|
||||
* @brief Config file containing directory path
|
||||
* (usually: /home/user/.config/robomongo)
|
||||
*/
|
||||
const QString _configDir = QString("%1/.config/robomongo/0.9").arg(QDir::homePath());
|
||||
const auto _configDir = QString("%1/.config/robomongo/1.0").arg(QDir::homePath());
|
||||
}
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
int SettingsManager::_uniqueIdCounter = 0;
|
||||
|
||||
/**
|
||||
* @brief Robomongo config. files
|
||||
*/
|
||||
const auto CONFIG_FILE_0_8_5 = QString("%1/.config/robomongo/robomongo.json").arg(QDir::homePath());
|
||||
const auto CONFIG_FILE_0_9 = QString("%1/.config/robomongo/0.9/robomongo.json").arg(QDir::homePath());
|
||||
const auto CONFIG_FILE_1_0 = QString("%1/.config/robomongo/1.0/robomongo.json").arg(QDir::homePath());
|
||||
|
||||
struct ConfigFileAndImportFunction
|
||||
{
|
||||
ConfigFileAndImportFunction(QString const& configFile, std::function<bool()> importFunction)
|
||||
: file(configFile), import(importFunction) {}
|
||||
|
||||
QString file;
|
||||
std::function<bool()> import;
|
||||
};
|
||||
|
||||
// Warning: Config. file and import function of a new release must be placed as first element of
|
||||
// vector initializer list below in order.
|
||||
std::vector<ConfigFileAndImportFunction> const SettingsManager::_configFilesAndImportFunctions
|
||||
{
|
||||
ConfigFileAndImportFunction(CONFIG_FILE_0_9, SettingsManager::importConnectionsFrom_0_9_to_1_0),
|
||||
ConfigFileAndImportFunction(CONFIG_FILE_0_8_5, SettingsManager::importConnectionsFrom_0_8_5_to_0_9)
|
||||
};
|
||||
|
||||
std::vector<ConnectionSettings*> SettingsManager::_connections;
|
||||
|
||||
/**
|
||||
* Creates SettingsManager for config file in default location
|
||||
* ~/.config/robomongo/robomongo.json
|
||||
@@ -43,20 +75,28 @@ namespace Robomongo
|
||||
_timeZone(Utc),
|
||||
_viewMode(Robomongo::Tree),
|
||||
_autocompletionMode(AutocompleteAll),
|
||||
_batchSize(50),
|
||||
_loadMongoRcJs(false),
|
||||
_minimizeToTray(false),
|
||||
_lineNumbers(false),
|
||||
_disableConnectionShortcuts(false),
|
||||
_batchSize(50),
|
||||
_textFontFamily(""),
|
||||
_textFontPointSize(-1),
|
||||
_lineNumbers(false),
|
||||
_loadMongoRcJs(false)
|
||||
_mongoTimeoutSec(10),
|
||||
_shellTimeoutSec(15),
|
||||
_imported(false)
|
||||
{
|
||||
load();
|
||||
if (!load()) { // if load fails (probably due to non-existing config. file or directory)
|
||||
save(); // create empty settings file
|
||||
load(); // try loading again for the purpose of import from previous Robomongo versions
|
||||
}
|
||||
|
||||
LOG_MSG("SettingsManager initialized in " + _configPath, mongo::logger::LogSeverity::Info(), false);
|
||||
}
|
||||
|
||||
SettingsManager::~SettingsManager()
|
||||
{
|
||||
std::for_each(_connections.begin() ,_connections.end(), stdutils::default_delete<ConnectionSettings *>());
|
||||
std::for_each(_connections.begin(), _connections.end(), stdutils::default_delete<ConnectionSettings *>());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,7 +165,7 @@ namespace Robomongo
|
||||
if (encoding > 3 || encoding < 0)
|
||||
encoding = 0;
|
||||
|
||||
_uuidEncoding = (UUIDEncoding) encoding;
|
||||
_uuidEncoding = (UUIDEncoding)encoding;
|
||||
|
||||
|
||||
// 3. Load view mode
|
||||
@@ -133,26 +173,24 @@ namespace Robomongo
|
||||
int viewMode = map.value("viewMode").toInt();
|
||||
if (viewMode > 2 || viewMode < 0)
|
||||
viewMode = Custom; // Default View Mode
|
||||
_viewMode = (ViewMode) viewMode;
|
||||
} else {
|
||||
_viewMode = (ViewMode)viewMode;
|
||||
}
|
||||
else {
|
||||
_viewMode = Custom; // Default View Mode
|
||||
}
|
||||
|
||||
_autoExpand = map.contains("autoExpand") ?
|
||||
map.value("autoExpand").toBool() : true;
|
||||
|
||||
_autoExec = map.contains("autoExec") ?
|
||||
map.value("autoExec").toBool() : true;
|
||||
|
||||
_lineNumbers = map.contains("lineNumbers") ?
|
||||
map.value("lineNumbers").toBool() : false;
|
||||
_autoExpand = map.contains("autoExpand") ? map.value("autoExpand").toBool() : true;
|
||||
_autoExec = map.contains("autoExec") ? map.value("autoExec").toBool() : true;
|
||||
_minimizeToTray = map.contains("minimizeToTray") ? map.value("minimizeToTray").toBool() : false;
|
||||
_lineNumbers = map.contains("lineNumbers") ? map.value("lineNumbers").toBool() : false;
|
||||
_imported = map.contains("imported") ? map.value("imported").toBool() : false;
|
||||
|
||||
// 4. Load TimeZone
|
||||
int timeZone = map.value("timeZone").toInt();
|
||||
if (timeZone > 1 || timeZone < 0)
|
||||
timeZone = 0;
|
||||
|
||||
_timeZone = (SupportedTimes) timeZone;
|
||||
_timeZone = (SupportedTimes)timeZone;
|
||||
_loadMongoRcJs = map.value("loadMongoRcJs").toBool();
|
||||
_disableConnectionShortcuts = map.value("disableConnectionShortcuts").toBool();
|
||||
|
||||
@@ -161,8 +199,9 @@ namespace Robomongo
|
||||
int autocompletionMode = map.value("autocompletionMode").toInt();
|
||||
if (autocompletionMode < 0 || autocompletionMode > 2)
|
||||
autocompletionMode = AutocompleteAll; // Default Mode
|
||||
_autocompletionMode = (AutocompletionMode) autocompletionMode;
|
||||
} else {
|
||||
_autocompletionMode = (AutocompletionMode)autocompletionMode;
|
||||
}
|
||||
else {
|
||||
_autocompletionMode = AutocompleteAll; // Default Mode
|
||||
}
|
||||
|
||||
@@ -170,6 +209,7 @@ namespace Robomongo
|
||||
_batchSize = map.value("batchSize").toInt();
|
||||
if (_batchSize == 0)
|
||||
_batchSize = 50;
|
||||
|
||||
_currentStyle = map.value("style").toString();
|
||||
if (_currentStyle.isEmpty()) {
|
||||
_currentStyle = AppStyle::StyleName;
|
||||
@@ -179,32 +219,47 @@ namespace Robomongo
|
||||
_textFontFamily = map.value("textFontFamily").toString();
|
||||
_textFontPointSize = map.value("textFontPointSize").toInt();
|
||||
|
||||
if (map.contains("mongoTimeoutSec")) {
|
||||
_mongoTimeoutSec = map.value("mongoTimeoutSec").toInt();
|
||||
}
|
||||
|
||||
if (map.contains("shellTimeoutSec")) {
|
||||
_shellTimeoutSec = map.value("shellTimeoutSec").toInt();
|
||||
}
|
||||
|
||||
// 5. Load connections
|
||||
_connections.clear();
|
||||
|
||||
QVariantList list = map.value("connections").toList();
|
||||
for (QVariantList::iterator it = list.begin(); it != list.end(); ++it) {
|
||||
ConnectionSettings *record = new ConnectionSettings();
|
||||
record->fromVariant((*it).toMap());
|
||||
_connections.push_back(record);
|
||||
auto connSettings = new ConnectionSettings(false);
|
||||
connSettings->fromVariant((*it).toMap());
|
||||
addConnection(connSettings);
|
||||
}
|
||||
|
||||
_toolbars = map.value("toolbars").toMap();
|
||||
ToolbarSettingsContainerType::const_iterator it = _toolbars.find("connect");
|
||||
if (_toolbars.end() == it)
|
||||
_toolbars["connect"] = true;
|
||||
|
||||
it = _toolbars.find("open_save");
|
||||
if (_toolbars.end() == it)
|
||||
_toolbars["open_save"] = true;
|
||||
|
||||
it = _toolbars.find("exec");
|
||||
if (_toolbars.end() == it)
|
||||
_toolbars["exec"] = true;
|
||||
|
||||
it = _toolbars.find("explorer");
|
||||
if (_toolbars.end() == it)
|
||||
_toolbars["explorer"] = true;
|
||||
|
||||
it = _toolbars.find("logs");
|
||||
if (_toolbars.end() == it)
|
||||
_toolbars["logs"] = false;
|
||||
|
||||
// Load connection settings from previous versions of Robomongo
|
||||
importConnections();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -239,6 +294,8 @@ namespace Robomongo
|
||||
|
||||
// 8. Save batchSize
|
||||
map.insert("batchSize", _batchSize);
|
||||
map.insert("mongoTimeoutSec", _mongoTimeoutSec);
|
||||
map.insert("shellTimeoutSec", _shellTimeoutSec);
|
||||
|
||||
// 9. Save style
|
||||
map.insert("style", _currentStyle);
|
||||
@@ -250,26 +307,27 @@ namespace Robomongo
|
||||
// 11. Save connections
|
||||
QVariantList list;
|
||||
|
||||
for (ConnectionSettingsContainerType::const_iterator it = _connections.begin(); it!=_connections.end(); ++it) {
|
||||
for (ConnectionSettingsContainerType::const_iterator it = _connections.begin(); it != _connections.end(); ++it) {
|
||||
QVariantMap rm = (*it)->toVariant().toMap();
|
||||
list.append(rm);
|
||||
}
|
||||
|
||||
map.insert("connections", list);
|
||||
|
||||
map.insert("autoExec", _autoExec);
|
||||
|
||||
map.insert("minimizeToTray", _minimizeToTray);
|
||||
map.insert("toolbars", _toolbars);
|
||||
map.insert("imported", _imported);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds connection to the end of list
|
||||
* Adds connection to the end of list and set it's uniqueID
|
||||
*/
|
||||
void SettingsManager::addConnection(ConnectionSettings *connection)
|
||||
{
|
||||
_connections.push_back(connection);
|
||||
connection->setUniqueId(_uniqueIdCounter++);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -277,13 +335,24 @@ namespace Robomongo
|
||||
*/
|
||||
void SettingsManager::removeConnection(ConnectionSettings *connection)
|
||||
{
|
||||
ConnectionSettingsContainerType::iterator it = std::find(_connections.begin(),_connections.end(),connection);
|
||||
if (it!=_connections.end()) {
|
||||
ConnectionSettingsContainerType::iterator it = std::find(_connections.begin(), _connections.end(), connection);
|
||||
if (it != _connections.end()) {
|
||||
_connections.erase(it);
|
||||
delete connection;
|
||||
}
|
||||
}
|
||||
|
||||
ConnectionSettings* SettingsManager::getConnectionSettings(int uniqueId)
|
||||
{
|
||||
for (auto const& connSettings : _connections){
|
||||
if (connSettings->uniqueId() == uniqueId)
|
||||
return connSettings;
|
||||
}
|
||||
|
||||
LOG_MSG("Failed to find connection settings object by unique ID.", mongo::logger::LogSeverity::Warning());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SettingsManager::setCurrentStyle(const QString& style)
|
||||
{
|
||||
_currentStyle = style;
|
||||
@@ -308,4 +377,167 @@ namespace Robomongo
|
||||
_toolbars[toolbarName] = visible;
|
||||
}
|
||||
|
||||
void SettingsManager::importConnections()
|
||||
{
|
||||
// Skip when settings already imported
|
||||
if (_imported)
|
||||
return;
|
||||
|
||||
// Find and import 'only' from the latest version
|
||||
for (auto const& configFileAndImportFunction : _configFilesAndImportFunctions) {
|
||||
if (QFile::exists(configFileAndImportFunction.file)) {
|
||||
configFileAndImportFunction.import();
|
||||
setImported(true); // Mark as imported
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SettingsManager::importConnectionsFrom_0_8_5_to_0_9()
|
||||
{
|
||||
// Load old configuration file (used till version 0.8.5)
|
||||
|
||||
if (!QFile::exists(CONFIG_FILE_0_8_5))
|
||||
return false;
|
||||
|
||||
QFile oldConfigFile(CONFIG_FILE_0_8_5);
|
||||
if (!oldConfigFile.open(QIODevice::ReadOnly))
|
||||
return false;
|
||||
|
||||
bool ok;
|
||||
QJson::Parser parser;
|
||||
QVariantMap vmap = parser.parse(oldConfigFile.readAll(), &ok).toMap();
|
||||
if (!ok)
|
||||
return false;
|
||||
|
||||
QVariantList vconns = vmap.value("connections").toList();
|
||||
for (QVariantList::iterator itconn = vconns.begin(); itconn != vconns.end(); ++itconn)
|
||||
{
|
||||
QVariantMap vconn = (*itconn).toMap();
|
||||
|
||||
auto conn = new ConnectionSettings(false);
|
||||
conn->setImported(true);
|
||||
conn->setConnectionName(QtUtils::toStdString(vconn.value("connectionName").toString()));
|
||||
conn->setServerHost(QtUtils::toStdString(vconn.value("serverHost").toString().left(300)));
|
||||
conn->setServerPort(vconn.value("serverPort").toInt());
|
||||
conn->setDefaultDatabase(QtUtils::toStdString(vconn.value("defaultDatabase").toString()));
|
||||
|
||||
// SSH settings
|
||||
if (vconn.contains("sshAuthMethod")) {
|
||||
SshSettings *ssh = conn->sshSettings();
|
||||
ssh->setHost(QtUtils::toStdString(vconn.value("sshHost").toString()));
|
||||
ssh->setUserName(QtUtils::toStdString(vconn.value("sshUserName").toString()));
|
||||
ssh->setPort(vconn.value("sshPort").toInt());
|
||||
ssh->setUserPassword(QtUtils::toStdString(vconn.value("sshUserPassword").toString()));
|
||||
ssh->setPublicKeyFile(QtUtils::toStdString(vconn.value("sshPublicKey").toString()));
|
||||
ssh->setPrivateKeyFile(QtUtils::toStdString(vconn.value("sshPrivateKey").toString()));
|
||||
ssh->setPassphrase(QtUtils::toStdString(vconn.value("sshPassphrase").toString()));
|
||||
|
||||
int auth = vconn.value("sshAuthMethod").toInt();
|
||||
ssh->setEnabled(auth == 1 || auth == 2);
|
||||
ssh->setAuthMethod(auth == 2 ? "publickey" : "password");
|
||||
}
|
||||
|
||||
// SSL settings
|
||||
if (vconn.contains("sshEnabled")) {
|
||||
SslSettings *ssl = conn->sslSettings();
|
||||
ssl->enableSSL(vconn.value("enabled").toBool());
|
||||
ssl->setPemKeyFile(QtUtils::toStdString(vconn.value("sslPemKeyFile").toString()));
|
||||
}
|
||||
|
||||
// Credentials
|
||||
QVariantList vcreds = vconn.value("credentials").toList();
|
||||
for (QVariantList::const_iterator itcred = vcreds.begin(); itcred != vcreds.end(); ++itcred) {
|
||||
QVariantMap vcred = (*itcred).toMap();
|
||||
|
||||
auto cred = new CredentialSettings();
|
||||
cred->setUserName(QtUtils::toStdString(vcred.value("userName").toString()));
|
||||
cred->setUserPassword(QtUtils::toStdString(vcred.value("userPassword").toString()));
|
||||
cred->setDatabaseName(QtUtils::toStdString(vcred.value("databaseName").toString()));
|
||||
cred->setMechanism("MONGODB-CR");
|
||||
cred->setEnabled(vcred.value("enabled").toBool());
|
||||
|
||||
conn->addCredential(cred);
|
||||
}
|
||||
|
||||
// Check that we didn't have similar connection
|
||||
bool matched = false;
|
||||
for (std::vector<ConnectionSettings*>::const_iterator it = _connections.begin(); it != _connections.end(); ++it) {
|
||||
ConnectionSettings *econn = *it; // Existing connection
|
||||
|
||||
if (conn->serverPort() != econn->serverPort() ||
|
||||
conn->serverHost() != econn->serverHost() ||
|
||||
conn->defaultDatabase() != econn->defaultDatabase())
|
||||
continue;
|
||||
|
||||
CredentialSettings *cred = conn->primaryCredential();
|
||||
CredentialSettings *ecred = econn->primaryCredential();
|
||||
if (cred->databaseName() != ecred->databaseName() ||
|
||||
cred->userName() != ecred->userName() ||
|
||||
cred->userPassword() != ecred->userPassword() ||
|
||||
cred->enabled() != ecred->enabled())
|
||||
continue;
|
||||
|
||||
SshSettings *ssh = conn->sshSettings();
|
||||
SshSettings *essh = econn->sshSettings();
|
||||
if (ssh->enabled() != essh->enabled() ||
|
||||
ssh->port() != essh->port() ||
|
||||
ssh->host() != essh->host() ||
|
||||
ssh->privateKeyFile() != essh->privateKeyFile() ||
|
||||
ssh->userPassword() != essh->userPassword() ||
|
||||
ssh->userName() != essh->userName())
|
||||
continue;
|
||||
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Import connection only if we didn't find similar one
|
||||
if (!matched)
|
||||
addConnection(conn);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SettingsManager::importConnectionsFrom_0_9_to_1_0()
|
||||
{
|
||||
if (!QFile::exists(CONFIG_FILE_0_9))
|
||||
return false;
|
||||
|
||||
QFile oldConfigFile(CONFIG_FILE_0_9);
|
||||
if (!oldConfigFile.open(QIODevice::ReadOnly))
|
||||
return false;
|
||||
|
||||
bool ok;
|
||||
QJson::Parser parser;
|
||||
QVariantMap vmap = parser.parse(oldConfigFile.readAll(), &ok).toMap();
|
||||
if (!ok)
|
||||
return false;
|
||||
|
||||
QVariantList const& vconns = vmap.value("connections").toList();
|
||||
for (auto const& vcon : vconns)
|
||||
{
|
||||
QVariantMap const& vconn = vcon.toMap();
|
||||
auto connSettings = new ConnectionSettings(false);
|
||||
connSettings->fromVariant(vconn);
|
||||
connSettings->setImported(true);
|
||||
|
||||
addConnection(connSettings);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int SettingsManager::importedConnectionsCount() {
|
||||
int count = 0;
|
||||
for (std::vector<ConnectionSettings*>::const_iterator it = _connections.begin(); it != _connections.end(); ++it) {
|
||||
ConnectionSettings *conn = *it;
|
||||
if (conn->imported())
|
||||
++count;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
namespace Robomongo
|
||||
{
|
||||
class ConnectionSettings;
|
||||
struct ConfigFileAndImportFunction;
|
||||
|
||||
/**
|
||||
* @brief SettingsManager gives you access to all settings, that is used
|
||||
* by Robomongo. It can load() and save() them. Config file usually
|
||||
@@ -23,6 +25,7 @@ namespace Robomongo
|
||||
public:
|
||||
typedef std::vector<ConnectionSettings *> ConnectionSettingsContainerType;
|
||||
typedef QMap<QString, QVariant> ToolbarSettingsContainerType;
|
||||
|
||||
/**
|
||||
* @brief Creates SettingsManager for config file in default location
|
||||
* (usually ~/.config/robomongo/robomongo.json)
|
||||
@@ -50,13 +53,22 @@ namespace Robomongo
|
||||
* @brief Adds connection to the end of list.
|
||||
* Connection now will be owned by SettingsManager.
|
||||
*/
|
||||
void addConnection(ConnectionSettings *connection);
|
||||
static void addConnection(ConnectionSettings *connection);
|
||||
|
||||
/**
|
||||
* @brief Removes connection by index
|
||||
*/
|
||||
void removeConnection(ConnectionSettings *connection);
|
||||
|
||||
/**
|
||||
* @brief Finds and returns original (non-clone) connection settings which is
|
||||
* loaded/saved from/into Robomongo config. file.
|
||||
* @param uniqueID : Unique ID for an original connection settings.
|
||||
* @return If uniqueID is valid returns original connection settings,
|
||||
* nullptr otherwise.
|
||||
*/
|
||||
ConnectionSettings* getConnectionSettings(int uniqueId);
|
||||
|
||||
void reorderConnections(const ConnectionSettingsContainerType &connections);
|
||||
|
||||
void setToolbarSettings(QString toolbarName, bool visible);
|
||||
@@ -72,7 +84,7 @@ namespace Robomongo
|
||||
UUIDEncoding uuidEncoding() const { return _uuidEncoding; }
|
||||
|
||||
void setTimeZone(SupportedTimes timeZ) { _timeZone = timeZ; }
|
||||
SupportedTimes timeZone() const { return _timeZone;}
|
||||
SupportedTimes timeZone() const { return _timeZone; }
|
||||
|
||||
void setViewMode(ViewMode viewMode) { _viewMode = viewMode; }
|
||||
ViewMode viewMode() const { return _viewMode; }
|
||||
@@ -86,6 +98,9 @@ namespace Robomongo
|
||||
void setAutoExec(bool isAutoExec) { _autoExec = isAutoExec; }
|
||||
bool autoExec() const { return _autoExec; }
|
||||
|
||||
void setMinimizeToTray(bool isMinimizingToTray) { _minimizeToTray = isMinimizingToTray; }
|
||||
bool minimizeToTray() const { return _minimizeToTray; }
|
||||
|
||||
void setLineNumbers(bool showLineNumbers) { _lineNumbers = showLineNumbers; }
|
||||
bool lineNumbers() const { return _lineNumbers; }
|
||||
|
||||
@@ -98,7 +113,7 @@ namespace Robomongo
|
||||
void setBatchSize(int batchSize) { _batchSize = batchSize; }
|
||||
int batchSize() const { return _batchSize; }
|
||||
|
||||
QString currentStyle() const {return _currentStyle; }
|
||||
QString currentStyle() const { return _currentStyle; }
|
||||
void setCurrentStyle(const QString& style);
|
||||
|
||||
QString textFontFamily() const { return _textFontFamily; }
|
||||
@@ -107,47 +122,79 @@ namespace Robomongo
|
||||
int textFontPointSize() const { return _textFontPointSize; }
|
||||
void setTextFontPointSize(int pointSize);
|
||||
|
||||
int mongoTimeoutSec() const { return _mongoTimeoutSec; }
|
||||
int shellTimeoutSec() const { return _shellTimeoutSec; }
|
||||
|
||||
// True when settings from previous versions of
|
||||
// Robomongo are imported
|
||||
void setImported(bool imported) { _imported = imported; }
|
||||
bool imported() const { return _imported; }
|
||||
|
||||
/**
|
||||
* Returns number of imported connections
|
||||
*/
|
||||
int importedConnectionsCount();
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Load settings from the map. Existings settings will be overwritten.
|
||||
* Load settings from the map. Existing settings will be overwritten.
|
||||
*/
|
||||
void loadFromMap(QVariantMap &map);
|
||||
|
||||
/**
|
||||
* @brief Save all settings to map.
|
||||
* Save all settings to map.
|
||||
*/
|
||||
QVariantMap convertToMap() const;
|
||||
|
||||
/**
|
||||
* Load connection settings from previous versions of Robomongo
|
||||
*/
|
||||
void importConnections();
|
||||
static bool importConnectionsFrom_0_8_5_to_0_9();
|
||||
static bool importConnectionsFrom_0_9_to_1_0();
|
||||
|
||||
/**
|
||||
* @brief Version of settings schema currently loaded
|
||||
*/
|
||||
QString _version;
|
||||
|
||||
/**
|
||||
* @brief UUID encoding
|
||||
*/
|
||||
UUIDEncoding _uuidEncoding;
|
||||
SupportedTimes _timeZone;
|
||||
/**
|
||||
* @brief view mode
|
||||
*/
|
||||
ViewMode _viewMode;
|
||||
AutocompletionMode _autocompletionMode;
|
||||
bool _loadMongoRcJs;
|
||||
bool _autoExpand;
|
||||
bool _autoExec;
|
||||
bool _minimizeToTray;
|
||||
bool _lineNumbers;
|
||||
bool _disableConnectionShortcuts;
|
||||
int _batchSize;
|
||||
QString _currentStyle;
|
||||
QString _textFontFamily;
|
||||
int _textFontPointSize;
|
||||
|
||||
int _mongoTimeoutSec;
|
||||
int _shellTimeoutSec;
|
||||
|
||||
// True when settings from previous versions of
|
||||
// Robomongo are imported
|
||||
bool _imported;
|
||||
|
||||
/**
|
||||
* @brief List of connections
|
||||
*/
|
||||
ConnectionSettingsContainerType _connections;
|
||||
static std::vector<ConnectionSettings*> _connections;
|
||||
|
||||
ToolbarSettingsContainerType _toolbars;
|
||||
|
||||
// List of config. file absolute paths and related import functions.
|
||||
// Must be updated with care and with every new version. Details on cpp file.
|
||||
static const std::vector<ConfigFileAndImportFunction> _configFilesAndImportFunctions;
|
||||
|
||||
// Unique Id counter which is incremented only when a non-clone ConnectionSettings
|
||||
// object is created and assigned can be assigned to clone or non-clone
|
||||
// ConnectionSettings objects in order to identify source object ID for a clone.
|
||||
static int _uniqueIdCounter;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ namespace Robomongo
|
||||
SshSettings::SshSettings() :
|
||||
_port(22),
|
||||
_authMethod("publickey"),
|
||||
_enabled(false) {
|
||||
_enabled(false),
|
||||
_askPassword(false),
|
||||
_logLevel(1) {
|
||||
|
||||
}
|
||||
|
||||
@@ -27,6 +29,7 @@ namespace Robomongo
|
||||
map.insert("passphrase", QtUtils::toQString(passphrase()));
|
||||
map.insert("method", QtUtils::toQString(authMethod()));
|
||||
map.insert("enabled", enabled());
|
||||
map.insert("askPassword", askPassword());
|
||||
return map;
|
||||
}
|
||||
|
||||
@@ -40,5 +43,6 @@ namespace Robomongo
|
||||
setPassphrase(QtUtils::toStdString(map.value("passphrase").toString()));
|
||||
setAuthMethod(QtUtils::toStdString(map.value("method").toString()));
|
||||
setEnabled(map.value("enabled").toBool());
|
||||
setAskPassword(map.value("askPassword").toBool());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,9 @@ namespace Robomongo
|
||||
QVariant toVariant() const;
|
||||
void fromVariant(const QVariantMap &map);
|
||||
|
||||
/*
|
||||
* Domain or IPv4 or IPv6 address
|
||||
*/
|
||||
std::string host() const { return _host; }
|
||||
void setHost(const std::string &host) { _host = host; }
|
||||
|
||||
@@ -43,6 +46,7 @@ namespace Robomongo
|
||||
std::string passphrase() const { return _passphrase; }
|
||||
void setPassphrase(const std::string &passphrase) { _passphrase = passphrase; }
|
||||
|
||||
// "password" or "publickey"
|
||||
std::string authMethod() const { return _authMethod.empty() ? "publickey" : _authMethod; }
|
||||
void setAuthMethod(const std::string &method) { _authMethod = method; }
|
||||
|
||||
@@ -53,11 +57,17 @@ namespace Robomongo
|
||||
bool enabled() const { return _enabled; }
|
||||
void setEnabled(bool enabled) { _enabled = enabled; }
|
||||
|
||||
bool askPassword() const { return _askPassword; }
|
||||
void setAskPassword(bool ask) { _askPassword = ask; }
|
||||
|
||||
std::string askedPassword() const { return _askedPassword; }
|
||||
void setAskedPassword(const std::string &asked) { _askedPassword = asked; }
|
||||
|
||||
int logLevel() const { return _logLevel; }
|
||||
void setLogLevel(const int logLevel) { _logLevel = logLevel; }
|
||||
|
||||
private:
|
||||
std::string _host;
|
||||
std::string _host; // domain or IPv4/v6
|
||||
int _port;
|
||||
std::string _userName;
|
||||
std::string _userPassword;
|
||||
@@ -66,11 +76,16 @@ namespace Robomongo
|
||||
std::string _passphrase;
|
||||
std::string _authMethod; // "password" or "publickey"
|
||||
|
||||
// Should we ask user about password or passphrase
|
||||
// each time when we try to connect
|
||||
bool _askPassword;
|
||||
|
||||
/**
|
||||
* Flag, indicating whether we should use
|
||||
* this SSH tunnel or not.
|
||||
*/
|
||||
bool _enabled;
|
||||
int _logLevel; // this property is not persisted
|
||||
int _logLevel; // this property is not persisted
|
||||
std::string _askedPassword; // this property is not persisted
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
#include "robomongo/core/settings/SslSettings.h"
|
||||
|
||||
#include "robomongo/core/utils/QtUtils.h"
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
SslSettings::SslSettings() :
|
||||
_sslEnabled(false), _caFile(""), _pemKeyFile(""), _pemPassPhrase(""), _crlFile(""),
|
||||
_allowInvalidHostnames(false), _allowInvalidCertificates(false), _usePemFile(false),
|
||||
_useAdvancedOptions(false), _askPassphrase(false)
|
||||
{}
|
||||
|
||||
SslSettings *SslSettings::clone() const
|
||||
{
|
||||
SslSettings *cloned = new SslSettings(*this);
|
||||
return cloned;
|
||||
}
|
||||
|
||||
QVariant SslSettings::toVariant() const
|
||||
{
|
||||
QVariantMap map;
|
||||
map.insert("sslEnabled", sslEnabled());
|
||||
map.insert("caFile", QtUtils::toQString(caFile()));
|
||||
map.insert("usePemFile", usePemFile());
|
||||
map.insert("pemKeyFile", QtUtils::toQString(pemKeyFile()));
|
||||
map.insert("pemPassPhrase", QtUtils::toQString(pemPassPhrase()));
|
||||
map.insert("useAdvancedOptions", useAdvancedOptions());
|
||||
map.insert("crlFile", QtUtils::toQString(crlFile()));
|
||||
map.insert("allowInvalidHostnames", allowInvalidHostnames());
|
||||
map.insert("allowInvalidCertificates", allowInvalidCertificates());
|
||||
map.insert("askPassphrase", askPassphrase());
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
void SslSettings::fromVariant(const QVariantMap &map)
|
||||
{
|
||||
enableSSL(map.value("sslEnabled").toBool());
|
||||
setCaFile(QtUtils::toStdString(map.value("caFile").toString()));
|
||||
setUsePemFile(map.value("usePemFile").toBool());
|
||||
setPemKeyFile(QtUtils::toStdString(map.value("pemKeyFile").toString()));
|
||||
setPemPassPhrase(QtUtils::toStdString(map.value("pemPassPhrase").toString()));
|
||||
setUseAdvancedOptions(map.value("useAdvancedOptions").toBool());
|
||||
setCrlFile(QtUtils::toStdString(map.value("crlFile").toString()));
|
||||
setAllowInvalidHostnames(map.value("allowInvalidHostnames").toBool());
|
||||
setAllowInvalidCertificates(map.value("allowInvalidCertificates").toBool());
|
||||
setAskPassphrase(map.value("askPassphrase").toBool());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
#include <QVariantMap>
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
class SslSettings
|
||||
{
|
||||
public:
|
||||
SslSettings();
|
||||
|
||||
/**
|
||||
* Clones credential settings.
|
||||
*/
|
||||
SslSettings *clone() const;
|
||||
|
||||
/**
|
||||
* Converts to QVariantMap
|
||||
*/
|
||||
QVariant toVariant() const;
|
||||
void fromVariant(const QVariantMap &map);
|
||||
|
||||
|
||||
// Getters for mongo:: SSLGlobalParams related settings
|
||||
std::string pemKeyFile() const { return _pemKeyFile; }
|
||||
std::string caFile() const { return _caFile; }
|
||||
std::string pemPassPhrase() const { return _pemPassPhrase; }
|
||||
std::string crlFile() const { return _crlFile; }
|
||||
bool allowInvalidHostnames() const { return _allowInvalidHostnames; }
|
||||
bool allowInvalidCertificates() const { return _allowInvalidCertificates; }
|
||||
|
||||
// Getters for helper SSL settings
|
||||
bool usePemFile() const { return _usePemFile; }
|
||||
bool useAdvancedOptions() const { return _useAdvancedOptions; }
|
||||
bool askPassphrase() const { return _askPassphrase; }
|
||||
|
||||
// Setters for mongo:: SSLGlobalParams related settings
|
||||
void setPemKeyFile(const std::string &path) { _pemKeyFile = path; }
|
||||
void setCaFile(const std::string &path) { _caFile = path; }
|
||||
void setPemPassPhrase(const std::string &pemPassPhrase) { _pemPassPhrase = pemPassPhrase; }
|
||||
void setCrlFile(const std::string &path) { _crlFile = path; }
|
||||
void setAllowInvalidHostnames(const bool state) { _allowInvalidHostnames = state; }
|
||||
void setAllowInvalidCertificates(const bool state) { _allowInvalidCertificates = state; }
|
||||
|
||||
// Setters for helper SSL settings
|
||||
void setUsePemFile(const bool state) { _usePemFile = state; }
|
||||
void setUseAdvancedOptions(const bool state) { _useAdvancedOptions = state; }
|
||||
void setAskPassphrase(const bool state) { _askPassphrase = state; }
|
||||
|
||||
/**
|
||||
* Flag, indicating whether we should use
|
||||
* this SSL settings or not.
|
||||
*/
|
||||
bool sslEnabled() const { return _sslEnabled; }
|
||||
void enableSSL(bool enabled) { _sslEnabled = enabled; }
|
||||
|
||||
private:
|
||||
|
||||
// mongo:: SSL Global params related settings
|
||||
std::string _caFile;
|
||||
std::string _pemKeyFile;
|
||||
std::string _pemPassPhrase;
|
||||
std::string _crlFile;
|
||||
bool _allowInvalidHostnames;
|
||||
bool _allowInvalidCertificates;
|
||||
|
||||
// Helper settings indirectly effecting what to pass to SSL global params
|
||||
bool _usePemFile;
|
||||
bool _useAdvancedOptions;
|
||||
bool _askPassphrase;
|
||||
|
||||
/**
|
||||
* Flag, indicating whether SSL enabled for related connection
|
||||
*/
|
||||
bool _sslEnabled;
|
||||
};
|
||||
}
|
||||
@@ -80,23 +80,23 @@ namespace Robomongo
|
||||
s << (isArray ? "[" : "{");
|
||||
BSONObjIterator i(obj);
|
||||
BSONElement e = i.next();
|
||||
if ( !e.eoo() ){
|
||||
if ( !e.eoo() ) {
|
||||
while ( 1 ) {
|
||||
if ( pretty ) {
|
||||
s << '\n';
|
||||
for( int x = 0; x < pretty; x++ ){
|
||||
for( int x = 0; x < pretty; x++ ) {
|
||||
s << " ";
|
||||
}
|
||||
}
|
||||
else {
|
||||
s << " ";
|
||||
}
|
||||
s << jsonString(e, format, true, pretty?pretty+1:0, uuidEncoding, timeFormat, isArray);
|
||||
s << jsonString(e, format, true, pretty ? pretty + 1 : 0, uuidEncoding, timeFormat, isArray);
|
||||
e = i.next();
|
||||
|
||||
if (e.eoo()) {
|
||||
s << '\n';
|
||||
for( int x = 0; x < pretty - 1; x++ ){
|
||||
for( int x = 0; x < pretty - 1; x++ ) {
|
||||
s << " ";
|
||||
}
|
||||
s << (isArray ? "]" : "}");
|
||||
@@ -130,15 +130,21 @@ namespace Robomongo
|
||||
s << "NumberLong(" << elem._numberLong() << ")";
|
||||
break;
|
||||
case NumberInt:
|
||||
s << elem._numberInt();
|
||||
break;
|
||||
case NumberDouble:
|
||||
{
|
||||
int sign=0;
|
||||
int sign = 0;
|
||||
if ( elem.number() >= -std::numeric_limits< double >::max() &&
|
||||
elem.number() <= std::numeric_limits< double >::max() ) {
|
||||
|
||||
// was 16
|
||||
s.precision(std::numeric_limits<double>::digits10);
|
||||
s << elem.number();
|
||||
|
||||
// Leave trailing zero if needed
|
||||
if (elem.number() == (long long)elem.number())
|
||||
s << ".0";
|
||||
}
|
||||
else if (std::isnan(elem.number()) ) {
|
||||
s << "NaN";
|
||||
@@ -166,7 +172,7 @@ namespace Robomongo
|
||||
break;
|
||||
case Object: {
|
||||
BSONObj obj = elem.embeddedObject();
|
||||
s << jsonString(obj, format, pretty, uuidEncoding,timeFormat);
|
||||
s << jsonString(obj, format, pretty, uuidEncoding, timeFormat);
|
||||
}
|
||||
break;
|
||||
case mongo::Array: {
|
||||
@@ -180,7 +186,7 @@ namespace Robomongo
|
||||
if ( !e.eoo() ) {
|
||||
int count = 0;
|
||||
while ( 1 ) {
|
||||
if( pretty ) {
|
||||
if ( pretty ) {
|
||||
s << '\n';
|
||||
for( int x = 0; x < pretty; x++ )
|
||||
s << " ";
|
||||
@@ -190,7 +196,7 @@ namespace Robomongo
|
||||
s << "undefined";
|
||||
}
|
||||
else {
|
||||
s << jsonString(e, format, false, pretty?pretty+1:0, uuidEncoding, timeFormat, true);
|
||||
s << jsonString(e, format, false, pretty ? pretty + 1 : 0, uuidEncoding, timeFormat, true);
|
||||
e = i.next();
|
||||
}
|
||||
count++;
|
||||
@@ -260,13 +266,13 @@ namespace Robomongo
|
||||
case mongo::Date:
|
||||
{
|
||||
Date_t d = elem.date();
|
||||
long long ms = d.toMillisSinceEpoch();//static_cast<long long>(d.millis);
|
||||
long long ms = d.toMillisSinceEpoch(); //static_cast<long long>(d.millis);
|
||||
bool isSupportedDate = miutil::minDate < ms && ms < miutil::maxDate;
|
||||
|
||||
if ( format == Strict )
|
||||
s << "{ \"$date\" : ";
|
||||
else{
|
||||
if(isSupportedDate){
|
||||
if (isSupportedDate) {
|
||||
s << "ISODate(";
|
||||
}
|
||||
else{
|
||||
@@ -275,7 +281,7 @@ namespace Robomongo
|
||||
}
|
||||
|
||||
if ( pretty && isSupportedDate) {
|
||||
boost::posix_time::ptime epoch(boost::gregorian::date(1970,1,1));
|
||||
boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1));
|
||||
boost::posix_time::time_duration diff = boost::posix_time::millisec(ms);
|
||||
boost::posix_time::ptime time = epoch + diff;
|
||||
std::string timestr = miutil::isotimeString(time, true, timeFormat == LocalTime);
|
||||
@@ -563,13 +569,13 @@ namespace Robomongo
|
||||
con.append(e.fieldName());
|
||||
con.append("\"");
|
||||
con.append(" : ");
|
||||
buildJsonString(e,con,uuid,tz);
|
||||
buildJsonString(e, con, uuid, tz);
|
||||
con.append(", \n");
|
||||
}
|
||||
con.append("\n}\n\n");
|
||||
}
|
||||
|
||||
void buildJsonString(const mongo::BSONElement &elem,std::string &con, UUIDEncoding uuid, SupportedTimes tz)
|
||||
void buildJsonString(const mongo::BSONElement &elem, std::string &con, UUIDEncoding uuid, SupportedTimes tz)
|
||||
{
|
||||
switch (elem.type())
|
||||
{
|
||||
@@ -578,6 +584,11 @@ namespace Robomongo
|
||||
std::stringstream s;
|
||||
s.precision(std::numeric_limits<double>::digits10);
|
||||
s << elem.Double();
|
||||
|
||||
// Leave trailing zero if needed
|
||||
if (elem.Double() == (long long)elem.Double())
|
||||
s << ".0";
|
||||
|
||||
con.append(s.str());
|
||||
}
|
||||
break;
|
||||
@@ -626,13 +637,13 @@ namespace Robomongo
|
||||
long long ms = (long long) elem.Date().toMillisSinceEpoch();
|
||||
bool isSupportedDate = miutil::minDate < ms && ms < miutil::maxDate;
|
||||
|
||||
boost::posix_time::ptime epoch(boost::gregorian::date(1970,1,1));
|
||||
boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1));
|
||||
boost::posix_time::time_duration diff = boost::posix_time::millisec(ms);
|
||||
boost::posix_time::ptime time = epoch + diff;
|
||||
|
||||
std::string date;
|
||||
if(isSupportedDate)
|
||||
date = miutil::isotimeString(time,false,tz==LocalTime);
|
||||
if (isSupportedDate)
|
||||
date = miutil::isotimeString(time, false, tz == LocalTime);
|
||||
else
|
||||
date = boost::lexical_cast<std::string>(ms);
|
||||
|
||||
@@ -652,7 +663,7 @@ namespace Robomongo
|
||||
case 'g':
|
||||
case 'i':
|
||||
case 'm':
|
||||
con+=*f;
|
||||
con += *f;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -678,15 +689,15 @@ namespace Robomongo
|
||||
break;
|
||||
case NumberInt:
|
||||
{
|
||||
char num[16]={0};
|
||||
sprintf(num,"%d",elem.Int());
|
||||
char num[16] = {0};
|
||||
sprintf(num, "%d", elem.Int());
|
||||
con.append(num);
|
||||
break;
|
||||
}
|
||||
case bsonTimestamp:
|
||||
{
|
||||
Date_t date = elem.timestampTime();
|
||||
unsigned long long millis = date.toMillisSinceEpoch();// millis;
|
||||
unsigned long long millis = date.toMillisSinceEpoch(); // millis;
|
||||
if ((long long)millis >= 0 &&
|
||||
((long long)millis/1000) < (std::numeric_limits<time_t>::max)()) {
|
||||
con.append(date.toString());
|
||||
@@ -695,8 +706,8 @@ namespace Robomongo
|
||||
}
|
||||
case NumberLong:
|
||||
{
|
||||
char num[32]={0};
|
||||
sprintf(num,"%lld",elem.Long());
|
||||
char num[32] = {0};
|
||||
sprintf(num, "%lld", elem.Long());
|
||||
con.append(num);
|
||||
break;
|
||||
}
|
||||
@@ -706,13 +717,13 @@ namespace Robomongo
|
||||
}
|
||||
}
|
||||
|
||||
mongo::BSONElement indexOf(const mongo::BSONObj &doc,int index)
|
||||
mongo::BSONElement indexOf(const mongo::BSONObj &doc, int index)
|
||||
{
|
||||
mongo::BSONObjIterator iterator(doc);
|
||||
for (int i = 0; iterator.more(); ++i)
|
||||
{
|
||||
mongo::BSONElement element = iterator.next();
|
||||
if(i==index){
|
||||
if (i == index) {
|
||||
return element;
|
||||
}
|
||||
}
|
||||
@@ -722,7 +733,7 @@ namespace Robomongo
|
||||
int elementsCount(const mongo::BSONObj &doc)
|
||||
{
|
||||
mongo::BSONObjIterator iterator(doc);
|
||||
int i=0;
|
||||
int i = 0;
|
||||
for (; iterator.more(); ++i)
|
||||
{
|
||||
iterator.next();
|
||||
|
||||
@@ -100,8 +100,8 @@ namespace Robomongo
|
||||
|
||||
const char* BSONTypeToString(mongo::BSONType type, mongo::BinDataType binDataType, UUIDEncoding uuidEncoding);
|
||||
|
||||
void buildJsonString(const mongo::BSONObj &obj,std::string &con, UUIDEncoding uuid, SupportedTimes tz);
|
||||
void buildJsonString(const mongo::BSONElement &elem,std::string &con, UUIDEncoding uuid, SupportedTimes tz);
|
||||
void buildJsonString(const mongo::BSONObj &obj, std::string &con, UUIDEncoding uuid, SupportedTimes tz);
|
||||
void buildJsonString(const mongo::BSONElement &elem, std::string &con, UUIDEncoding uuid, SupportedTimes tz);
|
||||
|
||||
mongo::BSONElement indexOf(const mongo::BSONObj &doc, int index);
|
||||
int elementsCount(const mongo::BSONObj &doc);
|
||||
|
||||
@@ -2,13 +2,16 @@
|
||||
|
||||
#include <QDir>
|
||||
#include <QMetaType>
|
||||
|
||||
#include "robomongo/core/utils/QtUtils.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
std::string getLoggerPath()
|
||||
{
|
||||
static std::string path = Robomongo::QtUtils::toStdString(QString("%1/" PROJECT_NAME_LOWERCASE ".log").arg(QDir::tempPath()));
|
||||
static std::string path =
|
||||
Robomongo::QtUtils::toStdString(QString("%1/" PROJECT_NAME_LOWERCASE ".log").arg(QDir::tempPath()));
|
||||
|
||||
return path;
|
||||
}
|
||||
}
|
||||
@@ -46,7 +49,15 @@ namespace Robomongo
|
||||
{
|
||||
// v0.9
|
||||
// LOG(level) << "[" PROJECT_NAME_TITLE "] " << QtUtils::toStdString(mess) << std::endl;
|
||||
if (notify)
|
||||
emit printed(mess, level);
|
||||
if (notify) {
|
||||
// Make uniform log level strings e.g "Error: ", "Info: " etc...
|
||||
auto logLevelStr = QString::fromStdString(level.toStringData().toString());
|
||||
if (!logLevelStr.isEmpty()) {
|
||||
logLevelStr = logLevelStr.toLower();
|
||||
logLevelStr[0] = logLevelStr[0].toUpper();
|
||||
logLevelStr += ": ";
|
||||
}
|
||||
emit printed(logLevelStr + mess, level);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace Robomongo
|
||||
|
||||
void cleanUpThread(QThread *const thread)
|
||||
{
|
||||
if (thread&&thread->isRunning()) {
|
||||
if (thread && thread->isRunning()) {
|
||||
//thread->stop();
|
||||
thread->wait();
|
||||
}
|
||||
|
||||
@@ -20,6 +20,6 @@ namespace Robomongo
|
||||
static const QString StyleName;
|
||||
virtual void drawControl(ControlElement element, const QStyleOption * option, QPainter * painter, const QWidget * widget) const;
|
||||
virtual void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const;
|
||||
virtual QRect subElementRect( SubElement element, const QStyleOption * option, const QWidget * widget=0 ) const;
|
||||
virtual QRect subElementRect( SubElement element, const QStyleOption * option, const QWidget * widget = 0 ) const;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -38,6 +38,36 @@ namespace Robomongo
|
||||
return serverIc;
|
||||
}
|
||||
|
||||
const QIcon &GuiRegistry::serverImportedIcon() const
|
||||
{
|
||||
static const QIcon importedIcon = QIcon(":/robomongo/icons/server_imported_16x16.png");
|
||||
return importedIcon;
|
||||
}
|
||||
|
||||
const QIcon &GuiRegistry::serverPrimaryIcon() const
|
||||
{
|
||||
static const QIcon icon(":/robomongo/icons/server_primary_16x16.png");
|
||||
return icon;
|
||||
}
|
||||
|
||||
const QIcon &GuiRegistry::serverSecondaryIcon() const
|
||||
{
|
||||
static const QIcon icon(":/robomongo/icons/server_secondary_16x16.png");
|
||||
return icon;
|
||||
}
|
||||
|
||||
const QIcon &GuiRegistry::replicaSetIcon() const
|
||||
{
|
||||
static const QIcon replicaSetIc(":/robomongo/icons/replica_set_16x16.png");
|
||||
return replicaSetIc;
|
||||
}
|
||||
|
||||
const QIcon &GuiRegistry::replicaSetOfflineIcon() const
|
||||
{
|
||||
static const QIcon icon(":/robomongo/icons/replica_set_offline_16x16.png");
|
||||
return icon;
|
||||
}
|
||||
|
||||
const QIcon &GuiRegistry::openIcon() const
|
||||
{
|
||||
static const QIcon openIc = qApp->style()->standardIcon(QStyle::SP_DialogOpenButton);
|
||||
@@ -84,14 +114,26 @@ namespace Robomongo
|
||||
|
||||
const QIcon &GuiRegistry::maximizeIcon() const
|
||||
{
|
||||
static const QIcon maximizeIc = QIcon(":/robomongo/icons/maximize.gif");
|
||||
static const QIcon maximizeIc(":/robomongo/icons/maximize.png");
|
||||
return maximizeIc;
|
||||
}
|
||||
|
||||
const QIcon &GuiRegistry::maximizeHighlightedIcon() const
|
||||
const QIcon &GuiRegistry::minimizeIcon() const
|
||||
{
|
||||
static const QIcon maximizeHighlightedIc = QIcon(":/robomongo/icons/maximize_highlighted_16x16.png");
|
||||
return maximizeHighlightedIc;
|
||||
static const QIcon minimizeIc(":/robomongo/icons/minimize.png");
|
||||
return minimizeIc;
|
||||
}
|
||||
|
||||
const QIcon &GuiRegistry::undockIcon() const
|
||||
{
|
||||
static const QIcon undockIc(":/robomongo/icons/undock.png");
|
||||
return undockIc;
|
||||
}
|
||||
|
||||
const QIcon &GuiRegistry::dockIcon() const
|
||||
{
|
||||
static const QIcon dockIc(":/robomongo/icons/dock.png");
|
||||
return dockIc;
|
||||
}
|
||||
|
||||
const QIcon &GuiRegistry::textIcon() const
|
||||
@@ -223,16 +265,28 @@ namespace Robomongo
|
||||
|
||||
const QIcon &GuiRegistry::noMarkIcon() const
|
||||
{
|
||||
static const QIcon noMarkIc = QIcon(":/robomongo/icons/no_mark_16x16.png");
|
||||
static const QIcon noMarkIc = QIcon(":/robomongo/icons/no_mark_24x24.png");
|
||||
return noMarkIc;
|
||||
}
|
||||
|
||||
const QIcon &GuiRegistry::yesMarkIcon() const
|
||||
{
|
||||
static const QIcon yesMarkIc = QIcon(":/robomongo/icons/yes_mark_16x16.png");
|
||||
static const QIcon yesMarkIc = QIcon(":/robomongo/icons/yes_mark_24x24.png");
|
||||
return yesMarkIc;
|
||||
}
|
||||
|
||||
const QIcon &GuiRegistry::skipMarkIcon() const
|
||||
{
|
||||
static const QIcon skipMarkIc = QIcon(":/robomongo/icons/skip_mark_24x24.png");
|
||||
return skipMarkIc;
|
||||
}
|
||||
|
||||
const QIcon &GuiRegistry::questionMarkIcon() const
|
||||
{
|
||||
static const QIcon questionMarkIc = QIcon(":/robomongo/icons/question_mark_24x24.png");
|
||||
return questionMarkIc;
|
||||
}
|
||||
|
||||
const QIcon &GuiRegistry::timeIcon() const
|
||||
{
|
||||
static const QIcon timeIc = QIcon(":/robomongo/icons/time_16x16.png");
|
||||
@@ -245,9 +299,33 @@ namespace Robomongo
|
||||
return keyIc;
|
||||
}
|
||||
|
||||
const QIcon &GuiRegistry::showIcon() const
|
||||
{
|
||||
static const QIcon icon = QIcon(":/robomongo/icons/show_64x64.png");
|
||||
return icon;
|
||||
}
|
||||
|
||||
const QIcon &GuiRegistry::hideIcon() const
|
||||
{
|
||||
static const QIcon icon = QIcon(":/robomongo/icons/hide_64x64.png");
|
||||
return icon;
|
||||
}
|
||||
|
||||
const QIcon &GuiRegistry::plusIcon() const
|
||||
{
|
||||
static const QIcon icon {":/robomongo/icons/plus_sign.png"};
|
||||
return icon;
|
||||
}
|
||||
|
||||
const QIcon &GuiRegistry::minusIcon() const
|
||||
{
|
||||
static const QIcon icon {":/robomongo/icons/minus_sign.png"};
|
||||
return icon;
|
||||
}
|
||||
|
||||
const QBrush &GuiRegistry::typeBrush() const
|
||||
{
|
||||
static const QBrush typeBrush = QBrush(QColor(150,150, 150));
|
||||
static const QBrush typeBrush = QBrush(QColor(150, 150, 150));
|
||||
return typeBrush;
|
||||
}
|
||||
|
||||
@@ -269,6 +347,12 @@ namespace Robomongo
|
||||
static const QIcon mongodbIc = QIcon(":/robomongo/icons/mongodb_16x16.png");
|
||||
return mongodbIc;
|
||||
}
|
||||
|
||||
const QIcon &GuiRegistry::mongodbIconForMAC() const
|
||||
{
|
||||
static const QIcon mongodbIc = QIcon(":/robomongo/icons/mongodb_icon_for_MAC.png");
|
||||
return mongodbIc;
|
||||
}
|
||||
|
||||
const QIcon &GuiRegistry::connectIcon() const
|
||||
{
|
||||
@@ -288,6 +372,18 @@ namespace Robomongo
|
||||
return stopIc;
|
||||
}
|
||||
|
||||
const QIcon &GuiRegistry::exportIcon() const
|
||||
{
|
||||
static const QIcon exportIc = QIcon(":/robomongo/icons/export_64x64.png");
|
||||
return exportIc;
|
||||
}
|
||||
|
||||
const QIcon &GuiRegistry::importIcon() const
|
||||
{
|
||||
static const QIcon importIc = QIcon(":/robomongo/icons/import_64x64.png");
|
||||
return importIc;
|
||||
}
|
||||
|
||||
const QIcon &GuiRegistry::mainWindowIcon() const
|
||||
{
|
||||
static const QIcon mainWindowIc = QIcon(":/robomongo/icons/logo-256x256.png");
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Robomongo
|
||||
/**
|
||||
* @brief Returns single instance of GuiRegistry
|
||||
*/
|
||||
static GuiRegistry &instance()
|
||||
static GuiRegistry& instance()
|
||||
{
|
||||
static GuiRegistry _instance;
|
||||
return _instance;
|
||||
@@ -28,52 +28,68 @@ namespace Robomongo
|
||||
/**
|
||||
* @brief Functions that provide access to various icons
|
||||
*/
|
||||
const QIcon &serverIcon() const;
|
||||
const QIcon &saveIcon() const;
|
||||
const QIcon &openIcon() const;
|
||||
const QIcon &databaseIcon() const;
|
||||
const QIcon &collectionIcon() const;
|
||||
const QIcon &indexIcon() const;
|
||||
const QIcon &userIcon() const;
|
||||
const QIcon &functionIcon() const;
|
||||
const QIcon &maximizeIcon() const;
|
||||
const QIcon &maximizeHighlightedIcon() const;
|
||||
const QIcon &textIcon() const;
|
||||
const QIcon &textHighlightedIcon() const;
|
||||
const QIcon &treeIcon() const;
|
||||
const QIcon &treeHighlightedIcon() const;
|
||||
const QIcon &tableIcon() const;
|
||||
const QIcon &tableHighlightedIcon() const;
|
||||
const QIcon &customIcon() const;
|
||||
const QIcon &customHighlightedIcon() const;
|
||||
const QIcon &rotateIcon() const;
|
||||
const QIcon &visualIcon() const;
|
||||
const QIcon &folderIcon() const;
|
||||
const QIcon &circleIcon() const;
|
||||
const QIcon &leftIcon() const;
|
||||
const QIcon &rightIcon() const;
|
||||
const QIcon &mongodbIcon() const;
|
||||
const QIcon &connectIcon() const;
|
||||
const QIcon &executeIcon() const;
|
||||
const QIcon &stopIcon() const;
|
||||
const QIcon &mainWindowIcon() const;
|
||||
const QIcon &bsonObjectIcon() const;
|
||||
const QIcon &bsonArrayIcon() const;
|
||||
const QIcon &bsonStringIcon() const;
|
||||
const QIcon &bsonIntegerIcon() const;
|
||||
const QIcon &bsonDoubleIcon() const;
|
||||
const QIcon &bsonDateTimeIcon() const;
|
||||
const QIcon &bsonBinaryIcon() const;
|
||||
const QIcon &bsonNullIcon() const;
|
||||
const QIcon &bsonBooleanIcon() const;
|
||||
const QIcon &noMarkIcon() const;
|
||||
const QIcon &yesMarkIcon() const;
|
||||
const QIcon &timeIcon() const;
|
||||
const QIcon &keyIcon() const;
|
||||
const QIcon& serverIcon() const;
|
||||
const QIcon& serverImportedIcon() const;
|
||||
const QIcon& serverPrimaryIcon() const;
|
||||
const QIcon& serverSecondaryIcon() const;
|
||||
const QIcon& replicaSetIcon() const;
|
||||
const QIcon& replicaSetOfflineIcon() const;
|
||||
const QIcon& saveIcon() const;
|
||||
const QIcon& openIcon() const;
|
||||
const QIcon& databaseIcon() const;
|
||||
const QIcon& collectionIcon() const;
|
||||
const QIcon& indexIcon() const;
|
||||
const QIcon& userIcon() const;
|
||||
const QIcon& functionIcon() const;
|
||||
const QIcon& maximizeIcon() const;
|
||||
const QIcon& minimizeIcon() const;
|
||||
const QIcon& undockIcon() const;
|
||||
const QIcon& dockIcon() const;
|
||||
const QIcon& textIcon() const;
|
||||
const QIcon& textHighlightedIcon() const;
|
||||
const QIcon& treeIcon() const;
|
||||
const QIcon& treeHighlightedIcon() const;
|
||||
const QIcon& tableIcon() const;
|
||||
const QIcon& tableHighlightedIcon() const;
|
||||
const QIcon& customIcon() const;
|
||||
const QIcon& customHighlightedIcon() const;
|
||||
const QIcon& rotateIcon() const;
|
||||
const QIcon& visualIcon() const;
|
||||
const QIcon& folderIcon() const;
|
||||
const QIcon& circleIcon() const;
|
||||
const QIcon& leftIcon() const;
|
||||
const QIcon& rightIcon() const;
|
||||
const QIcon& mongodbIcon() const;
|
||||
const QIcon& mongodbIconForMAC() const;
|
||||
const QIcon& connectIcon() const;
|
||||
const QIcon& executeIcon() const;
|
||||
const QIcon& stopIcon() const;
|
||||
const QIcon& exportIcon() const;
|
||||
const QIcon& importIcon() const;
|
||||
const QIcon& mainWindowIcon() const;
|
||||
const QIcon& bsonObjectIcon() const;
|
||||
const QIcon& bsonArrayIcon() const;
|
||||
const QIcon& bsonStringIcon() const;
|
||||
const QIcon& bsonIntegerIcon() const;
|
||||
const QIcon& bsonDoubleIcon() const;
|
||||
const QIcon& bsonDateTimeIcon() const;
|
||||
const QIcon& bsonBinaryIcon() const;
|
||||
const QIcon& bsonNullIcon() const;
|
||||
const QIcon& bsonBooleanIcon() const;
|
||||
const QIcon& noMarkIcon() const;
|
||||
const QIcon& yesMarkIcon() const;
|
||||
const QIcon& skipMarkIcon() const;
|
||||
const QIcon& questionMarkIcon() const;
|
||||
const QIcon& timeIcon() const;
|
||||
const QIcon& keyIcon() const;
|
||||
const QIcon& showIcon() const;
|
||||
const QIcon& hideIcon() const;
|
||||
const QIcon& plusIcon() const;
|
||||
const QIcon& minusIcon() const;
|
||||
|
||||
const QBrush &typeBrush() const;
|
||||
const QBrush& typeBrush() const;
|
||||
|
||||
const QFont &font() const;
|
||||
const QFont& font() const;
|
||||
private:
|
||||
/**
|
||||
* @brief Private, because this is singleton
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
#include <QLabel>
|
||||
#include <QStatusBar>
|
||||
#include <QHBoxLayout>
|
||||
#include <QSettings>
|
||||
#include <QSystemTrayIcon>
|
||||
|
||||
#include <mongo/logger/log_severity.h>
|
||||
#include "robomongo/core/settings/SettingsManager.h"
|
||||
@@ -29,13 +31,17 @@
|
||||
|
||||
#include "robomongo/gui/widgets/LogWidget.h"
|
||||
#include "robomongo/gui/widgets/explorer/ExplorerWidget.h"
|
||||
#include "robomongo/gui/widgets/explorer/ExplorerCollectionTreeItem.h"
|
||||
#include "robomongo/gui/widgets/explorer/ExplorerTreeWidget.h"
|
||||
#include "robomongo/gui/widgets/workarea/WorkAreaTabWidget.h"
|
||||
#include "robomongo/gui/widgets/workarea/QueryWidget.h"
|
||||
#include "robomongo/gui/dialogs/ConnectionsDialog.h"
|
||||
#include "robomongo/gui/dialogs/AboutDialog.h"
|
||||
#include "robomongo/gui/dialogs/PreferencesDialog.h"
|
||||
#include "robomongo/gui/dialogs/ExportDialog.h"
|
||||
#include "robomongo/gui/GuiRegistry.h"
|
||||
#include "robomongo/gui/AppStyle.h"
|
||||
#include "robomongo/gui/widgets/workarea/QueryWidget.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -67,21 +73,30 @@ namespace
|
||||
Robomongo::AppRegistry::instance().settingsManager()->save();
|
||||
}
|
||||
|
||||
void saveMinimizeToTraySettings(bool isMinimizingToTray)
|
||||
{
|
||||
Robomongo::AppRegistry::instance().settingsManager()->setMinimizeToTray(isMinimizingToTray);
|
||||
Robomongo::AppRegistry::instance().settingsManager()->save();
|
||||
}
|
||||
|
||||
void saveLineNumbers(bool showLineNumbers)
|
||||
{
|
||||
Robomongo::AppRegistry::instance().settingsManager()->setLineNumbers(showLineNumbers);
|
||||
Robomongo::AppRegistry::instance().settingsManager()->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
/* End of anonymous namespace */
|
||||
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
/* ------ ConnectionMenu ------ */
|
||||
class ConnectionMenu : public QMenu
|
||||
{
|
||||
public:
|
||||
ConnectionMenu(QWidget *parent) : QMenu(parent) {}
|
||||
protected:
|
||||
virtual void keyPressEvent(QKeyEvent *event)
|
||||
virtual void keyPressEvent(QKeyEvent *event) override
|
||||
{
|
||||
if (event->key() == Qt::Key_F12) {
|
||||
hide();
|
||||
@@ -92,12 +107,19 @@ namespace Robomongo
|
||||
}
|
||||
};
|
||||
|
||||
/* ------ MainWindow ------ */
|
||||
MainWindow::MainWindow()
|
||||
: BaseClass(),
|
||||
_app(AppRegistry::instance().app()),
|
||||
_workArea(NULL),
|
||||
_connectionsMenu(NULL)
|
||||
{
|
||||
_logDock(nullptr), _workArea(nullptr), _explorer(nullptr), _app(AppRegistry::instance().app()),
|
||||
_connectionsMenu(nullptr), _connectButton(nullptr), _viewMenu(nullptr), _toolbarsMenu(nullptr),
|
||||
_connectAction(nullptr), _openAction(nullptr), _saveAction(nullptr), _saveAsAction(nullptr),
|
||||
_executeAction(nullptr), _stopAction(nullptr), _orientationAction(nullptr), _execToolBar(nullptr),
|
||||
//_exportAction(nullptr), _importAction(nullptr), // Temporarily disabling export/import feature
|
||||
#if defined(Q_OS_WIN)
|
||||
_trayIcon(nullptr),
|
||||
#endif
|
||||
_allowExit(false)
|
||||
{
|
||||
QColor background = palette().window().color();
|
||||
QString controlKey = "Ctrl";
|
||||
|
||||
@@ -134,7 +156,7 @@ namespace Robomongo
|
||||
// Exit action
|
||||
QAction *exitAction = new QAction("&Exit", this);
|
||||
exitAction->setShortcuts(QKeySequence::Quit);
|
||||
VERIFY(connect(exitAction, SIGNAL(triggered()), this, SLOT(close())));
|
||||
VERIFY(connect(exitAction, SIGNAL(triggered()), this, SLOT(exit())));
|
||||
|
||||
// Connect action
|
||||
_connectAction = new QAction("&Connect...", this);
|
||||
@@ -165,6 +187,26 @@ namespace Robomongo
|
||||
QWidgetAction *connectButtonAction = new QWidgetAction(this);
|
||||
connectButtonAction->setDefaultWidget(_connectButton);
|
||||
|
||||
// Tray icon
|
||||
#if defined(Q_OS_WIN)
|
||||
_trayIcon = new QSystemTrayIcon(GuiRegistry::instance().mainWindowIcon());
|
||||
VERIFY(connect(_trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
|
||||
this, SLOT(trayActivated(QSystemTrayIcon::ActivationReason))));
|
||||
|
||||
auto trayMinimizeAction = new QAction("Minimize to Tray", _trayIcon);
|
||||
VERIFY(connect(trayMinimizeAction, SIGNAL(triggered()), this, SLOT(toggleMinimize())));
|
||||
|
||||
auto trayExitAction = new QAction("Exit", _trayIcon);
|
||||
VERIFY(connect(trayExitAction, SIGNAL(triggered()), this, SLOT(exit())));
|
||||
|
||||
auto contextMenu = new QMenu();
|
||||
contextMenu->addAction(trayMinimizeAction);
|
||||
contextMenu->addSeparator();
|
||||
contextMenu->addAction(trayExitAction);
|
||||
|
||||
_trayIcon->setContextMenu(contextMenu);
|
||||
#endif
|
||||
|
||||
// Orientation action
|
||||
_orientationAction = new QAction("&Rotate", this);
|
||||
_orientationAction->setShortcut(Qt::Key_F10);
|
||||
@@ -176,7 +218,7 @@ namespace Robomongo
|
||||
ViewMode viewMode = AppRegistry::instance().settingsManager()->viewMode();
|
||||
|
||||
// Text mode action
|
||||
QAction *textModeAction = new QAction("&Text Mode", this);
|
||||
auto textModeAction = new QAction("&Text Mode", this);
|
||||
textModeAction->setShortcut(Qt::Key_F4);
|
||||
textModeAction->setIcon(GuiRegistry::instance().textHighlightedIcon());
|
||||
textModeAction->setToolTip("Show current tab in text mode, and make this mode default for all subsequent queries <b>(F4)</b>");
|
||||
@@ -323,17 +365,17 @@ namespace Robomongo
|
||||
AutocompletionMode autocompletionMode = AppRegistry::instance().settingsManager()->autocompletionMode();
|
||||
|
||||
// Autocompletion
|
||||
QAction *autocompletionAllAction = new QAction("All",this);
|
||||
QAction *autocompletionAllAction = new QAction("All", this);
|
||||
autocompletionAllAction->setCheckable(true);
|
||||
autocompletionAllAction->setChecked(autocompletionMode == AutocompleteAll);
|
||||
VERIFY(connect(autocompletionAllAction, SIGNAL(triggered()), this, SLOT(setShellAutocompletionAll())));
|
||||
|
||||
QAction *autocompletionNoCollectionNamesAction = new QAction("All (Except Collection Names)",this);
|
||||
QAction *autocompletionNoCollectionNamesAction = new QAction("All (Except Collection Names)", this);
|
||||
autocompletionNoCollectionNamesAction->setCheckable(true);
|
||||
autocompletionNoCollectionNamesAction->setChecked(autocompletionMode == AutocompleteNoCollectionNames);
|
||||
VERIFY(connect(autocompletionNoCollectionNamesAction, SIGNAL(triggered()), this, SLOT(setShellAutocompletionNoCollectionNames())));
|
||||
|
||||
QAction *autocompletionNoneAction = new QAction("None",this);
|
||||
QAction *autocompletionNoneAction = new QAction("None", this);
|
||||
autocompletionNoneAction->setCheckable(true);
|
||||
autocompletionNoneAction->setChecked(autocompletionMode == AutocompleteNone);
|
||||
VERIFY(connect(autocompletionNoneAction, SIGNAL(triggered()), this, SLOT(setShellAutocompletionNone())));
|
||||
@@ -348,7 +390,7 @@ namespace Robomongo
|
||||
autocompletionGroup->addAction(autocompletionNoCollectionNamesAction);
|
||||
autocompletionGroup->addAction(autocompletionNoneAction);
|
||||
|
||||
QAction *loadMongoRcJs = new QAction("Load .mongorc.js",this);
|
||||
QAction *loadMongoRcJs = new QAction("Load .mongorc.js", this);
|
||||
loadMongoRcJs->setCheckable(true);
|
||||
loadMongoRcJs->setChecked(AppRegistry::instance().settingsManager()->loadMongoRcJs());
|
||||
VERIFY(connect(loadMongoRcJs, SIGNAL(triggered()), this, SLOT(setLoadMongoRcJs())));
|
||||
@@ -369,19 +411,27 @@ namespace Robomongo
|
||||
VERIFY(connect(showLineNumbers, SIGNAL(triggered()), this, SLOT(toggleLineNumbers())));
|
||||
optionsMenu->addAction(showLineNumbers);
|
||||
|
||||
QAction *disabelConnectionShortcuts = new QAction("Disable Connection Shortcuts",this);
|
||||
QAction *disabelConnectionShortcuts = new QAction("Disable Connection Shortcuts", this);
|
||||
disabelConnectionShortcuts->setCheckable(true);
|
||||
disabelConnectionShortcuts->setChecked(AppRegistry::instance().settingsManager()->disableConnectionShortcuts());
|
||||
VERIFY(connect(disabelConnectionShortcuts, SIGNAL(triggered()), this, SLOT(setDisableConnectionShortcuts())));
|
||||
optionsMenu->addAction(disabelConnectionShortcuts);
|
||||
|
||||
QAction *autoExec = new QAction(tr("Automatically execute code in new tab"),this);
|
||||
QAction *autoExec = new QAction(tr("Automatically execute code in new tab"), this);
|
||||
autoExec->setCheckable(true);
|
||||
autoExec->setChecked(AppRegistry::instance().settingsManager()->autoExec());
|
||||
VERIFY(connect(autoExec, SIGNAL(triggered()), this, SLOT(toggleAutoExec())));
|
||||
optionsMenu->addAction(autoExec);
|
||||
|
||||
QAction *preferencesAction = new QAction("Preferences",this);
|
||||
#if defined(Q_OS_WIN)
|
||||
QAction *minimizeTray = new QAction("Close button should minimize to system tray");
|
||||
minimizeTray->setCheckable(true);
|
||||
minimizeTray->setChecked(AppRegistry::instance().settingsManager()->minimizeToTray());
|
||||
VERIFY(connect(minimizeTray, SIGNAL(triggered()), this, SLOT(toggleMinimizeToTray())));
|
||||
optionsMenu->addAction(minimizeTray);
|
||||
#endif
|
||||
|
||||
QAction *preferencesAction = new QAction("Preferences", this);
|
||||
VERIFY(connect(preferencesAction, SIGNAL(triggered()), this, SLOT(openPreferences())));
|
||||
preferencesAction->setVisible(false);
|
||||
optionsMenu->addAction(preferencesAction);
|
||||
@@ -492,9 +542,31 @@ namespace Robomongo
|
||||
_toolbarsMenu->addAction(_execToolBar->toggleViewAction());
|
||||
VERIFY(connect(_execToolBar->toggleViewAction(), SIGNAL(triggered(bool)), this, SLOT(onExecToolbarVisibilityChanged(bool))));
|
||||
|
||||
/* --- Temporarily disabling export/import feature
|
||||
// Export/Import Toolbar
|
||||
auto expImpToolBar = new QToolBar(tr("Export/Import Toolbar"), this);
|
||||
expImpToolBar->setToolButtonStyle(Qt::ToolButtonIconOnly);
|
||||
expImpToolBar->setMovable(false);
|
||||
// Add export action
|
||||
_exportAction = new QAction(this);
|
||||
_exportAction->setData("Export");
|
||||
_exportAction->setIcon(GuiRegistry::instance().exportIcon());
|
||||
_exportAction->setDisabled(true);
|
||||
VERIFY(connect(_exportAction, SIGNAL(triggered()), this, SLOT(openExportDialog())));
|
||||
addToolBar(expImpToolBar);
|
||||
expImpToolBar->addAction(_exportAction);
|
||||
// Add import action
|
||||
_importAction = new QAction(this);
|
||||
_importAction->setData("Import");
|
||||
_importAction->setIcon(GuiRegistry::instance().importIcon());
|
||||
_importAction->setDisabled(true);
|
||||
addToolBar(expImpToolBar);
|
||||
expImpToolBar->addAction(_importAction);
|
||||
*/
|
||||
|
||||
createTabs();
|
||||
createStatusBar();
|
||||
setWindowTitle(PROJECT_NAME_TITLE " " PROJECT_VERSION);
|
||||
setWindowTitle("Robomongo 1.0-RC1");
|
||||
setWindowIcon(GuiRegistry::instance().mainWindowIcon());
|
||||
|
||||
QTimer::singleShot(0, this, SLOT(manageConnections()));
|
||||
@@ -504,6 +576,12 @@ namespace Robomongo
|
||||
AppRegistry::instance().bus()->subscribe(this, ScriptExecutedEvent::Type);
|
||||
AppRegistry::instance().bus()->subscribe(this, ScriptExecutingEvent::Type);
|
||||
AppRegistry::instance().bus()->subscribe(this, QueryWidgetUpdatedEvent::Type);
|
||||
AppRegistry::instance().bus()->subscribe(this, OperationFailedEvent::Type);
|
||||
|
||||
restoreWindowSettings();
|
||||
|
||||
// Catch application windows focus changes
|
||||
VERIFY(connect(qApp, SIGNAL(focusChanged(QWidget*, QWidget*)), this, SLOT(on_focusChanged())));
|
||||
}
|
||||
|
||||
void MainWindow::createStylesMenu()
|
||||
@@ -516,7 +594,7 @@ namespace Robomongo
|
||||
const QString ¤tStyle = AppRegistry::instance().settingsManager()->currentStyle();
|
||||
for (QStringList::const_iterator it = supportedStyles.begin(); it != supportedStyles.end(); ++it) {
|
||||
const QString &style = *it;
|
||||
QAction *styleAction = new QAction(style,this);
|
||||
QAction *styleAction = new QAction(style, this);
|
||||
styleAction->setCheckable(true);
|
||||
styleAction->setChecked(style == currentStyle);
|
||||
styleGroup->addAction(styleAction);
|
||||
@@ -565,18 +643,57 @@ namespace Robomongo
|
||||
AppRegistry::instance().settingsManager()->save();
|
||||
}
|
||||
|
||||
void MainWindow::exit()
|
||||
{
|
||||
_allowExit = true;
|
||||
close();
|
||||
}
|
||||
|
||||
void MainWindow::restoreWindowSettings()
|
||||
{
|
||||
QSettings settings("Paralect", "Robomongo");
|
||||
// Restore settings if registery key exists, otherwise resize as app started for the first time.
|
||||
if (settings.contains("MainWindow/geometry"))
|
||||
{
|
||||
restoreGeometry(settings.value("MainWindow/geometry").toByteArray());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Resize main window. We are trying to keep it "almost" maximized.
|
||||
QRect screenGeometry = QApplication::desktop()->availableGeometry();
|
||||
int horizontalMargin = static_cast<int>(screenGeometry.width() * 0.1);
|
||||
int verticalMargin = static_cast<int>(screenGeometry.height() * 0.1);
|
||||
int _width = screenGeometry.width() - horizontalMargin;
|
||||
int _height = screenGeometry.height() - verticalMargin;
|
||||
resize(QSize(_width, _height));
|
||||
|
||||
// Center main window
|
||||
int x = (screenGeometry.width() - width()) / 2;
|
||||
int y = (screenGeometry.height() - height()) / 2;
|
||||
move(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::saveWindowSettings() const
|
||||
{
|
||||
QSettings settings("Paralect", "Robomongo");
|
||||
settings.setValue("MainWindow/geometry", saveGeometry());
|
||||
}
|
||||
|
||||
void MainWindow::open()
|
||||
{
|
||||
QueryWidget *wid = _workArea->currentQueryWidget();
|
||||
if (wid) {
|
||||
wid->openFile();
|
||||
}
|
||||
else {
|
||||
SettingsManager::ConnectionSettingsContainerType connections = AppRegistry::instance().settingsManager()->connections();
|
||||
if (connections.size() == 1) {
|
||||
else { // todo: currently this case not handled properly, since "Open" button is
|
||||
// disabled when no tabs exist
|
||||
auto connectionsCopy = AppRegistry::instance().settingsManager()->connections();
|
||||
if (connectionsCopy.size() == 1) {
|
||||
ScriptInfo inf = ScriptInfo(QString());
|
||||
if (inf.loadFromFile()) {
|
||||
_app->openShell(connections.at(0), inf);
|
||||
// todo: for now do not open new shell
|
||||
_app->openShell(nullptr, connectionsCopy.at(0), inf);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -598,23 +715,13 @@ namespace Robomongo
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
if (event->key() == Qt::Key_F12) {
|
||||
_connectButton->showMenu();
|
||||
return;
|
||||
}
|
||||
|
||||
BaseClass::keyPressEvent(event);
|
||||
}
|
||||
|
||||
void MainWindow::updateConnectionsMenu()
|
||||
{
|
||||
_connectionsMenu->clear();
|
||||
int number = 1;
|
||||
// Populate list with connections
|
||||
SettingsManager::ConnectionSettingsContainerType connections = AppRegistry::instance().settingsManager()->connections();
|
||||
for(SettingsManager::ConnectionSettingsContainerType::const_iterator it = connections.begin(); it!= connections.end(); ++it) {
|
||||
for (SettingsManager::ConnectionSettingsContainerType::const_iterator it = connections.begin(); it != connections.end(); ++it) {
|
||||
ConnectionSettings *connection = *it;
|
||||
QAction *action = new QAction(QtUtils::toQString(connection->getReadableName()), this);
|
||||
action->setData(QVariant::fromValue(connection));
|
||||
@@ -641,8 +748,14 @@ namespace Robomongo
|
||||
|
||||
void MainWindow::manageConnections()
|
||||
{
|
||||
ConnectionsDialog dialog(AppRegistry::instance().settingsManager(), this);
|
||||
#if defined(Q_OS_WIN)
|
||||
_trayIcon->hide(); // hide the tray icon so the main window can't be hidden behind the connections dialog
|
||||
#endif
|
||||
|
||||
static bool checkForImported = true;
|
||||
ConnectionsDialog dialog(AppRegistry::instance().settingsManager(), checkForImported, this);
|
||||
int result = dialog.exec();
|
||||
checkForImported = false;
|
||||
|
||||
// save settings and update connection menu
|
||||
AppRegistry::instance().settingsManager()->save();
|
||||
@@ -663,13 +776,17 @@ namespace Robomongo
|
||||
}
|
||||
|
||||
try {
|
||||
_app->openServer(selected, true);
|
||||
_app->openServer(selected, ConnectionPrimary);
|
||||
} catch(const std::exception &) {
|
||||
QString message = QString("Cannot connect to MongoDB (%1)").arg(QtUtils::toQString(selected->getFullAddress()));
|
||||
QMessageBox::information(this, "Error", message);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
_trayIcon->show(); // show the tray icon once the connections dialog is gone
|
||||
#endif
|
||||
|
||||
// on linux focus is lost - we need to activate main window back
|
||||
activateWindow();
|
||||
}
|
||||
@@ -788,7 +905,7 @@ namespace Robomongo
|
||||
|
||||
void MainWindow::refreshConnections()
|
||||
{
|
||||
QToolTip::showText(QPoint(0,0),
|
||||
QToolTip::showText(QPoint(0, 0),
|
||||
QString("Refresh not working yet... : <br/> <b>Ctrl+D</b> : push Button"));
|
||||
}
|
||||
|
||||
@@ -804,6 +921,18 @@ namespace Robomongo
|
||||
dlg.exec();
|
||||
}
|
||||
|
||||
/* --- Temporarily disabling export/import feature
|
||||
void MainWindow::openExportDialog()
|
||||
{
|
||||
auto selectedItem = dynamic_cast<ExplorerCollectionTreeItem*>(_explorer->getSelectedTreeItem());
|
||||
auto dbName = QString::fromStdString(selectedItem->collection()->database()->name());
|
||||
auto collName = QString::fromStdString(selectedItem->collection()->name());
|
||||
|
||||
auto dialog = new ExportDialog(dbName, collName, this);
|
||||
dialog->show(); // show it mode-less so that user can perform multiple simultaneous exports
|
||||
}
|
||||
*/
|
||||
|
||||
void MainWindow::setDefaultUuidEncoding()
|
||||
{
|
||||
AppRegistry::instance().settingsManager()->setUuidEncoding(DefaultEncoding);
|
||||
@@ -883,7 +1012,7 @@ namespace Robomongo
|
||||
QVariant data = connectionAction->data();
|
||||
ConnectionSettings *ptr = data.value<ConnectionSettings *>();
|
||||
try {
|
||||
_app->openServer(ptr, true);
|
||||
_app->openServer(ptr, ConnectionPrimary);
|
||||
}
|
||||
catch(const std::exception &) {
|
||||
QString message = QString("Cannot connect to the MongoDB at %1.")
|
||||
@@ -894,7 +1023,18 @@ namespace Robomongo
|
||||
|
||||
void MainWindow::handle(ConnectionFailedEvent *event)
|
||||
{
|
||||
QMessageBox::information(this, "Error", QtUtils::toQString(event->message));
|
||||
// Handle only Primary connections
|
||||
if (event->connectionType != ConnectionPrimary)
|
||||
return;
|
||||
|
||||
// Very temporary solution to prevent multiple connection error messages
|
||||
// from both SshTunnelWorker and MongoWorker
|
||||
static int lastServerHandle = -1;
|
||||
if (event->serverHandle <= lastServerHandle)
|
||||
return;
|
||||
|
||||
lastServerHandle = event->serverHandle;
|
||||
QMessageBox::critical(this, "Error", QtUtils::toQString(event->message));
|
||||
}
|
||||
|
||||
void MainWindow::handle(ScriptExecutingEvent *)
|
||||
@@ -909,6 +1049,62 @@ namespace Robomongo
|
||||
_executeAction->setDisabled(false);
|
||||
}
|
||||
|
||||
void MainWindow::handle(OperationFailedEvent *event)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << event->userFriendlyErrorMessage << std::endl << std::endl
|
||||
<< "Error:" << std::endl << event->technicalErrorMessage;
|
||||
|
||||
QMessageBox::critical(NULL, "Operation failed", QtUtils::toQString(ss.str()));
|
||||
}
|
||||
|
||||
void MainWindow::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
if (event->key() == Qt::Key_F12) {
|
||||
_connectButton->showMenu();
|
||||
return;
|
||||
}
|
||||
|
||||
BaseClass::keyPressEvent(event);
|
||||
}
|
||||
|
||||
void MainWindow::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
saveWindowSettings();
|
||||
#if defined(Q_OS_WIN)
|
||||
if (AppRegistry::instance().settingsManager()->minimizeToTray() && !_allowExit) {
|
||||
event->ignore();
|
||||
hide(); // hide the window because it can be reopened with the tray icon
|
||||
}
|
||||
else {
|
||||
if (_trayIcon->isVisible())
|
||||
_trayIcon->hide();
|
||||
|
||||
QMainWindow::closeEvent(event);
|
||||
}
|
||||
#else
|
||||
QMainWindow::closeEvent(event);
|
||||
#endif
|
||||
}
|
||||
|
||||
void MainWindow::hideEvent(QHideEvent *event)
|
||||
{
|
||||
#if defined(Q_OS_WIN)
|
||||
if (_trayIcon->contextMenu()->actions().size() > 0 && isHidden()) {
|
||||
_trayIcon->contextMenu()->actions().at(0)->setText("Show Robomongo");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void MainWindow::showEvent(QShowEvent *event)
|
||||
{
|
||||
#if defined(Q_OS_WIN)
|
||||
if (_trayIcon->contextMenu()->actions().size() > 0) {
|
||||
_trayIcon->contextMenu()->actions().at(0)->setText("Minimize to Tray");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void MainWindow::handle(QueryWidgetUpdatedEvent *event)
|
||||
{
|
||||
_orientationAction->setEnabled(event->numOfResults() > 1);
|
||||
@@ -916,14 +1112,14 @@ namespace Robomongo
|
||||
|
||||
void MainWindow::createDatabaseExplorer()
|
||||
{
|
||||
ExplorerWidget *explorer = new ExplorerWidget(this);
|
||||
AppRegistry::instance().bus()->subscribe(explorer, ConnectingEvent::Type);
|
||||
AppRegistry::instance().bus()->subscribe(explorer, ConnectionFailedEvent::Type);
|
||||
AppRegistry::instance().bus()->subscribe(explorer, ConnectionEstablishedEvent::Type);
|
||||
_explorer = new ExplorerWidget(this);
|
||||
AppRegistry::instance().bus()->subscribe(_explorer, ConnectingEvent::Type);
|
||||
AppRegistry::instance().bus()->subscribe(_explorer, ConnectionFailedEvent::Type);
|
||||
AppRegistry::instance().bus()->subscribe(_explorer, ConnectionEstablishedEvent::Type);
|
||||
|
||||
QDockWidget *explorerDock = new QDockWidget(tr("Database Explorer"));
|
||||
explorerDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
|
||||
explorerDock->setWidget(explorer);
|
||||
explorerDock->setWidget(_explorer);
|
||||
explorerDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable);
|
||||
|
||||
QWidget *titleWidget = new QWidget(this); // this lines simply remove
|
||||
@@ -950,7 +1146,7 @@ namespace Robomongo
|
||||
_logDock = new QDockWidget(tr("Logs"));
|
||||
_logDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea | Qt::BottomDockWidgetArea | Qt::TopDockWidgetArea);
|
||||
_logDock->setWidget(log);
|
||||
_logDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable);
|
||||
_logDock->setFeatures(QDockWidget::DockWidgetClosable);
|
||||
_logDock->setVisible(false);
|
||||
|
||||
QAction *action = _logDock->toggleViewAction();
|
||||
@@ -980,6 +1176,7 @@ namespace Robomongo
|
||||
_workArea = new WorkAreaTabWidget(this);
|
||||
AppRegistry::instance().bus()->subscribe(_workArea, OpeningShellEvent::Type);
|
||||
VERIFY(connect(_workArea, SIGNAL(currentChanged(int)), this, SLOT(updateMenus())));
|
||||
VERIFY(connect(_workArea, SIGNAL(currentChanged(int)), this, SLOT(on_tabChange())));
|
||||
|
||||
QHBoxLayout *hlayout = new QHBoxLayout;
|
||||
hlayout->setContentsMargins(0, 3, 0, 0);
|
||||
@@ -1013,4 +1210,57 @@ namespace Robomongo
|
||||
AppRegistry::instance().settingsManager()->setToolbarSettings("explorer", isVisible);
|
||||
AppRegistry::instance().settingsManager()->save();
|
||||
}
|
||||
|
||||
/* --- Temporarily disabling export/import feature
|
||||
void MainWindow::onExplorerItemSelected(QTreeWidgetItem *selectedItem)
|
||||
{
|
||||
auto collectionItem = dynamic_cast<ExplorerCollectionTreeItem*>(selectedItem);
|
||||
if (collectionItem) {
|
||||
_exportAction->setEnabled(true);
|
||||
}
|
||||
else {
|
||||
_exportAction->setDisabled(true);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
void MainWindow::on_tabChange()
|
||||
{
|
||||
auto activeTab = dynamic_cast<QueryWidget*>(_workArea->currentWidget());
|
||||
if (activeTab) {
|
||||
activeTab->bringDockToFront();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::toggleMinimize()
|
||||
{
|
||||
if (isHidden()) {
|
||||
show();
|
||||
}
|
||||
else {
|
||||
hide();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::trayActivated(QSystemTrayIcon::ActivationReason reason)
|
||||
{
|
||||
if (QSystemTrayIcon::DoubleClick == reason && isHidden()) {
|
||||
show();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::toggleMinimizeToTray()
|
||||
{
|
||||
QAction *send = qobject_cast<QAction*>(sender());
|
||||
saveMinimizeToTraySettings(send->isChecked());
|
||||
}
|
||||
|
||||
void MainWindow::on_focusChanged()
|
||||
{
|
||||
// If focus is on floating output window, make it's parent (which is a QueryWidget tab) as active tab
|
||||
auto const activeDock = dynamic_cast<QueryWidget::CustomDockWidget*>(qApp->activeWindow());
|
||||
if (activeDock) {
|
||||
_workArea->setCurrentWidget(activeDock->getParentQueryWidget());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QSystemTrayIcon>
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QLabel;
|
||||
class QToolBar;
|
||||
class QDockWidget;
|
||||
class QToolButton;
|
||||
class QTreeWidgetItem;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Robomongo
|
||||
@@ -13,10 +15,13 @@ namespace Robomongo
|
||||
class ConnectionFailedEvent;
|
||||
class ScriptExecutingEvent;
|
||||
class ScriptExecutedEvent;
|
||||
class OperationFailedEvent;
|
||||
|
||||
class QueryWidgetUpdatedEvent;
|
||||
class WorkAreaTabWidget;
|
||||
class ConnectionMenu;
|
||||
class App;
|
||||
class ExplorerWidget;
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
@@ -25,7 +30,6 @@ namespace Robomongo
|
||||
public:
|
||||
typedef QMainWindow BaseClass;
|
||||
MainWindow();
|
||||
void keyPressEvent(QKeyEvent *event);
|
||||
|
||||
public Q_SLOTS:
|
||||
void manageConnections();
|
||||
@@ -49,6 +53,7 @@ namespace Robomongo
|
||||
void save();
|
||||
void saveAs();
|
||||
void changeStyle(QAction *);
|
||||
void exit();
|
||||
|
||||
void setDefaultUuidEncoding();
|
||||
void setJavaUuidEncoding();
|
||||
@@ -66,22 +71,55 @@ namespace Robomongo
|
||||
void handle(ScriptExecutingEvent *event);
|
||||
void handle(ScriptExecutedEvent *event);
|
||||
void handle(QueryWidgetUpdatedEvent *event);
|
||||
void handle(OperationFailedEvent *event);
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent *event) override;
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
void hideEvent(QHideEvent *event) override;
|
||||
void showEvent(QShowEvent *event) override;
|
||||
|
||||
private Q_SLOTS:
|
||||
void updateMenus();
|
||||
void setUtcTimeZone();
|
||||
void setLocalTimeZone();
|
||||
void openPreferences();
|
||||
|
||||
// Temporarily disabling export/import feature
|
||||
//void openExportDialog();
|
||||
|
||||
void onConnectToolbarVisibilityChanged(bool isVisisble);
|
||||
void onOpenSaveToolbarVisibilityChanged(bool isVisisble);
|
||||
void onExecToolbarVisibilityChanged(bool isVisisble);
|
||||
void onExplorerVisibilityChanged(bool isVisisble);
|
||||
|
||||
// Temporarily disabling export/import feature
|
||||
//void onExplorerItemSelected(QTreeWidgetItem *selectedItem);
|
||||
|
||||
void on_tabChange();
|
||||
|
||||
void toggleMinimize();
|
||||
void trayActivated(QSystemTrayIcon::ActivationReason reason);
|
||||
void toggleMinimizeToTray();
|
||||
|
||||
// On application focus changes
|
||||
void on_focusChanged();
|
||||
|
||||
private:
|
||||
void updateConnectionsMenu();
|
||||
void createDatabaseExplorer();
|
||||
void createTabs();
|
||||
void createStylesMenu();
|
||||
void createStatusBar();
|
||||
void restoreWindowSettings();
|
||||
void saveWindowSettings() const;
|
||||
|
||||
QDockWidget *_logDock;
|
||||
|
||||
WorkAreaTabWidget *_workArea;
|
||||
|
||||
ExplorerWidget* _explorer;
|
||||
|
||||
App *_app;
|
||||
|
||||
ConnectionMenu *_connectionsMenu;
|
||||
@@ -89,19 +127,28 @@ namespace Robomongo
|
||||
QMenu *_viewMenu;
|
||||
QMenu *_toolbarsMenu;
|
||||
QAction *_connectAction;
|
||||
// Open/Save tool bar
|
||||
QAction *_openAction;
|
||||
QAction *_saveAction;
|
||||
QAction *_saveAsAction;
|
||||
// Execution tool bar
|
||||
QAction *_executeAction;
|
||||
QAction *_stopAction;
|
||||
QAction *_orientationAction;
|
||||
QToolBar *_execToolBar;
|
||||
|
||||
// Temporarily disabling export/import feature
|
||||
/*
|
||||
// Export/import tool bar
|
||||
QAction *_exportAction;
|
||||
QAction *_importAction;
|
||||
*/
|
||||
|
||||
void updateConnectionsMenu();
|
||||
void createDatabaseExplorer();
|
||||
void createTabs();
|
||||
void createStylesMenu();
|
||||
void createStatusBar();
|
||||
#if defined(Q_OS_WIN)
|
||||
QSystemTrayIcon *_trayIcon;
|
||||
#endif
|
||||
|
||||
bool _allowExit;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -19,11 +19,17 @@ namespace
|
||||
"<br/>"
|
||||
"<a href=\"https://" PROJECT_GITHUB_FORK "\">Fork</a> project or <a href=\"https://" PROJECT_GITHUB_ISSUES "\">submit</a> issues/proposals on GitHub. <br/>"
|
||||
"<br/>"
|
||||
"Copyright 2013-2016 <a href=\"http:// " PROJECT_COMPANYNAME_DOMAIN "\">" PROJECT_COMPANYNAME "</a>. All rights reserved.<br/>"
|
||||
"Copyright 2013-2017 <a href=\"http://" PROJECT_COMPANYNAME_DOMAIN "\">" PROJECT_COMPANYNAME "</a>. All rights reserved.<br/>"
|
||||
"<br/>"
|
||||
"The program is provided AS IS with NO WARRANTY OF ANY KIND, "
|
||||
"INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A "
|
||||
"PARTICULAR PURPOSE.<br/>");
|
||||
"PARTICULAR PURPOSE.<br/>"
|
||||
"<br>"
|
||||
"Credits: "
|
||||
"<br/>"
|
||||
"Some icons are designed by Freepik from <a href=http://www.flaticon.com>www.flaticon.com</a>"
|
||||
"<br/>"
|
||||
);
|
||||
}
|
||||
|
||||
namespace Robomongo
|
||||
|
||||
@@ -3,9 +3,25 @@
|
||||
#include <QLabel>
|
||||
#include <QGridLayout>
|
||||
#include <QLineEdit>
|
||||
/* --- Disabling unfinished export URI connection string feature
|
||||
#include <QPushButton>
|
||||
#include <QMessageBox>
|
||||
#include <QClipboard>
|
||||
#include <QApplication>
|
||||
#include <QCheckBox>
|
||||
#include <QToolTip>
|
||||
*/
|
||||
|
||||
#include <mongo/client/mongo_uri.h>
|
||||
|
||||
#include "robomongo/core/settings/ConnectionSettings.h"
|
||||
#include "robomongo/core/utils/QtUtils.h"
|
||||
/* --- Disabling unfinished export URI connection string feature
|
||||
#include "robomongo/core/settings/CredentialSettings.h"
|
||||
#include "robomongo/core/settings/ReplicaSetSettings.h"
|
||||
#include "robomongo/core/settings/SslSettings.h"
|
||||
#include "robomongo/gui/utils/GuiConstants.h"
|
||||
*/
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
@@ -16,23 +32,123 @@ namespace Robomongo
|
||||
QLabel *defaultDatabaseDescriptionLabel = new QLabel(
|
||||
"Database, that will be default (<code>db</code> shell variable will point to this database). "
|
||||
"By default, default database will be the one you authenticate on, or <code>test</code> otherwise. "
|
||||
"Leave this field empty, if you want default behaviour.");
|
||||
"Leave this field empty, if you want default behavior.");
|
||||
defaultDatabaseDescriptionLabel->setWordWrap(true);
|
||||
defaultDatabaseDescriptionLabel->setContentsMargins(0, -2, 0, 20);
|
||||
|
||||
_defaultDatabaseName = new QLineEdit(QtUtils::toQString(_settings->defaultDatabase()));
|
||||
|
||||
QGridLayout *connectionLayout = new QGridLayout;
|
||||
connectionLayout->setAlignment(Qt::AlignTop);
|
||||
connectionLayout->addWidget(new QLabel("Default Database:"), 1, 0);
|
||||
connectionLayout->addWidget(_defaultDatabaseName, 1, 1, 1, 1);
|
||||
connectionLayout->addWidget(defaultDatabaseDescriptionLabel, 2, 1, 1, 1);
|
||||
/* --- Disabling unfinished export URI connection string feature
|
||||
_uriString = new QLineEdit;
|
||||
_uriString->setReadOnly(true);
|
||||
|
||||
setLayout(connectionLayout);
|
||||
_includePasswordCheckBox = new QCheckBox("Include passwords");
|
||||
VERIFY(connect(_includePasswordCheckBox, SIGNAL(toggled(bool)),
|
||||
this, SLOT(on_includePasswordsCheckBox_toggle(bool))));
|
||||
|
||||
auto generateButton = new QPushButton("Generate");
|
||||
generateButton->setFixedWidth(70);
|
||||
generateButton->setMaximumHeight(HighDpiConstants::WIN_HIGH_DPI_BUTTON_HEIGHT);
|
||||
VERIFY(connect(generateButton, SIGNAL(clicked()), this, SLOT(on_generateButton_clicked())));
|
||||
|
||||
_copyButton = new QPushButton("Copy");
|
||||
_copyButton->setFixedWidth(50);
|
||||
_copyButton->setMaximumHeight(HighDpiConstants::WIN_HIGH_DPI_BUTTON_HEIGHT);
|
||||
VERIFY(connect(_copyButton, SIGNAL(clicked()), this, SLOT(on_copyButton_clicked())));
|
||||
|
||||
auto hlay = new QHBoxLayout;
|
||||
hlay->addWidget(_includePasswordCheckBox);
|
||||
hlay->addWidget(generateButton, Qt::AlignLeft);
|
||||
hlay->addWidget(_copyButton, Qt::AlignLeft);
|
||||
*/
|
||||
|
||||
auto mainLayout = new QGridLayout;
|
||||
mainLayout->setAlignment(Qt::AlignTop);
|
||||
mainLayout->addWidget(new QLabel("Default Database:"), 1, 0);
|
||||
mainLayout->addWidget(_defaultDatabaseName, 1, 1, 1, 2);
|
||||
mainLayout->addWidget(defaultDatabaseDescriptionLabel, 2, 1, 1, 2);
|
||||
/* --- Disabling unfinished export URI connection string feature
|
||||
mainLayout->addWidget(new QLabel{ "URI Connection String:" }, 3, 0);
|
||||
mainLayout->addWidget(_uriString, 3, 1);
|
||||
mainLayout->addLayout(hlay, 4, 1);
|
||||
*/
|
||||
setLayout(mainLayout);
|
||||
}
|
||||
|
||||
void ConnectionAdvancedTab::accept()
|
||||
{
|
||||
_settings->setDefaultDatabase(QtUtils::toStdString(_defaultDatabaseName->text()));
|
||||
}
|
||||
|
||||
/* --- Disabling unfinished export URI connection string feature
|
||||
void ConnectionAdvancedTab::on_generateButton_clicked()
|
||||
{
|
||||
if (_settings->isReplicaSet()) {
|
||||
// todo:
|
||||
// handle set name will be empty initially. It will be available(cached) after first
|
||||
// successful connection.
|
||||
std::string connStr = { "mongodb://" };
|
||||
|
||||
// todo: which credential to use if there are more than one?
|
||||
if (_settings->hasEnabledPrimaryCredential() && !_settings->credentials().isEmpty()) {
|
||||
connStr.append(_settings->primaryCredential()->userName() + ":");
|
||||
connStr.append(_includePasswordCheckBox->isChecked() ?
|
||||
_settings->primaryCredential()->userPassword() : "PASSWORD");
|
||||
connStr.append("@");
|
||||
}
|
||||
|
||||
for (auto const& member : _settings->replicaSetSettings()->membersToHostAndPort()) {
|
||||
connStr.append(member.toString() + ",");
|
||||
}
|
||||
connStr.pop_back(); // delete last ","
|
||||
|
||||
connStr.append("/admin?");
|
||||
|
||||
connStr.append("replicaSet=" + _settings->replicaSetSettings()->setName());
|
||||
|
||||
if (_settings->sslSettings()->sslEnabled())
|
||||
connStr.append("&ssl=true");
|
||||
|
||||
if (_settings->hasEnabledPrimaryCredential() && !_settings->credentials().isEmpty())
|
||||
connStr.append("&authSource=" + _settings->primaryCredential()->databaseName());
|
||||
|
||||
// todo: validate connStr
|
||||
_uriString->setText(QString::fromStdString(connStr));
|
||||
_uriString->setCursorPosition(QTextCursor::Start);
|
||||
|
||||
}
|
||||
else { // standalone server
|
||||
mongo::HostAndPort server = _settings->hostAndPort();
|
||||
mongo::ConnectionString connStr{ server };
|
||||
auto str1 = connStr.toString();
|
||||
|
||||
auto uriWithStatus = mongo::StatusWith<mongo::MongoURI>(mongo::MongoURI::parse(connStr.toString()));
|
||||
auto str2 = uriWithStatus.getValue().toString();
|
||||
|
||||
_uriString->setText(QString::fromStdString("mongodb://" + str1 + "/admin"));
|
||||
_uriString->setCursorPosition(QTextCursor::Start);
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionAdvancedTab::on_copyButton_clicked()
|
||||
{
|
||||
if (_uriString->text().isEmpty())
|
||||
return;
|
||||
|
||||
QClipboard *clipboard = qApp->clipboard();
|
||||
clipboard->setText(_uriString->text());
|
||||
|
||||
auto posUnderGenerateButton = QPoint(_copyButton->pos().x()-100, _copyButton->y()+20);
|
||||
QToolTip::showText(mapToGlobal(posUnderGenerateButton), "Copied into clipboard", nullptr, QRect(), 2000);
|
||||
}
|
||||
|
||||
void ConnectionAdvancedTab::on_includePasswordsCheckBox_toggle(bool checked)
|
||||
{
|
||||
if (_uriString->text().isEmpty())
|
||||
return;
|
||||
|
||||
on_generateButton_clicked();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QLineEdit;
|
||||
class QCheckBox;
|
||||
class QPushButton;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Robomongo
|
||||
@@ -16,10 +18,24 @@ namespace Robomongo
|
||||
|
||||
public:
|
||||
ConnectionAdvancedTab(ConnectionSettings *settings);
|
||||
QLineEdit *_defaultDatabaseName;
|
||||
void accept();
|
||||
|
||||
/* --- Disabling unfinished export URI connection string feature
|
||||
private Q_SLOTS :
|
||||
void on_generateButton_clicked();
|
||||
void on_copyButton_clicked();
|
||||
void on_includePasswordsCheckBox_toggle(bool checked);
|
||||
*/
|
||||
|
||||
private:
|
||||
QLineEdit *_defaultDatabaseName;
|
||||
|
||||
/* --- Disabling unfinished export URI connection string feature
|
||||
QLineEdit *_uriString;
|
||||
QCheckBox *_includePasswordCheckBox;
|
||||
QPushButton *_copyButton;
|
||||
*/
|
||||
|
||||
ConnectionSettings *_settings;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include "robomongo/core/settings/ConnectionSettings.h"
|
||||
#include "robomongo/core/settings/CredentialSettings.h"
|
||||
#include "robomongo/core/utils/QtUtils.h"
|
||||
#include "robomongo/gui/GuiRegistry.h"
|
||||
#include "robomongo/gui/utils/GuiConstants.h"
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
@@ -43,7 +45,17 @@ namespace Robomongo
|
||||
_useAuth->setStyleSheet("margin-bottom: 7px");
|
||||
VERIFY(connect(_useAuth, SIGNAL(toggled(bool)), this, SLOT(authChecked(bool))));
|
||||
|
||||
_echoModeButton = new QPushButton("Show");
|
||||
_echoModeButton = new QPushButton;
|
||||
_echoModeButton->setIcon(GuiRegistry::instance().hideIcon());
|
||||
#ifdef Q_OS_MAC
|
||||
_echoModeButton->setMaximumWidth(50);
|
||||
#else
|
||||
_echoModeButton->setMinimumWidth(50);
|
||||
#endif
|
||||
// Attempt to fix the issue for Windows High DPI button height is slightly taller than other widgets
|
||||
#ifdef Q_OS_WIN
|
||||
_echoModeButton->setMaximumHeight(HighDpiConstants::WIN_HIGH_DPI_BUTTON_HEIGHT);
|
||||
#endif
|
||||
VERIFY(connect(_echoModeButton, SIGNAL(clicked()), this, SLOT(toggleEchoMode())));
|
||||
|
||||
_useAuth->setChecked(_settings->hasEnabledPrimaryCredential());
|
||||
@@ -57,23 +69,20 @@ namespace Robomongo
|
||||
_mechanismComboBox->setCurrentText(QtUtils::toQString(primaryCredential->mechanism()));
|
||||
}
|
||||
|
||||
QGridLayout *_authLayout = new QGridLayout;
|
||||
_authLayout->addWidget(_useAuth, 0, 0, 1, 3);
|
||||
_authLayout->addWidget(_databaseNameLabel, 1, 0);
|
||||
_authLayout->addWidget(_databaseName, 1, 1, 1, 2);
|
||||
_authLayout->addWidget(_databaseNameDescriptionLabel, 2, 1, 1, 2);
|
||||
_authLayout->addWidget(_userNameLabel, 3, 0);
|
||||
_authLayout->addWidget(_userName, 3, 1, 1, 2);
|
||||
_authLayout->addWidget(_userPasswordLabel, 4, 0);
|
||||
_authLayout->addWidget(_userPassword, 4, 1);
|
||||
_authLayout->addWidget(_echoModeButton, 4, 2);
|
||||
_authLayout->addWidget(_mechanismLabel, 5, 0);
|
||||
_authLayout->addWidget(_mechanismComboBox, 5, 1, 1, 2);
|
||||
_authLayout->setAlignment(Qt::AlignTop);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(_authLayout);
|
||||
setLayout(mainLayout);
|
||||
QGridLayout *authLayout = new QGridLayout;
|
||||
authLayout->addWidget(_useAuth, 0, 0, 1, 3);
|
||||
authLayout->addWidget(_databaseNameLabel, 1, 0);
|
||||
authLayout->addWidget(_databaseName, 1, 1, 1, 2);
|
||||
authLayout->addWidget(_databaseNameDescriptionLabel, 2, 1, 1, 2);
|
||||
authLayout->addWidget(_userNameLabel, 3, 0);
|
||||
authLayout->addWidget(_userName, 3, 1, 1, 2);
|
||||
authLayout->addWidget(_userPasswordLabel, 4, 0);
|
||||
authLayout->addWidget(_userPassword, 4, 1);
|
||||
authLayout->addWidget(_echoModeButton, 4, 2);
|
||||
authLayout->addWidget(_mechanismLabel, 5, 0);
|
||||
authLayout->addWidget(_mechanismComboBox, 5, 1, 1, 2);
|
||||
authLayout->setAlignment(Qt::AlignTop);
|
||||
setLayout(authLayout);
|
||||
}
|
||||
|
||||
void ConnectionAuthTab::accept()
|
||||
@@ -99,7 +108,7 @@ namespace Robomongo
|
||||
{
|
||||
bool isPassword = _userPassword->echoMode() == QLineEdit::Password;
|
||||
_userPassword->setEchoMode(isPassword ? QLineEdit::Normal: QLineEdit::Password);
|
||||
_echoModeButton->setText(isPassword ? "Hide": "Show");
|
||||
_echoModeButton->setIcon(isPassword ? GuiRegistry::instance().showIcon() : GuiRegistry::instance().hideIcon());
|
||||
}
|
||||
|
||||
void ConnectionAuthTab::authChecked(bool checked)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "robomongo/gui/dialogs/ConnectionBasicTab.h"
|
||||
#include "robomongo/gui/dialogs/ConnectionBasicTab.h"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
@@ -7,54 +7,260 @@
|
||||
#include <QCheckBox>
|
||||
#include <QPushButton>
|
||||
#include <QFileDialog>
|
||||
#include <QComboBox>
|
||||
#include <QTreeWidget>
|
||||
#include <QTreeWidgetItem>
|
||||
#include <QMessageBox>
|
||||
#include <QDialogButtonBox>
|
||||
|
||||
|
||||
#include "robomongo/core/utils/QtUtils.h"
|
||||
#include "robomongo/core/settings/ConnectionSettings.h"
|
||||
#include "robomongo/core/settings/ReplicaSetSettings.h"
|
||||
#include "robomongo/gui/GuiRegistry.h"
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
ConnectionBasicTab::ConnectionBasicTab(ConnectionSettings *settings) :
|
||||
_settings(settings)
|
||||
{
|
||||
QLabel *connectionDescriptionLabel = new QLabel(
|
||||
"Choose any connection name that will help you to identify this connection.");
|
||||
connectionDescriptionLabel->setWordWrap(true);
|
||||
connectionDescriptionLabel->setContentsMargins(0, -2, 0, 20);
|
||||
|
||||
QLabel *serverDescriptionLabel = new QLabel(
|
||||
"Specify host and port of MongoDB server. Host can be either IPv4, IPv6 or domain name.");
|
||||
serverDescriptionLabel->setWordWrap(true);
|
||||
serverDescriptionLabel->setContentsMargins(0, -2, 0, 20);
|
||||
|
||||
_typeLabel = new QLabel("Type:");
|
||||
_connectionType = new QComboBox;
|
||||
_connectionType->addItem(tr("Direct Connection"));
|
||||
_connectionType->addItem(tr("Replica Set"));
|
||||
_connectionType->setCurrentIndex(static_cast<int>(_settings->isReplicaSet()));
|
||||
VERIFY(connect(_connectionType, SIGNAL(currentIndexChanged(int)), this, SLOT(on_ConnectionTypeChange(int))));
|
||||
|
||||
_nameLabel = new QLabel("Name:");
|
||||
_connectionName = new QLineEdit(QtUtils::toQString(_settings->connectionName()));
|
||||
_connInfoLabel = new QLabel("Choose any connection name that will help you to identify this connection.");
|
||||
_connInfoLabel->setWordWrap(true);
|
||||
_connInfoLabel->setContentsMargins(0, -2, 0, 20);
|
||||
|
||||
_addressLabel = new QLabel("Address:");
|
||||
_serverAddress = new QLineEdit(QtUtils::toQString(_settings->serverHost()));
|
||||
_colon = new QLabel(":");
|
||||
_serverPort = new QLineEdit(QString::number(_settings->serverPort()));
|
||||
_serverPort->setFixedWidth(80);
|
||||
QRegExp rx("\\d+");//(0-65554)
|
||||
_serverPort->setValidator(new QRegExpValidator(rx, this));
|
||||
QRegExp rx("\\d+"); //(0-65554)
|
||||
_serverPort->setValidator(new QRegExpValidator(rx, this));
|
||||
_addInfoLabel = new QLabel("Specify host and port of MongoDB server. Host can be either IPv4, IPv6 or domain name.");
|
||||
_addInfoLabel->setWordWrap(true);
|
||||
_addInfoLabel->setContentsMargins(0, -2, 0, 20);
|
||||
|
||||
QGridLayout *connectionLayout = new QGridLayout;
|
||||
_membersLabel = new QLabel("Members:");
|
||||
_membersLabel->setFixedWidth(_membersLabel->sizeHint().width());
|
||||
_members = new QTreeWidget;
|
||||
_members->setHeaderHidden(true);
|
||||
_members->setIndentation(0);
|
||||
VERIFY(connect(_members, SIGNAL(itemChanged(QTreeWidgetItem*, int)),
|
||||
this, SLOT(on_replicaMemberItemEdit(QTreeWidgetItem*, int))));
|
||||
|
||||
if (_settings->isReplicaSet()) {
|
||||
for (const std::string& str : _settings->replicaSetSettings()->members())
|
||||
{
|
||||
if (!str.empty()) {
|
||||
auto item = new QTreeWidgetItem;
|
||||
item->setText(0, QString::fromStdString(str));
|
||||
item->setFlags(item->flags() | Qt::ItemIsEditable);
|
||||
_members->addTopLevelItem(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int const BUTTON_SIZE = 60;
|
||||
_addButton = new QPushButton;
|
||||
_addButton->setIcon(GuiRegistry::instance().plusIcon());
|
||||
_removeButton = new QPushButton;
|
||||
_removeButton->setIcon(GuiRegistry::instance().minusIcon());
|
||||
VERIFY(connect(_addButton, SIGNAL(clicked()), this, SLOT(on_addButton_clicked())));
|
||||
VERIFY(connect(_removeButton, SIGNAL(clicked()), this, SLOT(on_removeButton_clicked())));
|
||||
|
||||
_minusPlusButtonBox = new QDialogButtonBox(this);
|
||||
_minusPlusButtonBox->setOrientation(Qt::Horizontal);
|
||||
#ifdef _WIN32
|
||||
_minusPlusButtonBox->addButton(_addButton, QDialogButtonBox::NoRole);
|
||||
_minusPlusButtonBox->addButton(_removeButton, QDialogButtonBox::NoRole);
|
||||
#else
|
||||
_minusPlusButtonBox->addButton(_removeButton, QDialogButtonBox::NoRole);
|
||||
_minusPlusButtonBox->addButton(_addButton, QDialogButtonBox::NoRole);
|
||||
#endif
|
||||
|
||||
auto connectionLayout = new QGridLayout;
|
||||
connectionLayout->setVerticalSpacing(10);
|
||||
connectionLayout->setAlignment(Qt::AlignTop);
|
||||
connectionLayout->addWidget(new QLabel("Name:"), 1, 0);
|
||||
connectionLayout->addWidget(_connectionName, 1, 1, 1, 3);
|
||||
connectionLayout->addWidget(connectionDescriptionLabel, 2, 1, 1, 3);
|
||||
connectionLayout->addWidget(new QLabel("Address:"), 3, 0);
|
||||
connectionLayout->addWidget(_serverAddress, 3, 1);
|
||||
connectionLayout->addWidget(new QLabel(":"), 3, 2);
|
||||
connectionLayout->addWidget(_serverPort, 3, 3);
|
||||
connectionLayout->addWidget(serverDescriptionLabel, 4, 1, 1, 3);
|
||||
connectionLayout->addWidget(_typeLabel, 1, 0);
|
||||
connectionLayout->addWidget(_connectionType, 1, 1, 1, 3);
|
||||
connectionLayout->addWidget(new QLabel(""), 2, 0);
|
||||
connectionLayout->addWidget(_nameLabel, 3, 0);
|
||||
connectionLayout->addWidget(_connectionName, 3, 1, 1, 3);
|
||||
connectionLayout->addWidget(_connInfoLabel, 4, 1, 1, 3);
|
||||
connectionLayout->addWidget(_addressLabel, 5, 0);
|
||||
connectionLayout->addWidget(_serverAddress, 5, 1);
|
||||
connectionLayout->addWidget(_colon, 5, 2);
|
||||
connectionLayout->addWidget(_serverPort, 5, 3);
|
||||
connectionLayout->addWidget(_addInfoLabel, 6, 1, 1, 3);
|
||||
connectionLayout->addWidget(_membersLabel, 7, 0, Qt::AlignTop);
|
||||
connectionLayout->addWidget(_members, 7, 1, 1, 3);
|
||||
connectionLayout->addWidget(_minusPlusButtonBox, 8, 3, Qt::AlignRight | Qt::AlignTop);
|
||||
connectionLayout->addWidget(new QLabel(""), 9, 0);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
auto mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(connectionLayout);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_connectionName->setFocus();
|
||||
on_ConnectionTypeChange(_connectionType->currentIndex());
|
||||
}
|
||||
|
||||
void ConnectionBasicTab::accept()
|
||||
bool ConnectionBasicTab::accept()
|
||||
{
|
||||
_settings->setReplicaSet(static_cast<bool>(_connectionType->currentIndex()));
|
||||
_settings->setConnectionName(QtUtils::toStdString(_connectionName->text()));
|
||||
_settings->setServerHost(QtUtils::toStdString(_serverAddress->text()));
|
||||
_settings->setServerPort(_serverPort->text().toInt());
|
||||
|
||||
if (_settings->isReplicaSet() && _members->topLevelItemCount() == 0) {
|
||||
QMessageBox::critical(this, "Error", "Replica set members cannot be empty. "
|
||||
"Please enter at least one member.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check and warn if there is duplicate member or
|
||||
// if any of the replica set member items does not contain ":" character between hostname and port.
|
||||
if (_settings->isReplicaSet() && _members->topLevelItemCount() > 1) {
|
||||
QStringList members;
|
||||
for (int i = 0; i < _members->topLevelItemCount(); ++i) {
|
||||
QTreeWidgetItem const* item = _members->topLevelItem(i);
|
||||
QStringList const hostAndPort = item->text(0).split(":");
|
||||
if (hostAndPort.size() < 2) {
|
||||
QMessageBox::critical(this, "Error", "Replica set member items must all contain ':' between"
|
||||
" hostname and port.");
|
||||
return false;
|
||||
}
|
||||
if (!item->text(0).isEmpty())
|
||||
members.push_back(item->text(0));
|
||||
}
|
||||
if (members.size() > 1) {
|
||||
if (members.removeDuplicates() > 0) {
|
||||
QMessageBox::critical(this, "Error", "Please remove duplicate member, two replica"
|
||||
" set members cannot have the same hostname and port.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save to settings
|
||||
if (_settings->isReplicaSet() && _members->topLevelItemCount() > 0) {
|
||||
QStringList const hostAndPort = _members->topLevelItem(0)->text(0).split(":");
|
||||
_settings->setServerHost(hostAndPort[0].toStdString());
|
||||
_settings->setServerPort(hostAndPort[1].toInt());
|
||||
}
|
||||
else { // Single server
|
||||
_settings->setServerHost(QtUtils::toStdString(_serverAddress->text()));
|
||||
_settings->setServerPort(_serverPort->text().toInt());
|
||||
}
|
||||
|
||||
if (_settings->isReplicaSet()) {
|
||||
// Save replica members
|
||||
std::vector<std::string> members;
|
||||
for (int i = 0; i < _members->topLevelItemCount(); ++i)
|
||||
{
|
||||
QTreeWidgetItem const* item = _members->topLevelItem(i);
|
||||
if (!item->text(0).isEmpty())
|
||||
members.push_back(item->text(0).toStdString());
|
||||
}
|
||||
_settings->replicaSetSettings()->setMembers(members);
|
||||
// Clear cached set name
|
||||
_settings->replicaSetSettings()->setSetName("");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ConnectionBasicTab::on_ConnectionTypeChange(int index)
|
||||
{
|
||||
bool const isReplica = static_cast<bool>(index);
|
||||
|
||||
// Replica set
|
||||
_membersLabel->setVisible(isReplica);
|
||||
_members->setVisible(isReplica);
|
||||
_minusPlusButtonBox->setVisible(isReplica);
|
||||
// Direct Connection
|
||||
_addressLabel->setVisible(!isReplica);
|
||||
_serverAddress->setVisible(!isReplica);
|
||||
_serverPort->setVisible(!isReplica);
|
||||
_colon->setVisible(!isReplica);
|
||||
_addInfoLabel->setVisible(!isReplica);
|
||||
}
|
||||
|
||||
void ConnectionBasicTab::deleteItem()
|
||||
{
|
||||
delete _members->currentItem(); // todo: refactor
|
||||
}
|
||||
|
||||
void ConnectionBasicTab::on_addButton_clicked()
|
||||
{
|
||||
auto item = new QTreeWidgetItem;
|
||||
|
||||
// Make member addition little smarter than expected
|
||||
if (_members->topLevelItemCount() < 1) {
|
||||
item->setText(0, "localhost:27017");
|
||||
}
|
||||
else { // Add the next member using last entered hostname and incremented port by one
|
||||
QString const& lastMember = _members->topLevelItem(_members->topLevelItemCount()-1)->text(0);
|
||||
QStringList const& hostAndPort = lastMember.split(':');
|
||||
if (hostAndPort.size() == 2) {
|
||||
auto const& hostName = hostAndPort[0];
|
||||
auto const& port = hostAndPort[1].toInt();
|
||||
item->setText(0, hostName + ':' + QString::number(port + 1));
|
||||
}
|
||||
else {
|
||||
item->setText(0, "localhost:" + QString::number(_members->topLevelItemCount() + 27017));
|
||||
}
|
||||
}
|
||||
|
||||
item->setFlags(item->flags() | Qt::ItemIsEditable);
|
||||
_members->addTopLevelItem(item);
|
||||
}
|
||||
|
||||
void ConnectionBasicTab::on_removeButton_clicked()
|
||||
{
|
||||
if (_members->topLevelItemCount() <= 0)
|
||||
return;
|
||||
|
||||
if (_members->currentItem())
|
||||
delete _members->currentItem();
|
||||
else
|
||||
delete _members->topLevelItem(_members->topLevelItemCount() - 1);
|
||||
}
|
||||
|
||||
void ConnectionBasicTab::on_replicaMemberItemEdit(QTreeWidgetItem* item, int column)
|
||||
{
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
auto str = item->text(0);
|
||||
|
||||
// Remove white spaces
|
||||
str = str.simplified();
|
||||
str.remove(" ");
|
||||
|
||||
// Remove item from tree widget if it has empty text
|
||||
if (str.isEmpty()) {
|
||||
delete item;
|
||||
return;
|
||||
}
|
||||
|
||||
// Force port as integer
|
||||
QStringList const& hostAndPort = str.split(':');
|
||||
if (hostAndPort.size() >= 2) {
|
||||
auto const& hostName = hostAndPort[0];
|
||||
auto portStr = hostAndPort[1];
|
||||
portStr.remove(QRegExp("[^\\d]"));
|
||||
str = hostName + ':' + QString::number(portStr.toInt());
|
||||
}
|
||||
else
|
||||
str += ":27017";
|
||||
|
||||
item->setText(0, str);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,13 @@
|
||||
#include <QWidget>
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QLineEdit;
|
||||
class QLabel;
|
||||
class QCheckBox;
|
||||
class QPushButton;
|
||||
class QComboBox;
|
||||
class QTreeWidget;
|
||||
class QTreeWidgetItem;
|
||||
class QDialogButtonBox;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Robomongo
|
||||
@@ -17,12 +22,35 @@ namespace Robomongo
|
||||
|
||||
public:
|
||||
ConnectionBasicTab(ConnectionSettings *settings);
|
||||
void accept();
|
||||
bool accept();
|
||||
|
||||
private Q_SLOTS:
|
||||
void on_ConnectionTypeChange(int index);
|
||||
void deleteItem();
|
||||
void on_addButton_clicked();
|
||||
void on_removeButton_clicked();
|
||||
void on_replicaMemberItemEdit(QTreeWidgetItem* item, int column);
|
||||
|
||||
private:
|
||||
QLabel *_typeLabel;
|
||||
QComboBox *_connectionType;
|
||||
QLabel *_nameLabel;
|
||||
QLineEdit *_connectionName;
|
||||
QLabel *_connInfoLabel;
|
||||
QLabel *_addressLabel;
|
||||
QLineEdit *_serverAddress;
|
||||
QLabel *_colon;
|
||||
QLineEdit *_serverPort;
|
||||
QLabel *_addInfoLabel;
|
||||
QLabel *_membersLabel;
|
||||
QTreeWidget *_members;
|
||||
QPushButton *_addButton;
|
||||
QPushButton *_removeButton;
|
||||
QDialogButtonBox *_minusPlusButtonBox;
|
||||
QPushButton *_discoverButton;
|
||||
QLabel *_readPrefLabel;
|
||||
QComboBox *_readPreference;
|
||||
|
||||
ConnectionSettings *const _settings;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,169 +5,285 @@
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QMovie>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <mongo/client/dbclientinterface.h>
|
||||
#include "robomongo/core/settings/ConnectionSettings.h"
|
||||
#include "robomongo/core/settings/CredentialSettings.h"
|
||||
#include "robomongo/core/settings/SshSettings.h"
|
||||
#include "robomongo/core/settings/SslSettings.h"
|
||||
#include "robomongo/core/settings/ReplicaSetSettings.h"
|
||||
#include "robomongo/gui/GuiRegistry.h"
|
||||
#include "robomongo/core/AppRegistry.h"
|
||||
#include "robomongo/core/domain/App.h"
|
||||
#include "robomongo/core/domain/MongoServer.h"
|
||||
#include "robomongo/core/EventBus.h"
|
||||
#include "robomongo/core/utils/QtUtils.h"
|
||||
#include "robomongo/core/mongodb/SshTunnelWorker.h"
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
const QSize ConnectionDiagnosticDialog::dialogSize = QSize(300,200);
|
||||
|
||||
ConnectionDiagnosticDialog::ConnectionDiagnosticDialog(ConnectionSettings *connection, QWidget *parent) :
|
||||
BaseClass(parent),
|
||||
_connection(connection),
|
||||
_connectionStatusReceived(false),
|
||||
_authStatusReceived(false)
|
||||
QDialog(parent),
|
||||
_connSettings(connection->clone()),
|
||||
_server(NULL),
|
||||
_serverHandle(0),
|
||||
_continueExec(true)
|
||||
{
|
||||
AppRegistry::instance().bus()->subscribe(this, ConnectionEstablishedEvent::Type);
|
||||
AppRegistry::instance().bus()->subscribe(this, ConnectionFailedEvent::Type);
|
||||
|
||||
setWindowTitle("Diagnostic");
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); // Remove help button (?)
|
||||
//setFixedSize(dialogSize);
|
||||
|
||||
_yesIcon = GuiRegistry::instance().yesMarkIcon();
|
||||
_noIcon = GuiRegistry::instance().noMarkIcon();
|
||||
_yesPixmap = _yesIcon.pixmap(24, 24);
|
||||
_noPixmap = _noIcon.pixmap(24, 24);
|
||||
_questionIcon = GuiRegistry::instance().questionMarkIcon();
|
||||
_yesPixmap = _yesIcon.pixmap(20, 20);
|
||||
_noPixmap = _noIcon.pixmap(20, 20);
|
||||
_questionPixmap = _questionIcon.pixmap(20, 20);
|
||||
|
||||
QPushButton *closeButton = new QPushButton("&Close");
|
||||
VERIFY(connect(closeButton, SIGNAL(clicked()), this, SLOT(accept())));
|
||||
_connectionIconLabel = new QLabel;
|
||||
_authIconLabel = new QLabel;
|
||||
_connectionLabel = new QLabel;
|
||||
_authIconLabel = new QLabel;
|
||||
_authLabel = new QLabel;
|
||||
_sshIconLabel = new QLabel;
|
||||
_sshLabel = new QLabel;
|
||||
_listIconLabel = new QLabel;
|
||||
_listLabel = new QLabel;
|
||||
|
||||
_loadingMovie = new QMovie(":robomongo/icons/loading.gif", QByteArray(), this);
|
||||
_viewErrorLink = new QLabel("<a href='error' style='color: #777777;'>Show error details</a>");
|
||||
VERIFY(connect(_viewErrorLink, SIGNAL(linkActivated(QString)), this, SLOT(errorLinkActivated(QString))));
|
||||
|
||||
_loadingMovie = new QMovie(":robomongo/icons/loading_ticks_40x40.gif", QByteArray(), this);
|
||||
_loadingMovie->setScaledSize(QSize(20, 20));
|
||||
_loadingMovie->start();
|
||||
|
||||
_connectionIconLabel->setMovie(_loadingMovie);
|
||||
_sshIconLabel->setMovie(_loadingMovie);
|
||||
_authIconLabel->setMovie(_loadingMovie);
|
||||
|
||||
_connectionLabel->setText(QString("Connecting to <b>%1</b>...").arg(QtUtils::toQString(_connection->getFullAddress())));
|
||||
|
||||
if (_connection->hasEnabledPrimaryCredential()) {
|
||||
_authLabel->setText(QString("Authorizing on <b>%1</b> database as <b>%2</b>...")
|
||||
.arg(QtUtils::toQString(_connection->primaryCredential()->databaseName()))
|
||||
.arg(QtUtils::toQString(_connection->primaryCredential()->userName())));
|
||||
} else {
|
||||
_authLabel->setText("Authorization skipped by you");
|
||||
}
|
||||
_listIconLabel->setMovie(_loadingMovie);
|
||||
|
||||
QGridLayout *layout = new QGridLayout();
|
||||
layout->setContentsMargins(20, 20, 20, 10);
|
||||
layout->addWidget(_connectionIconLabel, 0, 0);
|
||||
layout->addWidget(_connectionLabel, 0, 1, Qt::AlignLeft);
|
||||
layout->addWidget(_authIconLabel, 1, 0);
|
||||
layout->addWidget(_authLabel, 1, 1, Qt::AlignLeft);
|
||||
layout->setSizeConstraint(QLayout::SetFixedSize);
|
||||
layout->addWidget(_sshIconLabel, 0, 0);
|
||||
layout->addWidget(_sshLabel, 0, 1, Qt::AlignLeft);
|
||||
layout->addWidget(_connectionIconLabel, 1, 0);
|
||||
layout->addWidget(_connectionLabel, 1, 1, Qt::AlignLeft);
|
||||
layout->addWidget(_authIconLabel, 2, 0);
|
||||
layout->addWidget(_authLabel, 2, 1, Qt::AlignLeft);
|
||||
layout->addWidget(_listIconLabel, 3, 0);
|
||||
layout->addWidget(_listLabel, 3, 1, Qt::AlignLeft);
|
||||
layout->setColumnStretch(0, 0) ; // Give column 0 no stretch ability
|
||||
layout->setColumnStretch(1, 1) ; // Give column 1 stretch ability of ratio 1
|
||||
|
||||
QHBoxLayout *hbox = new QHBoxLayout;
|
||||
hbox->addSpacing(21);
|
||||
hbox->addWidget(_viewErrorLink, 1, Qt::AlignLeft);
|
||||
hbox->addWidget(closeButton, 0, Qt::AlignRight);
|
||||
|
||||
QVBoxLayout *box = new QVBoxLayout;
|
||||
box->addLayout(layout);
|
||||
box->addSpacing(10);
|
||||
box->addWidget(closeButton, 0, Qt::AlignRight);
|
||||
box->addLayout(hbox);
|
||||
setLayout(box);
|
||||
|
||||
ConnectionDiagnosticThread *thread = new ConnectionDiagnosticThread(_connection);
|
||||
VERIFY(connect(thread, SIGNAL(connectionStatus(QString, bool)), this, SLOT(connectionStatus(QString, bool))));
|
||||
VERIFY(connect(thread, SIGNAL(authStatus(QString, bool)), this, SLOT(authStatus(QString, bool))));
|
||||
VERIFY(connect(thread, SIGNAL(completed()), this, SLOT(completed())));
|
||||
VERIFY(connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())));
|
||||
thread->start();
|
||||
}
|
||||
sshStatus(InitialState);
|
||||
connectionStatus(InitialState);
|
||||
authStatus(InitialState);
|
||||
listStatus(InitialState);
|
||||
|
||||
void ConnectionDiagnosticDialog::connectionStatus(QString error, bool connected)
|
||||
{
|
||||
_connectionStatusReceived = true;
|
||||
_viewErrorLink->hide();
|
||||
|
||||
if (connected) {
|
||||
_connectionIconLabel->setPixmap(_yesPixmap);
|
||||
_connectionLabel->setText(QString("Connected to <b>%1</b>").arg(QtUtils::toQString(_connection->getFullAddress())));
|
||||
} else {
|
||||
_connectionIconLabel->setPixmap(_noPixmap);
|
||||
_connectionLabel->setText(QString("Unable to connect to <b>%1</b>").arg(QtUtils::toQString(_connection->getFullAddress())));
|
||||
}
|
||||
|
||||
layout()->activate();
|
||||
setFixedSize(minimumSizeHint());
|
||||
}
|
||||
|
||||
void ConnectionDiagnosticDialog::authStatus(QString error, bool authed)
|
||||
{
|
||||
_authStatusReceived = true;
|
||||
|
||||
if (authed) {
|
||||
_authIconLabel->setPixmap(_yesPixmap);
|
||||
_authLabel->setText(QString("Authorized as <b>%1</b>").arg(QtUtils::toQString(_connection->primaryCredential()->userName())));
|
||||
} else {
|
||||
|
||||
_authIconLabel->setPixmap(_noPixmap);
|
||||
|
||||
if (_connection->hasEnabledPrimaryCredential())
|
||||
_authLabel->setText(QString("Authorization failed"));
|
||||
else
|
||||
_authLabel->setText(QString("Authorization skipped by you"));
|
||||
}
|
||||
|
||||
layout()->activate();
|
||||
setFixedSize(minimumSizeHint());
|
||||
}
|
||||
|
||||
void ConnectionDiagnosticDialog::completed()
|
||||
{
|
||||
if (!_connectionStatusReceived)
|
||||
connectionStatus("", false);
|
||||
|
||||
if (!_authStatusReceived)
|
||||
authStatus("", false);
|
||||
|
||||
// seems that it is wrong to call any method on thread,
|
||||
// because thread already can be disposed.
|
||||
// QThread *thread = static_cast<QThread *>(sender());
|
||||
// thread->quit();
|
||||
}
|
||||
|
||||
|
||||
ConnectionDiagnosticThread::ConnectionDiagnosticThread(ConnectionSettings *connection) :
|
||||
_connection(connection) { }
|
||||
|
||||
void ConnectionDiagnosticThread::run()
|
||||
{
|
||||
boost::scoped_ptr<mongo::DBClientConnection> connection;
|
||||
|
||||
try {
|
||||
connection.reset(new mongo::DBClientConnection);
|
||||
connection->connect(_connection->info());
|
||||
emit connectionStatus("", true);
|
||||
}
|
||||
catch(const mongo::UserException &ex) {
|
||||
const char *what = ex.what();
|
||||
emit connectionStatus(QString(what), false);
|
||||
emit completed();
|
||||
if (!AppRegistry::instance().app()->openServer(_connSettings, ConnectionTest)) {
|
||||
_continueExec = false;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (_connection->hasEnabledPrimaryCredential())
|
||||
{
|
||||
CredentialSettings *credentials = _connection->primaryCredential();
|
||||
_serverHandle = AppRegistry::instance().app()->getLastServerHandle();
|
||||
}
|
||||
|
||||
// Building BSON object:
|
||||
mongo::BSONObj authParams(mongo::BSONObjBuilder()
|
||||
.append("user", credentials->userName())
|
||||
.append("db", credentials->databaseName())
|
||||
.append("pwd", credentials->userPassword())
|
||||
.append("mechanism", credentials->mechanism())
|
||||
.obj());
|
||||
ConnectionDiagnosticDialog::~ConnectionDiagnosticDialog() {
|
||||
if (_server)
|
||||
AppRegistry::instance().app()->closeServer(_server);
|
||||
}
|
||||
|
||||
connection->auth(authParams);
|
||||
emit authStatus("", true);
|
||||
}
|
||||
} catch (const mongo::UserException &ex) {
|
||||
emit authStatus("", false);
|
||||
void ConnectionDiagnosticDialog::errorLinkActivated(const QString &link) {
|
||||
if (_lastErrorMessage.empty())
|
||||
return;
|
||||
|
||||
QMessageBox::information(this, "Error details", QtUtils::toQString(_lastErrorMessage));
|
||||
}
|
||||
|
||||
void ConnectionDiagnosticDialog::sshStatus(State state)
|
||||
{
|
||||
if (!_connSettings->sshSettings()->enabled()) {
|
||||
_sshIconLabel->setVisible(false);
|
||||
_sshLabel->setVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
emit completed();
|
||||
if (state == InitialState) {
|
||||
_sshIconLabel->setMovie(_loadingMovie);
|
||||
_sshLabel->setText(QString("Connecting to SSH server at <b>%1:%2</b>...")
|
||||
.arg(QtUtils::toQString(_connSettings->sshSettings()->host()))
|
||||
.arg(_connSettings->sshSettings()->port()));
|
||||
} else if (state == CompletedState) {
|
||||
_sshIconLabel->setPixmap(_yesPixmap);
|
||||
_sshLabel->setText(QString("Connected to SSH server at <b>%1:%2</b>")
|
||||
.arg(QtUtils::toQString(_connSettings->sshSettings()->host()))
|
||||
.arg(_connSettings->sshSettings()->port()));
|
||||
} else if (state == FailedState) {
|
||||
_sshIconLabel->setPixmap(_noPixmap);
|
||||
_sshLabel->setText(QString("Unable to connect to SSH server at <b>%1:%2</b>")
|
||||
.arg(QtUtils::toQString(_connSettings->sshSettings()->host()))
|
||||
.arg(_connSettings->sshSettings()->port()));
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionDiagnosticDialog::connectionStatus(State state)
|
||||
{
|
||||
// Add info about tunneling if SSH or SSL is used
|
||||
QString tunnelNote(""); // No tunnel info when neither SSH nor SSL enabled
|
||||
if (_connSettings->sshSettings()->enabled())
|
||||
tunnelNote = " via SSH tunnel";
|
||||
else if (_connSettings->sslSettings()->sslEnabled())
|
||||
tunnelNote = " via SSL tunnel";
|
||||
else
|
||||
tunnelNote = "";
|
||||
|
||||
|
||||
auto replicaSetStr = QString::fromStdString(_connSettings->connectionName()) + " [Replica Set]";
|
||||
replicaSetStr = (_connSettings->replicaSetSettings()->members().size() > 0)
|
||||
? replicaSetStr + '(' + QString::fromStdString(
|
||||
_connSettings->replicaSetSettings()->members()[0]) + ')'
|
||||
: replicaSetStr + "";
|
||||
|
||||
QString const& serverAddress = _connSettings->isReplicaSet()
|
||||
? replicaSetStr
|
||||
: QString::fromStdString(_connSettings->getFullAddress());
|
||||
|
||||
// Set main info text at dialog
|
||||
if (state == InitialState) {
|
||||
_connectionIconLabel->setMovie(_loadingMovie);
|
||||
_connectionLabel->setText(QString("Connecting to <b>%1</b>%2...").arg(serverAddress, tunnelNote));
|
||||
|
||||
} else if (state == CompletedState) {
|
||||
_connectionIconLabel->setPixmap(_yesPixmap);
|
||||
_connectionLabel->setText(QString("Connected to <b>%1</b>%2").arg(serverAddress, tunnelNote));
|
||||
} else if (state == FailedState) {
|
||||
_connectionIconLabel->setPixmap(_noPixmap);
|
||||
_connectionLabel->setText(QString("Failed to connect to <b>%1</b>%2").arg(serverAddress, tunnelNote));
|
||||
} else if (state == NotPerformedState) {
|
||||
_connectionIconLabel->setPixmap(_questionPixmap);
|
||||
_connectionLabel->setText(QString("No chance to try connection to <b>%1</b>%2").
|
||||
arg(serverAddress, tunnelNote));
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionDiagnosticDialog::authStatus(State state)
|
||||
{
|
||||
if (!_connSettings->hasEnabledPrimaryCredential()) {
|
||||
_authIconLabel->setVisible(false);
|
||||
_authLabel->setVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (state == InitialState) {
|
||||
_authIconLabel->setMovie(_loadingMovie);
|
||||
_authLabel->setText(QString("Authorizing on <b>%1</b> database as <b>%2</b>...")
|
||||
.arg(QtUtils::toQString(_connSettings->primaryCredential()->databaseName()))
|
||||
.arg(QtUtils::toQString(_connSettings->primaryCredential()->userName())));
|
||||
} else if (state == CompletedState) {
|
||||
_authIconLabel->setPixmap(_yesPixmap);
|
||||
_authLabel->setText(QString("Authorized on <b>%1</b> database as <b>%2</b>")
|
||||
.arg(QtUtils::toQString(_connSettings->primaryCredential()->databaseName()))
|
||||
.arg(QtUtils::toQString(_connSettings->primaryCredential()->userName())));
|
||||
} else if (state == FailedState) {
|
||||
_authIconLabel->setPixmap(_noPixmap);
|
||||
_authLabel->setText(QString("Authorization failed on <b>%1</b> database as <b>%2</b>")
|
||||
.arg(QtUtils::toQString(_connSettings->primaryCredential()->databaseName()))
|
||||
.arg(QtUtils::toQString(_connSettings->primaryCredential()->userName())));
|
||||
} else if (state == NotPerformedState) {
|
||||
_authIconLabel->setPixmap(_questionPixmap);
|
||||
_authLabel->setText(QString("No chance to authorize"));
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionDiagnosticDialog::listStatus(State state)
|
||||
{
|
||||
if (_connSettings->hasEnabledPrimaryCredential()) {
|
||||
_listIconLabel->setVisible(false);
|
||||
_listLabel->setVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (state == InitialState) {
|
||||
_listIconLabel->setMovie(_loadingMovie);
|
||||
_listLabel->setText(QString("Loading list of databases..."));
|
||||
} else if (state == CompletedState) {
|
||||
_listIconLabel->setPixmap(_yesPixmap);
|
||||
_listLabel->setText(QString("Access to databases is available"));
|
||||
} else if (state == FailedState) {
|
||||
_listIconLabel->setPixmap(_noPixmap);
|
||||
_listLabel->setText(QString("Failed to load list of databases"));
|
||||
} else if (state == NotPerformedState) {
|
||||
_listIconLabel->setPixmap(_questionPixmap);
|
||||
_listLabel->setText(QString("No chance to load list of databases"));
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionDiagnosticDialog::handle(ConnectionEstablishedEvent *event) {
|
||||
if (event->connectionType != ConnectionTest)
|
||||
return;
|
||||
|
||||
sshStatus(CompletedState);
|
||||
connectionStatus(CompletedState);
|
||||
authStatus(CompletedState);
|
||||
listStatus(CompletedState);
|
||||
|
||||
// Remember in order to delete on dialog close
|
||||
_server = static_cast<MongoServer*>(event->sender());
|
||||
}
|
||||
|
||||
void ConnectionDiagnosticDialog::handle(ConnectionFailedEvent *event) {
|
||||
if (event->connectionType != ConnectionTest || event->serverHandle != _serverHandle)
|
||||
return;
|
||||
|
||||
sshStatus(CompletedState);
|
||||
connectionStatus(CompletedState);
|
||||
authStatus(CompletedState);
|
||||
listStatus(CompletedState);
|
||||
|
||||
switch (event->reason) {
|
||||
case ConnectionFailedEvent::SshConnection:
|
||||
sshStatus(FailedState);
|
||||
connectionStatus(NotPerformedState);
|
||||
authStatus(NotPerformedState);
|
||||
listStatus(NotPerformedState);
|
||||
break;
|
||||
case ConnectionFailedEvent::SshChannel:
|
||||
case ConnectionFailedEvent::MongoConnection:
|
||||
connectionStatus(FailedState);
|
||||
authStatus(NotPerformedState);
|
||||
listStatus(NotPerformedState);
|
||||
break;
|
||||
case ConnectionFailedEvent::MongoAuth:
|
||||
authStatus(FailedState);
|
||||
listStatus(FailedState);
|
||||
break;
|
||||
case ConnectionFailedEvent::SslConnection:
|
||||
connectionStatus(FailedState);
|
||||
authStatus(NotPerformedState);
|
||||
listStatus(NotPerformedState);
|
||||
break;
|
||||
}
|
||||
|
||||
// Show link for additional error details
|
||||
if (!event->message.empty()) {
|
||||
_lastErrorMessage = event->message;
|
||||
_viewErrorLink->show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,63 +2,70 @@
|
||||
|
||||
#include <QDialog>
|
||||
#include <QIcon>
|
||||
#include <QThread>
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QLabel;
|
||||
class QMovie;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
class ConnectionEstablishedEvent;
|
||||
class ConnectionFailedEvent;
|
||||
class ConnectionSettings;
|
||||
class MongoServer;
|
||||
|
||||
class ConnectionDiagnosticDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
typedef QDialog BaseClass;
|
||||
static const QSize dialogSize;
|
||||
ConnectionDiagnosticDialog(ConnectionSettings *connection, QWidget *parent = 0);
|
||||
~ConnectionDiagnosticDialog();
|
||||
|
||||
bool continueExec() const { return _continueExec; }
|
||||
|
||||
protected Q_SLOTS:
|
||||
void connectionStatus(QString error, bool connected);
|
||||
void authStatus(QString error, bool authed);
|
||||
void completed();
|
||||
void handle(ConnectionEstablishedEvent *event);
|
||||
void handle(ConnectionFailedEvent *event);
|
||||
void errorLinkActivated(const QString &link);
|
||||
|
||||
private:
|
||||
ConnectionSettings *_connection;
|
||||
|
||||
enum State
|
||||
{
|
||||
InitialState,
|
||||
CompletedState,
|
||||
FailedState,
|
||||
NotPerformedState
|
||||
};
|
||||
|
||||
void sshStatus(State state);
|
||||
void connectionStatus(State state);
|
||||
void authStatus(State state);
|
||||
void listStatus(State state);
|
||||
|
||||
ConnectionSettings *_connSettings;
|
||||
QIcon _yesIcon;
|
||||
QIcon _noIcon;
|
||||
QIcon _waitIcon;
|
||||
QIcon _questionIcon;
|
||||
QMovie *_loadingMovie;
|
||||
|
||||
QPixmap _yesPixmap;
|
||||
QPixmap _noPixmap;
|
||||
QPixmap _questionPixmap;
|
||||
|
||||
QLabel *_sshIconLabel;
|
||||
QLabel *_sshLabel;
|
||||
QLabel *_connectionIconLabel;
|
||||
QLabel *_connectionLabel;
|
||||
QLabel *_authIconLabel;
|
||||
QLabel *_authLabel;
|
||||
QLabel *_listIconLabel; // List database names
|
||||
QLabel *_listLabel;
|
||||
|
||||
bool _connectionStatusReceived;
|
||||
bool _authStatusReceived;
|
||||
};
|
||||
QLabel *_viewErrorLink;
|
||||
std::string _lastErrorMessage;
|
||||
|
||||
class ConnectionDiagnosticThread : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ConnectionDiagnosticThread(ConnectionSettings *connection);
|
||||
|
||||
Q_SIGNALS:
|
||||
void connectionStatus(QString error, bool connected);
|
||||
void authStatus(QString error, bool authed);
|
||||
void completed();
|
||||
|
||||
protected:
|
||||
void run();
|
||||
|
||||
private:
|
||||
ConnectionSettings *_connection;
|
||||
MongoServer *_server;
|
||||
int _serverHandle;
|
||||
bool _continueExec;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "robomongo/gui/dialogs/ConnectionSslTab.h"
|
||||
#include "robomongo/gui/dialogs/ConnectionSslTab.h"
|
||||
#include "robomongo/gui/dialogs/SSHTunnelTab.h"
|
||||
#include "robomongo/gui/dialogs/SSLTab.h"
|
||||
#include "robomongo/gui/dialogs/ConnectionDiagnosticDialog.h"
|
||||
#include "robomongo/core/utils/QtUtils.h"
|
||||
|
||||
@@ -50,18 +51,14 @@ namespace Robomongo
|
||||
_basicTab = new ConnectionBasicTab(_connection);
|
||||
_authTab = new ConnectionAuthTab(_connection);
|
||||
_advancedTab = new ConnectionAdvancedTab(_connection);
|
||||
_sshTab = new SshTunnelTab(_connection);
|
||||
|
||||
// v0.9
|
||||
//_sslTab = new ConnectionSslTab(_connection);
|
||||
_sshTab = new SshTunnelTab(_connection);
|
||||
_sslTab = new SSLTab(_connection);
|
||||
|
||||
tabWidget->addTab(_basicTab, "Connection");
|
||||
tabWidget->addTab(_authTab, "Authentication");
|
||||
tabWidget->addTab(_advancedTab, "Advanced");
|
||||
tabWidget->addTab(_sshTab, "SSH");
|
||||
|
||||
// v0.9
|
||||
//tabWidget->addTab(_sslTab, "SSL");
|
||||
tabWidget->addTab(_sslTab, "SSL");
|
||||
tabWidget->addTab(_advancedTab, "Advanced");
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addWidget(tabWidget);
|
||||
@@ -69,6 +66,10 @@ namespace Robomongo
|
||||
setLayout(mainLayout);
|
||||
|
||||
_basicTab->setFocus();
|
||||
adjustSize();
|
||||
|
||||
// Set minimum width - adjustment after adding SSLTab
|
||||
setMinimumWidth(550);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,27 +77,29 @@ namespace Robomongo
|
||||
*/
|
||||
void ConnectionDialog::accept()
|
||||
{
|
||||
if (validateAndApply()){
|
||||
if (validateAndApply()) {
|
||||
QDialog::accept();
|
||||
}
|
||||
}
|
||||
|
||||
bool ConnectionDialog::validateAndApply()
|
||||
{
|
||||
#ifdef SSH_SUPPORT_ENABLED
|
||||
bool isSshAndSsl = _sslTab->isSslSupported() && _sshTab->isSshSupported();
|
||||
if (isSshAndSsl) {
|
||||
QMessageBox::warning(this, "Invalid Transport", "SSH and SSL cannot be enabled simultaneously. Please uncheck one of them.");
|
||||
bool sshAndSslChecked = (_sslTab->sslEnabled() && _sshTab->sshEnabled());
|
||||
if (sshAndSslChecked)
|
||||
{
|
||||
QMessageBox::warning(this, "Invalid Configuration", "SSH and SSL cannot be enabled simultaneously. "
|
||||
"Please use one of them.");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
_basicTab->accept();
|
||||
|
||||
_authTab->accept();
|
||||
_advancedTab->accept();
|
||||
_sshTab->accept();
|
||||
|
||||
// v0.9
|
||||
// _sslTab->accept();
|
||||
if (!_basicTab->accept() || !_sshTab->accept() || !_sslTab->accept())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -105,9 +108,13 @@ namespace Robomongo
|
||||
*/
|
||||
void ConnectionDialog::testConnection()
|
||||
{
|
||||
if(validateAndApply()){
|
||||
ConnectionDiagnosticDialog diag(_connection,this);
|
||||
diag.exec();
|
||||
}
|
||||
if (!validateAndApply())
|
||||
return;
|
||||
|
||||
ConnectionDiagnosticDialog diag(_connection, this);
|
||||
if (!diag.continueExec())
|
||||
return;
|
||||
|
||||
diag.exec();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Robomongo
|
||||
class ConnectionAuthTab;
|
||||
class ConnectionBasicTab;
|
||||
class ConnectionAdvancedTab;
|
||||
class ConnectionSslTab;
|
||||
class SSLTab;
|
||||
class SshTunnelTab;
|
||||
/**
|
||||
* @brief This Dialog allows to edit single connection
|
||||
@@ -44,9 +44,7 @@ namespace Robomongo
|
||||
ConnectionBasicTab *_basicTab;
|
||||
ConnectionAdvancedTab *_advancedTab;
|
||||
SshTunnelTab *_sshTab;
|
||||
|
||||
// v0.9
|
||||
//ConnectionSslTab *_sslTab;
|
||||
SSLTab *_sslTab;
|
||||
|
||||
#ifdef SSH_SUPPORT_ENABLED
|
||||
SshTunnelTab *_sshTab;
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace Robomongo
|
||||
_sslSupport->setChecked(_settings->sslInfo()._sslSupport);
|
||||
|
||||
_selectPemFileButton = new QPushButton("...");
|
||||
_selectPemFileButton->setFixedSize(20,20);
|
||||
_selectPemFileButton->setFixedSize(20, 20);
|
||||
VERIFY(connect(_selectPemFileButton, SIGNAL(clicked()), this, SLOT(setSslPEMKeyFile())));
|
||||
|
||||
_sslPemLabel = new QLabel("SSL Certificate:");
|
||||
|
||||
@@ -9,8 +9,15 @@
|
||||
#include <QDialogButtonBox>
|
||||
#include <QTreeWidgetItem>
|
||||
#include <QKeyEvent>
|
||||
#include <QApplication>
|
||||
#include <QSettings>
|
||||
|
||||
#include "robomongo/core/AppRegistry.h"
|
||||
#include "robomongo/core/settings/ConnectionSettings.h"
|
||||
#include "robomongo/core/settings/ReplicaSetSettings.h"
|
||||
#include "robomongo/core/settings/SettingsManager.h"
|
||||
#include "robomongo/core/settings/SslSettings.h"
|
||||
#include "robomongo/core/settings/SshSettings.h"
|
||||
#include "robomongo/core/settings/CredentialSettings.h"
|
||||
#include "robomongo/core/settings/SettingsManager.h"
|
||||
#include "robomongo/core/utils/QtUtils.h"
|
||||
@@ -42,20 +49,49 @@ namespace Robomongo
|
||||
*/
|
||||
void setConnection(ConnectionSettings *connection)
|
||||
{
|
||||
setText(0, QtUtils::toQString(connection->connectionName()));
|
||||
setText(1, QtUtils::toQString(connection->getFullAddress()));
|
||||
_connection = connection;
|
||||
|
||||
if (connection->hasEnabledPrimaryCredential()) {
|
||||
QString authString = QString("%1 / %2").arg(QtUtils::toQString(connection->primaryCredential()->databaseName())).arg(QtUtils::toQString(connection->primaryCredential()->userName()));
|
||||
setText(2, authString);
|
||||
setIcon(2, GuiRegistry::instance().keyIcon());
|
||||
} else {
|
||||
setIcon(2, QIcon());
|
||||
setText(2, "");
|
||||
if (_connection->isReplicaSet()) {
|
||||
setIcon(0, GuiRegistry::instance().replicaSetIcon());
|
||||
setText(0, QtUtils::toQString(_connection->connectionName()));
|
||||
auto const repSetSize = _connection->replicaSetSettings()->members().size();
|
||||
auto addrText = QString::number(repSetSize) + ((repSetSize > 1) ? " nodes" : " node");
|
||||
if (!_connection->replicaSetSettings()->members().empty()) {
|
||||
addrText += QString::fromStdString(" (" + _connection->replicaSetSettings()->members().front() + ")");
|
||||
}
|
||||
setText(1, addrText);
|
||||
}
|
||||
else {
|
||||
setIcon(0, GuiRegistry::instance().serverIcon());
|
||||
setText(0, QtUtils::toQString(_connection->connectionName()));
|
||||
setText(1, QtUtils::toQString(_connection->getFullAddress()));
|
||||
}
|
||||
|
||||
if (_connection->imported()) {
|
||||
setIcon(0, GuiRegistry::instance().serverImportedIcon());
|
||||
}
|
||||
|
||||
// Header "Attributes" (column[2])
|
||||
setText(2, _connection->isReplicaSet() ? "Replica Set" : "");
|
||||
|
||||
if (_connection->sslSettings()->sslEnabled())
|
||||
setText(2, text(2) + (text(2).isEmpty() ? "SSL" : ", SSL"));
|
||||
|
||||
if (_connection->sshSettings()->enabled())
|
||||
setText(2, text(2) + (text(2).isEmpty() ? "SSH" : ", SSH"));
|
||||
|
||||
// Header "Auth. Database/User" (column[3])
|
||||
if (_connection->hasEnabledPrimaryCredential()) {
|
||||
auto authString = QString("%1 / %2").arg(QtUtils::toQString(_connection->primaryCredential()->databaseName()))
|
||||
.arg(QtUtils::toQString(_connection->primaryCredential()->userName()));
|
||||
setText(3, authString + " ");
|
||||
setIcon(3, GuiRegistry::instance().keyIcon());
|
||||
}
|
||||
else {
|
||||
setIcon(3, QIcon());
|
||||
setText(3, "");
|
||||
}
|
||||
|
||||
_connection = connection;
|
||||
setIcon(0, GuiRegistry::instance().serverIcon());
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -65,8 +101,8 @@ namespace Robomongo
|
||||
/**
|
||||
* @brief Creates dialog
|
||||
*/
|
||||
ConnectionsDialog::ConnectionsDialog(SettingsManager *settingsManager, QWidget *parent)
|
||||
: QDialog(parent), _settingsManager(settingsManager)
|
||||
ConnectionsDialog::ConnectionsDialog(SettingsManager *settingsManager, bool checkForImported, QWidget *parent)
|
||||
: QDialog(parent), _settingsManager(settingsManager), _checkForImported(checkForImported)
|
||||
{
|
||||
setWindowIcon(GuiRegistry::instance().connectIcon());
|
||||
setWindowTitle("MongoDB Connections");
|
||||
@@ -94,12 +130,13 @@ namespace Robomongo
|
||||
_listWidget->setIndentation(5);
|
||||
|
||||
QStringList colums;
|
||||
colums << "Name" << "Address" << "Auth. Database / User";
|
||||
colums << "Name" << "Address" << "Attributes" << "Auth. Database / User";
|
||||
_listWidget->setHeaderLabels(colums);
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
|
||||
_listWidget->header()->setSectionResizeMode(0, QHeaderView::Stretch);
|
||||
_listWidget->header()->setSectionResizeMode(1, QHeaderView::Stretch);
|
||||
_listWidget->header()->setSectionResizeMode(2, QHeaderView::Stretch);
|
||||
_listWidget->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
|
||||
_listWidget->header()->setSectionResizeMode(3, QHeaderView::ResizeToContents);
|
||||
#endif
|
||||
//_listWidget->setViewMode(QListView::ListMode);
|
||||
_listWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
|
||||
@@ -124,13 +161,33 @@ namespace Robomongo
|
||||
VERIFY(connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())));
|
||||
|
||||
QHBoxLayout *bottomLayout = new QHBoxLayout;
|
||||
bottomLayout->addWidget(buttonBox);
|
||||
|
||||
QLabel *intro = new QLabel(
|
||||
"<a href='create'>Create</a>, "
|
||||
"<a href='edit'>edit</a>, "
|
||||
"<a href='remove'>remove</a>, "
|
||||
"<a href='clone'>clone</a> or reorder connections via drag'n'drop.");
|
||||
// Information message is shown when connection
|
||||
// settings are imported from previous version of Robomongo
|
||||
int importedCount = _settingsManager->importedConnectionsCount();
|
||||
if (_checkForImported && importedCount > 0) {
|
||||
QIcon importIcon = qApp->style()->standardIcon(QStyle::SP_MessageBoxInformation);
|
||||
QPixmap importPixmap = importIcon.pixmap(20, 20);
|
||||
QLabel *importLabelIcon = new QLabel;
|
||||
importLabelIcon->setPixmap(importPixmap);
|
||||
QString importedRecords = importedCount > 1 ? "records" : "record";
|
||||
QLabel *importLabelMessage = new QLabel(QString(
|
||||
"<span style='color: #777777;'>"
|
||||
"Connection settings have been imported (%1 %2)"
|
||||
"</span>").arg(importedCount).arg(importedRecords));
|
||||
|
||||
bottomLayout->addWidget(importLabelIcon, 0, Qt::AlignLeft);
|
||||
bottomLayout->addWidget(importLabelMessage, 1, Qt::AlignLeft);
|
||||
}
|
||||
|
||||
bottomLayout->addWidget(buttonBox, 0, Qt::AlignRight);
|
||||
|
||||
QLabel *intro = new QLabel(QString(
|
||||
"<a style='color: %1' href='create'>Create</a>, "
|
||||
"<a style='color: %1' href='edit'>edit</a>, "
|
||||
"<a style='color: %1' href='remove'>remove</a>, "
|
||||
"<a style='color: %1' href='clone'>clone</a> "
|
||||
"or reorder connections via drag'n'drop.").arg("#106CD6"));
|
||||
intro->setWordWrap(true);
|
||||
|
||||
VERIFY(connect(intro, SIGNAL(linkActivated(QString)), this, SLOT(linkActivated(QString))));
|
||||
@@ -144,17 +201,19 @@ namespace Robomongo
|
||||
mainLayout->addLayout(firstColumnLayout, 1);
|
||||
|
||||
// Populate list with connections
|
||||
SettingsManager::ConnectionSettingsContainerType connections = _settingsManager->connections();
|
||||
for (SettingsManager::ConnectionSettingsContainerType::const_iterator it = connections.begin(); it != connections.end(); ++it) {
|
||||
ConnectionSettings *connectionModel = *it;
|
||||
std::vector<ConnectionSettings*> connectionSettings = _settingsManager->connections();
|
||||
for (auto const& connSetting : connectionSettings) {
|
||||
ConnectionSettings *connectionModel { connSetting };
|
||||
add(connectionModel);
|
||||
}
|
||||
|
||||
// Highlight first item
|
||||
// Highlight last item
|
||||
if (_listWidget->topLevelItemCount() > 0)
|
||||
_listWidget->setCurrentItem(_listWidget->topLevelItem(0));
|
||||
_listWidget->setCurrentItem(_listWidget->topLevelItem(_listWidget->topLevelItemCount()-1));
|
||||
|
||||
_listWidget->setFocus();
|
||||
|
||||
restoreWindowSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -162,17 +221,30 @@ namespace Robomongo
|
||||
*/
|
||||
void ConnectionsDialog::accept()
|
||||
{
|
||||
ConnectionListWidgetItem *currentItem = (ConnectionListWidgetItem *) _listWidget->currentItem();
|
||||
auto currentItem = dynamic_cast<ConnectionListWidgetItem*>(_listWidget->currentItem());
|
||||
|
||||
// Do nothing if no item selected
|
||||
if (!currentItem)
|
||||
return;
|
||||
|
||||
_selectedConnection = currentItem->connection();
|
||||
saveWindowSettings();
|
||||
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void ConnectionsDialog::reject()
|
||||
{
|
||||
saveWindowSettings();
|
||||
QDialog::reject();
|
||||
}
|
||||
|
||||
void ConnectionsDialog::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
saveWindowSettings();
|
||||
QWidget::closeEvent(event);
|
||||
}
|
||||
|
||||
void ConnectionsDialog::linkActivated(const QString &link)
|
||||
{
|
||||
if (link == "create")
|
||||
@@ -189,19 +261,19 @@ namespace Robomongo
|
||||
* @brief Initiate 'add' action, usually when user clicked on Add button
|
||||
*/
|
||||
void ConnectionsDialog::add()
|
||||
{
|
||||
ConnectionSettings *newModel = new ConnectionSettings();
|
||||
ConnectionDialog editDialog(newModel);
|
||||
{
|
||||
auto newConnSettings = std::unique_ptr<ConnectionSettings>(new ConnectionSettings(false));
|
||||
ConnectionDialog editDialog(newConnSettings.get());
|
||||
|
||||
// Do nothing if not accepted
|
||||
if (editDialog.exec() != QDialog::Accepted) {
|
||||
delete newModel;
|
||||
return;
|
||||
}
|
||||
|
||||
_settingsManager->addConnection(newModel);
|
||||
add(newConnSettings.get());
|
||||
_settingsManager->addConnection(newConnSettings.release());
|
||||
|
||||
_listWidget->setFocus();
|
||||
add(newModel);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -209,14 +281,13 @@ namespace Robomongo
|
||||
*/
|
||||
void ConnectionsDialog::edit()
|
||||
{
|
||||
ConnectionListWidgetItem *currentItem =
|
||||
(ConnectionListWidgetItem *) _listWidget->currentItem();
|
||||
auto currentItem = dynamic_cast<ConnectionListWidgetItem*>(_listWidget->currentItem());
|
||||
|
||||
// Do nothing if no item selected
|
||||
if (currentItem == 0)
|
||||
if (!currentItem)
|
||||
return;
|
||||
|
||||
ConnectionSettings *connection = currentItem->connection();
|
||||
auto connection = currentItem->connection();
|
||||
boost::scoped_ptr<ConnectionSettings> clonedConnection(connection->clone());
|
||||
ConnectionDialog editDialog(clonedConnection.get());
|
||||
|
||||
@@ -233,7 +304,7 @@ namespace Robomongo
|
||||
activateWindow();
|
||||
|
||||
int size = _connectionItems.size();
|
||||
for (int i=0; i<size; ++i)
|
||||
for (int i = 0; i<size; ++i)
|
||||
{
|
||||
ConnectionListWidgetItem *item = _connectionItems[i];
|
||||
if (_connectionItems[i]->connection() == connection) {
|
||||
@@ -255,18 +326,18 @@ namespace Robomongo
|
||||
if (!currentItem)
|
||||
return;
|
||||
|
||||
ConnectionSettings *connectionModel = currentItem->connection();
|
||||
ConnectionSettings *connSettings = currentItem->connection();
|
||||
|
||||
// Ask user
|
||||
int answer = QMessageBox::question(this,
|
||||
"Connections",
|
||||
QString("Really delete \"%1\" connection?").arg(QtUtils::toQString(connectionModel->getReadableName())),
|
||||
QString("Really delete \"%1\" connection?").arg(QtUtils::toQString(connSettings->getReadableName())),
|
||||
QMessageBox::Yes, QMessageBox::No, QMessageBox::NoButton);
|
||||
|
||||
if (answer != QMessageBox::Yes)
|
||||
return;
|
||||
|
||||
_settingsManager->removeConnection(connectionModel);
|
||||
_settingsManager->removeConnection(connSettings);
|
||||
delete currentItem;
|
||||
}
|
||||
|
||||
@@ -281,9 +352,10 @@ namespace Robomongo
|
||||
|
||||
// Clone connection
|
||||
ConnectionSettings *connection = currentItem->connection()->clone();
|
||||
std::string newConnectionName="Copy of "+connection->connectionName();
|
||||
std::string newConnectionName = "Copy of "+connection->connectionName();
|
||||
|
||||
connection->setConnectionName(newConnectionName);
|
||||
connection->replicaSetSettings()->setSetName("");
|
||||
|
||||
ConnectionDialog editDialog(connection);
|
||||
|
||||
@@ -303,15 +375,13 @@ namespace Robomongo
|
||||
*/
|
||||
void ConnectionsDialog::listWidget_layoutChanged()
|
||||
{
|
||||
int count = _listWidget->topLevelItemCount();
|
||||
|
||||
// Make childrens toplevel again. This is a bad, but quickiest item reordering
|
||||
// implementation.
|
||||
for(int i = 0; i < count; i++)
|
||||
for (int i = 0; i < _listWidget->topLevelItemCount(); i++)
|
||||
{
|
||||
ConnectionListWidgetItem * item = (ConnectionListWidgetItem *) _listWidget->topLevelItem(i);
|
||||
auto item = (ConnectionListWidgetItem *) _listWidget->topLevelItem(i);
|
||||
if (item->childCount() > 0) {
|
||||
ConnectionListWidgetItem *childItem = (ConnectionListWidgetItem *) item->child(0);
|
||||
auto childItem = (ConnectionListWidgetItem *) item->child(0);
|
||||
item->removeChild(childItem);
|
||||
_listWidget->insertTopLevelItem(++i, childItem);
|
||||
_listWidget->setCurrentItem(childItem);
|
||||
@@ -319,11 +389,10 @@ namespace Robomongo
|
||||
}
|
||||
}
|
||||
|
||||
count = _listWidget->topLevelItemCount();
|
||||
SettingsManager::ConnectionSettingsContainerType items;
|
||||
for(int i = 0; i < count; i++)
|
||||
for (int i = 0; i < _listWidget->topLevelItemCount(); i++)
|
||||
{
|
||||
ConnectionListWidgetItem * item = (ConnectionListWidgetItem *) _listWidget->topLevelItem(i);
|
||||
auto item = (ConnectionListWidgetItem *) _listWidget->topLevelItem(i);
|
||||
items.push_back(item->connection());
|
||||
}
|
||||
|
||||
@@ -335,8 +404,9 @@ namespace Robomongo
|
||||
*/
|
||||
void ConnectionsDialog::add(ConnectionSettings *connection)
|
||||
{
|
||||
ConnectionListWidgetItem *item = new ConnectionListWidgetItem(connection);
|
||||
auto item = new ConnectionListWidgetItem(connection);
|
||||
_listWidget->addTopLevelItem(item);
|
||||
_listWidget->setCurrentItem(item);
|
||||
_connectionItems.push_back(item);
|
||||
}
|
||||
|
||||
@@ -362,6 +432,17 @@ namespace Robomongo
|
||||
QDialog::keyPressEvent(event);
|
||||
}
|
||||
|
||||
void ConnectionsDialog::restoreWindowSettings()
|
||||
{
|
||||
QSettings settings("Paralect", "Robomongo");
|
||||
resize(settings.value("ConnectionsDialog/size").toSize());
|
||||
}
|
||||
|
||||
void ConnectionsDialog::saveWindowSettings() const
|
||||
{
|
||||
QSettings settings("Paralect", "Robomongo");
|
||||
settings.setValue("ConnectionsDialog/size", size());
|
||||
}
|
||||
|
||||
ConnectionsTreeWidget::ConnectionsTreeWidget()
|
||||
{
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Robomongo
|
||||
/**
|
||||
* @brief Creates dialog
|
||||
*/
|
||||
ConnectionsDialog(SettingsManager *manager, QWidget *parent = 0);
|
||||
ConnectionsDialog(SettingsManager *manager, bool checkForImported, QWidget *parent = 0);
|
||||
|
||||
/**
|
||||
* @brief ConnectionSettings, that was selected after pressing on
|
||||
@@ -31,19 +31,31 @@ namespace Robomongo
|
||||
*/
|
||||
ConnectionSettings *selectedConnection() const { return _selectedConnection; }
|
||||
|
||||
public Q_SLOTS:
|
||||
/**
|
||||
* @brief This function is called when user clicks on "Connect" button.
|
||||
*/
|
||||
virtual void accept();
|
||||
void accept() override;
|
||||
|
||||
/**
|
||||
* @brief Called when "Cancel" button clicked.
|
||||
*/
|
||||
void reject() override;
|
||||
|
||||
/**
|
||||
* @brief Add connection to the list widget
|
||||
*/
|
||||
void add(ConnectionSettings *connection);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Reimplementing closeEvent in order to do some pre-close actions.
|
||||
*/
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
|
||||
private Q_SLOTS:
|
||||
void linkActivated(const QString &link);
|
||||
|
||||
/**
|
||||
* @brief Add connection to the list widget
|
||||
*/
|
||||
void add(ConnectionSettings *connection);
|
||||
|
||||
/**
|
||||
* @brief Initiate 'add' action, usually when user clicked on Add button
|
||||
*/
|
||||
@@ -72,9 +84,20 @@ namespace Robomongo
|
||||
*/
|
||||
void listWidget_layoutChanged();
|
||||
|
||||
void keyPressEvent(QKeyEvent* event);
|
||||
void keyPressEvent(QKeyEvent* event) override;
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Restore window settings from system registry
|
||||
*/
|
||||
void restoreWindowSettings();
|
||||
|
||||
/**
|
||||
* @brief Save windows settings into system registry
|
||||
*/
|
||||
void saveWindowSettings() const;
|
||||
|
||||
/**
|
||||
* @brief ConnectionSettings, that was selected after pressing on
|
||||
* "Connect" button
|
||||
@@ -96,6 +119,8 @@ namespace Robomongo
|
||||
* ConnectionListWidgetItem*
|
||||
*/
|
||||
ConnectionListItemContainerType _connectionItems;
|
||||
|
||||
bool _checkForImported;
|
||||
};
|
||||
|
||||
class ConnectionsTreeWidget : public QTreeWidget
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
const QSize CopyCollection::minimumSize = QSize(300,150);
|
||||
const QSize CopyCollection::minimumSize = QSize(300, 150);
|
||||
|
||||
CopyCollection::CopyCollection(const QString &serverName, const QString &database,
|
||||
const QString &collection, QWidget *parent) :
|
||||
@@ -28,9 +28,9 @@ namespace Robomongo
|
||||
{
|
||||
App::MongoServersContainerType servers = AppRegistry::instance().app()->getServers();
|
||||
QSet<QString> uniqueConnectionsNames;
|
||||
for(App::MongoServersContainerType::const_iterator it = servers.begin(); it != servers.end(); ++it){
|
||||
for (App::MongoServersContainerType::const_iterator it = servers.begin(); it != servers.end(); ++it) {
|
||||
MongoServer *server = *it;
|
||||
if (server->isConnected() ){
|
||||
if (server->isConnected()) {
|
||||
_servers.push_back(server);
|
||||
uniqueConnectionsNames.insert(QtUtils::toQString(server->connectionRecord()->connectionName()));
|
||||
}
|
||||
@@ -109,7 +109,7 @@ namespace Robomongo
|
||||
}
|
||||
}
|
||||
_databaseComboBox->addItems(server->getDatabasesNames());
|
||||
if (_currentServerName==QtUtils::toQString(server->connectionRecord()->getFullAddress())) {
|
||||
if (_currentServerName == QtUtils::toQString(server->connectionRecord()->getFullAddress())) {
|
||||
_databaseComboBox->removeItem(_databaseComboBox->findText(_currentDatabase));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,581 @@
|
||||
#include "robomongo/gui/dialogs/CreateCollectionDialog.h"
|
||||
|
||||
#include <mongo/bson/bsonobjbuilder.h>
|
||||
#include <QApplication>
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLineEdit>
|
||||
#include <QLabel>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <QSettings>
|
||||
#include <Qsci/qscilexerjavascript.h>
|
||||
#include <Qsci/qsciscintilla.h>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "robomongo/gui/widgets/workarea/IndicatorLabel.h"
|
||||
#include "robomongo/gui/GuiRegistry.h"
|
||||
#include "robomongo/core/domain/MongoDatabase.h"
|
||||
#include "robomongo/core/utils/QtUtils.h"
|
||||
#include "robomongo/gui/editors/FindFrame.h"
|
||||
#include "robomongo/gui/editors/JSLexer.h"
|
||||
#include "robomongo/gui/editors/PlainJavaScriptEditor.h"
|
||||
#include "robomongo/shell/bson/json.h"
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
enum
|
||||
{
|
||||
OPTIONS_TAB = 0,
|
||||
STORAGE_ENGINE_TAB = 1,
|
||||
VALIDATOR_TAB = 2,
|
||||
INDEX_OPTION_DEFAULTS_TAB = 3,
|
||||
};
|
||||
|
||||
const QString CreateCollectionDialog::STORAGE_ENGINE_TAB_HINT = "Option available for WiredTiger storage engine only and database version 3.0 and higher.";
|
||||
const QString CreateCollectionDialog::VALIDATOR_TAB_HINT = "Option available for database version 3.2 and higher.";
|
||||
const QString CreateCollectionDialog::INDEX_OPTION_DEFAULTS_TAB_HINT = "Option available for database version 3.2 and higher.";
|
||||
const QString CreateCollectionDialog::NO_PADDING_HINT = "Option available for MMAPv1 storage engine only and database version 3.0 and higher.";
|
||||
const QString CreateCollectionDialog::USE_POWEROFTWO_HINT = "Option available for MMAPv1 storage engine only and deprecated since database version 3.0";
|
||||
const QString CreateCollectionDialog::AUTO_INDEXID_HINT = "Option deprecated since database version 3.2";
|
||||
|
||||
CreateCollectionDialog::CreateCollectionDialog(const QString &serverName, const float dbVersion, const std::string& storageEngine,
|
||||
const QString &database, const QString &collection, QWidget *parent) :
|
||||
QDialog(parent), _dbVersion(dbVersion), _storageEngine(storageEngine),
|
||||
_activeFrame(nullptr), _activeObj(&_storageEngineObj)
|
||||
{
|
||||
setWindowTitle(tr("Create Collection"));
|
||||
setMinimumSize(300,150);
|
||||
Indicator *serverIndicator = new Indicator(GuiRegistry::instance().serverIcon(), serverName);
|
||||
|
||||
QFrame *hline = new QFrame();
|
||||
hline->setFrameShape(QFrame::HLine);
|
||||
hline->setFrameShadow(QFrame::Sunken);
|
||||
|
||||
_inputEdit = new QLineEdit();
|
||||
_inputLabel = new QLabel(tr("Collection Name:"));
|
||||
_inputEdit->setMaxLength(60);
|
||||
|
||||
_buttonBox = new QDialogButtonBox(this);
|
||||
_buttonBox->setOrientation(Qt::Horizontal);
|
||||
_buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Save);
|
||||
_buttonBox->button(QDialogButtonBox::Save)->setText(tr("C&reate"));
|
||||
_buttonBox->button(QDialogButtonBox::Save)->setEnabled(false);
|
||||
VERIFY(connect(_buttonBox, SIGNAL(accepted()), this, SLOT(accept())));
|
||||
VERIFY(connect(_buttonBox, SIGNAL(rejected()), this, SLOT(reject())));
|
||||
VERIFY(connect(_inputEdit, SIGNAL(textChanged(const QString &)),
|
||||
this, SLOT(enableCreateButton(const QString &))));
|
||||
|
||||
_validateJsonButton = new QPushButton(tr("Validate JSON"));
|
||||
_validateJsonButton->setIcon(qApp->style()->standardIcon(QStyle::SP_MessageBoxInformation));
|
||||
_validateJsonButton->hide();
|
||||
VERIFY(connect(_validateJsonButton, SIGNAL(clicked()), this, SLOT(onValidateButtonClicked())));
|
||||
|
||||
// Create advanced options tabs
|
||||
_advancedOptions = new QTabWidget(this);
|
||||
_advancedOptions->addTab(createOptionsTab(), tr("Options"));
|
||||
_advancedOptions->addTab(createStorageEngineTab(), tr("Storage Engine"));
|
||||
_advancedOptions->addTab(createValidatorTab(), tr("Validator"));
|
||||
_advancedOptions->addTab(createIndexOptionDefaultsTab(), tr("Index Option Defaults"));
|
||||
_advancedOptions->setTabsClosable(false);
|
||||
VERIFY(connect(_advancedOptions, SIGNAL(currentChanged(int)), this, SLOT(onTabChanged(int))));
|
||||
// Create Advanced Button
|
||||
_advancedButton = new QPushButton(tr("Advanced"));
|
||||
_advancedButton->setCheckable(true);
|
||||
VERIFY(connect(_advancedButton, SIGNAL(toggled(bool)), this, SLOT(onAdvancedButtonToggled(bool))));
|
||||
|
||||
// Check mongodb version and storage engine type to enable/disable UI options
|
||||
disableUnsupportedOptions();
|
||||
|
||||
QHBoxLayout *buttomHlayout = new QHBoxLayout();
|
||||
buttomHlayout->addWidget(_validateJsonButton);
|
||||
buttomHlayout->addStretch(1);
|
||||
buttomHlayout->addWidget(_buttonBox);
|
||||
buttomHlayout->addWidget(_advancedButton);
|
||||
|
||||
QHBoxLayout *vlayout = new QHBoxLayout();
|
||||
if (!serverName.isEmpty())
|
||||
vlayout->addWidget(serverIndicator, 0, Qt::AlignLeft);
|
||||
if (!database.isEmpty())
|
||||
vlayout->addWidget(createDatabaseIndicator(database), 0, Qt::AlignLeft);
|
||||
if (!collection.isEmpty())
|
||||
vlayout->addWidget(createCollectionIndicator(collection), 0, Qt::AlignLeft);
|
||||
vlayout->addStretch(1);
|
||||
|
||||
QVBoxLayout *namelayout = new QVBoxLayout();
|
||||
namelayout->setContentsMargins(0, 7, 0, 7);
|
||||
namelayout->addWidget(_inputLabel);
|
||||
namelayout->addWidget(_inputEdit);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout();
|
||||
mainLayout->addLayout(vlayout);
|
||||
mainLayout->addWidget(hline);
|
||||
mainLayout->addLayout(namelayout);
|
||||
mainLayout->addWidget(_advancedOptions);
|
||||
mainLayout->addLayout(buttomHlayout);
|
||||
mainLayout->setSizeConstraint(QLayout::SetMaximumSize);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_advancedOptions->hide();
|
||||
_inputEdit->setFocus();
|
||||
}
|
||||
|
||||
const mongo::BSONObj CreateCollectionDialog::getExtraOptions() const
|
||||
{
|
||||
return _extraOptionsObj;
|
||||
}
|
||||
|
||||
QString CreateCollectionDialog::getCollectionName() const
|
||||
{
|
||||
return _inputEdit->text();
|
||||
}
|
||||
|
||||
bool CreateCollectionDialog::isCapped() const
|
||||
{
|
||||
return (_cappedCheckBox->checkState() == Qt::Checked);
|
||||
}
|
||||
|
||||
long long CreateCollectionDialog::getSizeInputValue() const
|
||||
{
|
||||
return (_sizeInputEdit->text().toLongLong());
|
||||
}
|
||||
|
||||
int CreateCollectionDialog::getMaxDocNumberInputValue() const
|
||||
{
|
||||
return (_maxDocNumberInputEdit->text().toInt());
|
||||
}
|
||||
|
||||
bool CreateCollectionDialog::isCheckedAutoIndexid() const
|
||||
{
|
||||
return (_autoIndexCheckBox->checkState() == Qt::Checked);
|
||||
}
|
||||
|
||||
bool CreateCollectionDialog::isCheckedUsePowerOfTwo() const
|
||||
{
|
||||
return (_usePowerOfTwoSizeCheckBox->checkState() == Qt::Checked);
|
||||
}
|
||||
|
||||
bool CreateCollectionDialog::isCheckedNoPadding() const
|
||||
{
|
||||
return (_noPaddingCheckBox->checkState() == Qt::Checked);
|
||||
}
|
||||
|
||||
void CreateCollectionDialog::accept()
|
||||
{
|
||||
if ( _inputEdit->text().isEmpty()
|
||||
|| !validateOptionDependencies()
|
||||
|| !validate(_storageEngineFrame, _storageEngineObj)
|
||||
|| !validate(_validatorFrame, _validatorObj)
|
||||
|| !validate(_indexOptionDefaultsFrame, _indexOptionDefaultsObj))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
makeExtraOptionsObj();
|
||||
saveWindowSettings();
|
||||
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void CreateCollectionDialog::reject()
|
||||
{
|
||||
saveWindowSettings();
|
||||
QDialog::reject();
|
||||
}
|
||||
|
||||
void CreateCollectionDialog::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
if (_advancedOptions->isVisible())
|
||||
{
|
||||
saveWindowSettings();
|
||||
}
|
||||
QWidget::closeEvent(event);
|
||||
}
|
||||
|
||||
void CreateCollectionDialog::onFrameTextChanged()
|
||||
{
|
||||
_activeFrame->sciScintilla()->clearIndicatorRange(0, 0, _activeFrame->sciScintilla()->lines(), 40, 0);
|
||||
}
|
||||
|
||||
void CreateCollectionDialog::onValidateButtonClicked()
|
||||
{
|
||||
validate(_activeFrame, *_activeObj, false);
|
||||
}
|
||||
|
||||
void CreateCollectionDialog::enableCreateButton(const QString &text)
|
||||
{
|
||||
_buttonBox->button(QDialogButtonBox::Save)->setEnabled(!text.isEmpty());
|
||||
}
|
||||
|
||||
void CreateCollectionDialog::onCappedCheckBoxChanged(int newState)
|
||||
{
|
||||
_sizeInputEdit->setEnabled((Qt::Checked == static_cast<Qt::CheckState>(newState)));
|
||||
_maxDocNumberInputEdit->setEnabled((Qt::Checked == static_cast<Qt::CheckState>(newState)));
|
||||
}
|
||||
|
||||
void CreateCollectionDialog::onTabChanged(int index)
|
||||
{
|
||||
if (OPTIONS_TAB == index) {
|
||||
_validateJsonButton->hide();
|
||||
}
|
||||
else {
|
||||
_validateJsonButton->show();
|
||||
if (STORAGE_ENGINE_TAB == index) {
|
||||
_activeFrame = _storageEngineFrame;
|
||||
_activeObj = &_storageEngineObj;
|
||||
}
|
||||
else if (VALIDATOR_TAB == index) {
|
||||
_activeFrame = _validatorFrame;
|
||||
_activeObj = &_validatorObj;
|
||||
}
|
||||
else if (INDEX_OPTION_DEFAULTS_TAB == index) {
|
||||
_activeFrame = _indexOptionDefaultsFrame;
|
||||
_activeObj = &_indexOptionDefaultsObj;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void CreateCollectionDialog::onAdvancedButtonToggled(bool state)
|
||||
{
|
||||
_advancedOptions->setVisible(state);
|
||||
if (state) // resize, dialog is expanding
|
||||
{
|
||||
setMinimumSize(560, 440);
|
||||
_validateJsonButton->setHidden(_advancedOptions->currentIndex() == OPTIONS_TAB);
|
||||
QSettings settings("Paralect", "Robomongo");
|
||||
if (settings.contains("CreateCollectionDialog/size"))
|
||||
{
|
||||
restoreWindowSettings();
|
||||
}
|
||||
else
|
||||
{
|
||||
resize(560, 440);
|
||||
adjustSize();
|
||||
}
|
||||
}
|
||||
else // resize, dialog is shrinking
|
||||
{
|
||||
saveWindowSettings(); // save expanded geometry first
|
||||
_validateJsonButton->hide();
|
||||
setFixedSize(300, 150);
|
||||
adjustSize();
|
||||
}
|
||||
}
|
||||
|
||||
Indicator *CreateCollectionDialog::createDatabaseIndicator(const QString &database)
|
||||
{
|
||||
return new Indicator(GuiRegistry::instance().databaseIcon(), database);
|
||||
}
|
||||
|
||||
Indicator *CreateCollectionDialog::createCollectionIndicator(const QString &collection)
|
||||
{
|
||||
return new Indicator(GuiRegistry::instance().collectionIcon(), collection);
|
||||
}
|
||||
|
||||
QWidget* CreateCollectionDialog::createOptionsTab()
|
||||
{
|
||||
QWidget *optionsTab = new QWidget(this);
|
||||
|
||||
_cappedCheckBox = new QCheckBox(tr("Create capped collection"), optionsTab);
|
||||
_sizeInputLabel = new QLabel(tr("Maximum size in bytes: "));
|
||||
_sizeInputLabel->setContentsMargins(22, 0, 0, 0);
|
||||
_sizeInputEdit = new QLineEdit();
|
||||
_sizeInputEdit->setEnabled(false);
|
||||
_maxDocNumberInputLabel = new QLabel(tr("Maximum number of documents: "));
|
||||
_maxDocNumberInputLabel->setContentsMargins(22, 0, 0, 0);
|
||||
_maxDocNumberInputEdit = new QLineEdit();
|
||||
_maxDocNumberInputEdit->setEnabled(false);
|
||||
_autoIndexCheckBox = new QCheckBox(tr("Auto index _id"), optionsTab);
|
||||
_autoIndexCheckBox->setChecked(true);
|
||||
_usePowerOfTwoSizeCheckBox = new QCheckBox(tr("Use power-of-2 sizes"), optionsTab);
|
||||
// Note: For mongodb 2.6 does not have storageEngine string due to the fact that it uses MMAPV1 only.
|
||||
if (MongoDatabase::StorageEngineType::MMAPV1 == _storageEngine || "" == _storageEngine)
|
||||
{
|
||||
_usePowerOfTwoSizeCheckBox->setChecked(true);
|
||||
}
|
||||
_noPaddingCheckBox = new QCheckBox(tr("No Padding"), optionsTab);
|
||||
|
||||
VERIFY(connect(_cappedCheckBox, SIGNAL(stateChanged(int)),
|
||||
this, SLOT(onCappedCheckBoxChanged(int))));
|
||||
|
||||
QGridLayout *layout = new QGridLayout;
|
||||
layout->addWidget(_cappedCheckBox, 0, 0, 1, 2);
|
||||
layout->addWidget(_sizeInputLabel, 1, 0);
|
||||
layout->addWidget(_sizeInputEdit, 1, 1);
|
||||
layout->addWidget(_maxDocNumberInputLabel, 2, 0);
|
||||
layout->addWidget(_maxDocNumberInputEdit, 2, 1);
|
||||
layout->addWidget(_autoIndexCheckBox, 3, 0);
|
||||
layout->addWidget(_usePowerOfTwoSizeCheckBox, 4, 0);
|
||||
layout->addWidget(_noPaddingCheckBox, 5, 0);
|
||||
layout->setAlignment(Qt::AlignTop);
|
||||
optionsTab->setLayout(layout);
|
||||
|
||||
return optionsTab;
|
||||
}
|
||||
|
||||
QWidget* CreateCollectionDialog::createStorageEngineTab()
|
||||
{
|
||||
QWidget *storageEngineTab = new QWidget(this);
|
||||
|
||||
_storageEngineFrameLabel = new QLabel(tr("Enter the configuration for the storage engine: "));
|
||||
_storageEngineFrame = new JSONFrame(this);
|
||||
configureFrameText(_storageEngineFrame);
|
||||
_storageEngineFrame->sciScintilla()->setText("{\n \n}");
|
||||
// clear modification state after setting the content
|
||||
_storageEngineFrame->sciScintilla()->setModified(false);
|
||||
VERIFY(connect(_storageEngineFrame->sciScintilla(), SIGNAL(textChanged()),
|
||||
this, SLOT(onFrameTextChanged())));
|
||||
|
||||
QGridLayout *layout = new QGridLayout;
|
||||
layout->addWidget(_storageEngineFrameLabel, 0, 0);
|
||||
layout->addWidget(_storageEngineFrame, 1, 0);
|
||||
layout->setAlignment(Qt::AlignTop);
|
||||
storageEngineTab->setLayout(layout);
|
||||
|
||||
return storageEngineTab;
|
||||
}
|
||||
|
||||
QWidget* CreateCollectionDialog::createValidatorTab()
|
||||
{
|
||||
QWidget *validatorEngineTab = new QWidget(this);
|
||||
|
||||
_validatorLevelLabel = new QLabel(tr("Validation Level: "));
|
||||
_validatorLevelComboBox = new QComboBox();
|
||||
_validatorLevelComboBox->addItem(tr("off"));
|
||||
_validatorLevelComboBox->addItem(tr("strict"));
|
||||
_validatorLevelComboBox->addItem(tr("moderate"));
|
||||
_validatorLevelComboBox->setCurrentIndex(1);
|
||||
|
||||
_validatorActionLabel = new QLabel(tr("Validation Action: "));
|
||||
_validatorActionComboBox = new QComboBox();
|
||||
_validatorActionComboBox->addItem(tr("error"));
|
||||
_validatorActionComboBox->addItem(tr("warn"));
|
||||
_validatorActionComboBox->setCurrentIndex(0);
|
||||
|
||||
_validatorFrameLabel = new QLabel(tr("Enter the validator document for this collection: "));
|
||||
_validatorFrame = new JSONFrame(this);
|
||||
configureFrameText(_validatorFrame);
|
||||
_validatorFrame->sciScintilla()->setText("{\n \n}");
|
||||
// clear modification state after setting the content
|
||||
_validatorFrame->sciScintilla()->setModified(false);
|
||||
VERIFY(connect(_validatorFrame->sciScintilla(), SIGNAL(textChanged()),
|
||||
this, SLOT(onFrameTextChanged())));
|
||||
|
||||
QHBoxLayout *validationOptionslayout = new QHBoxLayout();
|
||||
validationOptionslayout->addWidget(_validatorLevelLabel);
|
||||
validationOptionslayout->addWidget(_validatorLevelComboBox, Qt::AlignLeft);
|
||||
validationOptionslayout->addWidget(_validatorActionLabel);
|
||||
validationOptionslayout->addWidget(_validatorActionComboBox, Qt::AlignLeft);
|
||||
|
||||
QVBoxLayout *vlayout = new QVBoxLayout();
|
||||
vlayout->addWidget(_validatorFrameLabel);
|
||||
vlayout->addWidget(_validatorFrame);
|
||||
|
||||
QVBoxLayout *layout = new QVBoxLayout();
|
||||
layout->addLayout(validationOptionslayout);
|
||||
layout->addLayout(vlayout);
|
||||
validatorEngineTab->setLayout(layout);
|
||||
|
||||
return validatorEngineTab;
|
||||
}
|
||||
|
||||
QWidget* CreateCollectionDialog::createIndexOptionDefaultsTab()
|
||||
{
|
||||
QWidget *indexOptionDefaultsTab = new QWidget(this);
|
||||
|
||||
_indexOptionDefaultsFrameLabel = new QLabel(tr("Enter a default configuration for indexes when creating a collection: "));
|
||||
_indexOptionDefaultsFrame = new JSONFrame(this);
|
||||
configureFrameText(_indexOptionDefaultsFrame);
|
||||
_indexOptionDefaultsFrame->sciScintilla()->setText("{\n \n}");
|
||||
// clear modification state after setting the content
|
||||
_indexOptionDefaultsFrame->sciScintilla()->setModified(false);
|
||||
VERIFY(connect(_indexOptionDefaultsFrame->sciScintilla(), SIGNAL(textChanged()),
|
||||
this, SLOT(onFrameTextChanged())));
|
||||
|
||||
QGridLayout *layout = new QGridLayout;
|
||||
layout->addWidget(_indexOptionDefaultsFrameLabel, 0, 0);
|
||||
layout->addWidget(_indexOptionDefaultsFrame, 1, 0);
|
||||
layout->setAlignment(Qt::AlignTop);
|
||||
indexOptionDefaultsTab->setLayout(layout);
|
||||
|
||||
return indexOptionDefaultsTab;
|
||||
}
|
||||
|
||||
void CreateCollectionDialog::saveWindowSettings() const
|
||||
{
|
||||
QSettings settings("Paralect", "Robomongo");
|
||||
settings.setValue("CreateCollectionDialog/size", size());
|
||||
}
|
||||
|
||||
void CreateCollectionDialog::restoreWindowSettings()
|
||||
{
|
||||
QSettings settings("Paralect", "Robomongo");
|
||||
resize(settings.value("CreateCollectionDialog/size").toSize());
|
||||
}
|
||||
|
||||
void CreateCollectionDialog::configureFrameText(JSONFrame* frame)
|
||||
{
|
||||
QsciLexerJavaScript *javaScriptLexer = new JSLexer(this);
|
||||
QFont font = GuiRegistry::instance().font();
|
||||
javaScriptLexer->setFont(font);
|
||||
frame->sciScintilla()->setBraceMatching(QsciScintilla::StrictBraceMatch);
|
||||
frame->sciScintilla()->setFont(font);
|
||||
frame->sciScintilla()->setPaper(QColor(255, 0, 0, 127));
|
||||
frame->sciScintilla()->setLexer(javaScriptLexer);
|
||||
frame->sciScintilla()->setWrapMode((QsciScintilla::WrapMode)QsciScintilla::SC_WRAP_WORD);
|
||||
frame->sciScintilla()->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
frame->sciScintilla()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
frame->sciScintilla()->setStyleSheet("QFrame { background-color: rgb(73, 76, 78); border: 1px solid #c7c5c4; border-radius: 4px; margin: 0px; padding: 0px;}");
|
||||
}
|
||||
|
||||
bool CreateCollectionDialog::validate(JSONFrame* frame, mongo::BSONObj& bsonObj, bool silentOnSuccess /* = true */)
|
||||
{
|
||||
try {
|
||||
bsonObj = mongo::Robomongo::fromjson(jsonText(frame).toStdString());
|
||||
}
|
||||
catch (const mongo::Robomongo::ParseMsgAssertionException &ex) {
|
||||
// v0.9
|
||||
QString message = QtUtils::toQString(ex.reason());
|
||||
int offset = ex.offset();
|
||||
|
||||
int line = 0, pos = 0;
|
||||
frame->sciScintilla()->lineIndexFromPosition(offset, &line, &pos);
|
||||
frame->sciScintilla()->setCursorPosition(line, pos);
|
||||
|
||||
int lineHeight = frame->sciScintilla()->lineLength(line);
|
||||
frame->sciScintilla()->fillIndicatorRange(line, pos, line, lineHeight, 0);
|
||||
|
||||
message = QString(tr("Unable to parse JSON:<br /> <b>%1</b>, at (%2, %3)."))
|
||||
.arg(message).arg(line + 1).arg(pos + 1);
|
||||
|
||||
QMessageBox::critical(NULL, tr("Parsing error"), message);
|
||||
frame->setFocus();
|
||||
activateWindow();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!silentOnSuccess) {
|
||||
QMessageBox::information(NULL, tr("Validation"), tr("JSON is valid!"));
|
||||
frame->setFocus();
|
||||
activateWindow();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CreateCollectionDialog::makeExtraOptionsObj()
|
||||
{
|
||||
mongo::BSONObjBuilder builder;
|
||||
|
||||
if (_autoIndexCheckBox->isEnabled()) builder.append("autoIndexId", isCheckedAutoIndexid());
|
||||
if (_noPaddingCheckBox->isEnabled() && _usePowerOfTwoSizeCheckBox->isEnabled()) {
|
||||
builder.append("flags", (isCheckedNoPadding()*2 + isCheckedUsePowerOfTwo()*1));
|
||||
}
|
||||
else if (_noPaddingCheckBox->isEnabled()) {
|
||||
builder.append("flags", (isCheckedNoPadding()*2));
|
||||
}
|
||||
else if (_usePowerOfTwoSizeCheckBox->isEnabled()) {
|
||||
builder.append("flags", (isCheckedUsePowerOfTwo()*1));
|
||||
}
|
||||
if (_advancedOptions->isTabEnabled(STORAGE_ENGINE_TAB) ) {
|
||||
validate(_storageEngineFrame, _storageEngineObj);
|
||||
if(!_storageEngineObj.isEmpty()) builder.append("storageEngine", _storageEngineObj);
|
||||
}
|
||||
if (_advancedOptions->isTabEnabled(VALIDATOR_TAB)) {
|
||||
validate(_validatorFrame, _validatorObj);
|
||||
if (!_validatorObj.isEmpty()) {
|
||||
builder.append("validator", _validatorObj);
|
||||
builder.append("validationLevel", _validatorLevelComboBox->currentText().toStdString());
|
||||
builder.append("validationAction", _validatorActionComboBox->currentText().toStdString());
|
||||
}
|
||||
}
|
||||
if (_advancedOptions->isTabEnabled(INDEX_OPTION_DEFAULTS_TAB)) {
|
||||
validate(_indexOptionDefaultsFrame, _indexOptionDefaultsObj);
|
||||
if (!_indexOptionDefaultsObj.isEmpty()) builder.append("indexOptionDefaults", _indexOptionDefaultsObj);
|
||||
}
|
||||
// Complete and get resulting BSONObj
|
||||
_extraOptionsObj = builder.obj();
|
||||
}
|
||||
|
||||
bool CreateCollectionDialog::validateOptionDependencies() const
|
||||
{
|
||||
bool result(false);
|
||||
// Validate capped options
|
||||
if (isCapped()) {
|
||||
if (!(getSizeInputValue() > 0)) {
|
||||
QMessageBox::critical(NULL, tr("Error"), tr("Maximum size is required for capped collections"));
|
||||
return false;
|
||||
}
|
||||
result = true;
|
||||
}
|
||||
else {
|
||||
result = true;
|
||||
}
|
||||
|
||||
// Completed checking all dependendencies.
|
||||
return result;
|
||||
}
|
||||
|
||||
void CreateCollectionDialog::disableUnsupportedOptions() const
|
||||
{
|
||||
|
||||
// Handle WIRED_TIGER engine type
|
||||
if (MongoDatabase::StorageEngineType::WIRED_TIGER == _storageEngine) {
|
||||
disableOption(_usePowerOfTwoSizeCheckBox, USE_POWEROFTWO_HINT);
|
||||
disableOption(_noPaddingCheckBox, NO_PADDING_HINT);
|
||||
if (MongoDatabase::DBVersion::MONGODB_3_0 > _dbVersion) {
|
||||
disableTab(STORAGE_ENGINE_TAB, STORAGE_ENGINE_TAB_HINT);
|
||||
}
|
||||
}
|
||||
else {
|
||||
disableTab(STORAGE_ENGINE_TAB, STORAGE_ENGINE_TAB_HINT);
|
||||
}
|
||||
|
||||
// Handle MMAPV1 engine type
|
||||
// Note: For mongodb 2.6 does not have storageEngine string due to the fact that it uses MMAPV1 only.
|
||||
if (MongoDatabase::StorageEngineType::MMAPV1 == _storageEngine || "" == _storageEngine) {
|
||||
if (MongoDatabase::DBVersion::MONGODB_3_0 < _dbVersion) {
|
||||
_usePowerOfTwoSizeCheckBox->hide();
|
||||
}
|
||||
if (MongoDatabase::DBVersion::MONGODB_3_0 > _dbVersion) {
|
||||
disableOption(_noPaddingCheckBox, NO_PADDING_HINT);
|
||||
}
|
||||
}
|
||||
|
||||
if (MongoDatabase::DBVersion::MONGODB_3_2 < _dbVersion) {
|
||||
disableOption(_autoIndexCheckBox, AUTO_INDEXID_HINT);
|
||||
}
|
||||
|
||||
if (MongoDatabase::DBVersion::MONGODB_3_0 > _dbVersion) {
|
||||
disableTab(STORAGE_ENGINE_TAB, STORAGE_ENGINE_TAB_HINT);
|
||||
}
|
||||
if (MongoDatabase::DBVersion::MONGODB_3_2 > _dbVersion) {
|
||||
disableTab(VALIDATOR_TAB, VALIDATOR_TAB_HINT);
|
||||
disableTab(INDEX_OPTION_DEFAULTS_TAB, INDEX_OPTION_DEFAULTS_TAB_HINT);
|
||||
}
|
||||
}
|
||||
|
||||
void CreateCollectionDialog::disableOption(QWidget* option, const QString& hint) const
|
||||
{
|
||||
option->setDisabled(true);
|
||||
option->setToolTip(hint);
|
||||
}
|
||||
|
||||
void CreateCollectionDialog::disableTab(int index, const QString& hint) const
|
||||
{
|
||||
_advancedOptions->setTabEnabled(index, false);
|
||||
_advancedOptions->setTabToolTip(index, hint);
|
||||
}
|
||||
|
||||
void CreateCollectionDialog::setCursorPosition(int line, int column)
|
||||
{
|
||||
_activeFrame->sciScintilla()->setCursorPosition(line, column);
|
||||
}
|
||||
|
||||
QString CreateCollectionDialog::jsonText(JSONFrame* frame) const
|
||||
{
|
||||
return frame->sciScintilla()->text().trimmed();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,309 @@
|
||||
#pragma once
|
||||
|
||||
#include <mongo/bson/bsonobj.h>
|
||||
#include <QDialog>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QLabel;
|
||||
class QDialogButtonBox;
|
||||
class QLineEdit;
|
||||
class QTabWidget;
|
||||
class QCheckBox;
|
||||
class QComboBox;
|
||||
QT_END_NAMESPACE
|
||||
class QsciScintilla;
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
class FindFrame;
|
||||
class Indicator;
|
||||
|
||||
/**
|
||||
* @brief This Dialog allows to create collection with selective options.
|
||||
*/
|
||||
class CreateCollectionDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
using JSONFrame = FindFrame;
|
||||
|
||||
/**
|
||||
* @brief Construct dialog with parameters
|
||||
* @param serverName: Database name
|
||||
* @param dbVersion: Database version
|
||||
* @param storageEngine: Storage engine type
|
||||
* @param database: Name of the database
|
||||
* @param collection: Name of the collection
|
||||
*/
|
||||
explicit CreateCollectionDialog(const QString &serverName, const float dbVersion, const std::string& storageEngine,
|
||||
const QString &database = QString(), const QString &collection = QString(), QWidget *parent = 0);
|
||||
|
||||
/**
|
||||
* @brief Used if dialog exec() method returns QDialog::Accepted
|
||||
* @return Extra options as BSONObj
|
||||
*/
|
||||
const mongo::BSONObj getExtraOptions() const;
|
||||
|
||||
/**
|
||||
* @return Collection name
|
||||
*/
|
||||
QString getCollectionName() const;
|
||||
|
||||
/**
|
||||
* @return true if capped option checked, false otherwise
|
||||
*/
|
||||
bool isCapped() const;
|
||||
|
||||
/**
|
||||
* @return Size option input
|
||||
*/
|
||||
long long getSizeInputValue() const;
|
||||
|
||||
/**
|
||||
* @return Max number of documents option input
|
||||
*/
|
||||
int getMaxDocNumberInputValue() const;
|
||||
|
||||
/**
|
||||
* @return true if auto index_id option checked, false otherwise
|
||||
*/
|
||||
bool isCheckedAutoIndexid() const;
|
||||
|
||||
/**
|
||||
* @return true if use power-of-2 sizes option checked, false otherwise
|
||||
*/
|
||||
bool isCheckedUsePowerOfTwo() const;
|
||||
|
||||
/**
|
||||
* @return true if no padding option checked, false otherwise
|
||||
*/
|
||||
bool isCheckedNoPadding() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
/**
|
||||
* @brief Validate all input fields and make extraOptions object if all valid when create button clicked.
|
||||
*/
|
||||
void accept() override;
|
||||
|
||||
/**
|
||||
* @brief Called when "Cancel" button clicked.
|
||||
*/
|
||||
void reject() override;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Reimplementing closeEvent in order to do implement functionalities before close this dialog.
|
||||
*/
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
|
||||
private Q_SLOTS:
|
||||
/**
|
||||
* @brief Set indicator position when active JSON frame text changed
|
||||
*/
|
||||
void onFrameTextChanged();
|
||||
|
||||
/**
|
||||
* @brief Validate active JSON frame text when validate button clicked
|
||||
*/
|
||||
void onValidateButtonClicked();
|
||||
|
||||
/**
|
||||
* @brief Enable create button if input edit has text
|
||||
* @param text : string from _inputEdit widget
|
||||
*/
|
||||
void enableCreateButton(const QString &text);
|
||||
|
||||
/**
|
||||
* @brief Enable/disable capped options when capped checkbox state changed
|
||||
* @param newState : New state of capped checkbox
|
||||
*/
|
||||
void onCappedCheckBoxChanged(int newState);
|
||||
|
||||
/**
|
||||
* @brief Show/hide validate JSON button, update active JSON frame and object according to active tab
|
||||
* @param Index of active tab (i.e. index = 0 for the first tab)
|
||||
*/
|
||||
void onTabChanged(int index);
|
||||
|
||||
/**
|
||||
* @brief Show/hide advanced options menu (_tabwidget) when advancedButton toggled.
|
||||
* @param state : true if dialog is expanding, false if shrinking.
|
||||
*/
|
||||
void onAdvancedButtonToggled(bool state);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Tool tip strings for disabled options and tabs
|
||||
*/
|
||||
static const QString STORAGE_ENGINE_TAB_HINT;
|
||||
static const QString VALIDATOR_TAB_HINT;
|
||||
static const QString INDEX_OPTION_DEFAULTS_TAB_HINT;
|
||||
static const QString NO_PADDING_HINT;
|
||||
static const QString USE_POWEROFTWO_HINT;
|
||||
static const QString AUTO_INDEXID_HINT;
|
||||
|
||||
/**
|
||||
* @brief Create database indicator widget
|
||||
* @param database : Name of the database
|
||||
* @return : Pointer to newly created Indicator
|
||||
*/
|
||||
Indicator* createDatabaseIndicator(const QString &database);
|
||||
|
||||
/**
|
||||
* @brief Create collection indicator widget
|
||||
* @param database : Name of the collection
|
||||
* @return : Pointer to newly created Indicator
|
||||
*/
|
||||
Indicator* createCollectionIndicator(const QString &collection);
|
||||
|
||||
/**
|
||||
* @brief Creators for the tabs
|
||||
* @return : Pointer to newly created tab
|
||||
*/
|
||||
QWidget* createOptionsTab();
|
||||
QWidget* createStorageEngineTab();
|
||||
QWidget* createValidatorTab();
|
||||
QWidget* createIndexOptionDefaultsTab();
|
||||
|
||||
/**
|
||||
* @brief Save windows settings into system registry
|
||||
*/
|
||||
void saveWindowSettings() const;
|
||||
/**
|
||||
* @brief Restore windows settings from system registry
|
||||
*/
|
||||
void restoreWindowSettings();
|
||||
|
||||
/**
|
||||
* @brief Do initial configuration of given JSON frame.
|
||||
* @param frame : Pointer to newly created tab
|
||||
*/
|
||||
void configureFrameText(JSONFrame* frame);
|
||||
|
||||
/**
|
||||
* @brief Build extraOptions BSON Object
|
||||
*/
|
||||
void makeExtraOptionsObj();
|
||||
|
||||
/**
|
||||
* @brief Validate dependencies and requirements between all inputs
|
||||
* @return : true if all options valid, false at least one dependency not met
|
||||
*/
|
||||
bool validateOptionDependencies() const;
|
||||
|
||||
/**
|
||||
* @brief Disable options if they are not supported by database version or storage engine type
|
||||
*/
|
||||
void disableUnsupportedOptions() const;
|
||||
|
||||
/**
|
||||
* @brief Disable widget if unsupported
|
||||
* @param option : Widget to disable
|
||||
* @param hint : Hint string to be shown
|
||||
*/
|
||||
void disableOption(QWidget* option, const QString& hint) const;
|
||||
|
||||
/**
|
||||
* @brief Disable tab if unsupported
|
||||
* @param index : Tab index
|
||||
* @param hint : Hint string to be shown
|
||||
*/
|
||||
void disableTab(int index, const QString& hint) const;
|
||||
|
||||
/**
|
||||
* @brief Set cursor position of the active JSON frame
|
||||
* @param line : Line number
|
||||
* @param column : Column number
|
||||
*/
|
||||
void setCursorPosition(int line, int column);
|
||||
|
||||
/**
|
||||
* @return : String from given JSON frame
|
||||
* @param frame : Pointer to JSON frame
|
||||
*/
|
||||
QString jsonText(JSONFrame* frame) const;
|
||||
|
||||
/**
|
||||
* @brief Do JSON validation/creation for given frame text
|
||||
* @param frame : Pointer to JSON frame
|
||||
* @param bsonObj : BSON object to be validated
|
||||
* @param silentOnSuccess : true for silent validation, false for message box on validation
|
||||
* @return true if text is valid JSON, false otherwise
|
||||
*/
|
||||
bool validate(JSONFrame* frame, mongo::BSONObj& bsonObj, bool silentOnSuccess = true);
|
||||
|
||||
/**
|
||||
* @brief Main Window
|
||||
*/
|
||||
QLineEdit *_inputEdit;
|
||||
QLabel *_inputLabel;
|
||||
QTabWidget *_advancedOptions;
|
||||
QPushButton *_validateJsonButton;
|
||||
QDialogButtonBox *_buttonBox;
|
||||
QPushButton *_advancedButton;
|
||||
|
||||
/**
|
||||
* @brief Options Tab
|
||||
*/
|
||||
QCheckBox *_cappedCheckBox;
|
||||
QLabel *_sizeInputLabel;
|
||||
QLineEdit *_sizeInputEdit;
|
||||
QLabel *_maxDocNumberInputLabel;
|
||||
QLineEdit *_maxDocNumberInputEdit;
|
||||
QCheckBox *_autoIndexCheckBox;
|
||||
QCheckBox *_usePowerOfTwoSizeCheckBox;
|
||||
QCheckBox *_noPaddingCheckBox;
|
||||
/**
|
||||
* @brief Storage Engine Tab
|
||||
*/
|
||||
JSONFrame *_storageEngineFrame;
|
||||
QLabel * _storageEngineFrameLabel;
|
||||
/**
|
||||
* @brief Validator Tab
|
||||
*/
|
||||
QLabel * _validatorLevelLabel;
|
||||
QComboBox * _validatorLevelComboBox;
|
||||
QLabel * _validatorActionLabel;
|
||||
QComboBox * _validatorActionComboBox;
|
||||
JSONFrame *_validatorFrame;
|
||||
QLabel * _validatorFrameLabel;
|
||||
/**
|
||||
* @brief Index Options Defaults Tab
|
||||
*/
|
||||
JSONFrame *_indexOptionDefaultsFrame;
|
||||
QLabel * _indexOptionDefaultsFrameLabel;
|
||||
|
||||
/**
|
||||
* @brief Pointer to active tab's frame
|
||||
*/
|
||||
JSONFrame *_activeFrame;
|
||||
|
||||
/**
|
||||
* @brief Pointer to active tab's BSON obj.
|
||||
*/
|
||||
mongo::BSONObj *_activeObj;
|
||||
|
||||
/**
|
||||
* @brief Main BSON obj. with all extra options
|
||||
*/
|
||||
mongo::BSONObj _extraOptionsObj;
|
||||
|
||||
/**
|
||||
* @brief Sub objects for each JSON tab to create extraOptions object finally.
|
||||
*/
|
||||
mongo::BSONObj _storageEngineObj;
|
||||
mongo::BSONObj _validatorObj;
|
||||
mongo::BSONObj _indexOptionDefaultsObj;
|
||||
|
||||
/**
|
||||
* @brief Database version
|
||||
*/
|
||||
const float _dbVersion;
|
||||
|
||||
/**
|
||||
* @brief Storage engine type
|
||||
*/
|
||||
const std::string _storageEngine;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
#include "robomongo/gui/dialogs/CreateConnectionDialog.h"
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QCloseEvent>
|
||||
#include <QHBoxLayout>
|
||||
#include <QVBoxLayout>
|
||||
#include <QMessageBox>
|
||||
#include <QApplication>
|
||||
#include <QSplitter>
|
||||
#include <QDialogButtonBox>
|
||||
|
||||
#include <QLineEdit>
|
||||
#include <QPixmap>
|
||||
#include <QLabel>
|
||||
|
||||
#include "robomongo/gui/GuiRegistry.h"
|
||||
#include "robomongo/gui/dialogs/ConnectionsDialog.h"
|
||||
#include "robomongo/gui/dialogs/ConnectionDiagnosticDialog.h"
|
||||
#include "robomongo/core/settings/ConnectionSettings.h"
|
||||
#include "robomongo/core/utils/QtUtils.h"
|
||||
|
||||
#include <mongo/client/connection_string.h>
|
||||
//#include <mongo/base/status_with.h>
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
/**
|
||||
* @brief Constructs dialog with specified connection
|
||||
*/
|
||||
CreateConnectionDialog::CreateConnectionDialog(ConnectionsDialog* parent)
|
||||
: QDialog(), _connectionsDialog(parent), _connection(nullptr), _fromURI(true), // todo: _fromURI:false
|
||||
_mongoUriWithStatus(nullptr)
|
||||
{
|
||||
setWindowTitle("Create New Connection");
|
||||
setWindowIcon(GuiRegistry::instance().serverIcon());
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); // Remove help button (?)
|
||||
setMinimumWidth(450);
|
||||
//setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
// Splitter-Left
|
||||
auto label01 = new QLabel("Replica Set");
|
||||
label01->setStyleSheet("font-weight: bold");
|
||||
auto wid1 = new QLabel;
|
||||
auto repSetImage = QPixmap("C:\\Users\\gsimsek\\Google Drive\\Cases - Drive\\replica_sets\\icons\\repset.png");
|
||||
wid1->setPixmap(repSetImage);
|
||||
|
||||
_uriLineEdit = new QLineEdit("mongodb://localhost:27017,localhost:27018,localhost:27019/admin?replicaSet=repset");
|
||||
|
||||
auto const& createConnStr= QString("b) <a style='color: %1' href='create'>Create connection manually</a>").arg("#106CD6");
|
||||
auto createConnLabel = new QLabel(createConnStr);
|
||||
VERIFY(connect(createConnLabel, SIGNAL(linkActivated(QString)), this, SLOT(on_createConnLinkActivated())));
|
||||
|
||||
auto splitterL = new QSplitter;
|
||||
splitterL->setOrientation(Qt::Vertical);
|
||||
splitterL->addWidget(label01);
|
||||
splitterL->addWidget(wid1);
|
||||
//splitterL->addWidget(new QLabel(""));
|
||||
//splitterL->addWidget(nameWid);
|
||||
splitterL->addWidget(new QLabel(""));
|
||||
splitterL->addWidget(new QLabel("a) I have a URI connection string:"));
|
||||
splitterL->addWidget(_uriLineEdit);
|
||||
splitterL->addWidget(new QLabel(""));
|
||||
splitterL->addWidget(createConnLabel);
|
||||
splitterL->addWidget(new QLabel(""));
|
||||
|
||||
|
||||
// Splitter-Right
|
||||
auto label2 = new QLabel("Stand Alone Server");
|
||||
label2->setStyleSheet("font-weight: bold");
|
||||
auto wid2 = new QLabel;
|
||||
auto standAlone = QPixmap("C:\\Users\\gsimsek\\Google Drive\\Cases - Drive\\replica_sets\\icons\\stand_alone.png");
|
||||
wid2->setPixmap(standAlone);
|
||||
|
||||
auto splitterR = new QSplitter;
|
||||
splitterR->setOrientation(Qt::Vertical);
|
||||
splitterR->addWidget(label2);
|
||||
splitterR->addWidget(wid2);
|
||||
|
||||
// Splitter layout
|
||||
auto splitterLay = new QHBoxLayout;
|
||||
splitterLay->addWidget(splitterL);
|
||||
//splitterLay->addWidget(splitterR);
|
||||
|
||||
// Button Box
|
||||
QPushButton *testButton = new QPushButton("&Test");
|
||||
testButton->setIcon(qApp->style()->standardIcon(QStyle::SP_MessageBoxInformation));
|
||||
VERIFY(connect(testButton, SIGNAL(clicked()), this, SLOT(testConnection())));
|
||||
|
||||
QHBoxLayout *bottomLayout = new QHBoxLayout;
|
||||
bottomLayout->addWidget(testButton, 1, Qt::AlignLeft);
|
||||
QDialogButtonBox *buttonBox = new QDialogButtonBox(this);
|
||||
buttonBox->setOrientation(Qt::Horizontal);
|
||||
buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Save);
|
||||
buttonBox->button(QDialogButtonBox::Save)->setText("Next");
|
||||
VERIFY(connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())));
|
||||
VERIFY(connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())));
|
||||
bottomLayout->addWidget(buttonBox);
|
||||
|
||||
// main layout
|
||||
auto mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(splitterLay);
|
||||
mainLayout->addLayout(bottomLayout);
|
||||
setLayout(mainLayout);
|
||||
|
||||
//_basicTab->setFocus();
|
||||
adjustSize();
|
||||
|
||||
// Set minimum width - adjustment after adding SSLTab
|
||||
setMinimumWidth(550);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Accept() is called when user agree with entered data.
|
||||
*/
|
||||
void CreateConnectionDialog::accept()
|
||||
{
|
||||
//if (validateAndApply()) {
|
||||
// QDialog::accept();
|
||||
//}
|
||||
|
||||
std::unique_ptr<mongo::StatusWith<mongo::MongoURI>> uriWithStatus = getUriWithStatus();
|
||||
if (!uriWithStatus || !uriWithStatus->isOK()) {
|
||||
return;
|
||||
}
|
||||
_mongoUriWithStatus.reset(uriWithStatus.release());
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
bool CreateConnectionDialog::validateAndApply()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<mongo::StatusWith<mongo::MongoURI>> CreateConnectionDialog::getUriWithStatus()
|
||||
{
|
||||
const std::string& url = _uriLineEdit->text().toStdString();
|
||||
|
||||
std::unique_ptr<mongo::StatusWith<mongo::MongoURI>> uriWithStatus = nullptr;
|
||||
try {
|
||||
uriWithStatus.reset(new mongo::StatusWith<mongo::MongoURI>(mongo::MongoURI::parse(url)));
|
||||
if (!uriWithStatus->isOK()) {
|
||||
QMessageBox::critical(this, "Error", "Failed to parse connection string URI.\n"
|
||||
"Reason: " + QString::fromStdString(uriWithStatus->getStatus().reason()));
|
||||
return uriWithStatus;
|
||||
}
|
||||
}
|
||||
catch (const std::exception& exception) {
|
||||
QMessageBox::critical(this, "Error", "Failed to parse connection string URI.\n"
|
||||
"Reason: " + QString::fromStdString(exception.what()));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return uriWithStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test current connection
|
||||
*/
|
||||
void CreateConnectionDialog::testConnection()
|
||||
{
|
||||
//if (!validateAndApply())
|
||||
// return;
|
||||
|
||||
auto uriWithStatus = getUriWithStatus();
|
||||
if (!uriWithStatus || !uriWithStatus->isOK()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto uri = uriWithStatus->getValue();
|
||||
ConnectionSettings connection(uri, true); // todo: refactor
|
||||
ConnectionDiagnosticDialog diag(&connection, this);
|
||||
if (!diag.continueExec())
|
||||
return;
|
||||
|
||||
diag.exec();
|
||||
}
|
||||
|
||||
void CreateConnectionDialog::on_createConnLinkActivated()
|
||||
{
|
||||
_fromURI = false;
|
||||
accept();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QLineEdit;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#include "robomongo/core/Core.h"
|
||||
|
||||
#include <mongo/client/mongo_uri.h>
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
class ConnectionsDialog;
|
||||
class ConnectionSettings;
|
||||
|
||||
/**
|
||||
* @brief This class is not finished, it is still under development.
|
||||
* It was planned to be used as very first dialog right after 'create' new
|
||||
* connection is clicked on connections dialog in order to provide user
|
||||
* some create more friendly create connection options with pictures
|
||||
* e.g. create connection from URI string, create replica set or create single server.
|
||||
*/
|
||||
class CreateConnectionDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
using MongoURIwithStatus = mongo::StatusWith<mongo::MongoURI>;
|
||||
|
||||
/**
|
||||
* @brief Constructs dialog with specified connection
|
||||
*/
|
||||
CreateConnectionDialog(ConnectionsDialog *parent);
|
||||
ConnectionSettings *const connection() const { return _connection; }
|
||||
|
||||
// todo:
|
||||
// mongo::MongoURI getMongoUri() const { return _mongoUri; }
|
||||
bool fromURI() const { return _fromURI; }
|
||||
|
||||
std::unique_ptr<mongo::StatusWith<mongo::MongoURI>> getMongoUriWithStatus()
|
||||
{
|
||||
return std::move(_mongoUriWithStatus);
|
||||
}
|
||||
|
||||
public Q_SLOTS:
|
||||
/**
|
||||
* @brief Accept() is called when user agree with entered data.
|
||||
*/
|
||||
virtual void accept();
|
||||
|
||||
private Q_SLOTS:
|
||||
/**
|
||||
* @brief Test current connection
|
||||
*/
|
||||
void testConnection();
|
||||
|
||||
void on_createConnLinkActivated();
|
||||
|
||||
private:
|
||||
bool validateAndApply();
|
||||
|
||||
// todo: may return nullptr
|
||||
/* example usage:
|
||||
auto uriWithStatus = getUriWithStatus();
|
||||
if (!uriWithStatus || !uriWithStatus->isOK()) {
|
||||
return;
|
||||
}
|
||||
auto uri = uriWithStatus->getValue();
|
||||
*/
|
||||
std::unique_ptr<MongoURIwithStatus> getUriWithStatus();
|
||||
|
||||
QLineEdit* _uriLineEdit;
|
||||
|
||||
/**
|
||||
* @brief Edited connection
|
||||
*/
|
||||
ConnectionSettings* _connection;
|
||||
ConnectionsDialog* _connectionsDialog;
|
||||
|
||||
std::unique_ptr<mongo::StatusWith<mongo::MongoURI>> _mongoUriWithStatus;
|
||||
bool _fromURI;
|
||||
};
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
const QSize CreateDatabaseDialog::dialogSize = QSize(300,150);
|
||||
const QSize CreateDatabaseDialog::dialogSize = QSize(300, 150);
|
||||
|
||||
CreateDatabaseDialog::CreateDatabaseDialog(const QString &serverName, const QString &database,
|
||||
const QString &collection, QWidget *parent) :
|
||||
@@ -31,7 +31,7 @@ namespace Robomongo
|
||||
hline->setFrameShadow(QFrame::Sunken);
|
||||
|
||||
_inputEdit = new QLineEdit();
|
||||
_inputLabel= new QLabel("Database Name:");
|
||||
_inputLabel = new QLabel("Database Name:");
|
||||
_inputEdit->setMaxLength(maxLenghtName);
|
||||
|
||||
_buttonBox = new QDialogButtonBox(this);
|
||||
|
||||
@@ -17,8 +17,19 @@
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
const char * rolesText[CreateUserDialog::RolesCount] = {"read","readWrite","dbAdmin","userAdmin","clusterAdmin","readAnyDatabase","readWriteAnyDatabase","userAdminAnyDatabase","dbAdminAnyDatabase"};
|
||||
const QSize CreateUserDialog::minimumSize = QSize(400,200);
|
||||
const char * rolesText[CreateUserDialog::RolesCount] = {
|
||||
"read",
|
||||
"readWrite",
|
||||
"dbAdmin",
|
||||
"userAdmin",
|
||||
"clusterAdmin",
|
||||
"readAnyDatabase",
|
||||
"readWriteAnyDatabase",
|
||||
"userAdminAnyDatabase",
|
||||
"dbAdminAnyDatabase"
|
||||
};
|
||||
|
||||
const QSize CreateUserDialog::minimumSize = QSize(400, 200);
|
||||
|
||||
bool containsWord(const std::string& sentence, const std::string& word)
|
||||
{
|
||||
@@ -31,7 +42,7 @@ namespace Robomongo
|
||||
if (pos) {
|
||||
isFirstAlpha = isalpha(sentence[pos - 1]);
|
||||
}
|
||||
if (!isFirstAlpha&&!isLastAlpha)
|
||||
if (!isFirstAlpha && !isLastAlpha)
|
||||
return true;
|
||||
}
|
||||
pos++;
|
||||
@@ -39,13 +50,11 @@ namespace Robomongo
|
||||
return false;
|
||||
}
|
||||
|
||||
CreateUserDialog::CreateUserDialog(const QStringList &databases,const QString &serverName,
|
||||
CreateUserDialog::CreateUserDialog(const QStringList &databases, const QString &serverName,
|
||||
const QString &database, const MongoUser &user,
|
||||
QWidget *parent) : QDialog(parent),
|
||||
_user(user)
|
||||
{
|
||||
VERIFY(!user.version() < MongoUser::minimumSupportedVersion);
|
||||
|
||||
setWindowTitle("Add User");
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); // Remove help button (?)
|
||||
setMinimumSize(minimumSize);
|
||||
@@ -57,10 +66,10 @@ namespace Robomongo
|
||||
hline->setFrameShape(QFrame::HLine);
|
||||
hline->setFrameShadow(QFrame::Sunken);
|
||||
|
||||
_userNameLabel= new QLabel("Name:");
|
||||
_userNameLabel = new QLabel("Name:");
|
||||
_userNameEdit = new QLineEdit();
|
||||
_userNameEdit->setText(QtUtils::toQString(user.name()));
|
||||
_userPassLabel= new QLabel("Password:");
|
||||
_userPassLabel = new QLabel("Password:");
|
||||
_userPassEdit = new QLineEdit();
|
||||
_userPassEdit->setEchoMode(QLineEdit::Password);
|
||||
_userSourceLabel = new QLabel("UserSource:");
|
||||
@@ -70,14 +79,14 @@ namespace Robomongo
|
||||
|
||||
QGridLayout *gridRoles = new QGridLayout();
|
||||
MongoUser::RoleType userRoles = user.role();
|
||||
for (unsigned i=0; i<RolesCount; ++i)
|
||||
for (unsigned i = 0; i<RolesCount; ++i)
|
||||
{
|
||||
int row = i%3;
|
||||
int col = i/3;
|
||||
_rolesArray[i] = new QCheckBox(rolesText[i],this);
|
||||
MongoUser::RoleType::const_iterator it = std::find(userRoles.begin(),userRoles.end(),rolesText[i]);
|
||||
_rolesArray[i]->setChecked(it!=userRoles.end());
|
||||
gridRoles->addWidget(_rolesArray[i],row,col);
|
||||
_rolesArray[i] = new QCheckBox(rolesText[i], this);
|
||||
MongoUser::RoleType::const_iterator it = std::find(userRoles.begin(), userRoles.end(), rolesText[i]);
|
||||
_rolesArray[i]->setChecked(it!= userRoles.end());
|
||||
gridRoles->addWidget(_rolesArray[i], row, col);
|
||||
}
|
||||
|
||||
QDialogButtonBox *buttonBox = new QDialogButtonBox(this);
|
||||
@@ -120,7 +129,6 @@ namespace Robomongo
|
||||
QWidget *parent) : QDialog(parent),
|
||||
_user(user)
|
||||
{
|
||||
VERIFY(user.version() < MongoUser::minimumSupportedVersion);
|
||||
setWindowTitle("Add User");
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); // Remove help button (?)
|
||||
setMinimumSize(minimumSize);
|
||||
@@ -132,7 +140,7 @@ namespace Robomongo
|
||||
hline->setFrameShape(QFrame::HLine);
|
||||
hline->setFrameShadow(QFrame::Sunken);
|
||||
|
||||
_userNameLabel= new QLabel("Name:");
|
||||
_userNameLabel = new QLabel("Name:");
|
||||
_userNameEdit = new QLineEdit();
|
||||
_userNameEdit->setText(QtUtils::toQString(user.name()));
|
||||
_userPassLabel= new QLabel("Password:");
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
#include <QMessageBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QDesktopWidget>
|
||||
#include <QSettings>
|
||||
#include <Qsci/qscilexerjavascript.h>
|
||||
|
||||
#include <mongo/client/dbclientinterface.h>
|
||||
|
||||
#include "robomongo/gui/editors/JSLexer.h"
|
||||
@@ -34,11 +36,15 @@ namespace Robomongo
|
||||
QSize size(screenGeometry.width() - horizontalMargin,
|
||||
screenGeometry.height() - verticalMargin);
|
||||
|
||||
resize(size);
|
||||
|
||||
int x = (screenGeometry.width() - width()) / 2;
|
||||
int y = (screenGeometry.height() - height()) / 2;
|
||||
move(x, y);
|
||||
QSettings settings("Paralect", "Robomongo");
|
||||
if (settings.contains("DocumentTextEditor/size"))
|
||||
{
|
||||
restoreWindowSettings();
|
||||
}
|
||||
else
|
||||
{
|
||||
resize(size);
|
||||
}
|
||||
|
||||
setWindowFlags(Qt::Window | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint);
|
||||
|
||||
@@ -109,6 +115,8 @@ namespace Robomongo
|
||||
if (!validate())
|
||||
return;
|
||||
|
||||
saveWindowSettings();
|
||||
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
@@ -131,6 +139,8 @@ namespace Robomongo
|
||||
return;
|
||||
}
|
||||
|
||||
saveWindowSettings();
|
||||
|
||||
QDialog::reject();
|
||||
}
|
||||
|
||||
@@ -144,18 +154,18 @@ namespace Robomongo
|
||||
int jsonLen = textString.length();
|
||||
int offset = 0;
|
||||
_obj.clear();
|
||||
while(offset!=jsonLen)
|
||||
while (offset != jsonLen)
|
||||
{
|
||||
mongo::BSONObj doc = mongo::Robomongo::fromjson(json+offset,&len);
|
||||
mongo::BSONObj doc = mongo::Robomongo::fromjson(json+offset, &len);
|
||||
_obj.push_back(doc);
|
||||
offset+=len;
|
||||
offset += len;
|
||||
}
|
||||
} catch (const mongo::Robomongo::ParseMsgAssertionException &ex) {
|
||||
// v0.9
|
||||
QString message = QtUtils::toQString(ex.reason());
|
||||
int offset = ex.offset();
|
||||
|
||||
int line=0, pos=0;
|
||||
int line = 0, pos = 0;
|
||||
_queryText->sciScintilla()->lineIndexFromPosition(offset, &line, &pos);
|
||||
_queryText->sciScintilla()->setCursorPosition(line, pos);
|
||||
|
||||
@@ -190,6 +200,12 @@ namespace Robomongo
|
||||
validate(false);
|
||||
}
|
||||
|
||||
void DocumentTextEditor::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
saveWindowSettings();
|
||||
QWidget::closeEvent(event);
|
||||
}
|
||||
|
||||
/*
|
||||
** Configure QsciScintilla query widget
|
||||
*/
|
||||
@@ -198,7 +214,7 @@ namespace Robomongo
|
||||
QsciLexerJavaScript *javaScriptLexer = new JSLexer(this);
|
||||
QFont font = GuiRegistry::instance().font();
|
||||
javaScriptLexer->setFont(font);
|
||||
_queryText->sciScintilla()->setBraceMatching(QsciScintilla::StrictBraceMatch);
|
||||
_queryText->sciScintilla()->setAppropriateBraceMatching();
|
||||
_queryText->sciScintilla()->setFont(font);
|
||||
_queryText->sciScintilla()->setPaper(QColor(255, 0, 0, 127));
|
||||
_queryText->sciScintilla()->setLexer(javaScriptLexer);
|
||||
@@ -208,4 +224,17 @@ namespace Robomongo
|
||||
|
||||
_queryText->sciScintilla()->setStyleSheet("QFrame { background-color: rgb(73, 76, 78); border: 1px solid #c7c5c4; border-radius: 4px; margin: 0px; padding: 0px;}");
|
||||
}
|
||||
|
||||
void DocumentTextEditor::saveWindowSettings() const
|
||||
{
|
||||
QSettings settings("Paralect", "Robomongo");
|
||||
settings.setValue("DocumentTextEditor/size", size());
|
||||
}
|
||||
|
||||
void DocumentTextEditor::restoreWindowSettings()
|
||||
{
|
||||
QSettings settings("Paralect", "Robomongo");
|
||||
resize(settings.value("DocumentTextEditor/size").toSize());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,16 +28,33 @@ namespace Robomongo
|
||||
void setCursorPosition(int line, int column);
|
||||
|
||||
public Q_SLOTS:
|
||||
virtual void accept();
|
||||
virtual void reject();
|
||||
void accept() override;
|
||||
void reject() override;
|
||||
bool validate(bool silentOnSuccess = true);
|
||||
|
||||
private Q_SLOTS:
|
||||
void onQueryTextChanged();
|
||||
void onValidateButtonClicked();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Reimplementing closeEvent in order to do some pre-close actions.
|
||||
*/
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
|
||||
private:
|
||||
void _configureQueryText();
|
||||
|
||||
/**
|
||||
* @brief Restore window settings from system registry
|
||||
*/
|
||||
void restoreWindowSettings();
|
||||
|
||||
/**
|
||||
* @brief Save window settings into system registry
|
||||
*/
|
||||
void saveWindowSettings() const;
|
||||
|
||||
const CollectionInfo _info;
|
||||
FindFrame *_queryText;
|
||||
bool _readonly;
|
||||
|
||||
@@ -0,0 +1,488 @@
|
||||
#include "robomongo/gui/dialogs/ExportDialog.h"
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QHBoxLayout>
|
||||
#include <QVBoxLayout>
|
||||
#include <QLineEdit>
|
||||
#include <QTextEdit>
|
||||
#include <QLabel>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QTreeWidget>
|
||||
#include <QComboBox>
|
||||
#include <QGroupBox>
|
||||
#include <QApplication>
|
||||
#include <QProcess>
|
||||
#include <QDir>
|
||||
#include <QFileDialog>
|
||||
#include <QDateTime>
|
||||
#include <QMessageBox>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QFileInfo>
|
||||
#include <QSizePolicy>
|
||||
#include <QProgressDialog>
|
||||
|
||||
#include "robomongo/core/utils/QtUtils.h"
|
||||
#include "robomongo/core/domain/App.h"
|
||||
#include "robomongo/core/AppRegistry.h"
|
||||
#include "robomongo/core/domain/MongoCollection.h"
|
||||
#include "robomongo/core/domain/MongoDatabase.h"
|
||||
#include "robomongo/gui/widgets/explorer/ExplorerServerTreeItem.h"
|
||||
#include "robomongo/gui/widgets/workarea/IndicatorLabel.h"
|
||||
#include "robomongo/gui/widgets/explorer/ExplorerWidget.h"
|
||||
#include "robomongo/gui/widgets/explorer/ExplorerTreeWidget.h"
|
||||
#include "robomongo/gui/widgets/explorer/ExplorerServerTreeItem.h"
|
||||
#include "robomongo/gui/widgets/explorer/ExplorerCollectionTreeItem.h"
|
||||
#include "robomongo/gui/widgets/explorer/ExplorerDatabaseCategoryTreeItem.h"
|
||||
#include "robomongo/gui/widgets/explorer/ExplorerDatabaseTreeItem.h"
|
||||
#include "robomongo/gui/widgets/explorer/ExplorerReplicaSetTreeItem.h"
|
||||
#include "robomongo/gui/utils/GuiConstants.h"
|
||||
#include "robomongo/gui/GuiRegistry.h"
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
namespace
|
||||
{
|
||||
const QString defaultDir = "D:\\exports\\"; // Default location
|
||||
|
||||
auto const AUTO_MODE_SIZE = QSize(500, 450);
|
||||
auto const MANUAL_MODE_SIZE = QSize(500, 400);
|
||||
|
||||
auto const SHOW_DETAILS = "<a href='error' style='color: #777777;'>Show details</a>";
|
||||
auto const HIDE_DETAILS = "<a href='error' style='color: #777777;'>Hide details</a>";
|
||||
|
||||
// This structure represents the arguments as in "mongoexport.exe --help"
|
||||
// See http://docs.mongodb.org/manual/reference/program/mongoexport/ for more information
|
||||
struct MongoExportArgs
|
||||
{
|
||||
static QString db(const QString& dbName) { return ("--db" + dbName); }
|
||||
static QString collection(const QString& collection) { return ("--collection" + collection); }
|
||||
|
||||
// i.e. absFilePath: "/exports/coll1.json"
|
||||
static QString out(const QString& absFilePath) { return ("--out " + absFilePath); }
|
||||
};
|
||||
}
|
||||
|
||||
ExportDialog::ExportDialog(QString const& dbName, QString const& collName, QWidget *parent) :
|
||||
QDialog(parent), _mode(AUTO), _mongoExportArgs(), _activeProcess(nullptr)
|
||||
{
|
||||
setWindowTitle("Export Collection");
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); // Remove help button (?)
|
||||
setMinimumSize(AUTO_MODE_SIZE);
|
||||
//setFixedHeight(AUTO_MODE_SIZE.height());
|
||||
|
||||
_activeProcess = new QProcess(this);
|
||||
//_activeProcess->setProcessChannelMode(QProcess::MergedChannels);
|
||||
VERIFY(connect(_activeProcess, SIGNAL(finished(int, QProcess::ExitStatus)),
|
||||
this, SLOT(on_exportFinished(int, QProcess::ExitStatus))));
|
||||
VERIFY(connect(_activeProcess, SIGNAL(errorOccurred(QProcess::ProcessError)),
|
||||
this, SLOT(on_processErrorOccurred(QProcess::ProcessError))));
|
||||
|
||||
// todo: move to a global location
|
||||
// Enable copyable text for QMessageBox
|
||||
qApp->setStyleSheet("QMessageBox { messagebox-text-interaction-flags: 5; }");
|
||||
|
||||
const QString serverName = "localhost:20017"; // todo: remove
|
||||
Indicator *serverIndicator = new Indicator(GuiRegistry::instance().serverIcon(), serverName);
|
||||
|
||||
// Horizontal line
|
||||
QFrame *horline = new QFrame();
|
||||
horline->setFrameShape(QFrame::HLine);
|
||||
horline->setFrameShadow(QFrame::Sunken);
|
||||
|
||||
// Widgets related to Input
|
||||
auto dbNameLabel = new QLabel("Database Name:");
|
||||
auto dbNameLineEdit = new QLineEdit;
|
||||
auto dbNameLay = new QHBoxLayout;
|
||||
dbNameLay->addWidget(dbNameLabel);
|
||||
dbNameLay->addWidget(dbNameLineEdit);
|
||||
|
||||
auto collNameLabel = new QLabel("Collection Name:");
|
||||
auto collNameLineEdit = new QLineEdit;
|
||||
auto collNameLay = new QHBoxLayout;
|
||||
collNameLay->addWidget(collNameLabel);
|
||||
collNameLay->addWidget(collNameLineEdit);
|
||||
|
||||
auto selectedCollLay = new QGridLayout;
|
||||
selectedCollLay->setAlignment(Qt::AlignTop);
|
||||
selectedCollLay->setColumnStretch(2, 1);
|
||||
|
||||
auto serverIcon = new QLabel("<html><img src=':/robomongo/icons/server_16x16.png'></html>");
|
||||
auto dbIcon = new QLabel("<html><img src=':/robomongo/icons/database_16x16.png'></html>");
|
||||
auto collIcon = new QLabel("<html><img src=':/robomongo/icons/collection_16x16.png'></html>");
|
||||
|
||||
selectedCollLay->addWidget(serverIcon, 1, 0);
|
||||
selectedCollLay->addWidget(new QLabel("Server: "), 1, 1);
|
||||
selectedCollLay->addWidget(new QLabel("local_(2)"), 1, 2);
|
||||
selectedCollLay->addWidget(dbIcon, 2, 0);
|
||||
selectedCollLay->addWidget(new QLabel("Database: "), 2, 1);
|
||||
selectedCollLay->addWidget(new QLabel(dbName), 2, 2);
|
||||
selectedCollLay->addWidget(collIcon, 3, 0);
|
||||
selectedCollLay->addWidget(new QLabel("Collection: "), 3, 1);
|
||||
selectedCollLay->addWidget(new QLabel(collName), 3, 2);
|
||||
|
||||
// Widgets related to Output
|
||||
_formatComboBox = new QComboBox;
|
||||
_formatComboBox->addItem("JSON");
|
||||
_formatComboBox->addItem("CSV");
|
||||
VERIFY(connect(_formatComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(on_formatComboBox_change(int))));
|
||||
|
||||
_fieldsLabel = new QLabel("Fields:");
|
||||
_fields = new QLineEdit; // todo: use textedit
|
||||
// Initially hidden
|
||||
_fieldsLabel->setHidden(true);
|
||||
_fields->setHidden(true);
|
||||
|
||||
_query = new QLineEdit("{}"); // todo: JSON frame can be used
|
||||
_outputFileName = new QLineEdit;
|
||||
_outputDir = new QLineEdit;
|
||||
_browseButton = new QPushButton("...");
|
||||
_browseButton->setMaximumWidth(50);
|
||||
VERIFY(connect(_browseButton, SIGNAL(clicked()), this, SLOT(on_browseButton_clicked())));
|
||||
|
||||
// Export summary widgets
|
||||
_exportOutput = new QTextEdit;
|
||||
QFontMetrics font(_exportOutput->font());
|
||||
_exportOutput->setFixedHeight((4+1.5) * (font.lineSpacing())); // 4-line text edit
|
||||
_exportOutput->setReadOnly(true);
|
||||
|
||||
_mongoExportOutput = new QTextEdit;
|
||||
_mongoExportOutput->setFixedHeight(4 * (font.lineSpacing() + 8)); // 4-line text edit
|
||||
_mongoExportOutput->setReadOnly(true);
|
||||
_mongoExportOutput->setHidden(true);
|
||||
|
||||
// Attempt to fix issue for Windows High DPI button height is slightly taller than other widgets
|
||||
#ifdef Q_OS_WIN
|
||||
_browseButton->setMaximumHeight(HighDpiConstants::WIN_HIGH_DPI_BUTTON_HEIGHT);
|
||||
#endif
|
||||
auto outputsInnerLay = new QGridLayout;
|
||||
outputsInnerLay->addWidget(new QLabel("Format:"), 0, 0);
|
||||
outputsInnerLay->addWidget(_formatComboBox, 0, 1, 1, 2);
|
||||
outputsInnerLay->addWidget(_fieldsLabel, 1, 0, Qt::AlignTop);
|
||||
outputsInnerLay->addWidget(_fields, 1, 1, 1, 2);
|
||||
outputsInnerLay->addWidget(new QLabel("Query:"), 2, 0);
|
||||
outputsInnerLay->addWidget(_query, 2, 1, 1, 2);
|
||||
outputsInnerLay->addWidget(new QLabel("File Name:"), 3, 0);
|
||||
outputsInnerLay->addWidget(_outputFileName, 3, 1, 1, 2);
|
||||
outputsInnerLay->addWidget(new QLabel("Directory:"), 4, 0);
|
||||
outputsInnerLay->addWidget(_outputDir, 4, 1);
|
||||
outputsInnerLay->addWidget(_browseButton, 4, 2);
|
||||
//outputsInnerLay->addWidget(new QLabel("Result:"), 5, 0, Qt::AlignTop);
|
||||
//outputsInnerLay->addWidget(_exportOutput, 6, 0, 1, 3, Qt::AlignTop);
|
||||
|
||||
auto manualLayout = new QGridLayout;
|
||||
auto cmdLabel = new QLabel("Command:");
|
||||
cmdLabel->setFixedHeight(cmdLabel->sizeHint().height());
|
||||
_manualExportCmd = new QTextEdit;
|
||||
QFontMetrics font1(_manualExportCmd->font());
|
||||
_manualExportCmd->setFixedHeight(2 * (font1.lineSpacing()+8)); // 2-line text edit
|
||||
manualLayout->addWidget(cmdLabel, 0, 0, Qt::AlignTop);
|
||||
manualLayout->addWidget(_manualExportCmd, 1, 0, Qt::AlignTop);
|
||||
|
||||
// Button box and Manual Mode button
|
||||
_modeButton = new QPushButton("Manual Mode");
|
||||
VERIFY(connect(_modeButton, SIGNAL(clicked()), this, SLOT(on_modeButton_clicked())));
|
||||
_buttonBox = new QDialogButtonBox(this);
|
||||
_buttonBox->setOrientation(Qt::Horizontal);
|
||||
_buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Save);
|
||||
_buttonBox->button(QDialogButtonBox::Save)->setText("E&xport");
|
||||
_buttonBox->button(QDialogButtonBox::Save)->setMaximumWidth(70);
|
||||
_buttonBox->button(QDialogButtonBox::Cancel)->setMaximumWidth(70);
|
||||
VERIFY(connect(_buttonBox, SIGNAL(accepted()), this, SLOT(accept())));
|
||||
VERIFY(connect(_buttonBox, SIGNAL(rejected()), this, SLOT(reject())));
|
||||
|
||||
// Sub layouts
|
||||
auto serverIndicatorlayout = new QHBoxLayout();
|
||||
if (!serverName.isEmpty()) {
|
||||
serverIndicatorlayout->addWidget(serverIndicator, 0, Qt::AlignLeft);
|
||||
}
|
||||
|
||||
// Input layout
|
||||
_inputsGroupBox = new QGroupBox("Selected Collection");
|
||||
_inputsGroupBox->setLayout(selectedCollLay);
|
||||
_inputsGroupBox->setStyleSheet("QGroupBox::title { left: 0px }");
|
||||
_inputsGroupBox->setFixedHeight(_inputsGroupBox->sizeHint().height());
|
||||
|
||||
// Outputs
|
||||
_autoOutputsGroup = new QGroupBox("Output Properties");
|
||||
_autoOutputsGroup->setLayout(outputsInnerLay);
|
||||
_autoOutputsGroup->setStyleSheet("QGroupBox::title { left: 0px }");
|
||||
_autoOutputsGroup->setFixedHeight(_autoOutputsGroup->sizeHint().height());
|
||||
|
||||
// Manual Groupbox
|
||||
_manualGroupBox = new QGroupBox("Manual Export");
|
||||
_manualGroupBox->setLayout(manualLayout);
|
||||
_manualGroupBox->setStyleSheet("QGroupBox::title { left: 0px }");
|
||||
_manualGroupBox->setHidden(true);
|
||||
|
||||
// Export Summary
|
||||
auto exportSummaryGroup = new QGroupBox("Export Summary");
|
||||
exportSummaryGroup->setStyleSheet("QGroupBox::title { left: 0px }");
|
||||
_viewOutputLink = new QLabel(SHOW_DETAILS);
|
||||
VERIFY(connect(_viewOutputLink, SIGNAL(linkActivated(QString)), this, SLOT(on_viewOutputLink(QString))));
|
||||
_viewOutputLink->setFixedHeight(_viewOutputLink->sizeHint().height());
|
||||
auto tempLayout = new QVBoxLayout();
|
||||
//tempLayout->setSizeConstraint(QLayout::SetFixedSize);
|
||||
tempLayout->addWidget(_exportOutput, Qt::AlignTop);
|
||||
tempLayout->addWidget(_viewOutputLink, Qt::AlignLeft | Qt::AlignTop);
|
||||
//tempLayout->addWidget(_mongoExportOutput);
|
||||
exportSummaryGroup->setLayout(tempLayout);
|
||||
exportSummaryGroup->setFixedHeight(exportSummaryGroup->sizeHint().height());
|
||||
|
||||
// Buttonbox layout
|
||||
auto hButtonBoxlayout = new QHBoxLayout();
|
||||
hButtonBoxlayout->addStretch(1);
|
||||
hButtonBoxlayout->addWidget(_buttonBox);
|
||||
hButtonBoxlayout->addWidget(_modeButton);
|
||||
|
||||
// Main Layout
|
||||
auto layout = new QVBoxLayout();
|
||||
layout->addWidget(_inputsGroupBox, Qt::AlignTop);
|
||||
//layout->addWidget(horline, Qt::AlignTop);
|
||||
layout->addWidget(_autoOutputsGroup, Qt::AlignTop);
|
||||
layout->addWidget(_manualGroupBox);
|
||||
layout->addWidget(exportSummaryGroup, Qt::AlignTop);
|
||||
layout->addLayout(hButtonBoxlayout);
|
||||
setLayout(layout);
|
||||
|
||||
// todo: move to a function
|
||||
// Help user filling inputs automatically
|
||||
auto date = QDateTime::currentDateTime().toString("dd.MM.yyyy");
|
||||
auto time = QDateTime::currentDateTime().toString("hh.mm.ss");
|
||||
auto timeStamp = date + "_" + time;
|
||||
auto format = _formatComboBox->currentIndex() == 0 ? "json" : "csv";
|
||||
|
||||
_outputFileName->setText(dbName + "." + collName + "_" + timeStamp + "." + format);
|
||||
_outputDir->setText(defaultDir);
|
||||
_manualExportCmd->setText("mongoexport --db " + dbName + " --collection " + collName +
|
||||
" --out " + _outputDir->text() + _outputFileName->text());
|
||||
|
||||
// todo: setExportArgs()
|
||||
// First set db and coll
|
||||
_mongoExportArgs = " --db " + dbName + " --collection " + collName;
|
||||
|
||||
_outputFileName->setFocus();
|
||||
}
|
||||
|
||||
void ExportDialog::setOkButtonText(const QString &text)
|
||||
{
|
||||
_buttonBox->button(QDialogButtonBox::Save)->setText(text);
|
||||
}
|
||||
|
||||
// todo: remove
|
||||
void ExportDialog::setInputLabelText(const QString &text)
|
||||
{
|
||||
//_inputLabel->setText(text);
|
||||
}
|
||||
|
||||
void ExportDialog::accept()
|
||||
{
|
||||
QString mongoExport = "D:\\mongo_export\\bin\\mongoexport.exe";
|
||||
|
||||
bool disable = false;
|
||||
enableDisableWidgets(disable);
|
||||
|
||||
if (AUTO == _mode)
|
||||
{
|
||||
_exportOutput->clear();
|
||||
_exportOutput->setText("Exporting...");
|
||||
|
||||
// If CSV append output format and fields
|
||||
if (_formatComboBox->currentIndex() == 1) {
|
||||
if (_fields->text().isEmpty()) {
|
||||
QMessageBox::critical(this, "Error", "\"Fields\" option is required in CSV mode.");
|
||||
return;
|
||||
}
|
||||
_mongoExportArgs.append(" --type=csv");
|
||||
_mongoExportArgs.append(" --fields " + _fields->text().replace(" ", ""));
|
||||
}
|
||||
|
||||
if (!_query->text().isEmpty() && _query->text() != "{}") {
|
||||
_mongoExportArgs.append(" --query " + _query->text());
|
||||
}
|
||||
|
||||
// Append file path and name
|
||||
auto absFilePath = _outputDir->text() + _outputFileName->text();
|
||||
_mongoExportArgs.append(" --out " + absFilePath);
|
||||
|
||||
// Start mongoexport non-blocking
|
||||
_activeProcess->start(mongoExport + _mongoExportArgs);
|
||||
}
|
||||
else if (MANUAL == _mode)
|
||||
{
|
||||
_exportOutput->clear();
|
||||
_exportOutput->setText("Exporting...");
|
||||
|
||||
// todo: check if _activeProcess->state() is QProcess::NotRunning
|
||||
// Start mongoexport non-blocking
|
||||
_activeProcess->start("D:\\mongo_export\\bin\\" + _manualExportCmd->toPlainText());
|
||||
}
|
||||
}
|
||||
|
||||
void ExportDialog::ui_itemExpanded(QTreeWidgetItem *item)
|
||||
{
|
||||
auto categoryItem = dynamic_cast<ExplorerDatabaseCategoryTreeItem *>(item);
|
||||
if (categoryItem) {
|
||||
categoryItem->expand();
|
||||
return;
|
||||
}
|
||||
|
||||
ExplorerServerTreeItem *serverItem = dynamic_cast<ExplorerServerTreeItem *>(item);
|
||||
if (serverItem) {
|
||||
serverItem->expand();
|
||||
return;
|
||||
}
|
||||
|
||||
auto dirItem = dynamic_cast<ExplorerCollectionDirIndexesTreeItem *>(item);
|
||||
if (dirItem) {
|
||||
dirItem->expand();
|
||||
}
|
||||
}
|
||||
|
||||
void ExportDialog::ui_itemDoubleClicked(QTreeWidgetItem *item, int column)
|
||||
{
|
||||
// todo
|
||||
}
|
||||
|
||||
void ExportDialog::on_browseButton_clicked()
|
||||
{
|
||||
// Select output directory
|
||||
QString origDir = QFileDialog::getExistingDirectory(this, tr("Select Directory"), defaultDir,
|
||||
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
|
||||
auto dir = QDir::toNativeSeparators(origDir);
|
||||
|
||||
QApplication::activeModalWidget()->raise();
|
||||
QApplication::activeModalWidget()->activateWindow();
|
||||
|
||||
if (dir.isNull())
|
||||
return;
|
||||
|
||||
_outputDir->setText(dir + "\\");
|
||||
}
|
||||
|
||||
void ExportDialog::on_formatComboBox_change(int index)
|
||||
{
|
||||
bool const isCsv = static_cast<bool>(index);
|
||||
_fieldsLabel->setVisible(isCsv);
|
||||
_fields->setVisible(isCsv);
|
||||
|
||||
// todo: divide ui_itemClicked()
|
||||
//ui_itemClicked(_treeWidget->currentItem());
|
||||
}
|
||||
|
||||
void ExportDialog::on_modeButton_clicked()
|
||||
{
|
||||
_exportOutput->clear();
|
||||
_mode = (AUTO == _mode ? MANUAL : AUTO);
|
||||
_modeButton->setText(AUTO == _mode ? "Manual Mode" : "Auto Mode");
|
||||
_autoOutputsGroup->setVisible(AUTO == _mode);
|
||||
_manualGroupBox->setVisible(MANUAL == _mode);
|
||||
setMinimumSize(AUTO == _mode ? AUTO_MODE_SIZE : MANUAL_MODE_SIZE);
|
||||
_inputsGroupBox->setTitle(AUTO == _mode ? "Selected Collection" : "Selected Server");
|
||||
adjustSize();
|
||||
}
|
||||
|
||||
void ExportDialog::on_exportFinished(int exitCode, QProcess::ExitStatus exitStatus)
|
||||
{
|
||||
bool enable = true;
|
||||
enableDisableWidgets(enable);
|
||||
|
||||
// Extract absolute file path
|
||||
QString absFilePath;
|
||||
if (AUTO == _mode)
|
||||
{
|
||||
absFilePath = _outputDir->text() + _outputFileName->text();
|
||||
}
|
||||
else if (MANUAL == _mode)
|
||||
{
|
||||
// extract absolute file path string
|
||||
QStringList strlist1 = _manualExportCmd->toPlainText().split("--out");
|
||||
if (strlist1.size() > 1) {
|
||||
QString str1 = strlist1[1];
|
||||
QStringList strlist2 = str1.split("--");
|
||||
if (strlist2.size() > 1) {
|
||||
absFilePath = strlist2[0];
|
||||
}
|
||||
else {
|
||||
absFilePath = str1;
|
||||
}
|
||||
}
|
||||
}
|
||||
absFilePath.replace(" ", ""); // todo: handle paths with white spaces
|
||||
|
||||
// todo: also check process exit code
|
||||
// Check exported file exists and mongoexport output does not contain error
|
||||
QFileInfo const file(absFilePath);
|
||||
_mongoExportOutputStr = _activeProcess->readAllStandardError(); // Extract mongoexport command output
|
||||
if (file.exists() && file.isFile() && _mongoExportOutputStr.contains("exported")) {
|
||||
QStringList splitA = _mongoExportOutputStr.split("exported");
|
||||
QStringList splitB = splitA[1].split("records");
|
||||
_exportResult = "Export Successful: \n"
|
||||
"Exported file: " + absFilePath + "\n"
|
||||
"Number of records exported:" + splitB[0];
|
||||
_exportOutput->setText(_exportResult);
|
||||
}
|
||||
else {
|
||||
_exportOutput->setText("Export Failed.\n");
|
||||
_exportOutput->append("Output:\n" + _mongoExportOutputStr);
|
||||
}
|
||||
|
||||
_exportOutput->moveCursor(QTextCursor::Start);
|
||||
}
|
||||
|
||||
void ExportDialog::on_processErrorOccurred(QProcess::ProcessError error)
|
||||
{
|
||||
bool enable = true;
|
||||
enableDisableWidgets(enable);
|
||||
|
||||
if (QProcess::FailedToStart == error) {
|
||||
_exportOutput->setText("Error: \"mongoexport\" process failed to start. Either the "
|
||||
"invoked program is missing, or you may have insufficient permissions to invoke the program.");
|
||||
}
|
||||
else if (QProcess::Crashed == error) {
|
||||
_exportOutput->setText("Error: \"mongoexport\" process crashed some time after starting"
|
||||
" successfully..");
|
||||
}
|
||||
else {
|
||||
_exportOutput->setText("Error: \"mongoexport\" process failed. Error code: "
|
||||
+ QString::number(error));
|
||||
}
|
||||
|
||||
_exportOutput->moveCursor(QTextCursor::Start);
|
||||
}
|
||||
|
||||
void ExportDialog::on_viewOutputLink(QString)
|
||||
{
|
||||
QMessageBox::information(this, "Details", _mongoExportOutputStr);
|
||||
}
|
||||
|
||||
Indicator *ExportDialog::createDatabaseIndicator(const QString &database)
|
||||
{
|
||||
return new Indicator(GuiRegistry::instance().databaseIcon(), database);
|
||||
}
|
||||
|
||||
Indicator *ExportDialog::createCollectionIndicator(const QString &collection)
|
||||
{
|
||||
return new Indicator(GuiRegistry::instance().collectionIcon(), collection);
|
||||
}
|
||||
|
||||
void ExportDialog::enableDisableWidgets(bool enable) const
|
||||
{
|
||||
// Auto mode widgets
|
||||
//_treeWidget->setEnabled(enable);
|
||||
_formatComboBox->setEnabled(enable);
|
||||
_fieldsLabel->setEnabled(enable);
|
||||
_fields->setEnabled(enable);
|
||||
_query->setEnabled(enable);
|
||||
_outputFileName->setEnabled(enable);
|
||||
_outputDir->setEnabled(enable);
|
||||
_browseButton->setEnabled(enable);
|
||||
_buttonBox->button(QDialogButtonBox::Save)->setEnabled(enable);
|
||||
_modeButton->setEnabled(enable);
|
||||
|
||||
// Manual mode widgets
|
||||
//_treeWidget->setEnabled(enable);
|
||||
_manualExportCmd->setEnabled(enable);
|
||||
_buttonBox->button(QDialogButtonBox::Save)->setEnabled(enable);
|
||||
_modeButton->setEnabled(enable);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
#include <QProcess>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QLabel;
|
||||
class QDialogButtonBox;
|
||||
class QLineEdit;
|
||||
class QTreeWidgetItem;
|
||||
class QTreeWidget;
|
||||
class QComboBox;
|
||||
class QPushButton;
|
||||
class QGroupBox;
|
||||
class QTextEdit;
|
||||
class QProcess;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
class Indicator;
|
||||
|
||||
/**
|
||||
* @brief This class is not finished, it is still under development.
|
||||
* Currently export is disabled in the GUI. If enabled in GUI, export
|
||||
* will work with current code. However, the code is unfinished;
|
||||
* it needs to finished, reviewed and tested.
|
||||
* Currently, there is support for simultaneous export which can be done
|
||||
* on multiple export dialogs without blocking each other or main window.
|
||||
* So user can still work while multiple export dialogs are doing export.
|
||||
* This achieved by the assumption that there will be usable export executable for
|
||||
* Robomongo to use. Currently, this location is hard coded as
|
||||
* "D:\\mongo_export\\bin\\mongoexport.exe". One of the next actions will be
|
||||
* to design the location of mongoexport which should be cross-platform,
|
||||
* generic and not hard coded.
|
||||
*/
|
||||
class ExportDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
enum MODE
|
||||
{
|
||||
AUTO = 0,
|
||||
MANUAL = 1
|
||||
};
|
||||
|
||||
public:
|
||||
explicit ExportDialog(QString const& dbName, QString const& collName, QWidget *parent = 0);
|
||||
void setOkButtonText(const QString &text);
|
||||
void setInputLabelText(const QString &text);
|
||||
void setInputText(const QString &text);
|
||||
enum { maxLenghtName = 60 };
|
||||
|
||||
public Q_SLOTS:
|
||||
virtual void accept();
|
||||
|
||||
private Q_SLOTS:
|
||||
void ui_itemExpanded(QTreeWidgetItem *item);
|
||||
void ui_itemDoubleClicked(QTreeWidgetItem *item, int column);
|
||||
void on_browseButton_clicked();
|
||||
void on_formatComboBox_change(int index);
|
||||
void on_modeButton_clicked();
|
||||
void on_exportFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
||||
void on_processErrorOccurred(QProcess::ProcessError);
|
||||
void on_viewOutputLink(QString);
|
||||
|
||||
private:
|
||||
// todo: remove if unused
|
||||
Indicator *createDatabaseIndicator(const QString &database);
|
||||
Indicator *createCollectionIndicator(const QString &collection);
|
||||
|
||||
// Enable/Disable widgets during/after export operation
|
||||
// @param enable: true to enable, false to disable widgets
|
||||
void enableDisableWidgets(bool enable) const;
|
||||
|
||||
// Auto Mode
|
||||
QGroupBox* _inputsGroupBox;
|
||||
QComboBox* _formatComboBox;
|
||||
QLabel* _fieldsLabel;
|
||||
QLineEdit* _fields;
|
||||
QLineEdit* _query;
|
||||
QLineEdit* _outputFileName;
|
||||
QLineEdit* _outputDir;
|
||||
QPushButton* _browseButton;
|
||||
QGroupBox* _autoOutputsGroup;
|
||||
QTextEdit* _exportOutput;
|
||||
QLabel* _viewOutputLink;
|
||||
//
|
||||
QTextEdit* _mongoExportOutput;
|
||||
|
||||
// Manual Mode
|
||||
QTextEdit* _manualExportCmd;
|
||||
QPushButton* _modeButton;
|
||||
QGroupBox* _manualGroupBox;
|
||||
// Common
|
||||
QDialogButtonBox* _buttonBox;
|
||||
|
||||
MODE _mode;
|
||||
QString _dbName;
|
||||
QString _collName;
|
||||
QString _mongoExportArgs;
|
||||
QString _exportResult;
|
||||
QString _mongoExportOutputStr;
|
||||
QProcess* _activeProcess; // pointer to running/finished process
|
||||
};
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user