Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b812ffba2 | ||
|
|
14d3caaa7a | ||
|
|
1124ec22c6 | ||
|
|
ab367fc890 | ||
|
|
22c632794f | ||
|
|
0fefd6d443 | ||
|
|
89f24ea07c | ||
|
|
d2a11bc0f2 | ||
|
|
575455b10e | ||
|
|
e858c68fa6 | ||
|
|
2df26740c6 | ||
|
|
39209ee2b8 | ||
|
|
3e9cd11aa3 | ||
|
|
f91b2c4a2f | ||
|
|
a2b1a07a4f | ||
|
|
82b5f434f9 | ||
|
|
a55b7398da | ||
|
|
d9b37b2094 | ||
|
|
230c5f37bc | ||
|
|
561bdbf2b4 | ||
|
|
0bafec7ce5 | ||
|
|
34dca72a09 | ||
|
|
428b576acf | ||
|
|
f26a08d9bc | ||
|
|
92ae9affd6 | ||
|
|
d52cd2e0c0 | ||
|
|
e11cbd1a26 | ||
|
|
4e58a6a6b4 | ||
|
|
e01de676fa | ||
|
|
9e43802e2a | ||
|
|
f02f4f6116 | ||
|
|
306689f30d | ||
|
|
43bf28968b | ||
|
|
df6e9a57d8 | ||
|
|
cbcafd28c9 | ||
|
|
4f181ec460 | ||
|
|
a72583df76 | ||
|
|
8f508e5e0f | ||
|
|
536f75fdb4 | ||
|
|
f37a070eda | ||
|
|
51c1267bf4 | ||
|
|
6a0b972aac | ||
|
|
94168f02ca | ||
|
|
88c8dfa26e | ||
|
|
c8050f0edd | ||
|
|
e3d90e63e8 | ||
|
|
ec20a3e4c9 | ||
|
|
83bac6b8aa | ||
|
|
34025743a1 | ||
|
|
ff02a303c7 | ||
|
|
4552576fa3 | ||
|
|
cc2c834359 | ||
|
|
b746aeb729 | ||
|
|
686fc08cef | ||
|
|
9d74aae3f5 | ||
|
|
b3fd99eb1b | ||
|
|
bf111c97f1 | ||
|
|
ac8d4cf899 | ||
|
|
04bd7fbffa | ||
|
|
8167e8d944 | ||
|
|
6c3174599a | ||
|
|
76aca637e8 | ||
|
|
832d843ea1 | ||
|
|
9aa702efcd | ||
|
|
cde6d9eaae | ||
|
|
91db033e3a | ||
|
|
7d6c0af7e7 | ||
|
|
d0c2345044 | ||
|
|
51c4b93bc7 | ||
|
|
242acbcd9d | ||
|
|
3079801e0f | ||
|
|
fdc02400b9 | ||
|
|
499b0da006 | ||
|
|
a7caf02ec6 | ||
|
|
70b819700d | ||
|
|
b1aca90888 | ||
|
|
73ccc5d3ad | ||
|
|
47d3fd7db9 | ||
|
|
56d2d7cb12 | ||
|
|
ac3c078141 | ||
|
|
0110a0df8d | ||
|
|
c1d6b51075 | ||
|
|
bc6c6a36da | ||
|
|
806d76d9a2 | ||
|
|
a0a476b57d | ||
|
|
d5dd24fa51 | ||
|
|
42561be37e | ||
|
|
c2fdee9cf8 | ||
|
|
9972ee4113 | ||
|
|
846b0ce9fd | ||
|
|
fe78fe4416 | ||
|
|
54d03ae788 | ||
|
|
55f895a7f5 | ||
|
|
22b557605b | ||
|
|
16ed6e844a | ||
|
|
d96b3de97a | ||
|
|
bd0436ffa0 | ||
|
|
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 | ||
|
|
702459918b | ||
|
|
9c419b1f2e | ||
|
|
0786489924 | ||
|
|
2835aa1685 | ||
|
|
9cc34df185 | ||
|
|
d5adcdf988 | ||
|
|
837c0e1a17 | ||
|
|
d458f43b63 | ||
|
|
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 | ||
|
|
0086df54f0 | ||
|
|
ddc0c910c8 | ||
|
|
31f74baef7 | ||
|
|
8cd4f50304 | ||
|
|
48f6abb361 | ||
|
|
47039d8685 | ||
|
|
bc9aded3b7 | ||
|
|
f919544d1e | ||
|
|
ea49d0508a | ||
|
|
578447ecc3 |
@@ -1,61 +1,154 @@
|
||||
|
||||
Latest Release:
|
||||
Robomongo 0.9.0 RC10 (Aug 8, 2016)
|
||||
http://blog.robomongo.org/robomongo-rc10/
|
||||
Latest Version:
|
||||
Robomongo 1.0 (21 Apr, 2017)
|
||||
http://blog.robomongo.org/robomongo-1-0
|
||||
|
||||
Highlights from Robomongo 0.9.0 RCs:
|
||||
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.
|
||||
- Support for SSL connections 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.
|
||||
|
||||
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)
|
||||
|
||||
Robomongo 1.x:
|
||||
Major Features:
|
||||
- Replica set support.
|
||||
- Update Notifications Bar
|
||||
- Welcome Tab
|
||||
- New operation logging for almost all operations.
|
||||
|
||||
Major Fixes/Improvements:
|
||||
- Improved error handling (no double error windows) for almost all operations.
|
||||
- Timestamp value is represented incorrectly (https://github.com/Studio3T/robomongo/issues/1159)
|
||||
- Local Timezone shows wrong date (https://github.com/Studio3T/robomongo/issues/501)
|
||||
|
||||
* What's new in patch 1.0.3 (7 June, 2017)
|
||||
- Attempt to prevent MAC OS Sierra crash related to updates notification bar
|
||||
|
||||
* What's new in 1.0 (21 Apr, 2017)
|
||||
New Features:
|
||||
- Update Notifications Bar
|
||||
- Welcome Tab
|
||||
- Welcome Wizard
|
||||
|
||||
Improvements:
|
||||
- Replica set name configurable on UI
|
||||
- Configuration file system location change
|
||||
- "About Robomongo" dialog changes
|
||||
|
||||
Fixes:
|
||||
- Fix to avoid the same set name problem (https://github.com/Studio3T/robomongo/issues/1302)
|
||||
- Timestamp value is represented incorrectly (https://github.com/Studio3T/robomongo/issues/1159)
|
||||
- Local Timezone shows wrong date (https://github.com/Studio3T/robomongo/issues/501)
|
||||
- Time zone shown as "000:00" when UTC +00:00 is used as local timezone(https://github.com/Studio3T/robomongo/issues/1309)
|
||||
|
||||
* 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
|
||||
- SSL support
|
||||
- MongoDB Atlas (Cloud MongoDB) support
|
||||
- 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
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
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)
|
||||
|
||||
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
|
||||
|
||||
@@ -130,7 +223,7 @@ http://blog.robomongo.org/robomongo-rc10/
|
||||
* [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
|
||||
|
||||
|
||||
@@ -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_PATCH "0")
|
||||
set(PROJECT_VERSION_BUILD "-RC10")
|
||||
set(PROJECT_VERSION_MAJOR "1")
|
||||
set(PROJECT_VERSION_MINOR "0")
|
||||
set(PROJECT_VERSION_PATCH "3")
|
||||
set(PROJECT_VERSION_BUILD "")
|
||||
set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}${PROJECT_VERSION_BUILD}")
|
||||
|
||||
# Modules
|
||||
@@ -27,6 +27,8 @@ find_package(Qt5Core REQUIRED)
|
||||
find_package(Qt5Gui REQUIRED)
|
||||
find_package(Qt5Widgets REQUIRED)
|
||||
find_package(Qt5PrintSupport REQUIRED)
|
||||
find_package(Qt5Network REQUIRED)
|
||||
find_package(Qt5Xml REQUIRED)
|
||||
find_package(MongoDB REQUIRED)
|
||||
find_package(OpenSSL REQUIRED)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Robomongo, shell-centric cross-platform MongoDB management tool.
|
||||
|
||||
Copyright 2013, Paralect.
|
||||
Copyright 2014-2017 3T Software Labs Ltd.
|
||||
|
||||
The Robomongo is released under the terms of the GNU General Public
|
||||
License, version 3.
|
||||
|
||||
@@ -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,31 +7,34 @@ 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 0.9.0 RC10?
|
||||
What's new in latest Robomongo 1.0?
|
||||
====================================
|
||||
Latest release of Robomongo 0.9.0 RC10 includes:
|
||||
- Support for SSL
|
||||
- MongoDB Atlas Support
|
||||
- HiDPI enhancements
|
||||
|
||||
Central feature of this release is full support for TSL/SSL protocol with advanced options. This version also brings support to MongoDB Atlas cloud service and includes enhancements for HiDPI displays.
|
||||
Latest release of Robomongo 1.0 includes:
|
||||
- Update Notifications Bar
|
||||
- Welcome Tab
|
||||
- Welcome Wizard
|
||||
- Replica set name configurable on UI
|
||||
- Fixes to avoid same replica set name problem
|
||||
- Fixes to solve various problems regarding Data and Timestamp data types
|
||||
|
||||
Blog: http://blog.robomongo.org/robomongo-rc10
|
||||
Blog: http://blog.robomongo.org/robomongo-1-0/
|
||||
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 Release?
|
||||
What's Planned for the Next Releases?
|
||||
====================================
|
||||
|
||||
We are currently working towards [Robomongo 0.9.0 RC11 milestone](https://github.com/paralect/robomongo/milestone/16).
|
||||
We are currently working towards [Robomongo 2.0 milestone](https://github.com/paralect/robomongo/milestone/21).
|
||||
|
||||
Currently in progress:
|
||||
- Replica Sets Support
|
||||
- MongoDB 3.4 Support
|
||||
|
||||
Plans for Future:
|
||||
- User Roles
|
||||
- Support execution of multiple simultaneous queries [#1161](https://github.com/paralect/robomongo/issues/1161)
|
||||
- Enhancements for stability, running without crashes
|
||||
- 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
|
||||
@@ -39,20 +42,19 @@ Plans for Future:
|
||||
Supported Platforms
|
||||
===============
|
||||
|
||||
**Supported MongoDBs:**
|
||||
| MongoDB Versions |
|
||||
| :-------------------- |
|
||||
| 3.2 |
|
||||
| 3.0 |
|
||||
| 2.6 |
|
||||
|
||||
| MongoDB Versions | MongoDB Cloud |
|
||||
| :-------------------- | :------------ |
|
||||
| 3.2 | MongoDB Atlas |
|
||||
| 3.0 | Compose |
|
||||
| 2.6 | mLab |
|
||||
| | ObjectRocket |
|
||||
|
||||
|
||||
|
||||
**Note**: Currently Robomongo supports connection to single server of a replica set. Support for connection to **Replica Sets** is one of the most important features of next Robomongo release plan.
|
||||
|
||||
**Supported OS Platforms:**
|
||||
| MongoDB Cloud Platforms|
|
||||
| :------------ |
|
||||
| MongoDB Atlas |
|
||||
| Compose |
|
||||
| mLab |
|
||||
| ObjectRocket |
|
||||
| Amazon EC2 |
|
||||
|
||||
| Windows | Mac | Linux |
|
||||
|:---------------------- | :---------------| :---------------------------|
|
||||
@@ -63,14 +65,13 @@ Supported Platforms
|
||||
|
||||
\* 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 release candidate is currently **Robomongo 0.9.0 RC10**. (http://blog.robomongo.org/robomongo-rc10/).
|
||||
|
||||
The latest stable release is currently **Robomongo 0.8.5**. (http://blog.robomongo.org/whats-new-in-robomongo-0-8-5/)
|
||||
The latest stable release is currently [**Robomongo 1.0**](http://blog.robomongo.org/robomongo-1-0/).
|
||||
|
||||
Support
|
||||
=======
|
||||
@@ -81,7 +82,7 @@ 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
|
||||
=====
|
||||
@@ -96,6 +97,7 @@ Contribute!
|
||||
### 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).
|
||||
|
||||
@@ -107,8 +109,6 @@ Code contributions are always welcome! Just try to follow our pre-commit checks
|
||||
|
||||
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.
|
||||
|
||||
For a general workflow, see Github's guide to [Fork a Repo](https://help.github.com/articles/fork-a-repo/).
|
||||
|
||||
Pull requests (PRs) should generally be for discrete issues (i.e. one issue per PR please) and be clean to merge against the current master branch. It would also be helpful if you can confirm what testing has been done (specific O/S targets and MongoDB versions if applicable).
|
||||
|
||||
A usual naming approach for feature branches is `issue-###`. Include the issue number in your commit message / pull request description to link the PR to the original issue.
|
||||
@@ -128,7 +128,7 @@ There are a number of issues we could use help with reproducing. They are marked
|
||||
License
|
||||
=======
|
||||
|
||||
Copyright (C) 2013-2016 [Paralect, Inc](http://www.paralect.com)
|
||||
Copyright 2014-2017 [3T Software Labs Ltd](https://studio3t.com/). All rights reserved.
|
||||
|
||||
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
|
||||
|
||||
@@ -8,6 +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("| Qt5 Network: ${Qt5Network_VERSION} ${Qt5Network_DIR}")
|
||||
message("| Qt5 Xml: ${Qt5Xml_VERSION} ${Qt5Xml_DIR}")
|
||||
message("| MongoDB: ${MongoDB_VERSION} ${MongoDB_BUILD_DIR}")
|
||||
message("| OpenSSL: ${OPENSSL_VERSION} ${OpenSSL_DIR}")
|
||||
message("|")
|
||||
|
||||
@@ -24,7 +24,7 @@ if(SYSTEM_LINUX)
|
||||
set(qt_conf_dir ${bin_dir})
|
||||
set(qt_conf_plugins "../lib")
|
||||
elseif(SYSTEM_MACOSX)
|
||||
set(bundle_name Robomongo.app)
|
||||
set(bundle_name "Robomongo.app")
|
||||
set(contents_path ${bundle_name}/Contents)
|
||||
|
||||
set(bin_dir ${contents_path}/MacOS)
|
||||
@@ -72,7 +72,9 @@ elseif(SYSTEM_MACOSX)
|
||||
elseif(SYSTEM_LINUX)
|
||||
install(
|
||||
FILES
|
||||
"${OpenSSL_DIR}/libssl.so.1.0.0"
|
||||
"${OpenSSL_DIR}/libssl.so"
|
||||
"${OpenSSL_DIR}/libssl.so.1.0.0"
|
||||
"${OpenSSL_DIR}/libcrypto.so"
|
||||
"${OpenSSL_DIR}/libcrypto.so.1.0.0"
|
||||
DESTINATION ${lib_dir})
|
||||
endif()
|
||||
@@ -93,7 +95,7 @@ install(
|
||||
DESTINATION ${license_dir})
|
||||
|
||||
# Install common dependencies
|
||||
install_qt_lib(Core Gui Widgets PrintSupport)
|
||||
install_qt_lib(Core Gui Widgets PrintSupport Network Xml)
|
||||
install_qt_plugins(QGifPlugin QICOPlugin)
|
||||
install_icu_libs()
|
||||
|
||||
@@ -111,7 +113,7 @@ elseif(SYSTEM_MACOSX)
|
||||
|
||||
# Install icon
|
||||
install(
|
||||
FILES "${CMAKE_SOURCE_DIR}/install/macosx/Robomongo.icns"
|
||||
FILES "${CMAKE_SOURCE_DIR}/install/macosx/robomongo.icns"
|
||||
DESTINATION "${resources_dir}")
|
||||
elseif(SYSTEM_WINDOWS)
|
||||
install_qt_plugins(
|
||||
|
||||
@@ -15,7 +15,7 @@ execute_process(
|
||||
string(TIMESTAMP timestamp "%Y-%m-%d")
|
||||
|
||||
# Package name (as it should appear in UI)
|
||||
set(CPACK_PACKAGE_NAME Robomongo)
|
||||
set(CPACK_PACKAGE_NAME robomongo)
|
||||
|
||||
# Version of the package
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
|
||||
@@ -34,8 +34,8 @@ set(CPACK_MONOLITHIC_INSTALL ON)
|
||||
set(CPACK_STRIP_FILES ON)
|
||||
|
||||
# Additional information
|
||||
set(CPACK_PACKAGE_VENDOR Paralect)
|
||||
set(CPACK_PACKAGE_CONTACT robomongo@paralect.com)
|
||||
set(CPACK_PACKAGE_VENDOR "3T Software Labs Ltd")
|
||||
set(CPACK_PACKAGE_CONTACT robomongo@3t.io)
|
||||
set(CPACK_PACKAGE_DESCRIPTION "Shell-centric cross-platform MongoDB management tool.")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Robomongo is a shell-centric cross-platform MongoDB management tool.")
|
||||
|
||||
@@ -62,7 +62,7 @@ elseif(SYSTEM_MACOSX)
|
||||
|
||||
elseif(SYSTEM_WINDOWS)
|
||||
set(files_dir "${CMAKE_SOURCE_DIR}/install/windows")
|
||||
set(exe_name "Robomongo.exe")
|
||||
set(exe_name "robomongo.exe")
|
||||
|
||||
set(CPACK_GENERATOR NSIS ZIP)
|
||||
set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_SOURCE_DIR}/DESCRIPTION")
|
||||
|
||||
@@ -18,9 +18,9 @@ endif()
|
||||
set(PROJECT_NAME "Robomongo")
|
||||
set(PROJECT_NAME_TITLE ${PROJECT_NAME})
|
||||
set(PROJECT_DOMAIN "www.robomongo.org")
|
||||
set(PROJECT_COMPANYNAME "Paralect")
|
||||
set(PROJECT_COPYRIGHT "Copyright (C) 2013-2015 ${PROJECT_COMPANYNAME} All Rights Reserved.")
|
||||
set(PROJECT_COMPANYNAME_DOMAIN "www.paralect.com")
|
||||
set(PROJECT_COMPANYNAME "3T Software Labs Ltd")
|
||||
set(PROJECT_COPYRIGHT "Copyright (C) 2014-2017 ${PROJECT_COMPANYNAME} All Rights Reserved.")
|
||||
set(PROJECT_COMPANYNAME_DOMAIN "https://studio3t.com/")
|
||||
set(PROJECT_GITHUB_FORK "www.github.com/paralect/robomongo")
|
||||
set(PROJECT_GITHUB_ISSUES "www.github.com/paralect/robomongo/issues")
|
||||
|
||||
|
||||
@@ -125,4 +125,15 @@ Install Robomongo to `build/release/install` folder:
|
||||
Create package for your OS in `build/release/package` folder:
|
||||
|
||||
$ bin/pack
|
||||
|
||||
|
||||
**Important 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
|
||||
|
||||
- For Centos builds, Robomongo /bin/configure step might fail due to error: `failed to find gl/gl.h`. In this case, the following packages must be installed:
|
||||
|
||||
```sh
|
||||
$ sudo yum install mesa-libGL
|
||||
$ sudo yum install mesa-libGL-devel
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -12,19 +12,46 @@ Runtime dependencies that are not privided by platforms but required by Robomong
|
||||
Diagnostic of dependencies
|
||||
--------------------------
|
||||
|
||||
##### Linux
|
||||
#### Linux
|
||||
|
||||
View dependencies of executable or shared library:
|
||||
a. View dependencies of executable or shared library:
|
||||
|
||||
$ ldd robomongo
|
||||
|
||||
View RPATH or RUNPATH records of executable or shared library:
|
||||
$ readelf -d robomongo
|
||||
|
||||
b. View dependency tree of executable or shared library:
|
||||
|
||||
$ lddtree robomongo
|
||||
$ ldd -v robomongo
|
||||
|
||||
c. View RPATH or RUNPATH records of executable or shared library:
|
||||
|
||||
$ objdump -x bin/robomongo | grep "RPATH\|RUNPATH"
|
||||
$ readelf -d bin/robomongo | grep RPATH
|
||||
|
||||
For Robomongo binary it should be `$ORIGIN/../lib`
|
||||
|
||||
##### Mac OS X
|
||||
d. Enable extensive logging for run-time shared library search
|
||||
|
||||
$ export LD_DEBUG=files
|
||||
$ bin/robomongo
|
||||
|
||||
Source:
|
||||
http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html
|
||||
|
||||
e. Modify RPATH using `chrpath` and the dynamic linker and RPATH using paths `patchelf`
|
||||
|
||||
$ patchelf --set-rpath '$ORIGIN/../lib' robo
|
||||
|
||||
More: https://nixos.org/patchelf.html
|
||||
|
||||
$ chrpath -r "/somepath/lib/" robomongo
|
||||
|
||||
More:
|
||||
https://linux.die.net/man/1/chrpath
|
||||
http://www.programering.com/a/MTOwcDNwATU.html // examples
|
||||
|
||||
#### Mac OS X
|
||||
|
||||
Show dependencies of executable or shared library:
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 93 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 827 B After Width: | Height: | Size: 827 B |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 93 KiB |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
@@ -26,10 +26,10 @@ IDI_ICON1 ICON "@windows_icon@"
|
||||
BEGIN
|
||||
BLOCK "040904e4"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Paralect, Inc\0"
|
||||
VALUE "CompanyName", "3T Software Labs Ltd\0"
|
||||
VALUE "FileDescription", "Robomongo, MongoDB management tool\0"
|
||||
VALUE "FileVersion", VER_FILEVERSION_STR
|
||||
VALUE "LegalCopyright", "Copyright Paralect, 2013 - 2016\0"
|
||||
VALUE "LegalCopyright", "Copyright 3T Software Labs Ltd, 2014 - 2017\0"
|
||||
VALUE "ProductName", "Robomongo\0"
|
||||
VALUE "ProductVersion", VER_PRODUCTVERSION_STR
|
||||
VALUE "OriginalFilename", "Robomongo.exe\0"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Robomongo shortcut key reference
|
||||
================================
|
||||
|
||||
"3T", "Robomongo"
|
||||
### Windows/Linux users
|
||||
|
||||
Ctrl+Enter or F5 or Ctrl+R
|
||||
|
||||
@@ -53,12 +53,16 @@ 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
|
||||
gui/dialogs/AboutDialog.cpp
|
||||
gui/dialogs/EulaDialog.cpp
|
||||
gui/dialogs/ConnectionAdvancedTab.cpp
|
||||
gui/dialogs/ConnectionAuthTab.cpp
|
||||
gui/dialogs/ConnectionBasicTab.cpp
|
||||
@@ -75,6 +79,7 @@ set(SOURCES
|
||||
# Isolated scope #4
|
||||
gui/dialogs/PreferencesDialog.cpp
|
||||
gui/dialogs/ConnectionsDialog.cpp
|
||||
gui/dialogs/CreateConnectionDialog.cpp
|
||||
gui/dialogs/ExportDialog.cpp
|
||||
|
||||
# Isolated scope #5
|
||||
@@ -87,6 +92,7 @@ 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
|
||||
@@ -118,6 +124,7 @@ set(SOURCES
|
||||
gui/widgets/workarea/QueryWidget.cpp
|
||||
gui/widgets/workarea/WorkAreaTabBar.cpp
|
||||
gui/widgets/workarea/WorkAreaTabWidget.cpp
|
||||
gui/widgets/workarea/WelcomeTab.cpp
|
||||
|
||||
# Final scope
|
||||
gui/widgets/LogWidget.cpp
|
||||
@@ -127,9 +134,10 @@ set(SOURCES
|
||||
gui/dialogs/SSLTab.cpp
|
||||
core/settings/SshSettings.cpp
|
||||
core/settings/SslSettings.cpp
|
||||
core/settings/ReplicaSetSettings.cpp
|
||||
core/settings/ReplicaSetSettings.cpp
|
||||
core/mongodb/SshTunnelWorker.cpp
|
||||
|
||||
resources/robo.qrc
|
||||
gui/resources/gui.qrc
|
||||
)
|
||||
|
||||
@@ -140,6 +148,8 @@ add_executable(robomongo MACOSX_BUNDLE WIN32 app/main.cpp ${SOURCES})
|
||||
target_link_libraries(robomongo
|
||||
PRIVATE
|
||||
Qt5::Widgets
|
||||
Qt5::Network
|
||||
Qt5::Xml
|
||||
qjson
|
||||
qscintilla
|
||||
mongodb
|
||||
@@ -161,6 +171,8 @@ target_compile_definitions(robomongo
|
||||
PROJECT_GITHUB_FORK="${PROJECT_GITHUB_FORK}"
|
||||
PROJECT_GITHUB_ISSUES="${PROJECT_GITHUB_ISSUES}"
|
||||
PROJECT_VERSION="${PROJECT_VERSION}"
|
||||
PROJECT_VERSION_SHORT="${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}"
|
||||
PROJECT_QT_VERSION="${Qt5Core_VERSION}"
|
||||
PROJECT_NAME_LOWERCASE="${PROJECT_NAME_LOWERCASE}")
|
||||
|
||||
if(SYSTEM_WINDOWS)
|
||||
@@ -191,17 +203,18 @@ elseif(SYSTEM_MACOSX)
|
||||
OUTPUT_NAME "Robomongo"
|
||||
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_SOURCE_DIR}/install/macosx/Info.plist.in"
|
||||
MACOSX_BUNDLE_BUNDLE_NAME "Robomongo"
|
||||
MACOSX_BUNDLE_GUI_IDENTIFIER "com.paralect.Robomongo"
|
||||
MACOSX_BUNDLE_INFO_STRING "Robomongo ${PROJECT_VERSION}. Copyright Paralect, 2013 - 2016"
|
||||
MACOSX_BUNDLE_GUI_IDENTIFIER "com.3tsoftwarelabs.robomongo"
|
||||
MACOSX_BUNDLE_INFO_STRING "Robomongo ${PROJECT_VERSION}. Copyright 3T Software Labs Ltd, 2014 - 2017"
|
||||
MACOSX_BUNDLE_SHORT_VERSION "${PROJECT_VERSION}"
|
||||
MACOSX_BUNDLE_LONG_VERSION "${PROJECT_VERSION}"
|
||||
MACOSX_BUNDLE_BUNDLE_VERSION "${PROJECT_VERSION}"
|
||||
MACOSX_BUNDLE_COPYRIGHT "Copyright Paralect, 2013 - 2016"
|
||||
MACOSX_BUNDLE_ICON_FILE "Robomongo")
|
||||
MACOSX_BUNDLE_COPYRIGHT "Copyright 3T Software Labs Ltd, 2014 - 2017"
|
||||
MACOSX_BUNDLE_ICON_FILE "robomongo")
|
||||
|
||||
elseif(SYSTEM_LINUX)
|
||||
set_target_properties(robomongo PROPERTIES
|
||||
INSTALL_RPATH "$ORIGIN/../lib")
|
||||
INSTALL_RPATH "$ORIGIN/../lib"
|
||||
OUTPUT_NAME "robomongo")
|
||||
endif()
|
||||
|
||||
# Install
|
||||
|
||||
@@ -10,16 +10,18 @@
|
||||
#include <mongo/base/initializer.h>
|
||||
#include <mongo/util/net/ssl_options.h>
|
||||
|
||||
#include "robomongo/core/AppRegistry.h"
|
||||
#include "robomongo/core/settings/SettingsManager.h"
|
||||
#include "robomongo/gui/MainWindow.h"
|
||||
#include "robomongo/gui/AppStyle.h"
|
||||
#include "robomongo/gui/dialogs/EulaDialog.h"
|
||||
#include "robomongo/ssh/ssh.h"
|
||||
|
||||
|
||||
int main(int argc, char *argv[], char** envp)
|
||||
{
|
||||
if (rbm_ssh_init()) {
|
||||
if (rbm_ssh_init())
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Please check, do we really need envp for other OSes?
|
||||
#ifdef Q_OS_WIN
|
||||
@@ -54,12 +56,25 @@ int main(int argc, char *argv[], char** envp)
|
||||
app.setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||
#endif
|
||||
|
||||
// EULA License Agreement
|
||||
auto const settingsManager = Robomongo::AppRegistry::instance().settingsManager();
|
||||
if (!settingsManager->acceptedEulaVersions().contains(PROJECT_VERSION)) {
|
||||
Robomongo::EulaDialog eulaDialog;
|
||||
if (eulaDialog.exec() == QDialog::Rejected) {
|
||||
rbm_ssh_cleanup();
|
||||
return 1;
|
||||
}
|
||||
// EULA accepted
|
||||
settingsManager->addAcceptedEulaVersion(PROJECT_VERSION);
|
||||
settingsManager->save();
|
||||
}
|
||||
|
||||
// Init GUI style
|
||||
Robomongo::AppStyleUtils::initStyle();
|
||||
|
||||
// Application main window
|
||||
Robomongo::MainWindow win;
|
||||
win.show();
|
||||
Robomongo::MainWindow mainWindow;
|
||||
mainWindow.show();
|
||||
|
||||
int rc = app.exec();
|
||||
rbm_ssh_cleanup();
|
||||
|
||||
@@ -32,16 +32,18 @@ namespace Robomongo
|
||||
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)
|
||||
// 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)
|
||||
// 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
|
||||
// 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
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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, ServerHasDifferentMembers };
|
||||
|
||||
/**
|
||||
* @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;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#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"
|
||||
@@ -105,7 +106,7 @@ namespace Robomongo
|
||||
* @param connection: ConnectionSettings, that will be owned by MongoServer.
|
||||
* @param visible: should this server be visible in UI (explorer) or not.
|
||||
*/
|
||||
MongoServer *App::openServerInternal(ConnectionSettings const* connection, ConnectionType type) {
|
||||
MongoServer *App::openServerInternal(ConnectionSettings* connSettings, ConnectionType type) {
|
||||
++_lastServerHandle;
|
||||
|
||||
if (type == ConnectionPrimary)
|
||||
@@ -113,16 +114,16 @@ namespace Robomongo
|
||||
|
||||
// When connection is SECONDARY or do not have
|
||||
// ssh settings enabled, continue without SSH Tunnel
|
||||
if (type == ConnectionSecondary || !connection->sshSettings()->enabled()) {
|
||||
return continueOpenServer(_lastServerHandle, connection, type);
|
||||
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, _lastServerHandle, sshWorker, settingsCopy, type));
|
||||
return NULL;
|
||||
@@ -134,7 +135,8 @@ 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());
|
||||
}
|
||||
|
||||
void App::openShell(MongoCollection *collection, const QString &filePathToSave)
|
||||
@@ -142,7 +144,8 @@ namespace Robomongo
|
||||
ConnectionSettings *connection = collection->database()->server()->connectionRecord();
|
||||
connection->setDefaultDatabase(collection->database()->name());
|
||||
QString script = detail::buildCollectionQuery(collection->name(), "find({})");
|
||||
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));
|
||||
}
|
||||
|
||||
void App::openShell(MongoServer *server, const QString &script, const std::string &dbName,
|
||||
@@ -154,7 +157,7 @@ namespace Robomongo
|
||||
if (!dbName.empty())
|
||||
connection->setDefaultDatabase(dbName);
|
||||
|
||||
openShell(connection, ScriptInfo(script, execute, cursorPosition, shellName, filePathToSave));
|
||||
openShell(server, connection, ScriptInfo(script, execute, cursorPosition, shellName, filePathToSave));
|
||||
}
|
||||
|
||||
void App::openShell(MongoDatabase *database, const QString &script,
|
||||
@@ -163,16 +166,19 @@ namespace Robomongo
|
||||
{
|
||||
ConnectionSettings *connection = database->server()->connectionRecord();
|
||||
connection->setDefaultDatabase(database->name());
|
||||
openShell(connection, ScriptInfo(script, execute, cursorPosition, shellName, filePathToSave));
|
||||
openShell(database->server(), connection, ScriptInfo(script, execute, cursorPosition,
|
||||
shellName, filePathToSave));
|
||||
}
|
||||
|
||||
void App::openShell(ConnectionSettings const* connection, const ScriptInfo &scriptInfo)
|
||||
void App::openShell(MongoServer* server, ConnectionSettings* connection, const ScriptInfo &scriptInfo)
|
||||
{
|
||||
MongoServer *server = openServerInternal(connection, ConnectionSecondary);
|
||||
if (!server)
|
||||
MongoServer *serverClone = openServerInternal(connection, ConnectionSecondary);
|
||||
if (!serverClone || !server)
|
||||
return;
|
||||
|
||||
MongoShell *shell = new MongoShell(server, scriptInfo);
|
||||
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();
|
||||
@@ -195,22 +201,33 @@ namespace Robomongo
|
||||
delete shell;
|
||||
}
|
||||
|
||||
MongoServer *App::continueOpenServer(int serverHandle, ConnectionSettings const* connection, ConnectionType type, 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 ((type == ConnectionPrimary || type == ConnectionTest)
|
||||
&& settings->sshSettings()->enabled()) {
|
||||
settings->setServerHost("127.0.0.1");
|
||||
settings->setServerPort(localport);
|
||||
&& connSettingsClone->sshSettings()->enabled()) {
|
||||
connSettingsClone->setServerHost("127.0.0.1");
|
||||
connSettingsClone->setServerPort(localport);
|
||||
}
|
||||
|
||||
MongoServer *server = new MongoServer(serverHandle, settings, type);
|
||||
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;
|
||||
}
|
||||
@@ -256,7 +273,6 @@ namespace Robomongo
|
||||
_bus->publish(new ConnectionFailedEvent(this, serverHandle, type, errormsg, reason));
|
||||
}
|
||||
|
||||
|
||||
bool App::askSslPassphrasePromptDialog(ConnectionSettings *connSettings) const
|
||||
{
|
||||
auto sslSettings = connSettings->sslSettings();
|
||||
|
||||
@@ -70,7 +70,11 @@ namespace Robomongo
|
||||
bool execute = true, const QString &shellName = QString(),
|
||||
const CursorPosition &cursorPosition = CursorPosition(), const QString &filePathToSave = QString());
|
||||
|
||||
void openShell(ConnectionSettings const* connection, const ScriptInfo &scriptInfo);
|
||||
/**
|
||||
* @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; };
|
||||
|
||||
/**
|
||||
@@ -89,8 +93,8 @@ namespace Robomongo
|
||||
void handle(LogEvent *event);
|
||||
|
||||
private:
|
||||
MongoServer *openServerInternal(ConnectionSettings const* connection, ConnectionType type);
|
||||
MongoServer *continueOpenServer(int serverHandle, ConnectionSettings const *connection, ConnectionType type, 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
|
||||
|
||||
@@ -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
|
||||
{
|
||||
@@ -37,115 +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, long long size, bool capped, int maxDocNum, const mongo::BSONObj& extraOptions)
|
||||
{
|
||||
_bus->send(_server->client(),
|
||||
_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()
|
||||
@@ -159,18 +194,91 @@ namespace Robomongo
|
||||
_collections.push_back(collection);
|
||||
}
|
||||
|
||||
void MongoDatabase::handle(CreateCollectionResponse *event) {
|
||||
genericResponseHandler(event, "Failed to create 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) {
|
||||
genericResponseHandler(event, "Failed to drop collection.");
|
||||
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::genericResponseHandler(Event *event, const std::string &userFriendlyMessage) {
|
||||
if (!event->isError())
|
||||
|
||||
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;
|
||||
|
||||
_bus->publish(new OperationFailedEvent(this, event->error().errorMessage(), userFriendlyMessage));
|
||||
if (EventError::SetPrimaryUnreachable == event->error().errorCode()) {
|
||||
auto refreshEvent = ReplicaSetRefreshed(this, event->error(), event->error().replicaSetInfo());
|
||||
_server->handle(&refreshEvent);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace Robomongo
|
||||
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);
|
||||
@@ -83,14 +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 genericResponseHandler(Event *event, const std::string &userFriendlyMessage);
|
||||
void handleIfReplicaSetUnreachable(Event *event);
|
||||
|
||||
private:
|
||||
MongoServer *_server;
|
||||
|
||||
@@ -4,12 +4,17 @@
|
||||
#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)
|
||||
@@ -17,45 +22,64 @@ namespace Robomongo {
|
||||
MongoServer::MongoServer(int handle, ConnectionSettings *settings, ConnectionType connectionType) : QObject(),
|
||||
_version(0.0f),
|
||||
_connectionType(connectionType),
|
||||
_client(NULL),
|
||||
_worker(nullptr),
|
||||
_isConnected(false),
|
||||
_settings(settings),
|
||||
_connSettings(settings),
|
||||
_handle(handle),
|
||||
_bus(AppRegistry::instance().bus()),
|
||||
_app(AppRegistry::instance().app()) { }
|
||||
_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, _connectionType));
|
||||
void MongoServer::tryConnect()
|
||||
{
|
||||
_bus->send(_worker, new EstablishConnectionRequest(this, _connectionType, _connSettings->uuid().toStdString()));
|
||||
}
|
||||
|
||||
QStringList MongoServer::getDatabasesNames() const {
|
||||
void MongoServer::tryRefresh()
|
||||
{
|
||||
_bus->send(_worker, new EstablishConnectionRequest(this, ConnectionRefresh, _connSettings->uuid().toStdString()));
|
||||
}
|
||||
|
||||
void MongoServer::tryRefreshReplicaSetConnection()
|
||||
{
|
||||
_bus->send(_worker, new EstablishConnectionRequest(this, ConnectionRefresh, _connSettings->uuid().toStdString()));
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -64,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) {
|
||||
@@ -79,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,
|
||||
@@ -90,7 +120,7 @@ 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) {
|
||||
@@ -100,33 +130,315 @@ namespace Robomongo {
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// 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()) {
|
||||
handleConnectionFailure(event);
|
||||
return;
|
||||
}
|
||||
|
||||
// --- Connections Successful
|
||||
// Save various information after successful connection
|
||||
const ConnectionInfo &info = event->info;
|
||||
_version = info._version;
|
||||
_storageEngineType = info._storageEngineType;
|
||||
_isConnected = true;
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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 (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(EstablishConnectionResponse *event) {
|
||||
if (event->isError()) {
|
||||
_isConnected = false;
|
||||
void MongoServer::handle(RefreshReplicaSetFolderResponse *event)
|
||||
{
|
||||
handleReplicaSetRefreshEvents(event->isError(), event->error(), event->replicaSet, event->expanded);
|
||||
}
|
||||
|
||||
std::stringstream ss("Unknown error");
|
||||
auto eventErrorReason = event->errorReason;
|
||||
void MongoServer::handle(LoadDatabaseNamesResponse *event)
|
||||
{
|
||||
if (event->isError()) {
|
||||
_bus->publish(new DatabaseListLoadedEvent(this, event->error()));
|
||||
return;
|
||||
}
|
||||
|
||||
clearDatabases();
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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()
|
||||
{
|
||||
_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()->setCachedSetName(
|
||||
_connSettings->replicaSetSettings()->setNameUserEntered().empty() ? _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()->setCachedSetName(
|
||||
_connSettings->replicaSetSettings()->setNameUserEntered().empty() ? _replicaSetInfo->setName : "");
|
||||
|
||||
if (_connSettings->replicaSetSettings()->setNameUserEntered().empty()) {
|
||||
// Cache replica set name for 2 times faster first connection
|
||||
if (ConnectionPrimary == _connectionType) {
|
||||
ConnectionSettings* originalConnSettings = AppRegistry::instance().settingsManager()
|
||||
->getConnectionSettingsByUuid(event->info._uuid);
|
||||
if (originalConnSettings) {
|
||||
auto setName = event->isError() ? "" : _replicaSetInfo->setName;
|
||||
originalConnSettings->replicaSetSettings()->setCachedSetName(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());
|
||||
}
|
||||
}
|
||||
else { // User entered set name is not empty, clear cached set name just in case
|
||||
ConnectionSettings* originalConnSettings = AppRegistry::instance().settingsManager()
|
||||
->getConnectionSettingsByUuid(event->info._uuid);
|
||||
if (originalConnSettings) {
|
||||
originalConnSettings->replicaSetSettings()->setCachedSetName("");
|
||||
AppRegistry::instance().settingsManager()->save();
|
||||
LOG_MSG("Replica set's cached set name cleared. Using user entered set name.",
|
||||
mongo::logger::LogSeverity::Info());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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() + "]";
|
||||
|
||||
if (event->error().errorCode() == EventError::ErrorCode::ServerHasDifferentMembers) {
|
||||
ss << "Cannot connect to replica set \"" << _connSettings->connectionName() << "\"" << server
|
||||
<< ". \n\nA primary with different host name [" << event->replicaSet.primary <<
|
||||
"] found in server side. "
|
||||
"Please double check if same host names and ports are used as in server's replica set"
|
||||
" configuration. \nIf same set name is used for different replica sets, this configuration"
|
||||
" is supported only on different instances of Robomongo. "
|
||||
" Please open a new Robomongo instance for each replica set which has the same set name."
|
||||
"\n\nReason:\n" << event->error().errorMessage();
|
||||
}
|
||||
else {
|
||||
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;
|
||||
@@ -147,85 +459,14 @@ namespace Robomongo {
|
||||
|
||||
// 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)
|
||||
if (_connectionType == ConnectionPrimary || _connectionType == ConnectionTest)
|
||||
{
|
||||
LOG_MSG("Establish connection failed. " + event->error().errorMessage() +
|
||||
". Connection: " + _connSettings->connectionName(),
|
||||
mongo::logger::LogSeverity::Error());
|
||||
_app->closeServer(this);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Save various information after successful connection
|
||||
const ConnectionInfo &info = event->info();
|
||||
_version = info._version;
|
||||
_storageEngineType = info._storageEngineType;
|
||||
_isConnected = true;
|
||||
// Save Replica Set Status
|
||||
_repPrimary = event->getRepPrimary(); // todo: what happens to this member if not replica set?
|
||||
_repMembersHealths = event->getRepMembersHealths();
|
||||
if (_settings->isReplicaSet()) {
|
||||
_settings->setServerHost(_repPrimary.host());
|
||||
_settings->setServerPort(_repPrimary.port());
|
||||
}
|
||||
|
||||
_bus->publish(new ConnectionEstablishedEvent(this, _connectionType));
|
||||
|
||||
// Do nothing if this is not a "primary" connection
|
||||
if (_connectionType != ConnectionPrimary)
|
||||
return;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
addDatabase(db);
|
||||
}
|
||||
|
||||
_bus->publish(new DatabaseListLoadedEvent(this, _databases));
|
||||
}
|
||||
|
||||
void MongoServer::handle(InsertDocumentResponse *event) {
|
||||
_bus->publish(new InsertDocumentResponse(event->sender(), event->error()));
|
||||
}
|
||||
|
||||
void MongoServer::handle(RemoveDocumentResponse *event) {
|
||||
_bus->publish(new RemoveDocumentResponse(event->sender(), event->error()));
|
||||
}
|
||||
|
||||
void MongoServer::runWorkerThread() {
|
||||
_client = new MongoWorker(_settings->clone(),
|
||||
AppRegistry::instance().settingsManager()->loadMongoRcJs(),
|
||||
AppRegistry::instance().settingsManager()->batchSize(),
|
||||
AppRegistry::instance().settingsManager()->mongoTimeoutSec(),
|
||||
AppRegistry::instance().settingsManager()->shellTimeoutSec());
|
||||
}
|
||||
|
||||
void MongoServer::handle(CreateDatabaseResponse *event) {
|
||||
genericResponseHandler(event, "Failed to create database.");
|
||||
}
|
||||
|
||||
void MongoServer::handle(DropDatabaseResponse *event) {
|
||||
genericResponseHandler(event, "Failed to drop database.");
|
||||
}
|
||||
|
||||
void MongoServer::genericResponseHandler(Event *event, const std::string &userFriendlyMessage) {
|
||||
if (!event->isError())
|
||||
return;
|
||||
|
||||
_bus->publish(new OperationFailedEvent(this, event->error().errorMessage(), userFriendlyMessage));
|
||||
}
|
||||
}
|
||||
} // namespace Robomongo
|
||||
|
||||
@@ -12,11 +12,12 @@ namespace Robomongo
|
||||
class App;
|
||||
|
||||
// Messages
|
||||
class EstablishConnectionResponse;
|
||||
struct EstablishConnectionResponse;
|
||||
struct RefreshReplicaSetFolderResponse;
|
||||
class LoadDatabaseNamesResponse;
|
||||
class InsertDocumentResponse;
|
||||
class CreateDatabaseResponse;
|
||||
class DropDatabaseResponse;
|
||||
struct CreateDatabaseResponse;
|
||||
struct DropDatabaseResponse;
|
||||
|
||||
/**
|
||||
* @brief MongoServer represents active connection to MongoDB server.
|
||||
@@ -40,22 +41,52 @@ namespace Robomongo
|
||||
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; }
|
||||
|
||||
@@ -68,27 +99,30 @@ namespace Robomongo
|
||||
* @brief Loads databases of this server asynchronously.
|
||||
*/
|
||||
void loadDatabases();
|
||||
MongoWorker *const client() const { return _client; }
|
||||
MongoWorker *const worker() const { return _worker; }
|
||||
|
||||
// --- Getters ---
|
||||
const mongo::HostAndPort& getRepPrimary() const { return _repPrimary; }
|
||||
const std::vector<bool>& getRepMembersHealths() const { return _repMembersHealths; }
|
||||
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 genericResponseHandler(Event *event, const std::string &userFriendlyMessage);
|
||||
void handleReplicaSetRefreshEvents(bool isError, EventError eventError, ReplicaSet const& replicaSet,
|
||||
bool expanded);
|
||||
void updateReplicaSetSettings(EstablishConnectionResponse* event);
|
||||
void handleConnectionFailure(EstablishConnectionResponse* event);
|
||||
|
||||
MongoWorker *_client;
|
||||
ConnectionSettings *_settings;
|
||||
MongoWorker *_worker;
|
||||
std::unique_ptr<ConnectionSettings> _connSettings;
|
||||
EventBus *_bus;
|
||||
App *_app;
|
||||
|
||||
@@ -99,10 +133,7 @@ namespace Robomongo
|
||||
int _handle;
|
||||
|
||||
QList<MongoDatabase *> _databases;
|
||||
|
||||
// Replica Set Status
|
||||
mongo::HostAndPort _repPrimary;
|
||||
std::vector<bool> _repMembersHealths; // todo: vector of pairs of host and health
|
||||
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));
|
||||
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));
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
@@ -41,17 +41,17 @@ namespace Robomongo
|
||||
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 *item)
|
||||
bool isArrayChild(BsonTreeItem const *item)
|
||||
{
|
||||
return Robomongo::BsonUtils::isArray(dynamic_cast<BsonTreeItem *>(item->parent())->type());
|
||||
return BsonUtils::isArray(dynamic_cast<BsonTreeItem*>(item->parent())->type());
|
||||
}
|
||||
|
||||
bool isDocumentRoot(BsonTreeItem *item)
|
||||
bool isDocumentRoot(BsonTreeItem const *item)
|
||||
{
|
||||
return ( item == item->superParent() );
|
||||
}
|
||||
@@ -102,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())));
|
||||
@@ -158,19 +158,18 @@ namespace Robomongo
|
||||
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();
|
||||
|
||||
if (onItem && isEditable) menu->addAction(_deleteDocumentAction);
|
||||
}
|
||||
|
||||
@@ -182,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;
|
||||
|
||||
@@ -194,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;
|
||||
}
|
||||
@@ -214,44 +215,97 @@ namespace Robomongo
|
||||
}
|
||||
|
||||
isNeededRefresh = true;
|
||||
_shell->server()->removeDocuments(query, _queryInfo._info._ns);
|
||||
}
|
||||
|
||||
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()
|
||||
@@ -263,13 +317,15 @@ 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);
|
||||
}
|
||||
for (auto index : selectedIndexes)
|
||||
items.push_back(QtUtils::item<BsonTreeItem*>(index));
|
||||
|
||||
deleteDocuments(items, true);
|
||||
}
|
||||
}
|
||||
@@ -343,7 +399,7 @@ namespace Robomongo
|
||||
json, true, dynamic_cast<QWidget*>(_observer));
|
||||
|
||||
editor->setWindowTitle("View Document");
|
||||
editor->show();
|
||||
editor->exec();
|
||||
}
|
||||
|
||||
void Notifier::onInsertDocument()
|
||||
@@ -365,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()
|
||||
@@ -386,53 +440,6 @@ namespace Robomongo
|
||||
clipboard->setText(documentItem->value());
|
||||
}
|
||||
|
||||
void Notifier::onCopyNameDocument()
|
||||
{
|
||||
QModelIndex selectedInd = _observer->selectedIndex();
|
||||
if (!selectedInd.isValid())
|
||||
return;
|
||||
|
||||
BsonTreeItem *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 selectedInd = _observer->selectedIndex();
|
||||
if (!selectedInd.isValid())
|
||||
return;
|
||||
|
||||
BsonTreeItem *documentItem = QtUtils::item<BsonTreeItem*>(selectedInd);
|
||||
if (!documentItem)
|
||||
return;
|
||||
|
||||
if ( !(detail::isSimpleType(documentItem) || detail::isDocumentType(documentItem) ||
|
||||
!detail::isDocumentRoot(documentItem)) )
|
||||
return;
|
||||
|
||||
QStringList namesList;
|
||||
BsonTreeItem *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::onCopyTimestamp()
|
||||
{
|
||||
QModelIndex selectedInd = _observer->selectedIndex();
|
||||
|
||||
@@ -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:
|
||||
@@ -55,13 +55,15 @@ namespace Robomongo
|
||||
void onViewDocument();
|
||||
void onInsertDocument();
|
||||
void onCopyDocument();
|
||||
void onCopyNameDocument();
|
||||
void onCopyPathDocument();
|
||||
void onCopyTimestamp();
|
||||
void onCopyJson();
|
||||
void handle(InsertDocumentResponse *event);
|
||||
void handle(RemoveDocumentResponse *event);
|
||||
|
||||
private Q_SLOTS:
|
||||
void onCopyNameDocument();
|
||||
void onCopyPathDocument();
|
||||
|
||||
private:
|
||||
QAction *_deleteDocumentAction;
|
||||
QAction *_deleteDocumentsAction;
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace Robomongo
|
||||
{
|
||||
ScriptEngine::ScriptEngine(ConnectionSettings *connection, int timeoutSec) :
|
||||
_connection(connection),
|
||||
_scope(NULL),
|
||||
_scope(nullptr),
|
||||
_engine(NULL),
|
||||
_timeoutSec(timeoutSec),
|
||||
_initialized(false),
|
||||
@@ -65,26 +65,20 @@ namespace Robomongo
|
||||
|
||||
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;
|
||||
@@ -108,23 +102,23 @@ 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/
|
||||
@@ -161,8 +155,10 @@ namespace Robomongo
|
||||
{
|
||||
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
|
||||
@@ -202,10 +198,15 @@ namespace Robomongo
|
||||
|
||||
if (true /* ! wascmd */) {
|
||||
try {
|
||||
bool failed = false;
|
||||
QElapsedTimer timer;
|
||||
timer.start();
|
||||
if ( _scope->exec( statement , "(shell)" , false , true , false, _timeoutSec * 1000) ) {
|
||||
_scope->exec( "__robomongoLastRes = __lastres__; shellPrintHelper( __lastres__ );" , "(shell2)" , true , 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();
|
||||
@@ -213,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)
|
||||
@@ -265,6 +270,9 @@ namespace Robomongo
|
||||
|
||||
void ScriptEngine::ping()
|
||||
{
|
||||
if (!_scope)
|
||||
return;
|
||||
|
||||
QMutexLocker lock(&_mutex);
|
||||
_scope->exec("if (db) { db.runCommand({ping:1}); }", "(ping)", false, false, false, 3000);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace Robomongo
|
||||
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();
|
||||
|
||||
@@ -29,6 +29,8 @@ namespace Robomongo
|
||||
|
||||
void invalidateDbCollectionsCache();
|
||||
|
||||
bool failedScope() const { return _failedScope; }
|
||||
|
||||
private:
|
||||
ConnectionSettings *_connection;
|
||||
|
||||
@@ -42,7 +44,8 @@ namespace Robomongo
|
||||
|
||||
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)
|
||||
|
||||
@@ -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,54 +27,118 @@ namespace Robomongo
|
||||
* @brief EstablishConnection
|
||||
*/
|
||||
|
||||
class EstablishConnectionRequest : public Event
|
||||
struct EstablishConnectionRequest : public Event
|
||||
{
|
||||
R_EVENT
|
||||
|
||||
EstablishConnectionRequest(QObject *sender, ConnectionType connectionType) :
|
||||
EstablishConnectionRequest(QObject *sender, ConnectionType connectionType, std::string const& uuid) :
|
||||
Event(sender),
|
||||
connectionType(connectionType) {}
|
||||
connectionType(connectionType),
|
||||
uuid(uuid) {}
|
||||
|
||||
ConnectionType connectionType;
|
||||
ConnectionType const connectionType;
|
||||
std::string const uuid;
|
||||
};
|
||||
|
||||
class EstablishConnectionResponse : public Event
|
||||
struct EstablishConnectionResponse : public Event
|
||||
{
|
||||
R_EVENT
|
||||
|
||||
enum ErrorReason {
|
||||
MongoConnection = 0,
|
||||
MongoAuth = 1,
|
||||
MongoSslConnection = 2
|
||||
NoError = 0,
|
||||
MongoConnection = 1,
|
||||
MongoAuth = 2,
|
||||
MongoSslConnection = 3
|
||||
};
|
||||
|
||||
EstablishConnectionResponse(QObject *sender, const ConnectionInfo &info, ConnectionType connectionType,
|
||||
const mongo::HostAndPort& repPrimary, const std::vector<bool>& repMembersHealths) :
|
||||
const ReplicaSet& replicaSet) :
|
||||
Event(sender),
|
||||
_info(info),
|
||||
info(info),
|
||||
connectionType(connectionType),
|
||||
_repPrimary(repPrimary),
|
||||
_repMembersHealths(repMembersHealths)
|
||||
replicaSet(replicaSet)
|
||||
{}
|
||||
|
||||
EstablishConnectionResponse(QObject *sender, const EventError &error, ConnectionType connectionType, ErrorReason errorReason) :
|
||||
EstablishConnectionResponse(QObject *sender, const EventError &error, ConnectionType connectionType,
|
||||
ConnectionInfo const& info, const ReplicaSet& replicaSet, ErrorReason errorReason) :
|
||||
Event(sender, error),
|
||||
_info(),
|
||||
info(info),
|
||||
connectionType(connectionType),
|
||||
errorReason(errorReason) {}
|
||||
errorReason(errorReason),
|
||||
replicaSet(replicaSet)
|
||||
{}
|
||||
|
||||
// Getters
|
||||
const ConnectionInfo &info() const { return _info; }
|
||||
const mongo::HostAndPort& getRepPrimary() const { return _repPrimary; }
|
||||
const std::vector<bool>& getRepMembersHealths() const { return _repMembersHealths; }
|
||||
|
||||
const ConnectionInfo _info;
|
||||
ConnectionType connectionType;
|
||||
ErrorReason errorReason;
|
||||
mongo::HostAndPort _repPrimary;
|
||||
std::vector<bool> _repMembersHealths; // todo: vector of host and health pair
|
||||
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) {}
|
||||
};
|
||||
|
||||
struct ReplicaSetFolderRefreshed : public Event
|
||||
{
|
||||
R_EVENT
|
||||
|
||||
ReplicaSetFolderRefreshed(QObject *sender, bool expanded) :
|
||||
Event(sender), expanded(expanded) {}
|
||||
|
||||
ReplicaSetFolderRefreshed(QObject *sender, const EventError &error, bool expanded) :
|
||||
Event(sender, error), expanded(expanded) {}
|
||||
|
||||
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
|
||||
@@ -349,7 +414,7 @@ namespace Robomongo
|
||||
InsertDocumentResponse(QObject *sender) :
|
||||
Event(sender) {}
|
||||
|
||||
InsertDocumentResponse(QObject *sender, const EventError &error) :
|
||||
InsertDocumentResponse(QObject *sender, EventError const& error) :
|
||||
Event(sender, error) {}
|
||||
};
|
||||
|
||||
@@ -357,37 +422,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;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -406,52 +480,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;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -472,30 +543,31 @@ namespace Robomongo
|
||||
Event(sender), _ns(ns), _extraOptions(extraOptions),
|
||||
_size(size), _capped(capped), _maxDocNum(maxDocNum) {}
|
||||
|
||||
MongoNamespace getNs() const { return _ns; }
|
||||
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;
|
||||
const long long _size;
|
||||
const bool _capped;
|
||||
const int _maxDocNum;
|
||||
const mongo::BSONObj _extraOptions;
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
@@ -519,16 +591,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;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -549,20 +623,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;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -583,20 +660,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;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -661,16 +742,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;
|
||||
};
|
||||
|
||||
|
||||
@@ -683,29 +765,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;
|
||||
};
|
||||
|
||||
|
||||
@@ -738,19 +822,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
|
||||
*/
|
||||
@@ -760,29 +846,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;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -939,17 +1026,19 @@ namespace Robomongo
|
||||
|
||||
// class ScriptExecute
|
||||
|
||||
class ConnectionEstablishedEvent : public Event
|
||||
struct ConnectionEstablishedEvent : public Event
|
||||
{
|
||||
R_EVENT
|
||||
|
||||
ConnectionEstablishedEvent(MongoServer *server, ConnectionType type) :
|
||||
ConnectionEstablishedEvent(MongoServer *server, ConnectionType type, ConnectionInfo connInfo) :
|
||||
Event((QObject *)server),
|
||||
server(server),
|
||||
connectionType(type) { }
|
||||
connectionType(type),
|
||||
connInfo(connInfo) { }
|
||||
|
||||
MongoServer *server;
|
||||
ConnectionType connectionType;
|
||||
ConnectionType const connectionType;
|
||||
ConnectionInfo const connInfo;
|
||||
};
|
||||
|
||||
class DatabaseListLoadedEvent : public Event
|
||||
|
||||
@@ -17,15 +17,19 @@ namespace Robomongo
|
||||
_languageOverride(languageOverride),
|
||||
_textWeights(textWeights) {}
|
||||
|
||||
ConnectionInfo::ConnectionInfo() :
|
||||
ConnectionInfo::ConnectionInfo(std::string const& uuid) :
|
||||
_address(),
|
||||
_databases(),
|
||||
_version(0.0f) {}
|
||||
_version(0.0f),
|
||||
_uuid(uuid)
|
||||
{}
|
||||
|
||||
ConnectionInfo::ConnectionInfo(const std::string &address, const std::vector<std::string> &databases,
|
||||
float version, const std::string& storageEngine) :
|
||||
float version, const std::string& storageEngine, std::string const& uuid) :
|
||||
_address(address),
|
||||
_databases(databases),
|
||||
_version(version),
|
||||
_storageEngineType(storageEngine) {}
|
||||
_storageEngineType(storageEngine),
|
||||
_uuid(uuid)
|
||||
{}
|
||||
}
|
||||
|
||||
@@ -34,11 +34,13 @@ namespace Robomongo
|
||||
|
||||
struct ConnectionInfo
|
||||
{
|
||||
ConnectionInfo();
|
||||
ConnectionInfo(const std::string &address, const std::vector<std::string> &databases, float version, const std::string& storageEngine);
|
||||
ConnectionInfo(std::string const& uuid);
|
||||
ConnectionInfo(const std::string &address, const std::vector<std::string> &databases, float version,
|
||||
const std::string& storageEngine, std::string const& uuid);
|
||||
const std::string _address;
|
||||
const std::vector<std::string> _databases;
|
||||
const float _version;
|
||||
const std::string _storageEngineType;
|
||||
std::string const _uuid;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -108,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);
|
||||
@@ -119,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)
|
||||
@@ -131,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)
|
||||
@@ -293,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -337,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)
|
||||
@@ -351,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;
|
||||
@@ -360,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());
|
||||
@@ -367,10 +392,18 @@ 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 std::string& ns, long long size, bool capped, int max, const mongo::BSONObj& extraOptions, mongo::BSONObj* info)
|
||||
void MongoClient::createCollection(const std::string& ns, long long size, bool capped, int max,
|
||||
const mongo::BSONObj& extraOptions, mongo::BSONObj* info)
|
||||
{
|
||||
verify(!capped || size);
|
||||
mongo::BSONObj o;
|
||||
@@ -390,7 +423,19 @@ namespace Robomongo
|
||||
}
|
||||
b.appendElements(extraOptions);
|
||||
|
||||
_dbclient->runCommand(db.c_str(), b.done(), *info);
|
||||
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)
|
||||
@@ -404,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)
|
||||
@@ -423,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());
|
||||
@@ -446,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)
|
||||
@@ -524,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,5 +1,7 @@
|
||||
#include "robomongo/core/mongodb/MongoWorker.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <QThread>
|
||||
|
||||
#include "mongo/client/global_conn_pool.h"
|
||||
@@ -19,17 +21,18 @@
|
||||
#include "robomongo/core/settings/CredentialSettings.h"
|
||||
#include "robomongo/core/settings/SettingsManager.h"
|
||||
#include "robomongo/core/settings/SslSettings.h"
|
||||
#include "robomongo/core/utils/BsonUtils.h"
|
||||
#include "robomongo/core/utils/Logger.h"
|
||||
#include "robomongo/core/utils/QtUtils.h"
|
||||
#include "robomongo/utils/string_operations.h"
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
std::string const PRIMARY_UNREACHABLE = "Replica set's primary is unreachable.";
|
||||
|
||||
MongoWorker::MongoWorker(ConnectionSettings *connection, bool isLoadMongoRcJs, int batchSize,
|
||||
int mongoTimeoutSec, int shellTimeoutSec, QObject *parent) : QObject(parent),
|
||||
_connSettings(connection),
|
||||
_scriptEngine(NULL),
|
||||
_dbclient(nullptr),
|
||||
_dbclientRepSet(nullptr),
|
||||
_scriptEngine(nullptr),
|
||||
_isAdmin(true),
|
||||
_isLoadMongoRcJs(isLoadMongoRcJs),
|
||||
_batchSize(batchSize),
|
||||
@@ -37,7 +40,10 @@ namespace Robomongo
|
||||
_dbAutocompleteCacheTimerId(-1),
|
||||
_mongoTimeoutSec(mongoTimeoutSec),
|
||||
_shellTimeoutSec(shellTimeoutSec),
|
||||
_isQuiting(0)
|
||||
_isQuiting(0),
|
||||
_dbclient(nullptr),
|
||||
_dbclientRepSet(nullptr),
|
||||
_connSettings(connection)
|
||||
{
|
||||
_thread = new QThread();
|
||||
moveToThread(_thread);
|
||||
@@ -53,8 +59,7 @@ namespace Robomongo
|
||||
return;
|
||||
}
|
||||
|
||||
if (_dbAutocompleteCacheTimerId == event->timerId() &&
|
||||
_scriptEngine != NULL) {
|
||||
if (_dbAutocompleteCacheTimerId == event->timerId() && !_scriptEngine) {
|
||||
_scriptEngine->invalidateDbCollectionsCache();
|
||||
return;
|
||||
}
|
||||
@@ -64,16 +69,11 @@ namespace Robomongo
|
||||
{
|
||||
try {
|
||||
if (_dbclient) {
|
||||
// Building { ping: 1 }
|
||||
mongo::BSONObjBuilder command;
|
||||
command.append("ping", 1);
|
||||
mongo::BSONObj result;
|
||||
std::string authBase = getAuthBase();
|
||||
if (authBase.empty()) {
|
||||
_dbclient->runCommand("admin", command.obj(), result);
|
||||
} else {
|
||||
_dbclient->runCommand(authBase, command.obj(), result);
|
||||
}
|
||||
pingDatabase(_dbclient.get());
|
||||
}
|
||||
|
||||
if (_dbclientRepSet) {
|
||||
pingDatabase(_dbclientRepSet.get());
|
||||
}
|
||||
|
||||
if (_scriptEngine) {
|
||||
@@ -81,21 +81,23 @@ namespace Robomongo
|
||||
}
|
||||
|
||||
} catch(std::exception &ex) {
|
||||
LOG_MSG(ex.what(), mongo::logger::LogSeverity::Error());
|
||||
LOG_MSG("Failed to ping the server. MongoWorker::keepAlive() failed. " + std::string(ex.what()),
|
||||
mongo::logger::LogSeverity::Error());
|
||||
}
|
||||
}
|
||||
|
||||
void MongoWorker::init()
|
||||
{
|
||||
try {
|
||||
_scriptEngine = new ScriptEngine(_connSettings, _shellTimeoutSec);
|
||||
_scriptEngine.reset(new ScriptEngine(_connSettings, _shellTimeoutSec));
|
||||
_scriptEngine->init(_isLoadMongoRcJs);
|
||||
_scriptEngine->use(_connSettings->defaultDatabase());
|
||||
_scriptEngine->setBatchSize(_batchSize);
|
||||
_timerId = startTimer(pingTimeMs);
|
||||
_dbAutocompleteCacheTimerId = startTimer(30000);
|
||||
} catch (const std::exception &ex) {
|
||||
LOG_MSG(ex.what(), mongo::logger::LogSeverity::Error());
|
||||
LOG_MSG("Failed to initialize MongoWorker. " + std::string(ex.what()),
|
||||
mongo::logger::LogSeverity::Error());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +121,6 @@ namespace Robomongo
|
||||
killTimer(_dbAutocompleteCacheTimerId);
|
||||
|
||||
delete _connSettings;
|
||||
delete _scriptEngine;
|
||||
|
||||
// QThread "_thread" and MongoWorker itself will be deleted later
|
||||
// (see MongoWorker() constructor)
|
||||
@@ -134,32 +135,75 @@ namespace Robomongo
|
||||
/**
|
||||
* @brief Initiate connection to MongoDB
|
||||
*/
|
||||
void MongoWorker::handle(EstablishConnectionRequest *event)
|
||||
bool MongoWorker::handle(EstablishConnectionRequest *event)
|
||||
{
|
||||
QMutexLocker lock(&_firstConnectionMutex);
|
||||
|
||||
std::unique_ptr<ReplicaSet> repSetInfo(new ReplicaSet);
|
||||
auto errorCode = EventError::ErrorCode::Unknown;
|
||||
|
||||
try {
|
||||
mongo::DBClientBase *conn = getConnection(true);
|
||||
if (conn == NULL) {
|
||||
// Protection by default value: Logically/ideally, this error should never be seen.
|
||||
|
||||
// --- Connection failed for single server & replica set (no member of the set is reachable)
|
||||
if (!conn)
|
||||
{
|
||||
auto errorReason = std::string("Connection failure: Unknown error.");
|
||||
if (_connSettings->sslSettings()->sslEnabled())
|
||||
{
|
||||
|
||||
if (_connSettings->sslSettings()->sslEnabled()) {
|
||||
// Note: Currently mongo-shell does not provide any interface to fetch actual error details
|
||||
// for some SSL connection failures that's why we are unable to show exact error here.
|
||||
errorReason = "SSL tunnel failure: Network is unreachable or SSL connection rejected by server.";
|
||||
}
|
||||
else
|
||||
{
|
||||
errorReason = "Network is unreachable.";
|
||||
else { // Non-SSL connections
|
||||
if (_connSettings->isReplicaSet()) {
|
||||
errorReason = "No member of the set is reachable.";
|
||||
std::vector<std::pair<std::string, bool>> membersAndHealths;
|
||||
for (auto const& member : _connSettings->replicaSetSettings()->members()) {
|
||||
membersAndHealths.push_back({ member, false });
|
||||
}
|
||||
repSetInfo.reset(new ReplicaSet("", mongo::HostAndPort(), membersAndHealths,
|
||||
"No member of the set is reachable."));
|
||||
}
|
||||
else // single server
|
||||
errorReason = "Network is unreachable.";
|
||||
}
|
||||
|
||||
resetGlobalSSLparams();
|
||||
|
||||
reply(event->sender(), new EstablishConnectionResponse(this,
|
||||
EventError(errorReason), event->connectionType,
|
||||
EstablishConnectionResponse::MongoConnection));
|
||||
return;
|
||||
reply(event->sender(), new EstablishConnectionResponse(this, EventError(errorReason, errorCode),
|
||||
event->connectionType, event->uuid, *repSetInfo.release(),
|
||||
EstablishConnectionResponse::MongoConnection));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// --- Single server: Connection successful
|
||||
// --- Replica set: Connection successful (primary reachable) or
|
||||
// Connection failed (primary unreachable with at least one reachable member)
|
||||
if (_connSettings->isReplicaSet()) {
|
||||
bool refresh = (ConnectionType::ConnectionRefresh == event->connectionType);
|
||||
ReplicaSet const& setInfo = getReplicaSetInfo(refresh);
|
||||
|
||||
// Check if same set name used with different members which is not supported
|
||||
auto const& members = _connSettings->replicaSetSettings()->members();
|
||||
if (std::find(members.cbegin(), members.cend(), setInfo.primary.toString()) == members.cend())
|
||||
{ // primary not found between user entered members
|
||||
std::string const errorStr = "Different members found under same replica set name \"" +
|
||||
setInfo.setName + "\".";
|
||||
repSetInfo.reset(new ReplicaSet(setInfo));
|
||||
errorCode = EventError::ErrorCode::ServerHasDifferentMembers;
|
||||
LOG_MSG(errorStr, mongo::logger::LogSeverity::Error());
|
||||
throw std::runtime_error(errorStr);
|
||||
}
|
||||
|
||||
if (setInfo.primary.empty()) { // No reachable primary
|
||||
repSetInfo.reset(new ReplicaSet(setInfo)); // Pass possible reachable secondary(ies) info
|
||||
LOG_MSG(setInfo.errorStr, mongo::logger::LogSeverity::Error());
|
||||
throw std::runtime_error(setInfo.errorStr);
|
||||
}
|
||||
else { // Primary is reachable, save setInfo and continue
|
||||
repSetInfo.reset(new ReplicaSet(setInfo));
|
||||
}
|
||||
}
|
||||
|
||||
if (_connSettings->hasEnabledPrimaryCredential()) {
|
||||
@@ -191,44 +235,45 @@ namespace Robomongo
|
||||
if (dbNames.size() == 0)
|
||||
throw mongo::DBException("Failed to execute \"listdatabases\" command.", 0);
|
||||
|
||||
// todo: move to func. getRepSetStatus();
|
||||
mongo::HostAndPort repPrimary;
|
||||
std::vector<bool> repMembersHealths;
|
||||
if (_connSettings->isReplicaSet())
|
||||
{
|
||||
// Refresh view of Replica Set Monitor to get live data
|
||||
auto repSetMonitor = mongo::globalRSMonitorManager.getMonitor(_dbclientRepSet->getSetName());
|
||||
repSetMonitor->startOrContinueRefresh().refreshAll();
|
||||
|
||||
// Update connection settings with primary(master) address and port
|
||||
repPrimary = repSetMonitor->getMasterOrUassert();
|
||||
_connSettings->setServerHost(repPrimary.host());
|
||||
_connSettings->setServerPort(repPrimary.port());
|
||||
|
||||
// Save health of members
|
||||
for (auto const& member : _connSettings->replicaSetSettings()->membersToHostAndPort())
|
||||
{
|
||||
repMembersHealths.push_back(repSetMonitor->isHostUp(member));
|
||||
}
|
||||
}
|
||||
|
||||
// Init MongoWorker
|
||||
init();
|
||||
if (!_connSettings->isReplicaSet())
|
||||
init(); // Init MongoWorker for single server (for replica set connections early init is used)
|
||||
|
||||
resetGlobalSSLparams();
|
||||
|
||||
reply(event->sender(), new EstablishConnectionResponse(this, ConnectionInfo(_connSettings->getFullAddress(),
|
||||
dbNames, client->getVersion(), client->getStorageEngineType()), event->connectionType, repPrimary, repMembersHealths));
|
||||
} catch(const std::exception &ex) {
|
||||
resetGlobalSSLparams();
|
||||
auto connInfo = ConnectionInfo(_connSettings->getFullAddress(), dbNames, client->getVersion(),
|
||||
client->getStorageEngineType(), event->uuid);
|
||||
|
||||
// todo: two ctors for rep.set and single server.
|
||||
reply(event->sender(), new EstablishConnectionResponse(this, connInfo, event->connectionType,
|
||||
*repSetInfo.release()));
|
||||
return true;
|
||||
}
|
||||
catch(const std::exception &ex) {
|
||||
resetGlobalSSLparams();
|
||||
auto errorReason = _connSettings->sslSettings()->sslEnabled() ?
|
||||
EstablishConnectionResponse::ErrorReason::MongoSslConnection :
|
||||
EstablishConnectionResponse::ErrorReason::MongoAuth;
|
||||
EstablishConnectionResponse::ErrorReason::MongoSslConnection :
|
||||
EstablishConnectionResponse::ErrorReason::MongoAuth;
|
||||
|
||||
reply(event->sender(),
|
||||
new EstablishConnectionResponse(this, EventError(ex.what()), event->connectionType, errorReason));
|
||||
reply(event->sender(), new EstablishConnectionResponse(this, EventError(ex.what(), errorCode),
|
||||
event->connectionType, ConnectionInfo(event->uuid), *repSetInfo.release(), errorReason));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void MongoWorker::handle(RefreshReplicaSetFolderRequest *event)
|
||||
{
|
||||
ReplicaSet const& replicaSetInfo = getReplicaSetInfo(true);
|
||||
|
||||
// Primary is unreachable, but there might be reachable secondary(ies)
|
||||
if (replicaSetInfo.primary.empty()) {
|
||||
reply(event->sender(), new RefreshReplicaSetFolderResponse(this, replicaSetInfo, event->expanded,
|
||||
EventError(replicaSetInfo.errorStr)));
|
||||
LOG_MSG(replicaSetInfo.errorStr, mongo::logger::LogSeverity::Error());
|
||||
return;
|
||||
}
|
||||
else // Primary is reachable
|
||||
reply(event->sender(), new RefreshReplicaSetFolderResponse(this, replicaSetInfo, event->expanded));
|
||||
}
|
||||
|
||||
std::string MongoWorker::getAuthBase() const
|
||||
@@ -306,8 +351,17 @@ namespace Robomongo
|
||||
|
||||
reply(event->sender(), new LoadCollectionNamesResponse(this, event->databaseName(), infos));
|
||||
} catch(const mongo::DBException &ex) {
|
||||
reply(event->sender(), new LoadCollectionNamesResponse(this, EventError(ex.what())));
|
||||
LOG_MSG(ex.what(), mongo::logger::LogSeverity::Error());
|
||||
if (_connSettings->isReplicaSet()) {
|
||||
ReplicaSet const& replicaSetInfo = getReplicaSetInfo(true);
|
||||
if (replicaSetInfo.primary.empty()) { // primary not reachable
|
||||
reply(event->sender(), new LoadCollectionNamesResponse(this,
|
||||
EventError(PRIMARY_UNREACHABLE, replicaSetInfo, false)));
|
||||
}
|
||||
else // other errors
|
||||
reply(event->sender(), new LoadCollectionNamesResponse(this, EventError(ex.what())));
|
||||
}
|
||||
else // single server
|
||||
reply(event->sender(), new LoadCollectionNamesResponse(this, EventError(ex.what())));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -320,8 +374,17 @@ namespace Robomongo
|
||||
|
||||
reply(event->sender(), new LoadUsersResponse(this, event->databaseName(), users));
|
||||
} catch(const mongo::DBException &ex) {
|
||||
reply(event->sender(), new LoadUsersResponse(this, EventError(ex.what())));
|
||||
LOG_MSG(ex.what(), mongo::logger::LogSeverity::Error());
|
||||
if (_connSettings->isReplicaSet()) {
|
||||
ReplicaSet const& replicaSetInfo = getReplicaSetInfo(true);
|
||||
if (replicaSetInfo.primary.empty()) { // primary not reachable
|
||||
reply(event->sender(), new LoadUsersResponse(this,
|
||||
EventError(PRIMARY_UNREACHABLE, replicaSetInfo, false)));
|
||||
}
|
||||
else // other errors
|
||||
reply(event->sender(), new LoadUsersResponse(this, EventError(ex.what())));
|
||||
}
|
||||
else // single server
|
||||
reply(event->sender(), new LoadUsersResponse(this, EventError(ex.what())));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -393,8 +456,17 @@ namespace Robomongo
|
||||
|
||||
reply(event->sender(), new LoadFunctionsResponse(this, event->databaseName(), funs));
|
||||
} catch(const mongo::DBException &ex) {
|
||||
reply(event->sender(), new LoadFunctionsResponse(this, EventError(ex.what())));
|
||||
LOG_MSG(ex.what(), mongo::logger::LogSeverity::Error());
|
||||
if (_connSettings->isReplicaSet()) {
|
||||
ReplicaSet const& replicaSetInfo = getReplicaSetInfo(true);
|
||||
if (replicaSetInfo.primary.empty()) { // primary not reachable
|
||||
reply(event->sender(), new LoadFunctionsResponse(this,
|
||||
EventError(PRIMARY_UNREACHABLE, replicaSetInfo, false)));
|
||||
}
|
||||
else // other errors
|
||||
reply(event->sender(), new LoadFunctionsResponse(this, EventError(ex.what())));
|
||||
}
|
||||
else // single server
|
||||
reply(event->sender(), new LoadFunctionsResponse(this, EventError(ex.what())));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -409,12 +481,22 @@ namespace Robomongo
|
||||
client->insertDocument(event->obj(), event->ns());
|
||||
|
||||
client->done();
|
||||
|
||||
reply(event->sender(), new InsertDocumentResponse(this));
|
||||
} catch(const mongo::DBException &ex) {
|
||||
EventError error = EventError("Error when saving document: " + ex.toString());
|
||||
reply(event->sender(), new InsertDocumentResponse(this, error));
|
||||
LOG_MSG(ex.what(), mongo::logger::LogSeverity::Error());
|
||||
}
|
||||
catch(const mongo::DBException &ex) {
|
||||
if (_connSettings->isReplicaSet()) {
|
||||
ReplicaSet const& replicaSetInfo = getReplicaSetInfo(true);
|
||||
if (replicaSetInfo.primary.empty()) { // primary not reachable
|
||||
reply(event->sender(), new InsertDocumentResponse(this,
|
||||
EventError(PRIMARY_UNREACHABLE, replicaSetInfo, false)));
|
||||
}
|
||||
else // other errors
|
||||
reply(event->sender(), new InsertDocumentResponse(this, EventError(ex.toString())));
|
||||
}
|
||||
else { // single server
|
||||
reply(event->sender(), new InsertDocumentResponse(this,
|
||||
EventError("Error when saving document: " + ex.toString())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -423,14 +505,25 @@ namespace Robomongo
|
||||
try {
|
||||
boost::scoped_ptr<MongoClient> client(getClient());
|
||||
|
||||
client->removeDocuments(event->ns(), event->query(), event->justOne());
|
||||
client->removeDocuments(event->ns(), event->query(), (event->removeCount() == RemoveDocumentCount::ONE));
|
||||
client->done();
|
||||
|
||||
reply(event->sender(), new RemoveDocumentResponse(this));
|
||||
} catch(const mongo::DBException &ex) {
|
||||
EventError error = EventError("Error when deleting document: " + ex.toString());
|
||||
reply(event->sender(), new RemoveDocumentResponse(this, error));
|
||||
LOG_MSG(ex.what(), mongo::logger::LogSeverity::Error());
|
||||
reply(event->sender(), new RemoveDocumentResponse(this, event->removeCount(), event->index()));
|
||||
}
|
||||
catch(const mongo::DBException &ex) {
|
||||
if (_connSettings->isReplicaSet()) {
|
||||
ReplicaSet const& replicaSetInfo = getReplicaSetInfo(true);
|
||||
if (replicaSetInfo.primary.empty()) { // primary not reachable
|
||||
reply(event->sender(), new RemoveDocumentResponse(this,
|
||||
EventError(PRIMARY_UNREACHABLE, replicaSetInfo, false), event->removeCount(), event->index()));
|
||||
}
|
||||
else // other errors
|
||||
reply(event->sender(), new RemoveDocumentResponse(this, EventError(ex.what()),
|
||||
event->removeCount(), event->index()));
|
||||
}
|
||||
else // single server
|
||||
reply(event->sender(), new RemoveDocumentResponse(this,
|
||||
EventError("Error when deleting document: " + ex.toString()), event->removeCount(), event->index()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -459,11 +552,49 @@ namespace Robomongo
|
||||
return;
|
||||
}
|
||||
|
||||
MongoShellExecResult result = _scriptEngine->exec(event->script, event->databaseName);
|
||||
// Try to handle case where new shell (which was opened when server unreachable) was re-executed
|
||||
if (_scriptEngine->failedScope()) {
|
||||
try {
|
||||
_scriptEngine->init(_isLoadMongoRcJs);
|
||||
}
|
||||
catch (std::exception const& ex) {
|
||||
LOG_MSG(captilizeFirstChar(ex.what()) + ", cannot init mongo scope",
|
||||
mongo::logger::LogSeverity::Error());
|
||||
}
|
||||
}
|
||||
|
||||
// todo: should we use dbName from event or _connSettings?
|
||||
MongoShellExecResult result = _scriptEngine->exec(event->script, _connSettings->defaultDatabase());
|
||||
// To fix the problem where 'result' comes with old primary address.
|
||||
if (_connSettings->isReplicaSet())
|
||||
result.setCurrentServer(_dbclientRepSet->getSuspectedPrimaryHostAndPort().toString());
|
||||
|
||||
|
||||
if (result.error()) {
|
||||
// If this is replica set, update script engine and try again
|
||||
if (_connSettings->isReplicaSet()) {
|
||||
ReplicaSet const& replicaSetInfo = getReplicaSetInfo(true);
|
||||
if (replicaSetInfo.primary.empty()) { // primary not reachable
|
||||
reply(event->sender(), new ExecuteScriptResponse(this,
|
||||
EventError(PRIMARY_UNREACHABLE, replicaSetInfo, false)));
|
||||
return;
|
||||
}
|
||||
else { // primary reachable
|
||||
_scriptEngine->init(_isLoadMongoRcJs, replicaSetInfo.primary.toString(),
|
||||
_connSettings->defaultDatabase());
|
||||
result = _scriptEngine->exec(event->script, _connSettings->defaultDatabase());
|
||||
}
|
||||
}
|
||||
else { // single server
|
||||
reply(event->sender(), new ExecuteScriptResponse(this, EventError(result.errorMessage())));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
reply(event->sender(), new ExecuteScriptResponse(this, result, event->script.empty()));
|
||||
} catch(const mongo::DBException &ex) {
|
||||
reply(event->sender(), new ExecuteScriptResponse(this, EventError(ex.what())));
|
||||
LOG_MSG(ex.what(), mongo::logger::LogSeverity::Error());
|
||||
}
|
||||
catch(const std::exception &ex) {
|
||||
reply(event->sender(), new ExecuteScriptResponse(this, EventError(ex.what(), EventError::Unknown, false)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -505,14 +636,23 @@ namespace Robomongo
|
||||
boost::scoped_ptr<MongoClient> client(getClient());
|
||||
client->createDatabase(event->database());
|
||||
|
||||
// Insert to list of created database
|
||||
// Read docs for this hashset in the header
|
||||
// Insert to list of created database. Read docs for this hashset in the header
|
||||
_createdDbs.insert(event->database());
|
||||
|
||||
reply(event->sender(), new CreateDatabaseResponse(this));
|
||||
reply(event->sender(), new CreateDatabaseResponse(this, event->database()));
|
||||
} catch(const mongo::DBException &ex) {
|
||||
reply(event->sender(), new CreateDatabaseResponse(this, EventError(ex.what())));
|
||||
LOG_MSG(ex.what(), mongo::logger::LogSeverity::Error());
|
||||
if (_connSettings->isReplicaSet()) {
|
||||
ReplicaSet const& replicaSetInfo = getReplicaSetInfo(true);
|
||||
if (replicaSetInfo.primary.empty()) { // primary not reachable
|
||||
reply(event->sender(), new CreateDatabaseResponse(this, event->database(),
|
||||
EventError(PRIMARY_UNREACHABLE, replicaSetInfo, false)));
|
||||
}
|
||||
else // other errors
|
||||
reply(event->sender(), new CreateDatabaseResponse(this, event->database(),
|
||||
EventError(ex.toString())));
|
||||
}
|
||||
else // single server
|
||||
reply(event->sender(), new CreateDatabaseResponse(this, event->database(), EventError(ex.what())));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -520,45 +660,76 @@ namespace Robomongo
|
||||
{
|
||||
try {
|
||||
boost::scoped_ptr<MongoClient> client(getClient());
|
||||
client->dropDatabase(event->database());
|
||||
client->dropDatabase(event->database);
|
||||
|
||||
// Remove from the list of created database
|
||||
// Read docs for this hashset in the header
|
||||
_createdDbs.erase(event->database());
|
||||
// Remove from the list of created database, Read docs for this hashset in the header
|
||||
_createdDbs.erase(event->database);
|
||||
|
||||
reply(event->sender(), new DropDatabaseResponse(this));
|
||||
} catch(const mongo::DBException &ex) {
|
||||
reply(event->sender(), new DropDatabaseResponse(this, EventError(ex.what())));
|
||||
LOG_MSG(ex.what(), mongo::logger::LogSeverity::Error());
|
||||
reply(event->sender(), new DropDatabaseResponse(this, event->database));
|
||||
}
|
||||
catch(const mongo::DBException &ex) {
|
||||
if (_connSettings->isReplicaSet()) {
|
||||
ReplicaSet const& replicaSetInfo = getReplicaSetInfo(true);
|
||||
if (replicaSetInfo.primary.empty()) { // primary not reachable
|
||||
reply(event->sender(), new DropDatabaseResponse(this, event->database,
|
||||
EventError(PRIMARY_UNREACHABLE, replicaSetInfo, false)));
|
||||
}
|
||||
else // other errors
|
||||
reply(event->sender(), new DropDatabaseResponse(this, event->database, EventError(ex.toString())));
|
||||
}
|
||||
else // single server
|
||||
reply(event->sender(), new DropDatabaseResponse(this, event->database, EventError(ex.what())));
|
||||
}
|
||||
}
|
||||
|
||||
void MongoWorker::handle(CreateCollectionRequest *event)
|
||||
{
|
||||
std::string const& collection = event->ns().collectionName();
|
||||
|
||||
try {
|
||||
boost::scoped_ptr<MongoClient> client(getClient());
|
||||
client->createCollection(event->getNs().toString(), event->getSize(), event->getCapped(),
|
||||
client->createCollection(event->ns().toString(), event->getSize(), event->getCapped(),
|
||||
event->getMaxDocNum(), event->getExtraOptions());
|
||||
client->done();
|
||||
|
||||
reply(event->sender(), new CreateCollectionResponse(this));
|
||||
reply(event->sender(), new CreateCollectionResponse(this, collection));
|
||||
} catch(const mongo::DBException &ex) {
|
||||
reply(event->sender(), new CreateCollectionResponse(this, EventError(ex.what())));
|
||||
LOG_MSG(ex.what(), mongo::logger::LogSeverity::Error());
|
||||
if (_connSettings->isReplicaSet()) {
|
||||
ReplicaSet const& replicaSetInfo = getReplicaSetInfo(true);
|
||||
if (replicaSetInfo.primary.empty()) { // primary not reachable
|
||||
reply(event->sender(), new CreateCollectionResponse(this, collection,
|
||||
EventError(PRIMARY_UNREACHABLE, replicaSetInfo, false)));
|
||||
}
|
||||
else // other errors
|
||||
reply(event->sender(), new CreateCollectionResponse(this, collection, EventError(ex.toString())));
|
||||
}
|
||||
else // single server
|
||||
reply(event->sender(), new CreateCollectionResponse(this, collection, EventError(ex.what())));
|
||||
}
|
||||
}
|
||||
|
||||
void MongoWorker::handle(DropCollectionRequest *event)
|
||||
{
|
||||
std::string const& collection = event->ns().collectionName();
|
||||
|
||||
try {
|
||||
boost::scoped_ptr<MongoClient> client(getClient());
|
||||
client->dropCollection(event->ns());
|
||||
client->done();
|
||||
|
||||
reply(event->sender(), new DropCollectionResponse(this));
|
||||
reply(event->sender(), new DropCollectionResponse(this, collection));
|
||||
} catch(const mongo::DBException &ex) {
|
||||
reply(event->sender(), new DropCollectionResponse(this, EventError(ex.what())));
|
||||
LOG_MSG(ex.what(), mongo::logger::LogSeverity::Error());
|
||||
if (_connSettings->isReplicaSet()) {
|
||||
ReplicaSet const& replicaSetInfo = getReplicaSetInfo(true);
|
||||
if (replicaSetInfo.primary.empty()) { // primary not reachable
|
||||
reply(event->sender(), new DropCollectionResponse(this, collection,
|
||||
EventError(PRIMARY_UNREACHABLE, replicaSetInfo, false)));
|
||||
}
|
||||
else // other errors
|
||||
reply(event->sender(), new DropCollectionResponse(this, collection, EventError(ex.toString())));
|
||||
}
|
||||
else // single server
|
||||
reply(event->sender(), new DropCollectionResponse(this, collection, EventError(ex.what())));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -569,24 +740,46 @@ namespace Robomongo
|
||||
client->renameCollection(event->ns(), event->newCollection());
|
||||
client->done();
|
||||
|
||||
reply(event->sender(), new RenameCollectionResponse(this));
|
||||
reply(event->sender(), new RenameCollectionResponse(this, event->ns().collectionName(),
|
||||
event->newCollection()));
|
||||
} catch(const mongo::DBException &ex) {
|
||||
reply(event->sender(), new RenameCollectionResponse(this, EventError(ex.what())));
|
||||
LOG_MSG(ex.what(), mongo::logger::LogSeverity::Error());
|
||||
if (_connSettings->isReplicaSet()) {
|
||||
ReplicaSet const& replicaSetInfo = getReplicaSetInfo(true);
|
||||
if (replicaSetInfo.primary.empty()) { // primary not reachable
|
||||
reply(event->sender(), new RenameCollectionResponse(this,
|
||||
EventError(PRIMARY_UNREACHABLE, replicaSetInfo, false)));
|
||||
}
|
||||
else // other errors
|
||||
reply(event->sender(), new RenameCollectionResponse(this, EventError(ex.toString())));
|
||||
}
|
||||
else // single server
|
||||
reply(event->sender(), new RenameCollectionResponse(this, EventError(ex.what())));
|
||||
}
|
||||
}
|
||||
|
||||
void MongoWorker::handle(DuplicateCollectionRequest *event)
|
||||
{
|
||||
std::string const& sourceCollection = event->ns().collectionName();
|
||||
|
||||
try {
|
||||
boost::scoped_ptr<MongoClient> client(getClient());
|
||||
client->duplicateCollection(event->ns(), event->newCollection());
|
||||
client->done();
|
||||
|
||||
reply(event->sender(), new DuplicateCollectionResponse(this));
|
||||
} catch(const mongo::DBException &ex) {
|
||||
reply(event->sender(), new DuplicateCollectionResponse(this, EventError(ex.what())));
|
||||
LOG_MSG(ex.what(), mongo::logger::LogSeverity::Error());
|
||||
reply(event->sender(), new DuplicateCollectionResponse(this, sourceCollection, event->newCollection()));
|
||||
}
|
||||
catch (const mongo::DBException &ex) {
|
||||
if (_connSettings->isReplicaSet()) {
|
||||
ReplicaSet const& replicaSetInfo = getReplicaSetInfo(true);
|
||||
if (replicaSetInfo.primary.empty()) { // primary not reachable
|
||||
reply(event->sender(), new DuplicateCollectionResponse(this, sourceCollection,
|
||||
EventError(PRIMARY_UNREACHABLE, replicaSetInfo, false)));
|
||||
}
|
||||
else // other errors
|
||||
reply(event->sender(), new DuplicateCollectionResponse(this, sourceCollection, EventError(ex.what())));
|
||||
}
|
||||
else // single server
|
||||
reply(event->sender(), new DuplicateCollectionResponse(this, sourceCollection, EventError(ex.what())));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -612,10 +805,21 @@ namespace Robomongo
|
||||
client->createUser(event->database(), event->user(), event->overwrite());
|
||||
client->done();
|
||||
|
||||
reply(event->sender(), new CreateUserResponse(this));
|
||||
reply(event->sender(), new CreateUserResponse(this, event->user().name()));
|
||||
} catch(const mongo::DBException &ex) {
|
||||
reply(event->sender(), new CreateUserResponse(this, EventError(ex.what())));
|
||||
LOG_MSG(ex.what(), mongo::logger::LogSeverity::Error());
|
||||
if (_connSettings->isReplicaSet()) {
|
||||
ReplicaSet const& replicaSetInfo = getReplicaSetInfo(true);
|
||||
if (replicaSetInfo.primary.empty()) { // primary not reachable
|
||||
reply(event->sender(), new CreateUserResponse(this, event->user().name(),
|
||||
EventError(PRIMARY_UNREACHABLE, replicaSetInfo, false)));
|
||||
}
|
||||
else // other errors
|
||||
reply(event->sender(), new CreateUserResponse(this, event->user().name(),
|
||||
EventError(ex.what())));
|
||||
}
|
||||
else // single server
|
||||
reply(event->sender(), new CreateUserResponse(this, event->user().name(),
|
||||
EventError(ex.what())));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -626,24 +830,44 @@ namespace Robomongo
|
||||
client->dropUser(event->database(), event->id());
|
||||
client->done();
|
||||
|
||||
reply(event->sender(), new DropUserResponse(this));
|
||||
reply(event->sender(), new DropUserResponse(this, event->username()));
|
||||
} catch(const mongo::DBException &ex) {
|
||||
reply(event->sender(), new DropUserResponse(this, EventError(ex.what())));
|
||||
LOG_MSG(ex.what(), mongo::logger::LogSeverity::Error());
|
||||
if (_connSettings->isReplicaSet()) {
|
||||
ReplicaSet const& replicaSetInfo = getReplicaSetInfo(true);
|
||||
if (replicaSetInfo.primary.empty()) { // primary not reachable
|
||||
reply(event->sender(), new DropUserResponse(this, event->username(),
|
||||
EventError(PRIMARY_UNREACHABLE, replicaSetInfo, false)));
|
||||
}
|
||||
else // other errors
|
||||
reply(event->sender(), new DropUserResponse(this, event->username(), EventError(ex.what())));
|
||||
}
|
||||
else // single server
|
||||
reply(event->sender(), new DropUserResponse(this, event->username(), EventError(ex.what())));
|
||||
}
|
||||
}
|
||||
|
||||
void MongoWorker::handle(CreateFunctionRequest *event)
|
||||
{
|
||||
std::string const& functionName = event->function().name();
|
||||
|
||||
try {
|
||||
boost::scoped_ptr<MongoClient> client(getClient());
|
||||
client->createFunction(event->database(), event->function(), event->existingFunctionName());
|
||||
client->done();
|
||||
|
||||
reply(event->sender(), new CreateFunctionResponse(this));
|
||||
reply(event->sender(), new CreateFunctionResponse(this, functionName));
|
||||
} catch(const mongo::DBException &ex) {
|
||||
reply(event->sender(), new CreateFunctionResponse(this, EventError(ex.what())));
|
||||
LOG_MSG(ex.what(), mongo::logger::LogSeverity::Error());
|
||||
if (_connSettings->isReplicaSet()) {
|
||||
ReplicaSet const& replicaSetInfo = getReplicaSetInfo(true);
|
||||
if (replicaSetInfo.primary.empty()) { // primary not reachable
|
||||
reply(event->sender(), new CreateFunctionResponse(this, functionName,
|
||||
EventError(PRIMARY_UNREACHABLE, replicaSetInfo, false)));
|
||||
}
|
||||
else // other errors
|
||||
reply(event->sender(), new CreateFunctionResponse(this, functionName, EventError(ex.what())));
|
||||
}
|
||||
else // single server
|
||||
reply(event->sender(), new CreateFunctionResponse(this, functionName, EventError(ex.what())));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -651,42 +875,52 @@ namespace Robomongo
|
||||
{
|
||||
try {
|
||||
boost::scoped_ptr<MongoClient> client(getClient());
|
||||
client->dropFunction(event->database(), event->name());
|
||||
client->dropFunction(event->database(), event->functionName());
|
||||
client->done();
|
||||
|
||||
reply(event->sender(), new DropFunctionResponse(this));
|
||||
} catch(const mongo::DBException &ex) {
|
||||
reply(event->sender(), new DropFunctionResponse(this, EventError(ex.what())));
|
||||
LOG_MSG(ex.what(), mongo::logger::LogSeverity::Error());
|
||||
reply(event->sender(), new DropFunctionResponse(this, event->functionName()));
|
||||
}
|
||||
catch (const mongo::DBException &ex) {
|
||||
if (_connSettings->isReplicaSet()) {
|
||||
ReplicaSet const& replicaSetInfo = getReplicaSetInfo(true);
|
||||
if (replicaSetInfo.primary.empty()) { // primary not reachable
|
||||
reply(event->sender(), new DropFunctionResponse(this, event->functionName(),
|
||||
EventError(PRIMARY_UNREACHABLE, replicaSetInfo, false)));
|
||||
}
|
||||
else // other errors
|
||||
reply(event->sender(), new DropFunctionResponse(this, event->functionName(), EventError(ex.what())));
|
||||
}
|
||||
else // single server
|
||||
reply(event->sender(), new DropFunctionResponse(this, event->functionName(), EventError(ex.what())));
|
||||
}
|
||||
}
|
||||
|
||||
mongo::DBClientBase *MongoWorker::getConnection(bool mayReturnNull /* = false */)
|
||||
{
|
||||
// --- Configure SSL first --- Todo: move to function?
|
||||
// As a precaution reset SSL global params for any kind of connection request (SSL or non-SSL)
|
||||
resetGlobalSSLparams();
|
||||
|
||||
// Update global mongo SSL settings according to SSL enable/disabled status
|
||||
if (_connSettings->sslSettings()->sslEnabled())
|
||||
{
|
||||
// Force SSL mode for outgoing connections
|
||||
mongo::sslGlobalParams.sslMode.store(mongo::SSLParams::SSLMode_requireSSL);
|
||||
updateGlobalSSLparams();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Disable forced SSL mode for outgoing connections
|
||||
mongo::sslGlobalParams.sslMode.store(mongo::SSLParams::SSLMode_allowSSL);
|
||||
}
|
||||
configureSSL();
|
||||
|
||||
// --- Perform connection ---
|
||||
if (_connSettings->isReplicaSet()) { // connection to replica set
|
||||
if (!_dbclientRepSet) {
|
||||
_dbclientRepSet = std::unique_ptr<mongo::DBClientReplicaSet>(
|
||||
// todo: where to get name of replica? (i.e. repset) - todo: this if causes crash after app close
|
||||
new mongo::DBClientReplicaSet("repset",_connSettings->replicaSetSettings()->membersToHostAndPort(), _mongoTimeoutSec));
|
||||
if (_connSettings->isReplicaSet()) // connection to replica set
|
||||
{
|
||||
if (!_dbclientRepSet)
|
||||
{
|
||||
init(); // Init mongoworker for early-use of _scriptEngine
|
||||
|
||||
// Step-1: Use user entered set name or retrieve set name from cache or from a reachable member
|
||||
std::string setName = _connSettings->replicaSetSettings()->setNameUserEntered();
|
||||
if (setName.empty()) {
|
||||
setName = _connSettings->replicaSetSettings()->cachedSetName();
|
||||
if (setName.empty()) // If there is no cached set name, get it from an on-line replica node
|
||||
setName = connectAndGetReplicaSetName();
|
||||
|
||||
if (setName.empty()) // It is not possible to continue with empty set name
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Step-2: Try connect to replica set with set name
|
||||
auto const& membersHostsAndPorts = _connSettings->replicaSetSettings()->membersToHostAndPort();
|
||||
_dbclientRepSet = DBClientReplicaSet(new mongo::DBClientReplicaSet(setName, membersHostsAndPorts,
|
||||
_mongoTimeoutSec));
|
||||
bool const connStatus = _dbclientRepSet->connect();
|
||||
|
||||
if (!connStatus) {
|
||||
@@ -695,17 +929,15 @@ namespace Robomongo
|
||||
}
|
||||
return _dbclientRepSet.get();
|
||||
}
|
||||
else { // connection to single server
|
||||
else { // connection to single server
|
||||
if (!_dbclient) {
|
||||
// Timeout for operations
|
||||
// Connect timeout is fixed, but short, at 5 seconds (see headers for DBClientConnection)
|
||||
_dbclient = std::unique_ptr<mongo::DBClientConnection>(new mongo::DBClientConnection(true, _mongoTimeoutSec));
|
||||
_dbclient = DBClientConnection(new mongo::DBClientConnection(true, _mongoTimeoutSec));
|
||||
|
||||
mongo::Status status = _dbclient->connect(_connSettings->info());
|
||||
|
||||
if (!status.isOK() && mayReturnNull) {
|
||||
mongo::Status status = _dbclient->connect(_connSettings->hostAndPort());
|
||||
if (!status.isOK() && mayReturnNull)
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return _dbclient.get();
|
||||
}
|
||||
@@ -716,6 +948,22 @@ namespace Robomongo
|
||||
return new MongoClient(getConnection());
|
||||
}
|
||||
|
||||
void MongoWorker::configureSSL()
|
||||
{
|
||||
// As a precaution reset SSL global params for any kind of connection request (SSL or non-SSL)
|
||||
resetGlobalSSLparams();
|
||||
// Update global SSL mode and global mongo SSL settings
|
||||
if (_connSettings->sslSettings()->sslEnabled()) {
|
||||
// Force SSL mode for outgoing connections
|
||||
mongo::sslGlobalParams.sslMode.store(mongo::SSLParams::SSLMode_requireSSL);
|
||||
updateGlobalSSLparams();
|
||||
}
|
||||
else {
|
||||
// Disable forced SSL mode for outgoing connections
|
||||
mongo::sslGlobalParams.sslMode.store(mongo::SSLParams::SSLMode_allowSSL);
|
||||
}
|
||||
}
|
||||
|
||||
void MongoWorker::updateGlobalSSLparams() const
|
||||
{
|
||||
resetGlobalSSLparams();
|
||||
@@ -747,6 +995,82 @@ namespace Robomongo
|
||||
mongo::sslGlobalParams.sslAllowInvalidHostnames = false;
|
||||
}
|
||||
|
||||
ReplicaSet MongoWorker::getReplicaSetInfo(bool refresh /*= true*/) const
|
||||
{
|
||||
std::string setName;
|
||||
mongo::HostAndPort primary;
|
||||
std::vector<std::pair<std::string, bool>> membersAndHealths;
|
||||
|
||||
// Refresh view of Replica Set Monitor to get live data
|
||||
auto repSetMonitor = mongo::globalRSMonitorManager.getMonitor(_dbclientRepSet->getSetName());
|
||||
|
||||
// Handle long lasting (e.g. overnight) connections which causes replica set monitor to be null
|
||||
if (!repSetMonitor) {
|
||||
mongo::ReplicaSetMonitor::createIfNeeded(_dbclientRepSet->getSetName(),
|
||||
_connSettings->replicaSetSettings()->membersToHostAndPortAsSet());
|
||||
repSetMonitor = mongo::globalRSMonitorManager.getMonitor(_dbclientRepSet->getSetName());
|
||||
if (!repSetMonitor) // if nullptr again even if the set is reachable, something went really wrong.
|
||||
return ReplicaSet(setName, primary, membersAndHealths,
|
||||
"Unexpected error. Unable to get replica set monitor for set name: " +
|
||||
_dbclientRepSet->getSetName() + "\nPlease open a new connection.");
|
||||
}
|
||||
|
||||
// refreshAll() takes long time, in order to do first establish connection request much faster
|
||||
// refreshAll() will be by-passed first and it will/must be done after a successful connection.
|
||||
if (refresh)
|
||||
repSetMonitor->startOrContinueRefresh().refreshAll();
|
||||
|
||||
setName = repSetMonitor->getName();
|
||||
auto const primaryOnly = mongo::ReadPreferenceSetting(mongo::ReadPreference::PrimaryOnly, mongo::TagSet());
|
||||
auto primaryWithStatus = repSetMonitor->getHostOrRefresh(primaryOnly, mongo::Milliseconds(2000)); // todo
|
||||
if (primaryWithStatus.isOK())
|
||||
primary = primaryWithStatus.getValue();
|
||||
|
||||
QStringList servers;
|
||||
// i.e. setNameAndMembers: "repset/localhost:27017,localhost:27018,localhost:27019"
|
||||
auto setNameAndMembers = QString::fromStdString(repSetMonitor->getServerAddress());
|
||||
QStringList result = setNameAndMembers.split("/");
|
||||
if (result.size() > 1)
|
||||
servers = result[1].split(",");
|
||||
|
||||
// Save address and health of replica members
|
||||
for (QString const& server : servers) {
|
||||
auto hostAndPort = mongo::HostAndPort(mongo::StringData(server.toStdString()));
|
||||
membersAndHealths.push_back({ server.toStdString(), repSetMonitor->isHostUp(hostAndPort) });
|
||||
}
|
||||
|
||||
return ReplicaSet(setName, primary, membersAndHealths, primaryWithStatus.getStatus().reason());
|
||||
}
|
||||
|
||||
std::string MongoWorker::connectAndGetReplicaSetName() const
|
||||
{
|
||||
auto dbclientTemp = DBClientConnection(new mongo::DBClientConnection(true, 10));
|
||||
std::string setName = "";
|
||||
|
||||
// Try connecting to the nodes one by one until getting replica set name.
|
||||
for (auto const& node : _connSettings->replicaSetSettings()->membersToHostAndPort())
|
||||
{
|
||||
mongo::Status const status = dbclientTemp->connect(node);
|
||||
if (status.isOK())
|
||||
{
|
||||
_scriptEngine->init(_isLoadMongoRcJs, node.toString());
|
||||
MongoShellExecResult result = _scriptEngine->exec("rs.status()", "");
|
||||
if (!result.results().empty())
|
||||
{
|
||||
auto resultDocs = result.results().front().documents();
|
||||
if (!resultDocs.empty())
|
||||
{
|
||||
setName = resultDocs.front()->bsonObj().getStringField("set");
|
||||
if (!setName.empty()) // We get the information, finish the loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return setName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send event to this MongoWorker
|
||||
*/
|
||||
@@ -768,4 +1092,19 @@ namespace Robomongo
|
||||
|
||||
AppRegistry::instance().bus()->send(receiver, event);
|
||||
}
|
||||
|
||||
void MongoWorker::pingDatabase(mongo::DBClientBase *dbclient) const
|
||||
{
|
||||
// Building { ping: 1 }
|
||||
mongo::BSONObjBuilder command;
|
||||
command.append("ping", 1);
|
||||
mongo::BSONObj result;
|
||||
std::string authBase = getAuthBase();
|
||||
if (authBase.empty()) {
|
||||
dbclient->runCommand("admin", command.obj(), result);
|
||||
}
|
||||
else {
|
||||
dbclient->runCommand(authBase, command.obj(), result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <QMutex>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <mongo/client/dbclient_rs.h> // todo: move
|
||||
#include <mongo/client/dbclient_rs.h>
|
||||
|
||||
#include "robomongo/core/events/MongoEvents.h"
|
||||
|
||||
@@ -24,16 +24,23 @@ namespace Robomongo
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
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();
|
||||
enum { pingTimeMs = 60 * 1000 };
|
||||
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.
|
||||
@@ -43,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
|
||||
@@ -130,14 +143,18 @@ namespace Robomongo
|
||||
* @brief Send event to this MongoWorker
|
||||
*/
|
||||
void send(Event *event);
|
||||
|
||||
DatabasesContainerType getDatabaseNamesSafe();
|
||||
std::string getAuthBase() const;
|
||||
|
||||
std::unique_ptr<mongo::DBClientConnection> _dbclient;
|
||||
std::unique_ptr<mongo::DBClientReplicaSet> _dbclientRepSet;
|
||||
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.
|
||||
@@ -149,14 +166,34 @@ namespace Robomongo
|
||||
*/
|
||||
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;
|
||||
@@ -167,6 +204,9 @@ namespace Robomongo
|
||||
int _shellTimeoutSec;
|
||||
QAtomicInteger<int> _isQuiting;
|
||||
|
||||
std::unique_ptr<mongo::DBClientConnection> _dbclient;
|
||||
std::unique_ptr<mongo::DBClientReplicaSet> _dbclientRepSet;
|
||||
|
||||
ConnectionSettings *_connSettings;
|
||||
|
||||
// Collection of created databases.
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
@@ -2,12 +2,16 @@
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <QUuid>
|
||||
|
||||
#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;
|
||||
@@ -19,21 +23,54 @@ namespace
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
|
||||
/**
|
||||
* Creates ConnectionSettings with default values
|
||||
*/
|
||||
ConnectionSettings::ConnectionSettings() : QObject(),
|
||||
ConnectionSettings::ConnectionSettings(bool isClone) : QObject(),
|
||||
_connectionName(defaultNameConnection),
|
||||
_host(defaultServerHost),
|
||||
_port(port),
|
||||
_imported(false),
|
||||
_sshSettings(new SshSettings()),
|
||||
_sslSettings(new SslSettings()),
|
||||
_isReplicaSet(false),
|
||||
_replicaSetSettings(new ReplicaSetSettings()),
|
||||
_isReplicaSet(false)
|
||||
_clone(isClone)
|
||||
{ }
|
||||
|
||||
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().left(maxLength)));
|
||||
@@ -60,6 +97,13 @@ namespace Robomongo
|
||||
_replicaSetSettings->fromVariant(map.value("replicaSet").toMap());
|
||||
}
|
||||
|
||||
// If UUID has never been created or is empty, create a new one. Otherwise load the existing.
|
||||
if (!map.contains("uuid") || map.value("uuid").toString().isEmpty())
|
||||
_uuid = QUuid::createUuid().toString();
|
||||
else
|
||||
_uuid = map.value("uuid").toString();
|
||||
|
||||
|
||||
//#ifdef MONGO_SSL
|
||||
// ,SSLInfo(map.value("sslEnabled").toBool(),QtUtils::toStdString(map.value("sslPemKeyFile").toString()))
|
||||
//#endif
|
||||
@@ -71,9 +115,6 @@ namespace Robomongo
|
||||
ConnectionSettings::~ConnectionSettings()
|
||||
{
|
||||
clearCredentials();
|
||||
delete _sshSettings;
|
||||
delete _sslSettings;
|
||||
delete _replicaSetSettings; // todo: unique_ptr
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,9 +122,10 @@ namespace Robomongo
|
||||
*/
|
||||
ConnectionSettings *ConnectionSettings::clone() const
|
||||
{
|
||||
ConnectionSettings *record = new ConnectionSettings();
|
||||
record->apply(this);
|
||||
return record;
|
||||
auto settings = new ConnectionSettings(true);
|
||||
settings->setUuid(_uuid);
|
||||
settings->apply(this);
|
||||
return settings;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,9 +146,9 @@ namespace Robomongo
|
||||
addCredential((*it)->clone());
|
||||
}
|
||||
|
||||
_sshSettings = source->sshSettings()->clone();
|
||||
_sslSettings = source->sslSettings()->clone();
|
||||
_replicaSetSettings = source->_replicaSetSettings->clone(); // todo: leak candidate
|
||||
_sshSettings.reset(source->sshSettings()->clone());
|
||||
_sslSettings.reset(source->sslSettings()->clone());
|
||||
_replicaSetSettings.reset(source->_replicaSetSettings->clone());
|
||||
|
||||
//#ifdef MONGO_SSL
|
||||
// setSslInfo(source->sslInfo());
|
||||
@@ -142,13 +184,14 @@ namespace Robomongo
|
||||
|
||||
map.insert("ssh", _sshSettings->toVariant());
|
||||
map.insert("ssl", _sslSettings->toVariant());
|
||||
map.insert("uuid", _uuid);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
CredentialSettings *ConnectionSettings::findCredential(const std::string &databaseName) const
|
||||
{
|
||||
CredentialSettings *result = NULL;
|
||||
CredentialSettings *result = nullptr;
|
||||
for (QList<CredentialSettings *>::const_iterator it = _credentials.begin(); it != _credentials.end(); ++it) {
|
||||
CredentialSettings *cred = *it;
|
||||
if (cred->databaseName() == databaseName) {
|
||||
@@ -172,7 +215,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;
|
||||
@@ -183,7 +226,7 @@ namespace Robomongo
|
||||
/**
|
||||
* @brief Returns primary credential
|
||||
*/
|
||||
CredentialSettings *ConnectionSettings::primaryCredential()
|
||||
CredentialSettings *ConnectionSettings::primaryCredential() const
|
||||
{
|
||||
if (_credentials.count() == 0) {
|
||||
_credentials.append(new CredentialSettings());
|
||||
@@ -203,9 +246,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,8 +3,10 @@
|
||||
#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
|
||||
{
|
||||
@@ -24,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
|
||||
@@ -64,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; }
|
||||
|
||||
/**
|
||||
@@ -93,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
|
||||
*/
|
||||
CredentialSettings *primaryCredential();
|
||||
CredentialSettings *primaryCredential() const;
|
||||
|
||||
/**
|
||||
* @brief Returns number of credentials
|
||||
@@ -133,45 +140,46 @@ 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);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
SshSettings *sshSettings() const { return _sshSettings; }
|
||||
SslSettings *sslSettings() const { return _sslSettings; }
|
||||
ReplicaSetSettings *replicaSetSettings() const { return _replicaSetSettings; }
|
||||
mongo::HostAndPort hostAndPort() const;
|
||||
SshSettings *sshSettings() const { return _sshSettings.get(); }
|
||||
SslSettings *sslSettings() const { return _sslSettings.get(); }
|
||||
ReplicaSetSettings *replicaSetSettings() const { return _replicaSetSettings.get(); }
|
||||
|
||||
bool isReplicaSet() const { return _isReplicaSet; }
|
||||
void setReplicaSet(bool flag) { _isReplicaSet = flag; }
|
||||
|
||||
QString uuid() const { return _uuid; }
|
||||
void setUuid(QString const& uuid) { _uuid = uuid; }
|
||||
|
||||
private:
|
||||
CredentialSettings *findCredential(const std::string &databaseName) const;
|
||||
bool _isReplicaSet;
|
||||
|
||||
std::string _connectionName;
|
||||
std::string _host;
|
||||
int _port;
|
||||
std::string _defaultDatabase;
|
||||
QList<CredentialSettings *> _credentials;
|
||||
SshSettings *_sshSettings;
|
||||
SslSettings *_sslSettings;
|
||||
ReplicaSetSettings *_replicaSetSettings;
|
||||
|
||||
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;
|
||||
|
||||
// UUID string taken from QUuid.
|
||||
// It is used to identify the unique ID of a connection settings object
|
||||
QString _uuid;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -6,9 +6,18 @@ namespace Robomongo
|
||||
{
|
||||
|
||||
ReplicaSetSettings::ReplicaSetSettings()
|
||||
: _members(), _readPreference(ReadPreference::PRIMARY)
|
||||
: _cachedSetName(""), _members(), _readPreference(ReadPreference::PRIMARY)
|
||||
{}
|
||||
|
||||
ReplicaSetSettings::ReplicaSetSettings(const mongo::MongoURI& uri)
|
||||
: ReplicaSetSettings()
|
||||
{
|
||||
_cachedSetName = 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);
|
||||
@@ -18,6 +27,8 @@ namespace Robomongo
|
||||
QVariant ReplicaSetSettings::toVariant() const
|
||||
{
|
||||
QVariantMap map;
|
||||
map.insert("setNameUserEntered", QString::fromStdString(_setNameUserEntered));
|
||||
map.insert("cachedSetName", QString::fromStdString(_cachedSetName));
|
||||
int idx = 0;
|
||||
for (std::string const& str : _members) {
|
||||
map.insert(QString::number(idx), QtUtils::toQString(str));
|
||||
@@ -29,9 +40,11 @@ namespace Robomongo
|
||||
|
||||
void ReplicaSetSettings::fromVariant(const QVariantMap &map)
|
||||
{
|
||||
setSetNameUserEntered(map.value("setNameUserEntered").toString().toStdString());
|
||||
setCachedSetName(map.value("cachedSetName").toString().toStdString());
|
||||
// Extract and set replica members
|
||||
std::vector<std::string> vec;
|
||||
auto& itr = map.begin();
|
||||
auto itr = map.begin();
|
||||
int idx = 0;
|
||||
do
|
||||
{
|
||||
@@ -44,14 +57,28 @@ namespace Robomongo
|
||||
// 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);
|
||||
}
|
||||
|
||||
const std::vector<mongo::HostAndPort> ReplicaSetSettings::membersToHostAndPort() const
|
||||
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)
|
||||
{
|
||||
for (auto const& member : _members)
|
||||
membersHostAndPort.push_back(mongo::HostAndPort(member));
|
||||
}
|
||||
|
||||
return membersHostAndPort;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <QVariantMap>
|
||||
|
||||
#include <mongo/util/net/hostandport.h>
|
||||
#include <mongo/client/mongo_uri.h>
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
@@ -15,9 +16,12 @@ namespace Robomongo
|
||||
{
|
||||
PRIMARY = 0,
|
||||
PRIMARY_PREFERRED = 1
|
||||
// todo: Add all supported read preferences
|
||||
};
|
||||
|
||||
ReplicaSetSettings();
|
||||
|
||||
ReplicaSetSettings(const mongo::MongoURI& uri);
|
||||
|
||||
/**
|
||||
* Clones Replica Set settings.
|
||||
@@ -30,22 +34,33 @@ namespace Robomongo
|
||||
QVariant toVariant() const;
|
||||
void fromVariant(const QVariantMap &map);
|
||||
|
||||
//// todo: remove
|
||||
//void addMember(const std::string& str) { _members.push_back(str); }
|
||||
|
||||
// Getters
|
||||
//
|
||||
const std::vector<std::string>& members() const { return _members; }
|
||||
const std::vector<mongo::HostAndPort> membersToHostAndPort() const;
|
||||
std::string const& setNameUserEntered() { return _setNameUserEntered; }
|
||||
std::string const& cachedSetName() { return _cachedSetName; }
|
||||
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 setMembers(const std::vector<std::string>& members) { _members = members; }
|
||||
void setSetNameUserEntered(const std::string& setName) { _setNameUserEntered = setName; }
|
||||
void setCachedSetName(const std::string& setName) { _cachedSetName = 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 _setNameUserEntered;
|
||||
std::string _cachedSetName;
|
||||
std::vector<std::string> _members;
|
||||
ReadPreference _readPreference;
|
||||
ReadPreference _readPreference = ReadPreference::PRIMARY;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,6 +3,23 @@
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QVariantList>
|
||||
#include <QUuid>
|
||||
#include <QJsonArray>
|
||||
#include <QXmlStreamReader>
|
||||
|
||||
// Todo:
|
||||
// Warning:
|
||||
// Currently, this include statement should be maintained manually.
|
||||
// The Qt version must be updated if Qt is upgraded. Otherwise it might cause build error.
|
||||
// The correct path will be: PROJECT_QT_VERSION/QtGui/private/qzipreader_p.h
|
||||
// Details:
|
||||
// Ideally, private class inside qzipreader_p.h should not be used since it is not
|
||||
// a publicly/officially supported API by Qt. It is used here since we are doing a
|
||||
// a very lightweight zip operation which is unzipping a zip archive with one, small file.
|
||||
// The alternative is including QuaZip and Zlib third party libraries into CMAKE project and
|
||||
// building, and using them which might be time consuming.
|
||||
#include <5.7.0/QtGui/private/qzipreader_p.h>
|
||||
|
||||
#include <parser.h>
|
||||
#include <serializer.h>
|
||||
|
||||
@@ -14,29 +31,68 @@
|
||||
#include "robomongo/core/utils/QtUtils.h"
|
||||
#include "robomongo/core/utils/StdUtils.h"
|
||||
#include "robomongo/gui/AppStyle.h"
|
||||
#include "robomongo/utils/common.h"
|
||||
|
||||
namespace
|
||||
namespace Robomongo
|
||||
{
|
||||
/**
|
||||
* @brief Version of schema
|
||||
*/
|
||||
const QString SchemaVersion = "2.0";
|
||||
// 3T config files
|
||||
auto const Studio3T_PropertiesDat = QString("%1/.3T/studio-3t/properties.dat").arg(QDir::homePath());
|
||||
auto const DataMongodb_PropertiesDat = QString("%1/.3T/data-man-mongodb/properties.dat").arg(QDir::homePath());
|
||||
auto const MongoChefPro_PropertiesDat = QString("%1/.3T/mongochef-pro/properties.dat").arg(QDir::homePath());
|
||||
auto const MongoChefEnt_PropertiesDat = QString("%1/.3T/mongochef-enterprise/properties.dat").arg(QDir::homePath());
|
||||
|
||||
/**
|
||||
* @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 std::vector<std::pair<QString, QString>> S_3T_ZipFile_And_ConfigFile_List
|
||||
{
|
||||
{ Studio3T_PropertiesDat, "Studio3T.properties" },
|
||||
{ DataMongodb_PropertiesDat, "3T.data-man-mongodb.properties" },
|
||||
{ MongoChefPro_PropertiesDat, "3T.mongochef-pro.properties" },
|
||||
{ MongoChefEnt_PropertiesDat, "3T.mongochef-enterprise.properties" }
|
||||
};
|
||||
|
||||
// Extract zipFile and find the value of "anonymousID" field in propFile
|
||||
QString extractAnonymousID(QString const& zipFile, QString const& propfile);
|
||||
|
||||
/**
|
||||
* @brief Version of schema
|
||||
*/
|
||||
const QString SchemaVersion = "2.0";
|
||||
|
||||
/**
|
||||
* @brief Config file containing directory path
|
||||
* (usually: /home/user/.config/robomongo)
|
||||
*/
|
||||
const QString _configDir = QString("%1/.config/robomongo/0.9").arg(QDir::homePath());
|
||||
}
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
|
||||
/**
|
||||
* @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_RC1 = QString("%1/.config/robomongo/1.0/robomongo.json").arg(QDir::homePath());
|
||||
// Change of path due to re-branding to 3T
|
||||
const auto CONFIG_FILE_1_0_0 = QString("%1/.3T/robomongo/1.0.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_1_0_RC1, SettingsManager::importConnectionsFrom_1_0_RC1_to_1_0_0),
|
||||
ConfigFileAndImportFunction(CONFIG_FILE_0_9, SettingsManager::importConnectionsFrom_0_9_to_1_0_RC1),
|
||||
ConfigFileAndImportFunction(CONFIG_FILE_0_8_5, SettingsManager::importConnectionsFrom_0_8_5_to_0_9)
|
||||
};
|
||||
|
||||
std::vector<ConnectionSettings*> SettingsManager::_connections;
|
||||
// Temporarily disabling Recent Connections feature
|
||||
// std::vector<RecentConnection> SettingsManager::_recentConnections;
|
||||
|
||||
/**
|
||||
* Creates SettingsManager for config file in default location
|
||||
* ~/.config/robomongo/robomongo.json
|
||||
@@ -47,18 +103,23 @@ 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),
|
||||
_imported(false),
|
||||
_mongoTimeoutSec(10),
|
||||
_shellTimeoutSec(15)
|
||||
_shellTimeoutSec(15),
|
||||
_imported(false)
|
||||
{
|
||||
load();
|
||||
LOG_MSG("SettingsManager initialized in " + _configPath, mongo::logger::LogSeverity::Info(), false);
|
||||
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 " + ConfigFilePath, mongo::logger::LogSeverity::Info(), false);
|
||||
}
|
||||
|
||||
SettingsManager::~SettingsManager()
|
||||
@@ -72,10 +133,10 @@ namespace Robomongo
|
||||
*/
|
||||
bool SettingsManager::load()
|
||||
{
|
||||
if (!QFile::exists(_configPath))
|
||||
if (!QFile::exists(ConfigFilePath))
|
||||
return false;
|
||||
|
||||
QFile f(_configPath);
|
||||
QFile f(ConfigFilePath);
|
||||
if (!f.open(QIODevice::ReadOnly))
|
||||
return false;
|
||||
|
||||
@@ -98,14 +159,14 @@ namespace Robomongo
|
||||
{
|
||||
QVariantMap map = convertToMap();
|
||||
|
||||
if (!QDir().mkpath(_configDir)) {
|
||||
LOG_MSG("ERROR: Could not create settings path: " + _configDir, mongo::logger::LogSeverity::Error());
|
||||
if (!QDir().mkpath(ConfigDir)) {
|
||||
LOG_MSG("ERROR: Could not create settings path: " + ConfigDir, mongo::logger::LogSeverity::Error());
|
||||
return false;
|
||||
}
|
||||
|
||||
QFile f(_configPath);
|
||||
QFile f(ConfigFilePath);
|
||||
if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
|
||||
LOG_MSG("ERROR: Could not write settings to: " + _configPath, mongo::logger::LogSeverity::Error());
|
||||
LOG_MSG("ERROR: Could not write settings to: " + ConfigFilePath, mongo::logger::LogSeverity::Error());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -114,11 +175,21 @@ namespace Robomongo
|
||||
s.setIndentMode(QJson::IndentFull);
|
||||
s.serialize(map, &f, &ok);
|
||||
|
||||
LOG_MSG("Settings saved to: " + _configPath, mongo::logger::LogSeverity::Info());
|
||||
LOG_MSG("Settings saved to: " + ConfigFilePath, mongo::logger::LogSeverity::Info());
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
void SettingsManager::addCacheData(QString const& key, QVariant const& value)
|
||||
{
|
||||
_cacheData.insert(key, value);
|
||||
}
|
||||
|
||||
QVariant SettingsManager::cacheData(QString const& key) const
|
||||
{
|
||||
return _cacheData.value(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load settings from the map. Existings settings will be overwritten.
|
||||
*/
|
||||
@@ -132,7 +203,7 @@ namespace Robomongo
|
||||
if (encoding > 3 || encoding < 0)
|
||||
encoding = 0;
|
||||
|
||||
_uuidEncoding = (UUIDEncoding) encoding;
|
||||
_uuidEncoding = (UUIDEncoding)encoding;
|
||||
|
||||
|
||||
// 3. Load view mode
|
||||
@@ -140,39 +211,41 @@ 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;
|
||||
|
||||
_imported = map.contains("imported") ?
|
||||
map.value("imported").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();
|
||||
|
||||
if (map.contains("acceptedEulaVersions"))
|
||||
_acceptedEulaVersions = map.value("acceptedEulaVersions").toStringList().toSet();
|
||||
|
||||
// Load anonymousID
|
||||
_anonymousID = getOrCreateAnonymousID(map);
|
||||
|
||||
// Load AutocompletionMode
|
||||
if (map.contains("autocompletionMode")) {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -180,6 +253,10 @@ namespace Robomongo
|
||||
_batchSize = map.value("batchSize").toInt();
|
||||
if (_batchSize == 0)
|
||||
_batchSize = 50;
|
||||
|
||||
if (map.contains("checkForUpdates"))
|
||||
_checkForUpdates = map.value("checkForUpdates").toBool();
|
||||
|
||||
_currentStyle = map.value("style").toString();
|
||||
if (_currentStyle.isEmpty()) {
|
||||
_currentStyle = AppStyle::StyleName;
|
||||
@@ -200,33 +277,48 @@ namespace Robomongo
|
||||
// 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());
|
||||
addConnection(record);
|
||||
QVariantList const& list = map.value("connections").toList();
|
||||
for (auto const& conn : list) {
|
||||
auto connSettings = new ConnectionSettings(false);
|
||||
connSettings->fromVariant(conn.toMap());
|
||||
addConnection(connSettings);
|
||||
}
|
||||
|
||||
/* Temporarily disabling Recent Connections feature
|
||||
// Load recent connections
|
||||
_recentConnections.clear();
|
||||
QVariantList const& rlist = map.value("recentConnections").toList();
|
||||
for (auto const& rconn : rlist) {
|
||||
_recentConnections.push_back(RecentConnection(rconn.toMap().value("uuid").toString(),
|
||||
rconn.toMap().value("name").toString().toStdString()));
|
||||
}
|
||||
*/
|
||||
|
||||
_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
|
||||
importPreviousConnections();
|
||||
_cacheData = map.value("cacheData").toMap();
|
||||
|
||||
// Load connection settings from previous versions of Robomongo
|
||||
importConnections();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -258,40 +350,84 @@ namespace Robomongo
|
||||
|
||||
// 7. Save disableConnectionShortcuts
|
||||
map.insert("disableConnectionShortcuts", _disableConnectionShortcuts);
|
||||
|
||||
// 8. Save acceptedEulaVersions array
|
||||
QJsonArray arr;
|
||||
for (auto const& str : _acceptedEulaVersions)
|
||||
arr.push_back(str);
|
||||
|
||||
// 8. Save batchSize
|
||||
map.insert("acceptedEulaVersions", arr.toVariantList());
|
||||
|
||||
// 9. Save batchSize
|
||||
map.insert("batchSize", _batchSize);
|
||||
map.insert("checkForUpdates", _checkForUpdates);
|
||||
map.insert("mongoTimeoutSec", _mongoTimeoutSec);
|
||||
map.insert("shellTimeoutSec", _shellTimeoutSec);
|
||||
|
||||
// 9. Save style
|
||||
// 10. Save style
|
||||
map.insert("style", _currentStyle);
|
||||
|
||||
// 10. Save font information
|
||||
// 11. Save font information
|
||||
map.insert("textFontFamily", _textFontFamily);
|
||||
map.insert("textFontPointSize", _textFontPointSize);
|
||||
|
||||
// 11. Save connections
|
||||
// 12. Save connections
|
||||
QVariantList list;
|
||||
|
||||
for (ConnectionSettingsContainerType::const_iterator it = _connections.begin(); it != _connections.end(); ++it) {
|
||||
QVariantMap rm = (*it)->toVariant().toMap();
|
||||
list.append(rm);
|
||||
}
|
||||
for (auto const conn : _connections)
|
||||
list.append(conn->toVariant().toMap());
|
||||
|
||||
map.insert("connections", list);
|
||||
|
||||
/* Temporarily disabling Recent Connections feature
|
||||
// 13. Save recent connections
|
||||
QVariantList recentConnsList;
|
||||
QVariantMap recentConnMap;
|
||||
for (auto const& rconn : _recentConnections) {
|
||||
recentConnMap.insert("uuid", rconn.uuid);
|
||||
recentConnMap.insert("name", QString::fromStdString(rconn.name));
|
||||
recentConnsList.append(recentConnMap);
|
||||
}
|
||||
map.insert("recentConnections", recentConnsList);
|
||||
*/
|
||||
|
||||
map.insert("autoExec", _autoExec);
|
||||
|
||||
map.insert("minimizeToTray", _minimizeToTray);
|
||||
map.insert("toolbars", _toolbars);
|
||||
|
||||
map.insert("imported", _imported);
|
||||
map.insert("anonymousID", _anonymousID);
|
||||
map.insert("cacheData", _cacheData);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
QString SettingsManager::getOrCreateAnonymousID(QVariantMap const& map) const
|
||||
{
|
||||
QString anonymousID = "";
|
||||
|
||||
// If anonymousID has never been created or is empty, create a new one. Otherwise load the existing.
|
||||
if (map.contains("anonymousID")) {
|
||||
QUuid id = map.value("anonymousID").toString();
|
||||
if (!id.isNull())
|
||||
anonymousID = id.toString();
|
||||
}
|
||||
|
||||
for (auto const& zipFileAndConfigFile : S_3T_ZipFile_And_ConfigFile_List) {
|
||||
if (!anonymousID.isEmpty())
|
||||
break;
|
||||
|
||||
QUuid const& id = extractAnonymousID(zipFileAndConfigFile.first, zipFileAndConfigFile.second);
|
||||
if (!id.isNull())
|
||||
anonymousID = id.toString();
|
||||
}
|
||||
|
||||
if (anonymousID.isEmpty())
|
||||
anonymousID = QUuid::createUuid().toString();
|
||||
|
||||
return anonymousID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds connection to the end of list
|
||||
* Adds connection to the end of list and set it's uniqueID
|
||||
*/
|
||||
void SettingsManager::addConnection(ConnectionSettings *connection)
|
||||
{
|
||||
@@ -310,6 +446,50 @@ namespace Robomongo
|
||||
}
|
||||
}
|
||||
|
||||
/* Temporarily disabling Recent Connections feature
|
||||
void SettingsManager::addRecentConnection(ConnectionSettings *connection)
|
||||
{
|
||||
_recentConnections.push_back(RecentConnection(connection->uuid(), connection->connectionName()));
|
||||
}
|
||||
|
||||
void SettingsManager::deleteRecentConnection(ConnectionSettings *conn)
|
||||
{
|
||||
for (int i = 0; i < _recentConnections.size(); ++i) {
|
||||
if (_recentConnections[i].uuid == conn->uuid())
|
||||
_recentConnections.erase(_recentConnections.begin() + i);
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsManager::setRecentConnections(std::vector<ConnectionSettings const*> const& recentConns)
|
||||
{
|
||||
_recentConnections.clear();
|
||||
|
||||
for(auto rconn : recentConns)
|
||||
_recentConnections.push_back(RecentConnection(rconn->uuid(), rconn->connectionName()));
|
||||
}
|
||||
|
||||
void SettingsManager::clearRecentConnections()
|
||||
{
|
||||
_recentConnections.clear();
|
||||
}
|
||||
*/
|
||||
|
||||
ConnectionSettings* SettingsManager::getConnectionSettingsByUuid(QString const& uuid) const
|
||||
{
|
||||
for (auto const connSettings : _connections){
|
||||
if (connSettings->uuid() == uuid)
|
||||
return connSettings;
|
||||
}
|
||||
|
||||
LOG_MSG("Failed to find connection settings object by UUID.", mongo::logger::LogSeverity::Warning());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ConnectionSettings* SettingsManager::getConnectionSettingsByUuid(std::string const& uuid) const
|
||||
{
|
||||
return getConnectionSettingsByUuid(QString::fromStdString(uuid));
|
||||
}
|
||||
|
||||
void SettingsManager::setCurrentStyle(const QString& style)
|
||||
{
|
||||
_currentStyle = style;
|
||||
@@ -334,32 +514,45 @@ namespace Robomongo
|
||||
_toolbars[toolbarName] = visible;
|
||||
}
|
||||
|
||||
void SettingsManager::importPreviousConnections() {
|
||||
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)
|
||||
const QString oldConfigPath = QString("%1/.config/robomongo/robomongo.json").arg(QDir::homePath());
|
||||
|
||||
if (!QFile::exists(oldConfigPath))
|
||||
return;
|
||||
if (!QFile::exists(CONFIG_FILE_0_8_5))
|
||||
return false;
|
||||
|
||||
QFile f(oldConfigPath);
|
||||
if (!f.open(QIODevice::ReadOnly))
|
||||
return;
|
||||
QFile oldConfigFile(CONFIG_FILE_0_8_5);
|
||||
if (!oldConfigFile.open(QIODevice::ReadOnly))
|
||||
return false;
|
||||
|
||||
bool ok;
|
||||
QJson::Parser parser;
|
||||
QVariantMap vmap = parser.parse(f.readAll(), &ok).toMap();
|
||||
QVariantMap vmap = parser.parse(oldConfigFile.readAll(), &ok).toMap();
|
||||
if (!ok)
|
||||
return;
|
||||
return false;
|
||||
|
||||
QVariantList vconns = vmap.value("connections").toList();
|
||||
for (QVariantList::iterator itconn = vconns.begin(); itconn != vconns.end(); ++itconn) {
|
||||
for (QVariantList::iterator itconn = vconns.begin(); itconn != vconns.end(); ++itconn)
|
||||
{
|
||||
QVariantMap vconn = (*itconn).toMap();
|
||||
|
||||
ConnectionSettings *conn = new ConnectionSettings();
|
||||
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)));
|
||||
@@ -394,7 +587,7 @@ namespace Robomongo
|
||||
for (QVariantList::const_iterator itcred = vcreds.begin(); itcred != vcreds.end(); ++itcred) {
|
||||
QVariantMap vcred = (*itcred).toMap();
|
||||
|
||||
CredentialSettings *cred = new CredentialSettings();
|
||||
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()));
|
||||
@@ -441,8 +634,46 @@ namespace Robomongo
|
||||
addConnection(conn);
|
||||
}
|
||||
|
||||
// Mark as imported
|
||||
setImported(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SettingsManager::importConnectionsFrom_0_9_to_1_0_RC1()
|
||||
{
|
||||
return importConnectionsFromOldVersion(CONFIG_FILE_0_9);
|
||||
}
|
||||
|
||||
bool SettingsManager::importConnectionsFrom_1_0_RC1_to_1_0_0()
|
||||
{
|
||||
return importConnectionsFromOldVersion(CONFIG_FILE_1_0_RC1);
|
||||
}
|
||||
|
||||
bool SettingsManager::importConnectionsFromOldVersion(QString const& oldConfigFilePath)
|
||||
{
|
||||
if (!QFile::exists(oldConfigFilePath))
|
||||
return false;
|
||||
|
||||
QFile oldConfigFile(oldConfigFilePath);
|
||||
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() {
|
||||
@@ -455,4 +686,26 @@ namespace Robomongo
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
QString extractAnonymousID(QString const& zipFile, QString const& propfile)
|
||||
{
|
||||
QZipReader zipReader(zipFile);
|
||||
if (!zipReader.exists() || !zipReader.isReadable())
|
||||
return QString("");
|
||||
|
||||
QXmlStreamReader reader(zipReader.fileData(propfile));
|
||||
while (!reader.atEnd()) {
|
||||
reader.readNext();
|
||||
if (reader.text().toString() == "AnonymousID") {
|
||||
reader.readNext();
|
||||
reader.readNext();
|
||||
reader.readNext();
|
||||
reader.readNext();
|
||||
return reader.text().toString();
|
||||
}
|
||||
}
|
||||
|
||||
return QString("");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,12 +2,46 @@
|
||||
|
||||
#include <QString>
|
||||
#include <QVariantMap>
|
||||
#include <QSet>
|
||||
#include <QDir>
|
||||
#include <vector>
|
||||
#include "robomongo/core/Enums.h"
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
class ConnectionSettings;
|
||||
struct ConfigFileAndImportFunction;
|
||||
|
||||
// Current cache directory
|
||||
QString const CacheDir = QString("%1/.3T/robomongo/%2/cache/").arg(QDir::homePath())
|
||||
.arg(PROJECT_VERSION);
|
||||
// Current config file
|
||||
QString const ConfigFilePath = QString("%1/.3T/robomongo/%2/robomongo.json").arg(QDir::homePath())
|
||||
.arg(PROJECT_VERSION);
|
||||
// Current config file directory
|
||||
QString const ConfigDir = QString("%1/.3T/robomongo/%2/").arg(QDir::homePath())
|
||||
.arg(PROJECT_VERSION);
|
||||
/* Temporarily disabling Recent Connections feature
|
||||
struct RecentConnection
|
||||
{
|
||||
RecentConnection(QString const& uuid, std::string const& name)
|
||||
: uuid(uuid), name(name) {}
|
||||
|
||||
// Not directly used but required by std::vector<T>::erase()
|
||||
RecentConnection& operator=(const RecentConnection& other)
|
||||
{
|
||||
uuid = other.uuid;
|
||||
name = other.name;
|
||||
return *this;
|
||||
}
|
||||
|
||||
QString uuid;
|
||||
std::string name;
|
||||
};
|
||||
*/
|
||||
|
||||
/* ----------------------------- SettingsManager ------------------------------ */
|
||||
|
||||
/**
|
||||
* @brief SettingsManager gives you access to all settings, that is used
|
||||
* by Robomongo. It can load() and save() them. Config file usually
|
||||
@@ -23,6 +57,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 +85,32 @@ 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 Functions of Recent Connections feature
|
||||
*/
|
||||
/* Temporarily disabling Recent Connections feature
|
||||
static void addRecentConnection(ConnectionSettings *connection);
|
||||
static void deleteRecentConnection(ConnectionSettings *connection);
|
||||
static void setRecentConnections(std::vector<ConnectionSettings const*> const& recentConns);
|
||||
static void clearRecentConnections();
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Finds and returns original (non-clone) connection settings which is
|
||||
* loaded/saved from/into Robomongo config. file.
|
||||
* @return If uniqueID is valid returns original connection settings,
|
||||
* nullptr otherwise.
|
||||
*/
|
||||
ConnectionSettings* getConnectionSettingsByUuid(QString const& uuid) const;
|
||||
ConnectionSettings* getConnectionSettingsByUuid(std::string const& uuid) const;
|
||||
|
||||
void reorderConnections(const ConnectionSettingsContainerType &connections);
|
||||
|
||||
void setToolbarSettings(QString toolbarName, bool visible);
|
||||
@@ -65,6 +119,9 @@ namespace Robomongo
|
||||
* @brief Returns list of connections
|
||||
*/
|
||||
ConnectionSettingsContainerType connections() const { return _connections; }
|
||||
|
||||
// Temporarily disabling Recent Connections feature
|
||||
// std::vector<RecentConnection>& recentConnections() const { return _recentConnections; }
|
||||
|
||||
ToolbarSettingsContainerType toolbars() const { return _toolbars; }
|
||||
|
||||
@@ -86,6 +143,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; }
|
||||
|
||||
@@ -95,6 +155,12 @@ namespace Robomongo
|
||||
void setDisableConnectionShortcuts(bool isDisable) { _disableConnectionShortcuts = isDisable; }
|
||||
bool disableConnectionShortcuts() const { return _disableConnectionShortcuts; }
|
||||
|
||||
void addAcceptedEulaVersion(QString const& version) { _acceptedEulaVersions.insert(version); }
|
||||
QSet<QString> const& acceptedEulaVersions() const { return _acceptedEulaVersions; }
|
||||
|
||||
void setCheckForUpdates(bool checkForUpdates) { _checkForUpdates = checkForUpdates; }
|
||||
bool checkForUpdates() const { return _checkForUpdates; }
|
||||
|
||||
void setBatchSize(int batchSize) { _batchSize = batchSize; }
|
||||
int batchSize() const { return _batchSize; }
|
||||
|
||||
@@ -110,15 +176,20 @@ namespace Robomongo
|
||||
int mongoTimeoutSec() const { return _mongoTimeoutSec; }
|
||||
int shellTimeoutSec() const { return _shellTimeoutSec; }
|
||||
|
||||
// True when settings from previous versions of
|
||||
// Robomongo are imported
|
||||
// True when settings from previous versions of Robomongo are imported
|
||||
void setImported(bool imported) { _imported = imported; }
|
||||
bool imported() const { return _imported; }
|
||||
|
||||
QString anonymousID() const { return _anonymousID; }
|
||||
|
||||
void addCacheData(QString const& key, QVariant const& value);
|
||||
QVariant cacheData(QString const& key) const;
|
||||
|
||||
/**
|
||||
* Returns number of imported connections
|
||||
*/
|
||||
int importedConnectionsCount();
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
@@ -131,10 +202,21 @@ namespace Robomongo
|
||||
*/
|
||||
QVariantMap convertToMap() const;
|
||||
|
||||
// Find existing anonymousID from Robomongo and 3T config files, if not found create
|
||||
// a new anonymousID.
|
||||
QString getOrCreateAnonymousID(QVariantMap const& map) const;
|
||||
|
||||
/**
|
||||
* Load connection settings from previous versions of Robomongo
|
||||
*/
|
||||
void importPreviousConnections();
|
||||
void importConnections();
|
||||
|
||||
// Imports connections from oldConfigFilePath into current config file
|
||||
static bool importConnectionsFromOldVersion(QString const& oldConfigFilePath);
|
||||
|
||||
static bool importConnectionsFrom_0_8_5_to_0_9();
|
||||
static bool importConnectionsFrom_0_9_to_1_0_RC1();
|
||||
static bool importConnectionsFrom_1_0_RC1_to_1_0_0();
|
||||
|
||||
/**
|
||||
* @brief Version of settings schema currently loaded
|
||||
@@ -148,9 +230,12 @@ namespace Robomongo
|
||||
bool _loadMongoRcJs;
|
||||
bool _autoExpand;
|
||||
bool _autoExec;
|
||||
bool _minimizeToTray;
|
||||
bool _lineNumbers;
|
||||
bool _disableConnectionShortcuts;
|
||||
QSet<QString> _acceptedEulaVersions;
|
||||
int _batchSize;
|
||||
bool _checkForUpdates = true;
|
||||
QString _currentStyle;
|
||||
QString _textFontFamily;
|
||||
int _textFontPointSize;
|
||||
@@ -158,14 +243,33 @@ namespace Robomongo
|
||||
int _mongoTimeoutSec;
|
||||
int _shellTimeoutSec;
|
||||
|
||||
// True when settings from previous versions of
|
||||
// Robomongo are imported
|
||||
// True when settings from previous versions of Robomongo are imported
|
||||
bool _imported;
|
||||
|
||||
/**
|
||||
* @brief This is an anonymous string taken from QUuid that is generated when Robomongo
|
||||
* is first installed on a user's machine and then launched for the first time
|
||||
* It stays the same throughout all upgrades.
|
||||
*/
|
||||
QString _anonymousID;
|
||||
|
||||
// Various cache data
|
||||
QMap<QString, QVariant> _cacheData;
|
||||
|
||||
/**
|
||||
* @brief List of connections
|
||||
*/
|
||||
ConnectionSettingsContainerType _connections;
|
||||
static std::vector<ConnectionSettings*> _connections;
|
||||
|
||||
/* Temporarily disabling Recent Connections feature
|
||||
// List of recent connections that are shown on Welcome Tab
|
||||
static std::vector<RecentConnection> _recentConnections;
|
||||
*/
|
||||
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -80,6 +80,7 @@ namespace Robomongo
|
||||
s << (isArray ? "[" : "{");
|
||||
BSONObjIterator i(obj);
|
||||
BSONElement e = i.next();
|
||||
|
||||
if ( !e.eoo() ) {
|
||||
while ( 1 ) {
|
||||
if ( pretty ) {
|
||||
@@ -91,6 +92,7 @@ namespace Robomongo
|
||||
else {
|
||||
s << " ";
|
||||
}
|
||||
|
||||
s << jsonString(e, format, true, pretty ? pretty + 1 : 0, uuidEncoding, timeFormat, isArray);
|
||||
e = i.next();
|
||||
|
||||
@@ -109,7 +111,8 @@ namespace Robomongo
|
||||
return s.str();
|
||||
}
|
||||
|
||||
std::string jsonString(const BSONElement &elem, JsonStringFormat format, bool includeFieldNames, int pretty, UUIDEncoding uuidEncoding, SupportedTimes timeFormat, bool isArray)
|
||||
std::string jsonString(const BSONElement &elem, JsonStringFormat format, bool includeFieldNames,
|
||||
int pretty, UUIDEncoding uuidEncoding, SupportedTimes timeFormat, bool isArray)
|
||||
{
|
||||
using namespace std;
|
||||
BSONType t = elem.type();
|
||||
@@ -331,12 +334,11 @@ namespace Robomongo
|
||||
break;
|
||||
|
||||
case bsonTimestamp:
|
||||
if ( format == TenGen ) {
|
||||
s << "Timestamp(" << ( elem.timestampValue() / 1000 ) << ", " << elem.timestampInc() << ")";
|
||||
}
|
||||
else {
|
||||
s << "{ \"$timestamp\" : { \"t\" : " << ( elem.timestampValue() / 1000 ) << ", \"i\" : " << elem.timestampInc() << " } }";
|
||||
}
|
||||
if ( format == TenGen )
|
||||
s << "Timestamp(" << elem.timestamp().getSecs() << ", " << elem.timestampInc() << ")";
|
||||
else
|
||||
s << "{ \"$timestamp\" : { \"t\" : " << elem.timestamp().getSecs() << ", \"i\" : "
|
||||
<< elem.timestampInc() << " } }";
|
||||
break;
|
||||
|
||||
case MinKey:
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,12 +44,30 @@ namespace Robomongo
|
||||
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 = QIcon(":/robomongo/icons/replica_set_16x16.png");
|
||||
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);
|
||||
@@ -96,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
|
||||
@@ -281,6 +311,18 @@ namespace Robomongo
|
||||
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));
|
||||
@@ -305,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
|
||||
{
|
||||
@@ -336,13 +384,36 @@ namespace Robomongo
|
||||
return importIc;
|
||||
}
|
||||
|
||||
const QIcon &GuiRegistry::deleteIcon() const
|
||||
{
|
||||
static const QIcon icon(":/robomongo/icons/delete.png");
|
||||
return icon;
|
||||
}
|
||||
|
||||
const QIcon &GuiRegistry::deleteIconRed() const
|
||||
{
|
||||
static const QIcon icon(":/robomongo/icons/delete_red_color.png");
|
||||
return icon;
|
||||
}
|
||||
|
||||
const QIcon &GuiRegistry::deleteIconMouseHovered() const
|
||||
{
|
||||
static const QIcon icon(":/robomongo/icons/delete_mouse_hovered.png");
|
||||
return icon;
|
||||
}
|
||||
|
||||
const QIcon &GuiRegistry::mainWindowIcon() const
|
||||
{
|
||||
static const QIcon mainWindowIc = QIcon(":/robomongo/icons/logo-256x256.png");
|
||||
// static const QIcon mainWindowIc = QIcon(":/robomongo/icons/main_window_icon.png");
|
||||
return mainWindowIc;
|
||||
}
|
||||
|
||||
const QIcon& GuiRegistry::welcomeTabIcon() const
|
||||
{
|
||||
static const QIcon icon(":/robomongo/icons/welcome_tab_icon.png");
|
||||
return icon;
|
||||
}
|
||||
|
||||
const QFont &GuiRegistry::font() const
|
||||
{
|
||||
QString family = AppRegistry::instance().settingsManager()->textFontFamily();
|
||||
|
||||
@@ -30,7 +30,10 @@ namespace Robomongo
|
||||
*/
|
||||
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;
|
||||
@@ -39,7 +42,9 @@ namespace Robomongo
|
||||
const QIcon& userIcon() const;
|
||||
const QIcon& functionIcon() const;
|
||||
const QIcon& maximizeIcon() const;
|
||||
const QIcon& maximizeHighlightedIcon() 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;
|
||||
@@ -55,11 +60,15 @@ namespace Robomongo
|
||||
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& deleteIcon() const;
|
||||
const QIcon& deleteIconRed() const;
|
||||
const QIcon& deleteIconMouseHovered() const;
|
||||
const QIcon& mainWindowIcon() const;
|
||||
const QIcon& bsonObjectIcon() const;
|
||||
const QIcon& bsonArrayIcon() const;
|
||||
@@ -78,6 +87,9 @@ namespace Robomongo
|
||||
const QIcon& keyIcon() const;
|
||||
const QIcon& showIcon() const;
|
||||
const QIcon& hideIcon() const;
|
||||
const QIcon& plusIcon() const;
|
||||
const QIcon& minusIcon() const;
|
||||
const QIcon& welcomeTabIcon() const;
|
||||
|
||||
const QBrush& typeBrush() const;
|
||||
|
||||
|
||||
@@ -17,6 +17,12 @@
|
||||
#include <QStatusBar>
|
||||
#include <QHBoxLayout>
|
||||
#include <QSettings>
|
||||
#include <QSystemTrayIcon>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkReply>
|
||||
#include <QUrl>
|
||||
#include <QTextDocument>
|
||||
|
||||
#include <mongo/logger/log_severity.h>
|
||||
#include "robomongo/core/settings/SettingsManager.h"
|
||||
@@ -34,6 +40,8 @@
|
||||
#include "robomongo/gui/widgets/explorer/ExplorerTreeWidget.h"
|
||||
#include "robomongo/gui/widgets/workarea/WorkAreaTabWidget.h"
|
||||
#include "robomongo/gui/widgets/workarea/QueryWidget.h"
|
||||
#include "robomongo/gui/widgets/workarea/QueryWidget.h"
|
||||
#include "robomongo/gui/widgets/workarea/WelcomeTab.h"
|
||||
#include "robomongo/gui/dialogs/ConnectionsDialog.h"
|
||||
#include "robomongo/gui/dialogs/AboutDialog.h"
|
||||
#include "robomongo/gui/dialogs/PreferencesDialog.h"
|
||||
@@ -41,6 +49,7 @@
|
||||
#include "robomongo/gui/GuiRegistry.h"
|
||||
#include "robomongo/gui/AppStyle.h"
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
void setToolBarIconSize(QToolBar *toolBar)
|
||||
@@ -71,21 +80,32 @@ 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();
|
||||
@@ -96,13 +116,18 @@ namespace Robomongo
|
||||
}
|
||||
};
|
||||
|
||||
/* -------------------------------- MainWindow --------------------------------- */
|
||||
MainWindow::MainWindow()
|
||||
: BaseClass(),
|
||||
_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)
|
||||
//_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";
|
||||
@@ -140,7 +165,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);
|
||||
@@ -171,6 +196,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);
|
||||
@@ -182,7 +227,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>");
|
||||
@@ -387,6 +432,20 @@ namespace Robomongo
|
||||
VERIFY(connect(autoExec, SIGNAL(triggered()), this, SLOT(toggleAutoExec())));
|
||||
optionsMenu->addAction(autoExec);
|
||||
|
||||
#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
|
||||
|
||||
auto *checkUpdates = new QAction(tr("Check for updates"), this);
|
||||
checkUpdates->setCheckable(true);
|
||||
checkUpdates->setChecked(AppRegistry::instance().settingsManager()->checkForUpdates());
|
||||
VERIFY(connect(checkUpdates, SIGNAL(triggered()), this, SLOT(toggleCheckUpdates())));
|
||||
optionsMenu->addAction(checkUpdates);
|
||||
|
||||
QAction *preferencesAction = new QAction("Preferences", this);
|
||||
VERIFY(connect(preferencesAction, SIGNAL(triggered()), this, SLOT(openPreferences())));
|
||||
preferencesAction->setVisible(false);
|
||||
@@ -439,6 +498,12 @@ namespace Robomongo
|
||||
duplicateAction->setVisible(true);
|
||||
VERIFY(connect(duplicateAction, SIGNAL(triggered()), SLOT(duplicateTab())));
|
||||
|
||||
// Open welcome tab action
|
||||
auto openWelcomeTabAction = new QAction("Open/Refresh Welcome Tab", this);
|
||||
openWelcomeTabAction->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_W);
|
||||
openWelcomeTabAction->setVisible(true);
|
||||
VERIFY(connect(openWelcomeTabAction, SIGNAL(triggered()), SLOT(openWelcomeTab())));
|
||||
|
||||
// Window menu
|
||||
QMenu *windowMenu = menuBar()->addMenu("Window");
|
||||
//minimize
|
||||
@@ -450,8 +515,10 @@ namespace Robomongo
|
||||
windowMenu->addSeparator();
|
||||
windowMenu->addAction(reloadAction);
|
||||
windowMenu->addAction(duplicateAction);
|
||||
windowMenu->addSeparator();
|
||||
windowMenu->addAction(openWelcomeTabAction);
|
||||
|
||||
SettingsManager::ToolbarSettingsContainerType toolbarsSettings = AppRegistry::instance().settingsManager()->toolbars();
|
||||
auto toolbarsSettings = AppRegistry::instance().settingsManager()->toolbars();
|
||||
|
||||
/*** About menu ***/
|
||||
|
||||
@@ -495,9 +562,41 @@ namespace Robomongo
|
||||
setToolBarIconSize(_execToolBar);
|
||||
addToolBar(_execToolBar);
|
||||
|
||||
_updateLabel = new QLabel;
|
||||
_updateLabel->setWordWrap(true);
|
||||
_updateLabel->setOpenExternalLinks(true);
|
||||
_updateLabel->setTextFormat(Qt::TextFormat::RichText);
|
||||
_updateLabel->setIndent(_updateLabel->fontMetrics().width("T"));
|
||||
|
||||
_closeButton = new QPushButton;
|
||||
_closeButton->setIcon(QIcon(":/robomongo/icons/close_hover_16x16.png"));
|
||||
_closeButton->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Minimum);
|
||||
_closeButton->setMouseTracking(true);
|
||||
_closeButton->setAttribute(Qt::WA_Hover);
|
||||
_closeButton->installEventFilter(this);
|
||||
VERIFY(connect(_closeButton, SIGNAL(clicked()), this, SLOT(on_closeButton_clicked())));
|
||||
|
||||
auto updateBarLay = new QHBoxLayout;
|
||||
updateBarLay->addWidget(_updateLabel);
|
||||
updateBarLay->addWidget(_closeButton, Qt::AlignRight);
|
||||
updateBarLay->setSpacing(0);
|
||||
updateBarLay->setMargin(0);
|
||||
|
||||
auto updateBarWid = new QWidget;
|
||||
updateBarWid->setLayout(updateBarLay);
|
||||
|
||||
_updateBar = new QToolBar("Updates Toolbar");
|
||||
_updateBar->addWidget(updateBarWid);
|
||||
_updateBar->setStyleSheet("background-color: #b3e0ff; border: none;"); // blue
|
||||
addToolBarBreak();
|
||||
addToolBar(_updateBar);
|
||||
_updateBar->setHidden(true);
|
||||
_updateBar->setMovable(false);
|
||||
|
||||
_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);
|
||||
@@ -517,14 +616,16 @@ namespace Robomongo
|
||||
_importAction->setDisabled(true);
|
||||
addToolBar(expImpToolBar);
|
||||
expImpToolBar->addAction(_importAction);
|
||||
*/
|
||||
|
||||
createTabs();
|
||||
createStatusBar();
|
||||
setWindowTitle(PROJECT_NAME_TITLE " " PROJECT_VERSION);
|
||||
setWindowTitle("Robomongo 1.0.3");
|
||||
setWindowIcon(GuiRegistry::instance().mainWindowIcon());
|
||||
|
||||
QTimer::singleShot(0, this, SLOT(manageConnections()));
|
||||
QTimer::singleShot(0, this, SLOT(manageConnections()));
|
||||
updateMenus();
|
||||
_updateMenusAtStart = false;
|
||||
|
||||
AppRegistry::instance().bus()->subscribe(this, ConnectionFailedEvent::Type);
|
||||
AppRegistry::instance().bus()->subscribe(this, ScriptExecutedEvent::Type);
|
||||
@@ -532,7 +633,21 @@ namespace Robomongo
|
||||
AppRegistry::instance().bus()->subscribe(this, QueryWidgetUpdatedEvent::Type);
|
||||
AppRegistry::instance().bus()->subscribe(this, OperationFailedEvent::Type);
|
||||
|
||||
restoreWindowsSettings();
|
||||
restoreWindowSettings();
|
||||
|
||||
// Catch application windows focus changes
|
||||
VERIFY(connect(qApp, SIGNAL(focusChanged(QWidget*, QWidget*)), this, SLOT(on_focusChanged())));
|
||||
|
||||
_networkAccessManager = new QNetworkAccessManager;
|
||||
VERIFY(connect(_networkAccessManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(on_networkReply(QNetworkReply*))));
|
||||
|
||||
// First check for updates 30 secs after program start
|
||||
QTimer::singleShot(30000, this, SLOT(checkUpdates())); // 30000 for 30secs
|
||||
|
||||
// Check for updates every 1 hour
|
||||
auto timer = new QTimer(this);
|
||||
VERIFY(connect(timer, SIGNAL(timeout()), this, SLOT(checkUpdates())));
|
||||
timer->start(3600000); // 1 hour = (3600000 msec = 60 * 60 * 1000 msec)
|
||||
}
|
||||
|
||||
void MainWindow::createStylesMenu()
|
||||
@@ -594,9 +709,15 @@ namespace Robomongo
|
||||
AppRegistry::instance().settingsManager()->save();
|
||||
}
|
||||
|
||||
void MainWindow::restoreWindowsSettings()
|
||||
void MainWindow::exit()
|
||||
{
|
||||
QSettings settings("Paralect", "Robomongo");
|
||||
_allowExit = true;
|
||||
close();
|
||||
}
|
||||
|
||||
void MainWindow::restoreWindowSettings()
|
||||
{
|
||||
QSettings settings("3T", "Robomongo");
|
||||
// Restore settings if registery key exists, otherwise resize as app started for the first time.
|
||||
if (settings.contains("MainWindow/geometry"))
|
||||
{
|
||||
@@ -606,8 +727,8 @@ namespace Robomongo
|
||||
{
|
||||
// Resize main window. We are trying to keep it "almost" maximized.
|
||||
QRect screenGeometry = QApplication::desktop()->availableGeometry();
|
||||
int horizontalMargin = (int)(screenGeometry.width() * 0.1); // todo: use C++ static cast instead of C-style cast
|
||||
int verticalMargin = (int)(screenGeometry.height() * 0.1);
|
||||
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));
|
||||
@@ -619,24 +740,47 @@ namespace Robomongo
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::saveWindowsSettings() const
|
||||
void MainWindow::saveWindowSettings() const
|
||||
{
|
||||
QSettings settings("Paralect", "Robomongo");
|
||||
QSettings settings("3T", "Robomongo");
|
||||
settings.setValue("MainWindow/geometry", saveGeometry());
|
||||
}
|
||||
|
||||
void MainWindow::adjustUpdatesBarHeight()
|
||||
{
|
||||
if (!AppRegistry::instance().settingsManager()->checkForUpdates() || !_updateBar->isVisible())
|
||||
return;
|
||||
|
||||
QTextDocument doc;
|
||||
doc.setHtml(_updateLabel->text());
|
||||
int const strWidth = _updateLabel->fontMetrics().width(doc.toPlainText());
|
||||
int const lineHeight = _updateLabel->fontMetrics().height();
|
||||
int const widthForUpdateStr = width() - _closeButton->width();
|
||||
|
||||
if (0 == widthForUpdateStr)
|
||||
return;
|
||||
|
||||
#ifdef __APPLE__
|
||||
_updateLabel->setFixedHeight((strWidth / widthForUpdateStr + 1) * lineHeight * 1.3);
|
||||
#else
|
||||
_updateLabel->setFixedHeight((strWidth / widthForUpdateStr + 1) * lineHeight);
|
||||
#endif
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -658,16 +802,6 @@ namespace Robomongo
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
if (event->key() == Qt::Key_F12) {
|
||||
_connectButton->showMenu();
|
||||
return;
|
||||
}
|
||||
|
||||
BaseClass::keyPressEvent(event);
|
||||
}
|
||||
|
||||
void MainWindow::updateConnectionsMenu()
|
||||
{
|
||||
_connectionsMenu->clear();
|
||||
@@ -699,8 +833,17 @@ namespace Robomongo
|
||||
_connectionsMenu->addAction(connectAction);
|
||||
}
|
||||
|
||||
WelcomeTab* MainWindow::getWelcomeTab()
|
||||
{
|
||||
return _workArea->getWelcomeTab();
|
||||
}
|
||||
|
||||
void MainWindow::manageConnections()
|
||||
{
|
||||
#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();
|
||||
@@ -732,6 +875,10 @@ namespace Robomongo
|
||||
}
|
||||
}
|
||||
|
||||
#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();
|
||||
}
|
||||
@@ -797,6 +944,13 @@ namespace Robomongo
|
||||
saveAutoExec(send->isChecked());
|
||||
}
|
||||
|
||||
void MainWindow::toggleCheckUpdates()
|
||||
{
|
||||
auto action = qobject_cast<QAction*>(sender());
|
||||
AppRegistry::instance().settingsManager()->setCheckForUpdates(action->isChecked());
|
||||
AppRegistry::instance().settingsManager()->save();
|
||||
}
|
||||
|
||||
void MainWindow::toggleLineNumbers()
|
||||
{
|
||||
QAction *send = qobject_cast<QAction*>(sender());
|
||||
@@ -866,6 +1020,13 @@ namespace Robomongo
|
||||
dlg.exec();
|
||||
}
|
||||
|
||||
|
||||
void MainWindow::openWelcomeTab()
|
||||
{
|
||||
_workArea->openWelcomeTab();
|
||||
}
|
||||
|
||||
/* --- Temporarily disabling export/import feature
|
||||
void MainWindow::openExportDialog()
|
||||
{
|
||||
auto selectedItem = dynamic_cast<ExplorerCollectionTreeItem*>(_explorer->getSelectedTreeItem());
|
||||
@@ -875,6 +1036,7 @@ namespace Robomongo
|
||||
auto dialog = new ExportDialog(dbName, collName, this);
|
||||
dialog->show(); // show it mode-less so that user can perform multiple simultaneous exports
|
||||
}
|
||||
*/
|
||||
|
||||
void MainWindow::setDefaultUuidEncoding()
|
||||
{
|
||||
@@ -970,7 +1132,6 @@ namespace Robomongo
|
||||
if (event->connectionType != ConnectionPrimary)
|
||||
return;
|
||||
|
||||
|
||||
// Very temporary solution to prevent multiple connection error messages
|
||||
// from both SshTunnelWorker and MongoWorker
|
||||
static int lastServerHandle = -1;
|
||||
@@ -978,7 +1139,7 @@ namespace Robomongo
|
||||
return;
|
||||
|
||||
lastServerHandle = event->serverHandle;
|
||||
QMessageBox::information(this, "Error", QtUtils::toQString(event->message));
|
||||
QMessageBox::critical(this, "Error", QtUtils::toQString(event->message));
|
||||
}
|
||||
|
||||
void MainWindow::handle(ScriptExecutingEvent *)
|
||||
@@ -999,13 +1160,78 @@ namespace Robomongo
|
||||
ss << event->userFriendlyErrorMessage << std::endl << std::endl
|
||||
<< "Error:" << std::endl << event->technicalErrorMessage;
|
||||
|
||||
QMessageBox::information(NULL, "Operation failed", QtUtils::toQString(ss.str()));
|
||||
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)
|
||||
{
|
||||
saveWindowsSettings();
|
||||
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
|
||||
}
|
||||
|
||||
bool MainWindow::eventFilter(QObject *target, QEvent *event)
|
||||
{
|
||||
auto closeUpdatesBarButton = qobject_cast<QPushButton*>(target);
|
||||
if (!closeUpdatesBarButton)
|
||||
return false;
|
||||
|
||||
if (event->type() == QEvent::HoverEnter) {
|
||||
closeUpdatesBarButton->setIcon(QIcon(":/robomongo/icons/close_hover_16x16_original.png"));
|
||||
return true;
|
||||
}
|
||||
else if (event->type() == QEvent::HoverLeave) {
|
||||
closeUpdatesBarButton->setIcon(QIcon(":/robomongo/icons/close_hover_16x16.png"));
|
||||
return true;
|
||||
}
|
||||
|
||||
return QWidget::eventFilter(target, event);
|
||||
}
|
||||
|
||||
void MainWindow::resizeEvent(QResizeEvent* event)
|
||||
{
|
||||
QMainWindow::resizeEvent(event);
|
||||
adjustUpdatesBarHeight();
|
||||
}
|
||||
|
||||
void MainWindow::handle(QueryWidgetUpdatedEvent *event)
|
||||
@@ -1049,7 +1275,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();
|
||||
@@ -1065,8 +1291,20 @@ namespace Robomongo
|
||||
|
||||
void MainWindow::updateMenus()
|
||||
{
|
||||
int contTab = _workArea->count();
|
||||
bool isEnable = _workArea && contTab > 0;
|
||||
if (!_workArea)
|
||||
return;
|
||||
|
||||
bool isEnable = false;
|
||||
if (_updateMenusAtStart)
|
||||
isEnable = false;
|
||||
else {
|
||||
if (getWelcomeTab() && getWelcomeTab()->isVisible())
|
||||
isEnable = false;
|
||||
else if (_workArea->count() == 0)
|
||||
isEnable = false;
|
||||
else
|
||||
isEnable = true;
|
||||
}
|
||||
|
||||
_execToolBar->setEnabled(isEnable);
|
||||
_openAction->setEnabled(isEnable);
|
||||
@@ -1079,6 +1317,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);
|
||||
@@ -1113,6 +1352,7 @@ namespace Robomongo
|
||||
AppRegistry::instance().settingsManager()->save();
|
||||
}
|
||||
|
||||
/* --- Temporarily disabling export/import feature
|
||||
void MainWindow::onExplorerItemSelected(QTreeWidgetItem *selectedItem)
|
||||
{
|
||||
auto collectionItem = dynamic_cast<ExplorerCollectionTreeItem*>(selectedItem);
|
||||
@@ -1123,4 +1363,92 @@ namespace Robomongo
|
||||
_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());
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::on_networkReply(QNetworkReply* reply)
|
||||
{
|
||||
QString str(QUrl::fromPercentEncoding(reply->readAll()));
|
||||
|
||||
if ("NO-UPDATES-ANNOUNCED.\n" == str || reply->error() != QNetworkReply::NoError || str.isEmpty()) {
|
||||
_updateLabel->setText("");
|
||||
_updateBar->setVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
str.replace('+', ' ');
|
||||
str.remove("Update,");
|
||||
|
||||
_updateLabel->setText(str);
|
||||
_updateBar->setVisible(true);
|
||||
adjustUpdatesBarHeight();
|
||||
}
|
||||
|
||||
void MainWindow::on_closeButton_clicked()
|
||||
{
|
||||
_updateBar->setVisible(false);
|
||||
}
|
||||
|
||||
void MainWindow::checkUpdates()
|
||||
{
|
||||
if (!Robomongo::AppRegistry::instance().settingsManager()->checkForUpdates())
|
||||
return;
|
||||
|
||||
#ifdef _WIN32
|
||||
QString const OS = "win";
|
||||
#elif __APPLE__
|
||||
QString const OS = "osx";
|
||||
#elif __linux__
|
||||
QString const OS = "linux";
|
||||
#else
|
||||
QString const OS = "unknown";
|
||||
#endif
|
||||
|
||||
// softwareId=8: Robomongo product ID
|
||||
QUrl url("http://updates.3tsoftwarelabs.com/check.php?os=" + OS + "&softwareId=8&softwareVersion=" +
|
||||
QString(PROJECT_VERSION) + "&licenseInfo=FREE&setup=" +
|
||||
AppRegistry::instance().settingsManager()->anonymousID() + "¬ify=true#");
|
||||
|
||||
_networkAccessManager->get(QNetworkRequest(url));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QSystemTrayIcon>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QLabel;
|
||||
class QToolBar;
|
||||
class QDockWidget;
|
||||
class QToolButton;
|
||||
class QPushButton;
|
||||
class QTreeWidgetItem;
|
||||
class QNetworkReply;
|
||||
class QNetworkAccessManager;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Robomongo
|
||||
@@ -21,6 +26,7 @@ namespace Robomongo
|
||||
class ConnectionMenu;
|
||||
class App;
|
||||
class ExplorerWidget;
|
||||
class WelcomeTab;
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
@@ -29,7 +35,8 @@ namespace Robomongo
|
||||
public:
|
||||
typedef QMainWindow BaseClass;
|
||||
MainWindow();
|
||||
void keyPressEvent(QKeyEvent *event);
|
||||
|
||||
WelcomeTab* getWelcomeTab();
|
||||
|
||||
public Q_SLOTS:
|
||||
void manageConnections();
|
||||
@@ -53,6 +60,7 @@ namespace Robomongo
|
||||
void save();
|
||||
void saveAs();
|
||||
void changeStyle(QAction *);
|
||||
void exit();
|
||||
|
||||
void setDefaultUuidEncoding();
|
||||
void setJavaUuidEncoding();
|
||||
@@ -73,20 +81,45 @@ namespace Robomongo
|
||||
void handle(OperationFailedEvent *event);
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent *event);
|
||||
|
||||
void keyPressEvent(QKeyEvent *event) override;
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
void hideEvent(QHideEvent *event) override;
|
||||
void showEvent(QShowEvent *event) override;
|
||||
bool eventFilter(QObject *target, QEvent *event) override;
|
||||
void resizeEvent(QResizeEvent* event) override;
|
||||
|
||||
private Q_SLOTS:
|
||||
void updateMenus();
|
||||
void setUtcTimeZone();
|
||||
void setLocalTimeZone();
|
||||
void openPreferences();
|
||||
void openExportDialog();
|
||||
void openWelcomeTab();
|
||||
|
||||
// Temporarily disabling export/import feature
|
||||
//void openExportDialog();
|
||||
|
||||
void onConnectToolbarVisibilityChanged(bool isVisisble);
|
||||
void onOpenSaveToolbarVisibilityChanged(bool isVisisble);
|
||||
void onExecToolbarVisibilityChanged(bool isVisisble);
|
||||
void onExplorerVisibilityChanged(bool isVisisble);
|
||||
void onExplorerItemSelected(QTreeWidgetItem *selectedItem);
|
||||
|
||||
// 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();
|
||||
|
||||
void on_networkReply(QNetworkReply* reply);
|
||||
void on_closeButton_clicked();
|
||||
|
||||
void checkUpdates();
|
||||
void toggleCheckUpdates();
|
||||
|
||||
private:
|
||||
void updateConnectionsMenu();
|
||||
@@ -94,8 +127,9 @@ namespace Robomongo
|
||||
void createTabs();
|
||||
void createStylesMenu();
|
||||
void createStatusBar();
|
||||
void restoreWindowsSettings();
|
||||
void saveWindowsSettings() const;
|
||||
void restoreWindowSettings();
|
||||
void saveWindowSettings() const;
|
||||
void adjustUpdatesBarHeight();
|
||||
|
||||
QDockWidget *_logDock;
|
||||
|
||||
@@ -119,9 +153,25 @@ namespace Robomongo
|
||||
QAction *_stopAction;
|
||||
QAction *_orientationAction;
|
||||
QToolBar *_execToolBar;
|
||||
QToolBar *_updateBar;
|
||||
QLabel *_updateLabel;
|
||||
QPushButton* _closeButton;
|
||||
|
||||
QNetworkAccessManager *_networkAccessManager;
|
||||
|
||||
// Temporarily disabling export/import feature
|
||||
/*
|
||||
// Export/import tool bar
|
||||
QAction *_exportAction;
|
||||
QAction *_importAction;
|
||||
*/
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
QSystemTrayIcon *_trayIcon;
|
||||
#endif
|
||||
|
||||
bool _allowExit;
|
||||
bool _updateMenusAtStart = true;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QTabWidget>
|
||||
#include <QTextBrowser>
|
||||
#include <QFile>
|
||||
|
||||
#include "robomongo/gui/GuiRegistry.h"
|
||||
#include "robomongo/core/utils/QtUtils.h"
|
||||
@@ -15,11 +18,11 @@ namespace
|
||||
"Shell-centric MongoDB management tool."
|
||||
"<br/>"
|
||||
"<br/>"
|
||||
"Visit " PROJECT_NAME_TITLE " website: <a href=\"http://" PROJECT_DOMAIN "\">" PROJECT_DOMAIN "</a> <br/>"
|
||||
"Visit " PROJECT_NAME_TITLE " website: <a href=\"https://" PROJECT_DOMAIN "\">" PROJECT_DOMAIN "</a> <br/>"
|
||||
"<br/>"
|
||||
"<a href=\"https://" PROJECT_GITHUB_FORK "\">Fork</a> project or <a href=\"https://" PROJECT_GITHUB_ISSUES "\">submit</a> issues/proposals on GitHub. <br/>"
|
||||
"<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 2014-2017 <a href= " 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 "
|
||||
@@ -37,30 +40,57 @@ namespace Robomongo
|
||||
AboutDialog::AboutDialog(QWidget *parent)
|
||||
: QDialog(parent)
|
||||
{
|
||||
setWindowIcon(GuiRegistry::instance().mainWindowIcon());
|
||||
|
||||
setWindowTitle("About " PROJECT_NAME_TITLE);
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
QGridLayout *layout = new QGridLayout(this);
|
||||
|
||||
//// About tab
|
||||
auto aboutTab = new QWidget;
|
||||
aboutTab->setWindowIcon(GuiRegistry::instance().mainWindowIcon());
|
||||
aboutTab->setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
auto layout = new QGridLayout(this);
|
||||
layout->setSizeConstraint(QLayout::SetFixedSize);
|
||||
|
||||
QLabel *copyRightLabel = new QLabel(description);
|
||||
auto copyRightLabel = new QLabel(description);
|
||||
copyRightLabel->setWordWrap(true);
|
||||
copyRightLabel->setOpenExternalLinks(true);
|
||||
copyRightLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
||||
|
||||
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close);
|
||||
QPushButton *closeButton = buttonBox->button(QDialogButtonBox::Close);
|
||||
buttonBox->addButton(closeButton, QDialogButtonBox::ButtonRole(QDialogButtonBox::RejectRole | QDialogButtonBox::AcceptRole));
|
||||
VERIFY(connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())));
|
||||
|
||||
QIcon icon = GuiRegistry::instance().mainWindowIcon();
|
||||
QPixmap iconPixmap = icon.pixmap(128, 128);
|
||||
|
||||
QLabel *logoLabel = new QLabel;
|
||||
auto logoLabel = new QLabel;
|
||||
logoLabel->setPixmap(iconPixmap);
|
||||
layout->addWidget(logoLabel , 0, 0, 1, 1);
|
||||
layout->addWidget(logoLabel, 0, 0, 1, 1);
|
||||
layout->addWidget(copyRightLabel, 0, 1, 4, 4);
|
||||
layout->addWidget(buttonBox, 4, 0, 1, 5);
|
||||
aboutTab->setLayout(layout);
|
||||
|
||||
//// License Agreement tab
|
||||
auto licenseTab = new QWidget;
|
||||
auto textBrowser = new QTextBrowser;
|
||||
textBrowser->setOpenExternalLinks(true);
|
||||
textBrowser->setOpenLinks(true);
|
||||
QFile file(":gnu_gpl3_license.html");
|
||||
if (file.open(QFile::ReadOnly | QFile::Text))
|
||||
textBrowser->setText(file.readAll());
|
||||
|
||||
auto licenseTabLay = new QVBoxLayout;
|
||||
licenseTabLay->addWidget(textBrowser);
|
||||
licenseTab->setLayout(licenseTabLay);
|
||||
|
||||
//// Button box
|
||||
auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Close);
|
||||
QPushButton *closeButton = buttonBox->button(QDialogButtonBox::Close);
|
||||
buttonBox->addButton(closeButton, QDialogButtonBox::ButtonRole(QDialogButtonBox::RejectRole |
|
||||
QDialogButtonBox::AcceptRole));
|
||||
VERIFY(connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())));
|
||||
|
||||
//// Main layout
|
||||
auto tabWidget = new QTabWidget;
|
||||
tabWidget->addTab(aboutTab, "About");
|
||||
tabWidget->addTab(licenseTab, "License Agreement");
|
||||
|
||||
auto mainLayout = new QVBoxLayout;
|
||||
mainLayout->addWidget(tabWidget);
|
||||
mainLayout->addWidget(buttonBox);
|
||||
setLayout(mainLayout);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace Robomongo
|
||||
#endif
|
||||
// Attempt to fix the issue for Windows High DPI button height is slightly taller than other widgets
|
||||
#ifdef Q_OS_WIN
|
||||
_echoModeButton->setMaximumHeight(HighDpiContants::WIN_HIGH_DPI_BUTTON_HEIGHT);
|
||||
_echoModeButton->setMaximumHeight(HighDpiConstants::WIN_HIGH_DPI_BUTTON_HEIGHT);
|
||||
#endif
|
||||
VERIFY(connect(_echoModeButton, SIGNAL(clicked()), this, SLOT(toggleEchoMode())));
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "robomongo/gui/dialogs/ConnectionBasicTab.h"
|
||||
#include "robomongo/gui/dialogs/ConnectionBasicTab.h"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
@@ -8,26 +8,26 @@
|
||||
#include <QPushButton>
|
||||
#include <QFileDialog>
|
||||
#include <QComboBox>
|
||||
#include <QListWidget>
|
||||
#include <QListWidgetItem>
|
||||
#include <QShortCut>
|
||||
#include <QKeySequence>
|
||||
#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)
|
||||
{
|
||||
const ReplicaSetSettings* const replicaSettings = _settings->replicaSetSettings();
|
||||
|
||||
_typeLabel = new QLabel("Type:");
|
||||
_connectionType = new QComboBox;
|
||||
_connectionType->addItem(tr("Direct Connection"));
|
||||
_connectionType->addItem(tr("Replica Set or Sharded Cluster"));
|
||||
_connectionType->addItem(tr("Replica Set"));
|
||||
_connectionType->setCurrentIndex(static_cast<int>(_settings->isReplicaSet()));
|
||||
VERIFY(connect(_connectionType, SIGNAL(currentIndexChanged(int)), this, SLOT(on_ConnectionTypeChange(int))));
|
||||
|
||||
@@ -49,56 +49,66 @@ namespace Robomongo
|
||||
_addInfoLabel->setContentsMargins(0, -2, 0, 20);
|
||||
|
||||
_membersLabel = new QLabel("Members:");
|
||||
_members = new QListWidget;
|
||||
if (_settings->isReplicaSet())
|
||||
{
|
||||
if (replicaSettings->members().empty())
|
||||
{
|
||||
auto item = new QListWidgetItem("localhost:27017");
|
||||
item->setFlags(item->flags() | Qt::ItemIsEditable);
|
||||
_members->addItem(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const std::string& str : replicaSettings->members())
|
||||
{
|
||||
auto item = new QListWidgetItem(QString::fromStdString(str));
|
||||
_membersLabel->setFixedWidth(_membersLabel->sizeHint().width());
|
||||
_members = new QTreeWidget;
|
||||
_members->setHeaderHidden(true);
|
||||
_members->setIndentation(0);
|
||||
#ifdef _WIN32
|
||||
auto lineHeight = _members->fontMetrics().height();
|
||||
_members->setFixedHeight(lineHeight * 8);
|
||||
#endif
|
||||
VERIFY(connect(_members, SIGNAL(itemChanged(QTreeWidgetItem*, int)),
|
||||
this, SLOT(on_replicaMemberItemEdit(QTreeWidgetItem*, int))));
|
||||
|
||||
if (_settings->isReplicaSet() && _settings->replicaSetSettings()->members().size() > 0) {
|
||||
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->addItem(item);
|
||||
_members->addTopLevelItem(item);
|
||||
}
|
||||
}
|
||||
// To fix strange MAC alignment issue
|
||||
#ifndef _WIN32
|
||||
auto lineHeight = _members->fontMetrics().height();
|
||||
_members->setFixedHeight(lineHeight * 8);
|
||||
#endif
|
||||
}
|
||||
else { // No members
|
||||
auto item = new QTreeWidgetItem;
|
||||
item->setText(0, "localhost:27017");
|
||||
item->setFlags(item->flags() | Qt::ItemIsEditable);
|
||||
_members->addTopLevelItem(item);
|
||||
}
|
||||
|
||||
int const BUTTON_SIZE = 60;
|
||||
_addButton = new QPushButton("Add");
|
||||
_addButton->setFixedWidth(BUTTON_SIZE);
|
||||
_removeButton = new QPushButton("Remove");
|
||||
_removeButton->setFixedWidth(BUTTON_SIZE);
|
||||
_discoverButton = new QPushButton("Discover");
|
||||
_discoverButton->setFixedWidth(BUTTON_SIZE);
|
||||
_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())));
|
||||
|
||||
auto hLayout = new QHBoxLayout;
|
||||
hLayout->addWidget(_addButton);
|
||||
hLayout->addWidget(_removeButton);
|
||||
hLayout->addWidget(_discoverButton);
|
||||
hLayout->setAlignment(Qt::AlignRight);
|
||||
_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
|
||||
|
||||
_readPrefLabel = new QLabel("Read Preference:");
|
||||
_readPrefLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
|
||||
_readPreference = new QComboBox;
|
||||
_readPreference->addItems(QStringList({ "Primary", "Primary Preferred" })); //todo
|
||||
if (_settings->isReplicaSet()) {
|
||||
_readPreference->setCurrentIndex(static_cast<int>(replicaSettings->readPreference()));
|
||||
}
|
||||
_setNameLabel = new QLabel("Set Name:<br><i><font color=\"gray\">(Optional)</font></i>");
|
||||
_setNameEdit = new QLineEdit(QString::fromStdString(_settings->replicaSetSettings()->setNameUserEntered()));
|
||||
auto _optionalLabel = new QLabel("<i><font color=\"gray\">(Optional)</font></i>");
|
||||
|
||||
|
||||
new QShortcut(QKeySequence(Qt::Key_Delete), this, SLOT(deleteItem()));
|
||||
|
||||
QGridLayout *connectionLayout = new QGridLayout;
|
||||
auto connectionLayout = new QGridLayout;
|
||||
connectionLayout->setVerticalSpacing(10);
|
||||
connectionLayout->setAlignment(Qt::AlignTop);
|
||||
connectionLayout->addWidget(_typeLabel, 1, 0);
|
||||
connectionLayout->addWidget(_connectionType, 1, 1, 1, 3);
|
||||
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);
|
||||
@@ -109,42 +119,85 @@ namespace Robomongo
|
||||
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->addLayout(hLayout, 8, 1, 1, 3, Qt::AlignRight);
|
||||
connectionLayout->addWidget(_members, 7, 1, 1, 3);
|
||||
connectionLayout->addWidget(_minusPlusButtonBox, 8, 3, Qt::AlignRight | Qt::AlignTop);
|
||||
connectionLayout->addWidget(new QLabel(""), 9, 0);
|
||||
connectionLayout->addWidget(_readPrefLabel, 10, 0);
|
||||
connectionLayout->addWidget(_readPreference, 10, 1, 1, 3);
|
||||
connectionLayout->addWidget(new QLabel(""), 11, 0);
|
||||
connectionLayout->addWidget(_setNameLabel, 10, 0/*, Qt::AlignBottom*/);
|
||||
connectionLayout->addWidget(_setNameEdit, 10, 1, 1, 3, Qt::AlignTop);
|
||||
//connectionLayout->addWidget(_optionalLabel, 11, 1, 1, 3, Qt::AlignLeft | Qt::AlignTop);
|
||||
connectionLayout->addWidget(new QLabel(""), 12, 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()
|
||||
{
|
||||
ReplicaSetSettings* const replicaSettings = _settings->replicaSetSettings();
|
||||
|
||||
_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()) {
|
||||
// save replica members
|
||||
std::vector<std::string> members;
|
||||
for (int i = 0; i < _members->count(); ++i)
|
||||
{
|
||||
QListWidgetItem const* item = _members->item(i);
|
||||
members.push_back(item->text().toStdString());
|
||||
}
|
||||
replicaSettings->setMembers(members);
|
||||
// save read preference option
|
||||
replicaSettings->setReadPreference(
|
||||
static_cast<ReplicaSetSettings::ReadPreference>(_readPreference->currentIndex()));
|
||||
|
||||
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);
|
||||
_settings->replicaSetSettings()->setSetNameUserEntered(_setNameEdit->text().toStdString());
|
||||
// Clear cached set name
|
||||
_settings->replicaSetSettings()->setCachedSetName("");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ConnectionBasicTab::on_ConnectionTypeChange(int index)
|
||||
@@ -154,12 +207,10 @@ namespace Robomongo
|
||||
// Replica set
|
||||
_membersLabel->setVisible(isReplica);
|
||||
_members->setVisible(isReplica);
|
||||
_addButton->setVisible(isReplica);
|
||||
_removeButton->setVisible(isReplica);
|
||||
_discoverButton->setVisible(isReplica);
|
||||
_readPrefLabel->setVisible(isReplica);
|
||||
_readPreference->setVisible(isReplica);
|
||||
|
||||
_minusPlusButtonBox->setVisible(isReplica);
|
||||
_setNameLabel->setVisible(isReplica);
|
||||
_setNameEdit->setVisible(isReplica);
|
||||
|
||||
// Direct Connection
|
||||
_addressLabel->setVisible(!isReplica);
|
||||
_serverAddress->setVisible(!isReplica);
|
||||
@@ -175,8 +226,68 @@ namespace Robomongo
|
||||
|
||||
void ConnectionBasicTab::on_addButton_clicked()
|
||||
{
|
||||
auto item = new QListWidgetItem("localhost:27017");
|
||||
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->addItem(item);
|
||||
_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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ class QLabel;
|
||||
class QCheckBox;
|
||||
class QPushButton;
|
||||
class QComboBox;
|
||||
class QListWidget;
|
||||
class QTreeWidget;
|
||||
class QTreeWidgetItem;
|
||||
class QDialogButtonBox;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Robomongo
|
||||
@@ -20,12 +22,14 @@ 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;
|
||||
@@ -39,12 +43,16 @@ namespace Robomongo
|
||||
QLineEdit *_serverPort;
|
||||
QLabel *_addInfoLabel;
|
||||
QLabel *_membersLabel;
|
||||
QListWidget *_members;
|
||||
QTreeWidget *_members;
|
||||
QPushButton *_addButton;
|
||||
QPushButton *_removeButton;
|
||||
QDialogButtonBox *_minusPlusButtonBox;
|
||||
QLabel *_setNameLabel;
|
||||
QLineEdit *_setNameEdit;
|
||||
QPushButton *_discoverButton;
|
||||
QLabel *_readPrefLabel;
|
||||
QComboBox *_readPreference;
|
||||
|
||||
ConnectionSettings *const _settings;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#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"
|
||||
@@ -23,13 +24,11 @@ namespace Robomongo
|
||||
{
|
||||
ConnectionDiagnosticDialog::ConnectionDiagnosticDialog(ConnectionSettings *connection, QWidget *parent) :
|
||||
QDialog(parent),
|
||||
_connection(NULL),
|
||||
_connSettings(connection->clone()),
|
||||
_server(NULL),
|
||||
_serverHandle(0),
|
||||
_continueExec(true)
|
||||
{
|
||||
_connection = connection->clone();
|
||||
|
||||
AppRegistry::instance().bus()->subscribe(this, ConnectionEstablishedEvent::Type);
|
||||
AppRegistry::instance().bus()->subscribe(this, ConnectionFailedEvent::Type);
|
||||
|
||||
@@ -97,7 +96,7 @@ namespace Robomongo
|
||||
|
||||
_viewErrorLink->hide();
|
||||
|
||||
if (!AppRegistry::instance().app()->openServer(_connection, ConnectionTest)) {
|
||||
if (!AppRegistry::instance().app()->openServer(_connSettings, ConnectionTest)) {
|
||||
_continueExec = false;
|
||||
return;
|
||||
}
|
||||
@@ -119,7 +118,7 @@ namespace Robomongo
|
||||
|
||||
void ConnectionDiagnosticDialog::sshStatus(State state)
|
||||
{
|
||||
if (!_connection->sshSettings()->enabled()) {
|
||||
if (!_connSettings->sshSettings()->enabled()) {
|
||||
_sshIconLabel->setVisible(false);
|
||||
_sshLabel->setVisible(false);
|
||||
return;
|
||||
@@ -128,18 +127,18 @@ namespace Robomongo
|
||||
if (state == InitialState) {
|
||||
_sshIconLabel->setMovie(_loadingMovie);
|
||||
_sshLabel->setText(QString("Connecting to SSH server at <b>%1:%2</b>...")
|
||||
.arg(QtUtils::toQString(_connection->sshSettings()->host()))
|
||||
.arg(_connection->sshSettings()->port()));
|
||||
.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(_connection->sshSettings()->host()))
|
||||
.arg(_connection->sshSettings()->port()));
|
||||
.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(_connection->sshSettings()->host()))
|
||||
.arg(_connection->sshSettings()->port()));
|
||||
.arg(QtUtils::toQString(_connSettings->sshSettings()->host()))
|
||||
.arg(_connSettings->sshSettings()->port()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,34 +146,45 @@ namespace Robomongo
|
||||
{
|
||||
// Add info about tunneling if SSH or SSL is used
|
||||
QString tunnelNote(""); // No tunnel info when neither SSH nor SSL enabled
|
||||
if (_connection->sshSettings()->enabled())
|
||||
{
|
||||
if (_connSettings->sshSettings()->enabled())
|
||||
tunnelNote = " via SSH tunnel";
|
||||
}
|
||||
else if (_connection->sslSettings()->sslEnabled())
|
||||
{
|
||||
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(QtUtils::toQString(_connection->getFullAddress()), tunnelNote));
|
||||
_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(QtUtils::toQString(_connection->getFullAddress()), tunnelNote));
|
||||
_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(QtUtils::toQString(_connection->getFullAddress()), tunnelNote));
|
||||
_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(QtUtils::toQString(_connection->getFullAddress()), tunnelNote));
|
||||
_connectionLabel->setText(QString("No chance to try connection to <b>%1</b>%2").
|
||||
arg(serverAddress, tunnelNote));
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionDiagnosticDialog::authStatus(State state)
|
||||
{
|
||||
if (!_connection->hasEnabledPrimaryCredential()) {
|
||||
if (!_connSettings->hasEnabledPrimaryCredential()) {
|
||||
_authIconLabel->setVisible(false);
|
||||
_authLabel->setVisible(false);
|
||||
return;
|
||||
@@ -183,18 +193,18 @@ namespace Robomongo
|
||||
if (state == InitialState) {
|
||||
_authIconLabel->setMovie(_loadingMovie);
|
||||
_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())));
|
||||
.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(_connection->primaryCredential()->databaseName()))
|
||||
.arg(QtUtils::toQString(_connection->primaryCredential()->userName())));
|
||||
.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(_connection->primaryCredential()->databaseName()))
|
||||
.arg(QtUtils::toQString(_connection->primaryCredential()->userName())));
|
||||
.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"));
|
||||
@@ -203,7 +213,7 @@ namespace Robomongo
|
||||
|
||||
void ConnectionDiagnosticDialog::listStatus(State state)
|
||||
{
|
||||
if (_connection->hasEnabledPrimaryCredential()) {
|
||||
if (_connSettings->hasEnabledPrimaryCredential()) {
|
||||
_listIconLabel->setVisible(false);
|
||||
_listLabel->setVisible(false);
|
||||
return;
|
||||
|
||||
@@ -8,7 +8,7 @@ class QMovie;
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
class ConnectionEstablishedEvent;
|
||||
struct ConnectionEstablishedEvent;
|
||||
class ConnectionFailedEvent;
|
||||
class ConnectionSettings;
|
||||
class MongoServer;
|
||||
@@ -29,7 +29,8 @@ namespace Robomongo
|
||||
|
||||
private:
|
||||
|
||||
enum State {
|
||||
enum State
|
||||
{
|
||||
InitialState,
|
||||
CompletedState,
|
||||
FailedState,
|
||||
@@ -41,7 +42,7 @@ namespace Robomongo
|
||||
void authStatus(State state);
|
||||
void listStatus(State state);
|
||||
|
||||
ConnectionSettings *_connection;
|
||||
ConnectionSettings *_connSettings;
|
||||
QIcon _yesIcon;
|
||||
QIcon _noIcon;
|
||||
QIcon _questionIcon;
|
||||
|
||||
@@ -91,11 +91,11 @@ namespace Robomongo
|
||||
"Please use one of them.");
|
||||
return false;
|
||||
}
|
||||
_basicTab->accept();
|
||||
|
||||
_authTab->accept();
|
||||
_advancedTab->accept();
|
||||
|
||||
if (!_sshTab->accept() || !_sslTab->accept())
|
||||
if (!_basicTab->accept() || !_sshTab->accept() || !_sslTab->accept())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -11,15 +11,22 @@
|
||||
#include <QKeyEvent>
|
||||
#include <QApplication>
|
||||
#include <QSettings>
|
||||
#include <QUuid>
|
||||
|
||||
#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"
|
||||
|
||||
#include "robomongo/gui/GuiRegistry.h"
|
||||
#include "robomongo/gui/dialogs/ConnectionDialog.h"
|
||||
#include "robomongo/gui/MainWindow.h"
|
||||
#include "robomongo/gui/widgets/workarea/WelcomeTab.h"
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
@@ -45,36 +52,49 @@ namespace Robomongo
|
||||
*/
|
||||
void setConnection(ConnectionSettings *connection)
|
||||
{
|
||||
if (connection->isReplicaSet()) {
|
||||
setText(0, QtUtils::toQString(connection->connectionName()) + " [Replica Set]");
|
||||
auto const repSetSize = connection->replicaSetSettings()->members().size();
|
||||
setText(1, QString::number(repSetSize) + " Servers (" + QtUtils::toQString(connection->getFullAddress()) + ")");
|
||||
}
|
||||
else {
|
||||
setText(0, QtUtils::toQString(connection->connectionName()));
|
||||
setText(1, QtUtils::toQString(connection->getFullAddress()));
|
||||
}
|
||||
|
||||
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, "");
|
||||
}
|
||||
|
||||
_connection = connection;
|
||||
if (connection->isReplicaSet()) {
|
||||
|
||||
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()) {
|
||||
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, "");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -113,12 +133,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);
|
||||
@@ -183,19 +204,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();
|
||||
|
||||
restoreWindowsSettings();
|
||||
restoreWindowSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -203,27 +224,27 @@ 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();
|
||||
saveWindowsSettings();
|
||||
saveWindowSettings();
|
||||
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void ConnectionsDialog::reject()
|
||||
{
|
||||
saveWindowsSettings();
|
||||
saveWindowSettings();
|
||||
QDialog::reject();
|
||||
}
|
||||
|
||||
void ConnectionsDialog::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
saveWindowsSettings();
|
||||
saveWindowSettings();
|
||||
QWidget::closeEvent(event);
|
||||
}
|
||||
|
||||
@@ -243,19 +264,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -263,14 +284,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());
|
||||
|
||||
@@ -302,42 +322,53 @@ namespace Robomongo
|
||||
*/
|
||||
void ConnectionsDialog::remove()
|
||||
{
|
||||
ConnectionListWidgetItem *currentItem =
|
||||
(ConnectionListWidgetItem *)_listWidget->currentItem();
|
||||
auto currentItem = dynamic_cast<ConnectionListWidgetItem*>(_listWidget->currentItem());
|
||||
|
||||
// Do nothing if no item selected
|
||||
if (!currentItem)
|
||||
return;
|
||||
|
||||
ConnectionSettings *connectionModel = currentItem->connection();
|
||||
ConnectionSettings *connSettings = currentItem->connection();
|
||||
|
||||
// Ask user
|
||||
int answer = QMessageBox::question(this,
|
||||
int const 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);
|
||||
/* Temporarily disabling Recent Connections feature
|
||||
_settingsManager->deleteRecentConnection(connSettings);
|
||||
// Remove from WelcomeTab
|
||||
for (auto widget : QApplication::topLevelWidgets()) {
|
||||
if (auto mainWin = dynamic_cast<MainWindow*>(widget))
|
||||
mainWin->getWelcomeTab()->removeRecentConnectionItem(connSettings);
|
||||
}
|
||||
*/
|
||||
|
||||
_settingsManager->removeConnection(connSettings);
|
||||
|
||||
delete currentItem;
|
||||
}
|
||||
|
||||
void ConnectionsDialog::clone()
|
||||
{
|
||||
ConnectionListWidgetItem *currentItem =
|
||||
(ConnectionListWidgetItem *) _listWidget->currentItem();
|
||||
auto currentItem = dynamic_cast<ConnectionListWidgetItem*>(_listWidget->currentItem());
|
||||
|
||||
// Do nothing if no item selected
|
||||
if (currentItem == 0)
|
||||
if (!currentItem)
|
||||
return;
|
||||
|
||||
// Clone connection
|
||||
ConnectionSettings *connection = currentItem->connection()->clone();
|
||||
std::string newConnectionName = "Copy of "+connection->connectionName();
|
||||
// This is a special clone which will actually be a new connection and must have unique UUID
|
||||
connection->setUuid(QUuid::createUuid().toString());
|
||||
std::string newConnectionName = "Copy of " + connection->connectionName();
|
||||
|
||||
connection->setConnectionName(newConnectionName);
|
||||
connection->replicaSetSettings()->setCachedSetName("");
|
||||
|
||||
ConnectionDialog editDialog(connection);
|
||||
|
||||
@@ -357,15 +388,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);
|
||||
@@ -373,11 +402,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());
|
||||
}
|
||||
|
||||
@@ -389,8 +417,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);
|
||||
}
|
||||
|
||||
@@ -416,15 +445,15 @@ namespace Robomongo
|
||||
QDialog::keyPressEvent(event);
|
||||
}
|
||||
|
||||
void ConnectionsDialog::restoreWindowsSettings()
|
||||
void ConnectionsDialog::restoreWindowSettings()
|
||||
{
|
||||
QSettings settings("Paralect", "Robomongo");
|
||||
QSettings settings("3T", "Robomongo");
|
||||
resize(settings.value("ConnectionsDialog/size").toSize());
|
||||
}
|
||||
|
||||
void ConnectionsDialog::saveWindowsSettings() const
|
||||
void ConnectionsDialog::saveWindowSettings() const
|
||||
{
|
||||
QSettings settings("Paralect", "Robomongo");
|
||||
QSettings settings("3T", "Robomongo");
|
||||
settings.setValue("ConnectionsDialog/size", size());
|
||||
}
|
||||
|
||||
|
||||
@@ -35,27 +35,27 @@ namespace Robomongo
|
||||
/**
|
||||
* @brief This function is called when user clicks on "Connect" button.
|
||||
*/
|
||||
virtual void accept();
|
||||
void accept() override;
|
||||
|
||||
/**
|
||||
* @brief Called when "Cancel" button clicked.
|
||||
*/
|
||||
virtual void reject();
|
||||
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);
|
||||
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
|
||||
*/
|
||||
@@ -84,19 +84,19 @@ namespace Robomongo
|
||||
*/
|
||||
void listWidget_layoutChanged();
|
||||
|
||||
void keyPressEvent(QKeyEvent* event);
|
||||
void keyPressEvent(QKeyEvent* event) override;
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Restore windows settings from system registry
|
||||
* @brief Restore window settings from system registry
|
||||
*/
|
||||
void restoreWindowsSettings();
|
||||
void restoreWindowSettings();
|
||||
|
||||
/**
|
||||
* @brief Save windows settings into system registry
|
||||
*/
|
||||
void saveWindowsSettings() const;
|
||||
void saveWindowSettings() const;
|
||||
|
||||
/**
|
||||
* @brief ConnectionSettings, that was selected after pressing on
|
||||
|
||||
@@ -243,7 +243,8 @@ namespace Robomongo
|
||||
if (state) // resize, dialog is expanding
|
||||
{
|
||||
setMinimumSize(560, 440);
|
||||
QSettings settings("Paralect", "Robomongo");
|
||||
_validateJsonButton->setHidden(_advancedOptions->currentIndex() == OPTIONS_TAB);
|
||||
QSettings settings("3T", "Robomongo");
|
||||
if (settings.contains("CreateCollectionDialog/size"))
|
||||
{
|
||||
restoreWindowSettings();
|
||||
@@ -257,7 +258,8 @@ namespace Robomongo
|
||||
else // resize, dialog is shrinking
|
||||
{
|
||||
saveWindowSettings(); // save expanded geometry first
|
||||
setMinimumSize(300, 150);
|
||||
_validateJsonButton->hide();
|
||||
setFixedSize(300, 150);
|
||||
adjustSize();
|
||||
}
|
||||
}
|
||||
@@ -403,13 +405,13 @@ namespace Robomongo
|
||||
|
||||
void CreateCollectionDialog::saveWindowSettings() const
|
||||
{
|
||||
QSettings settings("Paralect", "Robomongo");
|
||||
QSettings settings("3T", "Robomongo");
|
||||
settings.setValue("CreateCollectionDialog/size", size());
|
||||
}
|
||||
|
||||
void CreateCollectionDialog::restoreWindowSettings()
|
||||
{
|
||||
QSettings settings("Paralect", "Robomongo");
|
||||
QSettings settings("3T", "Robomongo");
|
||||
resize(settings.value("CreateCollectionDialog/size").toSize());
|
||||
}
|
||||
|
||||
|
||||
@@ -84,18 +84,18 @@ namespace Robomongo
|
||||
/**
|
||||
* @brief Validate all input fields and make extraOptions object if all valid when create button clicked.
|
||||
*/
|
||||
virtual void accept();
|
||||
void accept() override;
|
||||
|
||||
/**
|
||||
* @brief Called when "Cancel" button clicked.
|
||||
*/
|
||||
virtual void reject();
|
||||
void reject() override;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Reimplementing closeEvent in order to do implement functioanlites before close this dialog.
|
||||
* @brief Reimplementing closeEvent in order to do implement functionalities before close this dialog.
|
||||
*/
|
||||
void closeEvent(QCloseEvent *event);
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
|
||||
private Q_SLOTS:
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
@@ -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,7 +36,15 @@ namespace Robomongo
|
||||
QSize size(screenGeometry.width() - horizontalMargin,
|
||||
screenGeometry.height() - verticalMargin);
|
||||
|
||||
resize(size);
|
||||
QSettings settings("3T", "Robomongo");
|
||||
if (settings.contains("DocumentTextEditor/size"))
|
||||
{
|
||||
restoreWindowSettings();
|
||||
}
|
||||
else
|
||||
{
|
||||
resize(size);
|
||||
}
|
||||
|
||||
setWindowFlags(Qt::Window | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint);
|
||||
|
||||
@@ -105,6 +115,8 @@ namespace Robomongo
|
||||
if (!validate())
|
||||
return;
|
||||
|
||||
saveWindowSettings();
|
||||
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
@@ -127,6 +139,8 @@ namespace Robomongo
|
||||
return;
|
||||
}
|
||||
|
||||
saveWindowSettings();
|
||||
|
||||
QDialog::reject();
|
||||
}
|
||||
|
||||
@@ -186,6 +200,12 @@ namespace Robomongo
|
||||
validate(false);
|
||||
}
|
||||
|
||||
void DocumentTextEditor::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
saveWindowSettings();
|
||||
QWidget::closeEvent(event);
|
||||
}
|
||||
|
||||
/*
|
||||
** Configure QsciScintilla query widget
|
||||
*/
|
||||
@@ -204,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("3T", "Robomongo");
|
||||
settings.setValue("DocumentTextEditor/size", size());
|
||||
}
|
||||
|
||||
void DocumentTextEditor::restoreWindowSettings()
|
||||
{
|
||||
QSettings settings("3T", "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,243 @@
|
||||
#include "robomongo/gui/dialogs/EulaDialog.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QMessageBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QFile>
|
||||
#include <QSettings>
|
||||
#include <QLabel>
|
||||
#include <QTextBrowser>
|
||||
#include <QLineEdit>
|
||||
#include <QRadioButton>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkReply>
|
||||
#include <QUrlQuery>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonDocument>
|
||||
#include <QDesktopWidget>
|
||||
|
||||
#include "robomongo/core/utils/QtUtils.h"
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
|
||||
EulaDialog::EulaDialog(QWidget *parent)
|
||||
: QWizard(parent)
|
||||
{
|
||||
setWindowTitle("EULA");
|
||||
|
||||
//// First page
|
||||
auto firstPage = new QWizardPage;
|
||||
|
||||
auto agreeButton = new QRadioButton("I agree");
|
||||
VERIFY(connect(agreeButton, SIGNAL(clicked()),
|
||||
this, SLOT(on_agreeButton_clicked())));
|
||||
|
||||
auto notAgreeButton = new QRadioButton("I don't agree");
|
||||
notAgreeButton->setChecked(true);
|
||||
VERIFY(connect(notAgreeButton, SIGNAL(clicked()),
|
||||
this, SLOT(on_notAgreeButton_clicked())));
|
||||
|
||||
auto radioButtonsLay = new QHBoxLayout;
|
||||
radioButtonsLay->setAlignment(Qt::AlignHCenter);
|
||||
radioButtonsLay->setSpacing(30);
|
||||
radioButtonsLay->addWidget(agreeButton);
|
||||
radioButtonsLay->addWidget(notAgreeButton);
|
||||
|
||||
auto textBrowser = new QTextBrowser;
|
||||
textBrowser->setOpenExternalLinks(true);
|
||||
textBrowser->setOpenLinks(true);
|
||||
QFile file(":gnu_gpl3_license.html");
|
||||
if (file.open(QFile::ReadOnly | QFile::Text))
|
||||
textBrowser->setText(file.readAll());
|
||||
|
||||
auto hline = new QFrame();
|
||||
hline->setFrameShape(QFrame::HLine);
|
||||
hline->setFrameShadow(QFrame::Sunken);
|
||||
|
||||
auto mainLayout1 = new QVBoxLayout();
|
||||
mainLayout1->addWidget(new QLabel("<h3>End-User License Agreement</h3>"));
|
||||
mainLayout1->addWidget(new QLabel(""));
|
||||
mainLayout1->addWidget(textBrowser);
|
||||
mainLayout1->addWidget(new QLabel(""));
|
||||
mainLayout1->addLayout(radioButtonsLay, Qt::AlignCenter);
|
||||
mainLayout1->addWidget(new QLabel(""));
|
||||
mainLayout1->addWidget(hline);
|
||||
|
||||
firstPage->setLayout(mainLayout1);
|
||||
|
||||
//// Second page
|
||||
auto secondPage = new QWizardPage;
|
||||
|
||||
auto nameLabel = new QLabel("<b>First Name:</b>");
|
||||
_nameEdit = new QLineEdit;
|
||||
auto lastNameLabel = new QLabel("<b>Last Name:</b>");
|
||||
_lastNameEdit = new QLineEdit;
|
||||
|
||||
auto emailLabel = new QLabel("<b>Email:</b>");
|
||||
_emailEdit = new QLineEdit;
|
||||
|
||||
auto buttomLabel = new QLabel("By submitting this form I agree to 3T Software Labs "
|
||||
"<a href='https://studio3t.com/privacy-policy'>Privacy Policy</a>.");
|
||||
buttomLabel->setOpenExternalLinks(true);
|
||||
|
||||
auto bodyLabel = new QLabel("\nShare your email address with us and we'll keep you "
|
||||
"up-to-date with updates from us and new features as they come out.");
|
||||
bodyLabel->setWordWrap(true);
|
||||
|
||||
auto mainLayout2 = new QGridLayout();
|
||||
mainLayout2->addWidget(new QLabel, 0, 0, 1, 2);
|
||||
mainLayout2->addWidget(new QLabel("<h3>Thank you for choosing Robomongo!</h3>"), 1, 0, 1, 2);
|
||||
mainLayout2->addWidget(bodyLabel, 2, 0 , 1, 2);
|
||||
mainLayout2->addWidget(new QLabel, 3, 0, 1, 2);
|
||||
mainLayout2->addWidget(nameLabel, 4, 0);
|
||||
mainLayout2->addWidget(_nameEdit, 4, 1);
|
||||
mainLayout2->addWidget(lastNameLabel, 5, 0);
|
||||
mainLayout2->addWidget(_lastNameEdit, 5, 1);
|
||||
mainLayout2->addWidget(emailLabel, 6, 0);
|
||||
mainLayout2->addWidget(_emailEdit, 6, 1);
|
||||
mainLayout2->addWidget(new QLabel, 7, 0, 1, 2);
|
||||
mainLayout2->addWidget(buttomLabel, 8, 0, 1, 2);
|
||||
|
||||
secondPage->setLayout(mainLayout2);
|
||||
|
||||
addPage(firstPage);
|
||||
#if defined(_WIN32) || defined(__APPLE__)
|
||||
addPage(secondPage);
|
||||
#endif
|
||||
|
||||
//// Buttons
|
||||
setButtonText(QWizard::CustomButton1, tr("Back"));
|
||||
setButtonText(QWizard::CustomButton2, tr("Next"));
|
||||
setButtonText(QWizard::CustomButton3, tr("Finish"));
|
||||
|
||||
VERIFY(connect(button(QWizard::CustomButton1), SIGNAL(clicked()), this, SLOT(on_back_clicked())));
|
||||
VERIFY(connect(button(QWizard::CustomButton2), SIGNAL(clicked()), this, SLOT(on_next_clicked())));
|
||||
VERIFY(connect(button(QWizard::CustomButton3), SIGNAL(clicked()), this, SLOT(on_finish_clicked())));
|
||||
|
||||
setButtonLayout(QList<WizardButton>{ QWizard::Stretch, QWizard::CustomButton1, QWizard::CustomButton2,
|
||||
QWizard::CancelButton, QWizard::CustomButton3});
|
||||
|
||||
#if defined(_WIN32) || defined(__APPLE__)
|
||||
button(QWizard::CustomButton1)->setDisabled(true);
|
||||
button(QWizard::CustomButton2)->setDisabled(true);
|
||||
button(QWizard::CustomButton3)->setDisabled(true);
|
||||
#else // linux
|
||||
button(QWizard::CustomButton1)->setHidden(true);
|
||||
button(QWizard::CustomButton2)->setHidden(true);
|
||||
button(QWizard::CustomButton3)->setDisabled(true);
|
||||
#endif
|
||||
|
||||
setWizardStyle(QWizard::ModernStyle);
|
||||
|
||||
QSettings const settings("3T", "Robomongo");
|
||||
if (settings.contains("EulaDialog/size")) {
|
||||
restoreWindowSettings();
|
||||
}
|
||||
else {
|
||||
auto const desktop = QApplication::desktop();
|
||||
auto const& mainScreenSize = desktop->availableGeometry(desktop->primaryScreen()).size();
|
||||
resize(mainScreenSize.width()*0.5, mainScreenSize.height()*0.6);
|
||||
}
|
||||
}
|
||||
|
||||
void EulaDialog::accept()
|
||||
{
|
||||
saveWindowSettings();
|
||||
|
||||
// Build post data and send
|
||||
postUserData();
|
||||
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void EulaDialog::reject()
|
||||
{
|
||||
saveWindowSettings();
|
||||
QDialog::reject();
|
||||
}
|
||||
|
||||
void EulaDialog::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
saveWindowSettings();
|
||||
QWidget::closeEvent(event);
|
||||
}
|
||||
|
||||
void EulaDialog::on_agreeButton_clicked()
|
||||
{
|
||||
#if defined(_WIN32) || defined(__APPLE__)
|
||||
button(QWizard::CustomButton2)->setEnabled(true);
|
||||
#else
|
||||
button(QWizard::CustomButton3)->setEnabled(true);
|
||||
#endif
|
||||
}
|
||||
|
||||
void EulaDialog::on_notAgreeButton_clicked()
|
||||
{
|
||||
#if defined(_WIN32) || defined(__APPLE__)
|
||||
button(QWizard::CustomButton2)->setEnabled(false);
|
||||
#else
|
||||
button(QWizard::CustomButton3)->setEnabled(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void EulaDialog::on_next_clicked()
|
||||
{
|
||||
next();
|
||||
button(QWizard::CustomButton1)->setEnabled(true);
|
||||
button(QWizard::CustomButton2)->setEnabled(false);
|
||||
button(QWizard::CustomButton3)->setEnabled(true);
|
||||
}
|
||||
|
||||
void EulaDialog::on_back_clicked()
|
||||
{
|
||||
back();
|
||||
button(QWizard::CustomButton1)->setEnabled(false);
|
||||
button(QWizard::CustomButton2)->setEnabled(true);
|
||||
button(QWizard::CustomButton3)->setEnabled(false);
|
||||
}
|
||||
|
||||
void EulaDialog::on_finish_clicked()
|
||||
{
|
||||
accept();
|
||||
}
|
||||
|
||||
void EulaDialog::postUserData() const
|
||||
{
|
||||
if (_emailEdit->text().isEmpty())
|
||||
return;
|
||||
|
||||
// Build post data and send
|
||||
QJsonObject jsonStr {
|
||||
{ "email", _emailEdit->text() },
|
||||
{ "firstName", _nameEdit->text() },
|
||||
{ "lastName", _lastNameEdit->text() }
|
||||
};
|
||||
|
||||
QJsonDocument jsonDoc(jsonStr);
|
||||
QUrlQuery postData(jsonDoc.toJson());
|
||||
postData = QUrlQuery("rd=" + postData.toString(QUrl::FullyEncoded).toUtf8());
|
||||
|
||||
QNetworkRequest request(QUrl("https://rm-form.3t.io/"));
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
||||
|
||||
auto networkManager = new QNetworkAccessManager;
|
||||
_reply = networkManager->post(request, postData.toString(QUrl::FullyEncoded).toUtf8());
|
||||
}
|
||||
|
||||
void EulaDialog::saveWindowSettings() const
|
||||
{
|
||||
QSettings settings("3T", "Robomongo");
|
||||
settings.setValue("EulaDialog/size", size());
|
||||
}
|
||||
|
||||
void EulaDialog::restoreWindowSettings()
|
||||
{
|
||||
QSettings settings("3T", "Robomongo");
|
||||
resize(settings.value("EulaDialog/size").toSize());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#include <QWizard>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QLabel;
|
||||
class QLineEdit;
|
||||
class QCheckBox;
|
||||
class QPushButton;
|
||||
class QDialogButtonBox;
|
||||
class QComboBox;
|
||||
class QNetworkReply;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
|
||||
class EulaDialog : public QWizard
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static const QSize minimumSize;
|
||||
|
||||
explicit EulaDialog(QWidget *parent = nullptr);
|
||||
|
||||
public Q_SLOTS:
|
||||
void accept() override;
|
||||
void reject() override;
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
|
||||
private Q_SLOTS:
|
||||
void on_agreeButton_clicked();
|
||||
void on_notAgreeButton_clicked();
|
||||
void on_next_clicked();
|
||||
void on_back_clicked();
|
||||
void on_finish_clicked();
|
||||
|
||||
private:
|
||||
|
||||
// Send name, last name and email data of user to home server
|
||||
void postUserData() const;
|
||||
|
||||
/**
|
||||
* @brief Restore window settings from system registry
|
||||
*/
|
||||
void restoreWindowSettings();
|
||||
|
||||
/**
|
||||
* @brief Save window settings into system registry
|
||||
*/
|
||||
void saveWindowSettings() const;
|
||||
|
||||
QLineEdit* _nameEdit;
|
||||
QLineEdit* _lastNameEdit;
|
||||
QLineEdit* _emailEdit;
|
||||
|
||||
QByteArray _postData;
|
||||
mutable QNetworkReply* _reply;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@@ -40,8 +40,6 @@
|
||||
|
||||
namespace Robomongo
|
||||
{
|
||||
//const QSize ExportDialog::dialogSize = QSize(500, 250); // todo: remove
|
||||
|
||||
namespace
|
||||
{
|
||||
const QString defaultDir = "D:\\exports\\"; // Default location
|
||||
@@ -129,12 +127,12 @@ namespace Robomongo
|
||||
VERIFY(connect(_formatComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(on_formatComboBox_change(int))));
|
||||
|
||||
_fieldsLabel = new QLabel("Fields:");
|
||||
_fields = new QLineEdit; // todo: make textedit
|
||||
_fields = new QLineEdit; // todo: use textedit
|
||||
// Initially hidden
|
||||
_fieldsLabel->setHidden(true);
|
||||
_fields->setHidden(true);
|
||||
|
||||
_query = new QLineEdit("{}"); // todo: can use JSON frame
|
||||
_query = new QLineEdit("{}"); // todo: JSON frame can be used
|
||||
_outputFileName = new QLineEdit;
|
||||
_outputDir = new QLineEdit;
|
||||
_browseButton = new QPushButton("...");
|
||||
@@ -154,7 +152,7 @@ namespace Robomongo
|
||||
|
||||
// Attempt to fix issue for Windows High DPI button height is slightly taller than other widgets
|
||||
#ifdef Q_OS_WIN
|
||||
_browseButton->setMaximumHeight(HighDpiContants::WIN_HIGH_DPI_BUTTON_HEIGHT);
|
||||
_browseButton->setMaximumHeight(HighDpiConstants::WIN_HIGH_DPI_BUTTON_HEIGHT);
|
||||
#endif
|
||||
auto outputsInnerLay = new QGridLayout;
|
||||
outputsInnerLay->addWidget(new QLabel("Format:"), 0, 0);
|
||||
@@ -246,7 +244,7 @@ namespace Robomongo
|
||||
layout->addLayout(hButtonBoxlayout);
|
||||
setLayout(layout);
|
||||
|
||||
// todo: move to function
|
||||
// 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");
|
||||
@@ -276,13 +274,6 @@ namespace Robomongo
|
||||
//_inputLabel->setText(text);
|
||||
}
|
||||
|
||||
// todo: remove
|
||||
void ExportDialog::setInputText(const QString &text)
|
||||
{
|
||||
//_inputEdit->setText(text);
|
||||
//_inputEdit->selectAll();
|
||||
}
|
||||
|
||||
void ExportDialog::accept()
|
||||
{
|
||||
QString mongoExport = "D:\\mongo_export\\bin\\mongoexport.exe";
|
||||
@@ -349,17 +340,7 @@ namespace Robomongo
|
||||
|
||||
void ExportDialog::ui_itemDoubleClicked(QTreeWidgetItem *item, int column)
|
||||
{
|
||||
//auto collectionItem = dynamic_cast<ExplorerCollectionTreeItem*>(item);
|
||||
//if (collectionItem) {
|
||||
// AppRegistry::instance().app()->openShell(collectionItem->collection());
|
||||
// return;
|
||||
//}
|
||||
|
||||
// todo
|
||||
//auto dbTreeItem = dynamic_cast<ExplorerDatabaseTreeItem*>(item);
|
||||
//if (dbTreeItem) {
|
||||
// dbTreeItem->applySettingsForExportDialog();
|
||||
//}
|
||||
}
|
||||
|
||||
void ExportDialog::on_browseButton_clicked()
|
||||
@@ -444,6 +425,7 @@ namespace Robomongo
|
||||
_exportOutput->setText("Export Failed.\n");
|
||||
_exportOutput->append("Output:\n" + _mongoExportOutputStr);
|
||||
}
|
||||
|
||||
_exportOutput->moveCursor(QTextCursor::Start);
|
||||
}
|
||||
|
||||
@@ -464,6 +446,7 @@ namespace Robomongo
|
||||
_exportOutput->setText("Error: \"mongoexport\" process failed. Error code: "
|
||||
+ QString::number(error));
|
||||
}
|
||||
|
||||
_exportOutput->moveCursor(QTextCursor::Start);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,20 @@ 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
|
||||
@@ -32,12 +46,10 @@ namespace Robomongo
|
||||
|
||||
public:
|
||||
explicit ExportDialog(QString const& dbName, QString const& collName, QWidget *parent = 0);
|
||||
//QString databaseName() const;
|
||||
void setOkButtonText(const QString &text);
|
||||
void setInputLabelText(const QString &text);
|
||||
void setInputText(const QString &text);
|
||||
enum { maxLenghtName = 60 };
|
||||
//const static QSize dialogSize;
|
||||
|
||||
public Q_SLOTS:
|
||||
virtual void accept();
|
||||
@@ -53,7 +65,7 @@ namespace Robomongo
|
||||
void on_viewOutputLink(QString);
|
||||
|
||||
private:
|
||||
// todo: remove
|
||||
// todo: remove if unused
|
||||
Indicator *createDatabaseIndicator(const QString &database);
|
||||
Indicator *createCollectionIndicator(const QString &collection);
|
||||
|
||||
|
||||
@@ -152,9 +152,9 @@ namespace Robomongo
|
||||
#endif
|
||||
// Attempt to fix the issue for Windows High DPI button height is slightly taller than other widgets
|
||||
#ifdef Q_OS_WIN
|
||||
_passwordEchoModeButton->setMaximumHeight(HighDpiContants::WIN_HIGH_DPI_BUTTON_HEIGHT);
|
||||
_passphraseEchoModeButton->setMaximumHeight(HighDpiContants::WIN_HIGH_DPI_BUTTON_HEIGHT);
|
||||
_selectPrivateFileButton->setMaximumHeight(HighDpiContants::WIN_HIGH_DPI_BUTTON_HEIGHT);
|
||||
_passwordEchoModeButton->setMaximumHeight(HighDpiConstants::WIN_HIGH_DPI_BUTTON_HEIGHT);
|
||||
_passphraseEchoModeButton->setMaximumHeight(HighDpiConstants::WIN_HIGH_DPI_BUTTON_HEIGHT);
|
||||
_selectPrivateFileButton->setMaximumHeight(HighDpiConstants::WIN_HIGH_DPI_BUTTON_HEIGHT);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
@@ -189,10 +189,10 @@ namespace Robomongo
|
||||
|
||||
// Attempt to fix issue for Windows High DPI button height is slightly taller than other widgets
|
||||
#ifdef Q_OS_WIN
|
||||
_caFileBrowseButton->setMaximumHeight(HighDpiContants::WIN_HIGH_DPI_BUTTON_HEIGHT);
|
||||
_pemFileBrowseButton->setMaximumHeight(HighDpiContants::WIN_HIGH_DPI_BUTTON_HEIGHT);
|
||||
_pemPassShowButton->setMaximumHeight(HighDpiContants::WIN_HIGH_DPI_BUTTON_HEIGHT);
|
||||
_crlFileBrowseButton->setMaximumHeight(HighDpiContants::WIN_HIGH_DPI_BUTTON_HEIGHT);
|
||||
_caFileBrowseButton->setMaximumHeight(HighDpiConstants::WIN_HIGH_DPI_BUTTON_HEIGHT);
|
||||
_pemFileBrowseButton->setMaximumHeight(HighDpiConstants::WIN_HIGH_DPI_BUTTON_HEIGHT);
|
||||
_pemPassShowButton->setMaximumHeight(HighDpiConstants::WIN_HIGH_DPI_BUTTON_HEIGHT);
|
||||
_crlFileBrowseButton->setMaximumHeight(HighDpiConstants::WIN_HIGH_DPI_BUTTON_HEIGHT);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
@@ -20,18 +20,27 @@
|
||||
<file>scripts/esprima.js</file>
|
||||
<file>scripts/uuidhelpers.js</file>
|
||||
<file>icons/maximize.gif</file>
|
||||
<file>icons/undock.png</file>
|
||||
<file>icons/dock.png</file>
|
||||
<file>icons/maximize.png</file>
|
||||
<file>icons/minimize.png</file>
|
||||
<file>icons/text_16x16.png</file>
|
||||
<file>icons/tree_16x16.png</file>
|
||||
<file>icons/rotate_16x16.png</file>
|
||||
<file>icons/close_16x16.png</file>
|
||||
<file>icons/close_hover_16x16.png</file>
|
||||
<file>icons/close_2_16x16.png</file>
|
||||
<file>icons/close_hover_16x16_original.png</file>
|
||||
<file>icons/text2_16x16.png</file>
|
||||
<file>icons/mongodb_16x16.png</file>
|
||||
<file>icons/mongodb_icon_for_MAC.png</file>
|
||||
<file>icons/connect_24x24.png</file>
|
||||
<file>icons/server_16x16.png</file>
|
||||
<file>icons/server_primary_16x16.png</file>
|
||||
<file>icons/server_secondary_16x16.png</file>
|
||||
<file>icons/server_imported_16x16.png</file>
|
||||
<file>icons/replica_set_16x16.png</file>
|
||||
<file>icons/replica_set_offline_16x16.png</file>
|
||||
<file>icons/execute_24x24.png</file>
|
||||
<file>icons/main_window_icon_old.png</file>
|
||||
<file>icons/logo-256x256.png</file>
|
||||
@@ -64,5 +73,11 @@
|
||||
<file>icons/show_64x64.png</file>
|
||||
<file>icons/export_64x64.png</file>
|
||||
<file>icons/import_64x64.png</file>
|
||||
<file>icons/plus_sign.png</file>
|
||||
<file>icons/minus_sign.png</file>
|
||||
<file>icons/delete.png</file>
|
||||
<file>icons/delete_red_color.png</file>
|
||||
<file>icons/delete_mouse_hovered.png</file>
|
||||
<file>icons/welcome_tab_icon.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
|
After Width: | Height: | Size: 518 B |
|
After Width: | Height: | Size: 334 B |
|
After Width: | Height: | Size: 550 B |
|
After Width: | Height: | Size: 189 B |
|
After Width: | Height: | Size: 388 B |
|
After Width: | Height: | Size: 384 B |
|
After Width: | Height: | Size: 168 B |
|
After Width: | Height: | Size: 509 B |
|
After Width: | Height: | Size: 192 B |
|
Before Width: | Height: | Size: 511 B After Width: | Height: | Size: 446 B |
|
After Width: | Height: | Size: 407 B |
|
After Width: | Height: | Size: 397 B |