Compare commits

..
141 changed files with 1594 additions and 7111 deletions
+48 -141
View File
@@ -1,154 +1,61 @@
Latest Version:
Robomongo 1.0 (21 Apr, 2017)
http://blog.robomongo.org/robomongo-1-0
Latest Release:
Robomongo 0.9.0 RC10 (Aug 8, 2016)
http://blog.robomongo.org/robomongo-rc10/
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
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)
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
@@ -223,7 +130,7 @@ Robomongo 0.9:
* [Andres Kievsky]
- Added new app icon in a variety of png and iDraw formats (#693)
* [Michael Steinacher]
- Retina text display support (#724)
- Retina text display support (#724)
0.8.4 / November 27, 2013
+4 -6
View File
@@ -5,10 +5,10 @@ project(Robomongo)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
# Project version
set(PROJECT_VERSION_MAJOR "1")
set(PROJECT_VERSION_MINOR "0")
set(PROJECT_VERSION_PATCH "3")
set(PROJECT_VERSION_BUILD "")
set(PROJECT_VERSION_MAJOR "0")
set(PROJECT_VERSION_MINOR "9")
set(PROJECT_VERSION_PATCH "0")
set(PROJECT_VERSION_BUILD "-RC10")
set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}${PROJECT_VERSION_BUILD}")
# Modules
@@ -27,8 +27,6 @@ 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 -1
View File
@@ -1,6 +1,6 @@
Robomongo, shell-centric cross-platform MongoDB management tool.
Copyright 2014-2017 3T Software Labs Ltd.
Copyright 2013, Paralect.
The Robomongo is released under the terms of the GNU General Public
License, version 3.
+33 -33
View File
@@ -1,4 +1,4 @@
About Robomongo [![Join the chat at https://gitter.im/paralect/robomongo](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/paralect/robomongo?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
About Robomongo
===============
[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,34 +7,31 @@ Starting from version 0.9, Robomongo is compatibile with MongoDB 3.x (including
Robomongo 0.8.x embeds the **MongoDB 2.4.0** shell.
What's new in latest Robomongo 1.0?
What's new in latest Robomongo 0.9.0 RC10?
====================================
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
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.
Blog: http://blog.robomongo.org/robomongo-1-0/
Blog: http://blog.robomongo.org/robomongo-rc10
Download: https://robomongo.org/download
Watch: [Robomongo Youtube channel](https://www.youtube.com/channel/UCM_7WAseRWeeiBikExppstA)
Follow: https://twitter.com/Robomongo
What's Planned for the Next Releases?
What's Planned for the Next Release?
====================================
We are currently working towards [Robomongo 2.0 milestone](https://github.com/paralect/robomongo/milestone/21).
We are currently working towards [Robomongo 0.9.0 RC11 milestone](https://github.com/paralect/robomongo/milestone/16).
Currently in progress:
- MongoDB 3.4 Support
- Replica Sets Support
Plans for Future:
- User Roles
- Support execution of multiple simultaneous queries [#1161](https://github.com/paralect/robomongo/issues/1161)
- Selectable mongodb engines [#1249](https://github.com/paralect/robomongo/issues/1249)
- Enhancements for stability, running without crashes ([Stability Milestone](https://github.com/paralect/robomongo/milestone/15))
- Enhancements for stability, running without crashes
- More Documentation
- Unit Tests
- Refactoring Debug Log module
@@ -42,19 +39,20 @@ Plans for Future:
Supported Platforms
===============
| MongoDB Versions |
| :-------------------- |
| 3.2 |
| 3.0 |
| 2.6 |
**Supported MongoDBs:**
| MongoDB Cloud Platforms|
| :------------ |
| MongoDB Atlas |
| Compose |
| mLab |
| ObjectRocket |
| Amazon EC2 |
| 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:**
| Windows | Mac | Linux |
|:---------------------- | :---------------| :---------------------------|
@@ -65,13 +63,14 @@ 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 stable release is currently [**Robomongo 1.0**](http://blog.robomongo.org/robomongo-1-0/).
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/)
Support
=======
@@ -82,7 +81,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)
- [Join developer discussion on Gitter](https://gitter.im/paralect/robomongo) [![Join the chat at https://gitter.im/paralect/robomongo](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/paralect/robomongo?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
Build
=====
@@ -97,7 +96,6 @@ 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).
@@ -109,6 +107,8 @@ 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 2014-2017 [3T Software Labs Ltd](https://studio3t.com/). All rights reserved.
Copyright (C) 2013-2016 [Paralect, Inc](http://www.paralect.com)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3 as
@@ -8,8 +8,6 @@ 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("|")
+4 -6
View File
@@ -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,9 +72,7 @@ elseif(SYSTEM_MACOSX)
elseif(SYSTEM_LINUX)
install(
FILES
"${OpenSSL_DIR}/libssl.so"
"${OpenSSL_DIR}/libssl.so.1.0.0"
"${OpenSSL_DIR}/libcrypto.so"
"${OpenSSL_DIR}/libssl.so.1.0.0"
"${OpenSSL_DIR}/libcrypto.so.1.0.0"
DESTINATION ${lib_dir})
endif()
@@ -95,7 +93,7 @@ install(
DESTINATION ${license_dir})
# Install common dependencies
install_qt_lib(Core Gui Widgets PrintSupport Network Xml)
install_qt_lib(Core Gui Widgets PrintSupport)
install_qt_plugins(QGifPlugin QICOPlugin)
install_icu_libs()
@@ -113,7 +111,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(
+4 -4
View File
@@ -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 "3T Software Labs Ltd")
set(CPACK_PACKAGE_CONTACT robomongo@3t.io)
set(CPACK_PACKAGE_VENDOR Paralect)
set(CPACK_PACKAGE_CONTACT robomongo@paralect.com)
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")
+3 -3
View File
@@ -18,9 +18,9 @@ endif()
set(PROJECT_NAME "Robomongo")
set(PROJECT_NAME_TITLE ${PROJECT_NAME})
set(PROJECT_DOMAIN "www.robomongo.org")
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_COMPANYNAME "Paralect")
set(PROJECT_COPYRIGHT "Copyright (C) 2013-2015 ${PROJECT_COMPANYNAME} All Rights Reserved.")
set(PROJECT_COMPANYNAME_DOMAIN "www.paralect.com")
set(PROJECT_GITHUB_FORK "www.github.com/paralect/robomongo")
set(PROJECT_GITHUB_ISSUES "www.github.com/paralect/robomongo/issues")
+1 -12
View File
@@ -125,15 +125,4 @@ 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
```
+5 -32
View File
@@ -12,46 +12,19 @@ Runtime dependencies that are not privided by platforms but required by Robomong
Diagnostic of dependencies
--------------------------
#### Linux
##### Linux
a. View dependencies of executable or shared library:
View dependencies of executable or shared library:
$ ldd robomongo
$ 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:
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`
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
##### 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

+2 -2
View File
@@ -26,10 +26,10 @@ IDI_ICON1 ICON "@windows_icon@"
BEGIN
BLOCK "040904e4"
BEGIN
VALUE "CompanyName", "3T Software Labs Ltd\0"
VALUE "CompanyName", "Paralect, Inc\0"
VALUE "FileDescription", "Robomongo, MongoDB management tool\0"
VALUE "FileVersion", VER_FILEVERSION_STR
VALUE "LegalCopyright", "Copyright 3T Software Labs Ltd, 2014 - 2017\0"
VALUE "LegalCopyright", "Copyright Paralect, 2013 - 2016\0"
VALUE "ProductName", "Robomongo\0"
VALUE "ProductVersion", VER_PRODUCTVERSION_STR
VALUE "OriginalFilename", "Robomongo.exe\0"
+1 -1
View File
@@ -1,6 +1,6 @@
Robomongo shortcut key reference
================================
"3T", "Robomongo"
### Windows/Linux users
Ctrl+Enter or F5 or Ctrl+R
+6 -19
View File
@@ -53,16 +53,12 @@ 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
@@ -79,7 +75,6 @@ set(SOURCES
# Isolated scope #4
gui/dialogs/PreferencesDialog.cpp
gui/dialogs/ConnectionsDialog.cpp
gui/dialogs/CreateConnectionDialog.cpp
gui/dialogs/ExportDialog.cpp
# Isolated scope #5
@@ -92,7 +87,6 @@ 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
@@ -124,7 +118,6 @@ 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
@@ -134,10 +127,9 @@ 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
)
@@ -148,8 +140,6 @@ add_executable(robomongo MACOSX_BUNDLE WIN32 app/main.cpp ${SOURCES})
target_link_libraries(robomongo
PRIVATE
Qt5::Widgets
Qt5::Network
Qt5::Xml
qjson
qscintilla
mongodb
@@ -171,8 +161,6 @@ 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)
@@ -203,18 +191,17 @@ 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.3tsoftwarelabs.robomongo"
MACOSX_BUNDLE_INFO_STRING "Robomongo ${PROJECT_VERSION}. Copyright 3T Software Labs Ltd, 2014 - 2017"
MACOSX_BUNDLE_GUI_IDENTIFIER "com.paralect.Robomongo"
MACOSX_BUNDLE_INFO_STRING "Robomongo ${PROJECT_VERSION}. Copyright Paralect, 2013 - 2016"
MACOSX_BUNDLE_SHORT_VERSION "${PROJECT_VERSION}"
MACOSX_BUNDLE_LONG_VERSION "${PROJECT_VERSION}"
MACOSX_BUNDLE_BUNDLE_VERSION "${PROJECT_VERSION}"
MACOSX_BUNDLE_COPYRIGHT "Copyright 3T Software Labs Ltd, 2014 - 2017"
MACOSX_BUNDLE_ICON_FILE "robomongo")
MACOSX_BUNDLE_COPYRIGHT "Copyright Paralect, 2013 - 2016"
MACOSX_BUNDLE_ICON_FILE "Robomongo")
elseif(SYSTEM_LINUX)
set_target_properties(robomongo PROPERTIES
INSTALL_RPATH "$ORIGIN/../lib"
OUTPUT_NAME "robomongo")
INSTALL_RPATH "$ORIGIN/../lib")
endif()
# Install
+4 -19
View File
@@ -10,18 +10,16 @@
#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
@@ -56,25 +54,12 @@ 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 mainWindow;
mainWindow.show();
Robomongo::MainWindow win;
win.show();
int rc = app.exec();
rbm_ssh_cleanup();
+7 -9
View File
@@ -32,18 +32,16 @@ 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,
// 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
// The same as Primary, but is specifically for testing
// connections.
ConnectionTest = 2
};
}
+4 -20
View File
@@ -1,35 +1,19 @@
#include "robomongo/core/EventError.h"
#include "robomongo/utils/string_operations.h"
namespace Robomongo
{
EventError::EventError() :
_isNull(true) {}
EventError::EventError(const std::string &errorMessage, ErrorCode errorCode /*= Unknown*/,
bool showErrorWindow /* = true */) :
_errorMessage(captilizeFirstChar(errorMessage)),
_errorCode(errorCode),
_showErrorWindow(showErrorWindow),
EventError::EventError(const std::string &errorMessage) :
_errorMessage(errorMessage),
_isNull(false) {}
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
{
bool EventError::isNull() const {
return _isNull;
}
const std::string &EventError::errorMessage() const
{
const std::string &EventError::errorMessage() const {
return _errorMessage;
}
}
+4 -23
View File
@@ -1,8 +1,6 @@
#pragma once
#include <string>
#include "robomongo/core/mongodb/ReplicaSet.h"
namespace Robomongo
{
/**
@@ -12,11 +10,7 @@ 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.
@@ -27,14 +21,7 @@ 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, 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);
explicit EventError(const std::string &errorMessage);
/**
* @brief Tests whether error is "null" (or, in other words, empty).
@@ -48,19 +35,13 @@ 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
*/
std::string const _errorMessage;
ErrorCode const _errorCode = Unknown;
ReplicaSet const _replicaSetInfo;
bool const _showErrorWindow = true;
bool const _isNull;
const std::string _errorMessage;
bool _isNull;
};
}
+22 -38
View File
@@ -6,7 +6,6 @@
#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"
@@ -106,7 +105,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* connSettings, ConnectionType type) {
MongoServer *App::openServerInternal(ConnectionSettings const* connection, ConnectionType type) {
++_lastServerHandle;
if (type == ConnectionPrimary)
@@ -114,16 +113,16 @@ namespace Robomongo
// When connection is SECONDARY or do not have
// ssh settings enabled, continue without SSH Tunnel
if (type == ConnectionSecondary || !connSettings->sshSettings()->enabled()) {
return continueOpenServer(_lastServerHandle, connSettings, type);
if (type == ConnectionSecondary || !connection->sshSettings()->enabled()) {
return continueOpenServer(_lastServerHandle, connection, type);
}
// Open SSH channel and only after that open connection
LOG_MSG(QString("Creating SSH tunnel to %1:%2...")
.arg(QtUtils::toQString(connSettings->sshSettings()->host()))
.arg(connSettings->sshSettings()->port()), mongo::logger::LogSeverity::Info());
.arg(QtUtils::toQString(connection->sshSettings()->host()))
.arg(connection->sshSettings()->port()), mongo::logger::LogSeverity::Info());
ConnectionSettings* settingsCopy = connSettings->clone();
ConnectionSettings* settingsCopy = connection->clone();
SshTunnelWorker* sshWorker = new SshTunnelWorker(settingsCopy);
_bus->send(sshWorker, new EstablishSshConnectionRequest(this, _lastServerHandle, sshWorker, settingsCopy, type));
return NULL;
@@ -135,8 +134,7 @@ 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)
@@ -144,8 +142,7 @@ namespace Robomongo
ConnectionSettings *connection = collection->database()->server()->connectionRecord();
connection->setDefaultDatabase(collection->database()->name());
QString script = detail::buildCollectionQuery(collection->name(), "find({})");
openShell(collection->database()->server(), connection, ScriptInfo(script, true, CursorPosition(0, -2),
QtUtils::toQString(collection->database()->name()), filePathToSave));
openShell(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,
@@ -157,7 +154,7 @@ namespace Robomongo
if (!dbName.empty())
connection->setDefaultDatabase(dbName);
openShell(server, connection, ScriptInfo(script, execute, cursorPosition, shellName, filePathToSave));
openShell(connection, ScriptInfo(script, execute, cursorPosition, shellName, filePathToSave));
}
void App::openShell(MongoDatabase *database, const QString &script,
@@ -166,19 +163,16 @@ namespace Robomongo
{
ConnectionSettings *connection = database->server()->connectionRecord();
connection->setDefaultDatabase(database->name());
openShell(database->server(), connection, ScriptInfo(script, execute, cursorPosition,
shellName, filePathToSave));
openShell(connection, ScriptInfo(script, execute, cursorPosition, shellName, filePathToSave));
}
void App::openShell(MongoServer* server, ConnectionSettings* connection, const ScriptInfo &scriptInfo)
void App::openShell(ConnectionSettings const* connection, const ScriptInfo &scriptInfo)
{
MongoServer *serverClone = openServerInternal(connection, ConnectionSecondary);
if (!serverClone || !server)
MongoServer *server = openServerInternal(connection, ConnectionSecondary);
if (!server)
return;
MongoShell *shell = new MongoShell(serverClone, scriptInfo);
// Connection between explorer's server and tab's MongoShells
_bus->subscribe(server, ReplicaSetRefreshed::Type, shell);
MongoShell *shell = new MongoShell(server, scriptInfo);
_shells.push_back(shell);
_bus->publish(new OpeningShellEvent(this, shell));
shell->execute();
@@ -201,33 +195,22 @@ namespace Robomongo
delete shell;
}
MongoServer *App::continueOpenServer(int serverHandle, ConnectionSettings* connSettings, ConnectionType type,
int localport)
{
ConnectionSettings* connSettingsClone = connSettings->clone();
MongoServer *App::continueOpenServer(int serverHandle, ConnectionSettings const* connection, ConnectionType type, int localport) {
ConnectionSettings* settings = connection->clone();
// Modify connection settings when SSH tunnel is used
if ((type == ConnectionPrimary || type == ConnectionTest)
&& connSettingsClone->sshSettings()->enabled()) {
connSettingsClone->setServerHost("127.0.0.1");
connSettingsClone->setServerPort(localport);
&& settings->sshSettings()->enabled()) {
settings->setServerHost("127.0.0.1");
settings->setServerPort(localport);
}
MongoServer *server = new MongoServer(serverHandle, connSettingsClone, type);
MongoServer *server = new MongoServer(serverHandle, settings, type);
_servers.push_back(server);
server->runWorkerThread();
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());
LOG_MSG(QString("Connecting to %1...").arg(QtUtils::toQString(server->connectionRecord()->getFullAddress())), mongo::logger::LogSeverity::Info());
server->tryConnect();
return server;
}
@@ -273,6 +256,7 @@ namespace Robomongo
_bus->publish(new ConnectionFailedEvent(this, serverHandle, type, errormsg, reason));
}
bool App::askSslPassphrasePromptDialog(ConnectionSettings *connSettings) const
{
auto sslSettings = connSettings->sslSettings();
+3 -7
View File
@@ -70,11 +70,7 @@ namespace Robomongo
bool execute = true, const QString &shellName = QString(),
const CursorPosition &cursorPosition = CursorPosition(), const QString &filePathToSave = QString());
/**
* @brief Open new shell using explorer's MongoServer (ExplorerServerTreeItem's _server)
*/
void openShell(MongoServer* server, ConnectionSettings* connSettings, const ScriptInfo &scriptInfo);
void openShell(ConnectionSettings const* connection, const ScriptInfo &scriptInfo);
MongoServersContainerType getServers() const { return _servers; };
/**
@@ -93,8 +89,8 @@ namespace Robomongo
void handle(LogEvent *event);
private:
MongoServer *openServerInternal(ConnectionSettings* connSettings, ConnectionType type);
MongoServer *continueOpenServer(int serverHandle, ConnectionSettings* connSettings, ConnectionType type, int localport = 0);
MongoServer *openServerInternal(ConnectionSettings const* connection, ConnectionType type);
MongoServer *continueOpenServer(int serverHandle, ConnectionSettings const *connection, ConnectionType type, int localport = 0);
/**
* @brief Create prompt dialog to enter SSL PEM key passphrase and save passphrase into SSL settings
+35 -143
View File
@@ -5,8 +5,6 @@
#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
{
@@ -39,148 +37,115 @@ namespace Robomongo
void MongoDatabase::loadCollections()
{
_bus->publish(new MongoDatabaseCollectionsLoadingEvent(this));
_bus->send(_server->worker(), new LoadCollectionNamesRequest(this, _name));
_bus->send(_server->client(), new LoadCollectionNamesRequest(this, _name));
}
void MongoDatabase::loadUsers()
{
_bus->publish(new MongoDatabaseUsersLoadingEvent(this));
_bus->send(_server->worker(), new LoadUsersRequest(this, _name));
_bus->send(_server->client(), new LoadUsersRequest(this, _name));
}
void MongoDatabase::loadFunctions()
{
_bus->publish(new MongoDatabaseFunctionsLoadingEvent(this));
_bus->send(_server->worker(), new LoadFunctionsRequest(this, _name));
_bus->send(_server->client(), 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->worker(),
_bus->send(_server->client(),
new CreateCollectionRequest(this, MongoNamespace(_name, collection), extraOptions, size, capped, maxDocNum));
}
void MongoDatabase::dropCollection(const std::string &collection)
{
_bus->send(_server->worker(), new DropCollectionRequest(this, MongoNamespace(_name, collection)));
_bus->send(_server->client(), new DropCollectionRequest(this, MongoNamespace(_name, collection)));
}
void MongoDatabase::renameCollection(const std::string &collection, const std::string &newCollection)
{
_bus->send(_server->worker(), new RenameCollectionRequest(this, MongoNamespace(_name, collection),
newCollection));
_bus->send(_server->client(), new RenameCollectionRequest(this, MongoNamespace(_name, collection), newCollection));
}
void MongoDatabase::duplicateCollection(const std::string &collection, const std::string &newCollection)
{
_bus->send(_server->worker(), new DuplicateCollectionRequest(this, MongoNamespace(_name, collection), newCollection));
_bus->send(_server->client(), new DuplicateCollectionRequest(this, MongoNamespace(_name, collection), newCollection));
}
void MongoDatabase::copyCollection(MongoServer *server, const std::string &sourceDatabase, const std::string &collection)
{
_bus->send(_server->worker(), new CopyCollectionToDiffServerRequest(this, server->worker(), sourceDatabase, collection, _name));
_bus->send(_server->client(), new CopyCollectionToDiffServerRequest(this, server->client(), sourceDatabase, collection, _name));
}
void MongoDatabase::createUser(const MongoUser &user, bool overwrite)
{
_bus->send(_server->worker(), new CreateUserRequest(this, _name, user, overwrite));
_bus->send(_server->client(), new CreateUserRequest(this, _name, user, overwrite));
}
void MongoDatabase::dropUser(const mongo::OID &id, std::string const& userName)
void MongoDatabase::dropUser(const mongo::OID &id)
{
_bus->send(_server->worker(), new DropUserRequest(this, _name, id, userName));
_bus->send(_server->client(), new DropUserRequest(this, _name, id));
}
void MongoDatabase::createFunction(const MongoFunction &fun)
{
_bus->send(_server->worker(), new CreateFunctionRequest(this, _name, fun));
_bus->send(_server->client(), new CreateFunctionRequest(this, _name, fun));
}
void MongoDatabase::updateFunction(const std::string &name, const MongoFunction &fun)
{
_bus->send(_server->worker(), new CreateFunctionRequest(this, _name, fun, name));
_bus->send(_server->client(), new CreateFunctionRequest(this, _name, fun, name));
}
void MongoDatabase::dropFunction(const std::string &name)
{
_bus->send(_server->worker(), new DropFunctionRequest(this, _name, name));
_bus->send(_server->client(), new DropFunctionRequest(this, _name, name));
}
void MongoDatabase::handle(LoadCollectionNamesResponse *event)
void MongoDatabase::handle(LoadCollectionNamesResponse *loaded)
{
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);
if (loaded->isError()) {
_bus->publish(new MongoDatabaseCollectionListLoadedEvent(this, loaded->error()));
return;
}
clearCollections();
for (auto const& collectionInfo : event->collectionInfos())
addCollection(new MongoCollection(this, collectionInfo));
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);
}
_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()) {
handleIfReplicaSetUnreachable(event);
genericEventErrorHandler(event, "Failed to create user \'" + event->userName + "\'.", _bus, this);
}
else {
loadUsers();
LOG_MSG("User \'" + event->userName + "\' created.", mongo::logger::LogSeverity::Info());
}
if (event->isError())
return;
}
void MongoDatabase::handle(LoadUsersResponse *event)
{
if (event->isError()) {
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);
_bus->publish(new MongoDatabaseUsersLoadedEvent(this, event->error()));
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()) {
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);
_bus->publish(new MongoDatabaseFunctionsLoadedEvent(this, event->error()));
return;
}
_bus->publish(new MongoDatabaseFunctionsLoadedEvent(this, this, event->functions()));
LOG_MSG("'Functions' refreshed.", mongo::logger::LogSeverity::Info());
}
void MongoDatabase::clearCollections()
@@ -194,91 +159,18 @@ namespace Robomongo
_collections.push_back(collection);
}
void MongoDatabase::handle(CreateCollectionResponse *event)
{
if (event->isError()) {
handleIfReplicaSetUnreachable(event);
genericEventErrorHandler(event, "Failed to create collection \'" + event->collection + "\'.", _bus, this);
}
else {
loadCollections();
LOG_MSG("Collection \'" + event->collection + "\' created.", mongo::logger::LogSeverity::Info());
}
void MongoDatabase::handle(CreateCollectionResponse *event) {
genericResponseHandler(event, "Failed to create 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::handle(DropCollectionResponse *event) {
genericResponseHandler(event, "Failed to drop collection.");
}
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())
void MongoDatabase::genericResponseHandler(Event *event, const std::string &userFriendlyMessage) {
if (!event->isError())
return;
if (EventError::SetPrimaryUnreachable == event->error().errorCode()) {
auto refreshEvent = ReplicaSetRefreshed(this, event->error(), event->error().replicaSetInfo());
_server->handle(&refreshEvent);
}
_bus->publish(new OperationFailedEvent(this, event->error().errorMessage(), userFriendlyMessage));
}
}
+2 -7
View File
@@ -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, std::string const& userName);
void dropUser(const mongo::OID &id);
void createFunction(const MongoFunction &fun);
void updateFunction(const std::string &name, const MongoFunction &fun);
@@ -83,19 +83,14 @@ namespace Robomongo
void handle(LoadCollectionNamesResponse *event);
void handle(LoadUsersResponse *event);
void handle(LoadFunctionsResponse *event);
void handle(CreateFunctionResponse *event);
void handle(CreateUserResponse *event);
void handle(CreateCollectionResponse *event);
void handle(DropCollectionResponse *event);
void handle(DropFunctionResponse *event);
void handle(DropUserResponse *event);
void handle(RenameCollectionResponse *event);
void handle(DuplicateCollectionResponse *event);
private:
void clearCollections();
void addCollection(MongoCollection *collection);
void handleIfReplicaSetUnreachable(Event *event);
void genericResponseHandler(Event *event, const std::string &userFriendlyMessage);
private:
MongoServer *_server;
+110 -351
View File
@@ -4,17 +4,12 @@
#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)
@@ -22,64 +17,45 @@ namespace Robomongo {
MongoServer::MongoServer(int handle, ConnectionSettings *settings, ConnectionType connectionType) : QObject(),
_version(0.0f),
_connectionType(connectionType),
_worker(nullptr),
_client(NULL),
_isConnected(false),
_connSettings(settings),
_settings(settings),
_handle(handle),
_bus(AppRegistry::instance().bus()),
_app(AppRegistry::instance().app()),
_replicaSetInfo(nullptr)
{}
_app(AppRegistry::instance().app()) { }
bool MongoServer::isConnected() const {
return _isConnected;
}
ConnectionSettings *MongoServer::connectionRecord() const {
return _connSettings.get();
return _settings;
}
MongoServer::~MongoServer() {
clearDatabases();
if (_worker) {
_worker->stopAndDelete();
if (_client != NULL) {
_client->stopAndDelete();
}
// MongoWorker "_worker" is not deleted here, because it is now owned by
// MongoWorker "_client" does 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;
}
void MongoServer::tryConnect()
{
_bus->send(_worker, new EstablishConnectionRequest(this, _connectionType, _connSettings->uuid().toStdString()));
/**
* @brief Try to connect to MongoDB server.
* @throws MongoException, if fails
*/
void MongoServer::tryConnect() {
_bus->send(_client, new EstablishConnectionRequest(this, _connectionType));
}
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 MongoServer::getDatabasesNames() const {
QStringList result;
for (QList<MongoDatabase *>::const_iterator it = _databases.begin(); it != _databases.end(); ++it) {
MongoDatabase *datab = *it;
@@ -88,17 +64,11 @@ namespace Robomongo {
return result;
}
void MongoServer::addDatabase(MongoDatabase *database) {
_databases.append(database);
void MongoServer::createDatabase(const std::string &dbName) {
_bus->send(_client, new CreateDatabaseRequest(this, dbName));
}
void MongoServer::createDatabase(const std::string &dbName)
{
_bus->send(_worker, new CreateDatabaseRequest(this, dbName));
}
MongoDatabase *MongoServer::findDatabaseByName(const std::string &dbName) const
{
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) {
@@ -109,7 +79,7 @@ namespace Robomongo {
}
void MongoServer::dropDatabase(const std::string &dbName) {
_bus->send(_worker, new DropDatabaseRequest(this, dbName));
_bus->send(_client, new DropDatabaseRequest(this, dbName));
}
void MongoServer::insertDocuments(const std::vector<mongo::BSONObj> &objCont,
@@ -120,7 +90,7 @@ namespace Robomongo {
}
void MongoServer::insertDocument(const mongo::BSONObj &obj, const MongoNamespace &ns) {
_bus->send(_worker, new InsertDocumentRequest(this, obj, ns));
_bus->send(_client, new InsertDocumentRequest(this, obj, ns));
}
void MongoServer::saveDocuments(const std::vector<mongo::BSONObj> &objCont, const MongoNamespace &ns) {
@@ -130,315 +100,33 @@ namespace Robomongo {
}
void MongoServer::saveDocument(const mongo::BSONObj &obj, const MongoNamespace &ns) {
_bus->send(_worker, new InsertDocumentRequest(this, obj, ns, true));
_bus->send(_client, new InsertDocumentRequest(this, obj, ns, true));
}
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::removeDocuments(mongo::Query query, const MongoNamespace &ns, bool justOne) {
_bus->send(_client, new RemoveDocumentRequest(this, query, ns, justOne));
}
void MongoServer::loadDatabases()
{
void MongoServer::loadDatabases() {
_bus->publish(new MongoServerLoadingDatabasesEvent(this));
if (_connSettings->isReplicaSet()) {
tryRefreshReplicaSetConnection();
}
else { // single server
_bus->send(_worker, new LoadDatabaseNamesRequest(this));
}
_bus->send(_client, new LoadDatabaseNamesRequest(this));
}
void MongoServer::clearDatabases()
{
void MongoServer::clearDatabases() {
qDeleteAll(_databases);
_databases.clear();
}
void MongoServer::handle(EstablishConnectionResponse *event)
{
_connectionType = event->connectionType;
void MongoServer::addDatabase(MongoDatabase *database) {
_databases.append(database);
}
// 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
void MongoServer::handle(EstablishConnectionResponse *event) {
if (event->isError()) {
handleConnectionFailure(event);
return;
}
_isConnected = false;
// --- 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(RefreshReplicaSetFolderResponse *event)
{
handleReplicaSetRefreshEvents(event->isError(), event->error(), event->replicaSet, event->expanded);
}
void MongoServer::handle(LoadDatabaseNamesResponse *event)
{
if (event->isError()) {
_bus->publish(new DatabaseListLoadedEvent(this, event->error()));
return;
}
clearDatabases();
for (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
{
std::stringstream ss("Unknown error");
auto eventErrorReason = event->errorReason;
if (EstablishConnectionResponse::ErrorReason::MongoSslConnection == eventErrorReason)
{
auto reason = ConnectionFailedEvent::SslConnection;
@@ -459,14 +147,85 @@ 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);
}
}
} // namespace Robomongo
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));
}
}
+19 -50
View File
@@ -12,12 +12,11 @@ namespace Robomongo
class App;
// Messages
struct EstablishConnectionResponse;
struct RefreshReplicaSetFolderResponse;
class EstablishConnectionResponse;
class LoadDatabaseNamesResponse;
class InsertDocumentResponse;
struct CreateDatabaseResponse;
struct DropDatabaseResponse;
class CreateDatabaseResponse;
class DropDatabaseResponse;
/**
* @brief MongoServer represents active connection to MongoDB server.
@@ -41,52 +40,22 @@ namespace Robomongo
void runWorkerThread();
/**
* @brief Try to connect to MongoDB single server or replica set.
* @brief Try to connect to MongoDB server.
* @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, RemoveDocumentCount removeCount,
int index = 0);
void removeDocuments(mongo::Query query, const MongoNamespace &ns, bool justOne = true);
float version() const{ return _version; }
const std::string& getStorageEngineType() const { return _storageEngineType; }
@@ -99,30 +68,27 @@ namespace Robomongo
* @brief Loads databases of this server asynchronously.
*/
void loadDatabases();
MongoWorker *const worker() const { return _worker; }
MongoWorker *const client() const { return _client; }
ReplicaSet* replicaSetInfo() const { return _replicaSetInfo.get(); }
void handle(ReplicaSetRefreshed *event);
// --- Getters ---
const mongo::HostAndPort& getRepPrimary() const { return _repPrimary; }
const std::vector<bool>& getRepMembersHealths() const { return _repMembersHealths; }
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 handleReplicaSetRefreshEvents(bool isError, EventError eventError, ReplicaSet const& replicaSet,
bool expanded);
void updateReplicaSetSettings(EstablishConnectionResponse* event);
void handleConnectionFailure(EstablishConnectionResponse* event);
void addDatabase(MongoDatabase *database);
void genericResponseHandler(Event *event, const std::string &userFriendlyMessage);
MongoWorker *_worker;
std::unique_ptr<ConnectionSettings> _connSettings;
MongoWorker *_client;
ConnectionSettings *_settings;
EventBus *_bus;
App *_app;
@@ -133,7 +99,10 @@ namespace Robomongo
int _handle;
QList<MongoDatabase *> _databases;
std::unique_ptr<ReplicaSet> _replicaSetInfo;
// Replica Set Status
mongo::HostAndPort _repPrimary;
std::vector<bool> _repMembersHealths; // todo: vector of pairs of host and health
};
class MongoServerLoadingDatabasesEvent : public Event
+9 -18
View File
@@ -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->worker(), new ExecuteScriptRequest(this, query(), dbName));
AppRegistry::instance().bus()->send(_server->client(), 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->worker(), new ExecuteScriptRequest(this, query(), dbName));
AppRegistry::instance().bus()->send(_server->client(), 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->worker(), new ExecuteScriptRequest(this, query() , dbName));
AppRegistry::instance().bus()->send(_server->client(), new ExecuteScriptRequest(this, query() , dbName));
}
}
void MongoShell::query(int resultIndex, const MongoQueryInfo &info)
{
AppRegistry::instance().bus()->send(_server->worker(), new ExecuteQueryRequest(this, resultIndex, info));
AppRegistry::instance().bus()->send(_server->client(), 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->worker(), new AutocompleteRequest(this, prefix, autocompletionMode));
AppRegistry::instance().bus()->send(_server->client(), new AutocompleteRequest(this, prefix, autocompletionMode));
}
void MongoShell::stop()
{
// _server->worker()->interrupt();
// _server->client()->interrupt();
// mongo::Scope::setInterruptFlag(true);
}
@@ -94,17 +94,8 @@ namespace Robomongo
void MongoShell::handle(ExecuteScriptResponse *event)
{
if (event->isError()) {
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->error()));
return;
}
AppRegistry::instance().bus()->publish(new ScriptExecutedEvent(this, event->result, event->empty));
+1 -10
View File
@@ -29,22 +29,15 @@ namespace Robomongo
{
public:
MongoShellExecResult() { }
MongoShellExecResult(const std::vector<MongoShellResult> &results,
const std::string &currentServer, bool isCurrentServerValid,
const std::string &currentDatabase, 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; }
void setCurrentServer(std::string const& server) { _currentServer = server; }
std::string currentDatabase() const { return _currentDatabase; }
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;
@@ -52,7 +45,5 @@ namespace Robomongo
std::string _currentDatabase;
bool _isCurrentServerValid;
bool _isCurrentDatabaseValid;
std::string _errorMessage;
bool _error = false;
};
}
+92 -99
View File
@@ -25,10 +25,10 @@ namespace Robomongo
{
namespace detail
{
bool isSimpleType(Robomongo::BsonTreeItem const *item)
bool isSimpleType(Robomongo::BsonTreeItem *item)
{
return BsonUtils::isSimpleType(item->type()) ||
BsonUtils::isUuidType(item->type(), item->binType());
return Robomongo::BsonUtils::isSimpleType(item->type())
|| Robomongo::BsonUtils::isUuidType(item->type(), item->binType());
}
bool isObjectIdType(Robomongo::BsonTreeItem *item)
@@ -41,17 +41,17 @@ namespace Robomongo
return indexes.count() > 1;
}
bool isDocumentType(BsonTreeItem const *item)
bool isDocumentType(BsonTreeItem *item)
{
return BsonUtils::isDocument(item->type());
return Robomongo::BsonUtils::isDocument(item->type());
}
bool isArrayChild(BsonTreeItem const *item)
bool isArrayChild(BsonTreeItem *item)
{
return BsonUtils::isArray(dynamic_cast<BsonTreeItem*>(item->parent())->type());
return Robomongo::BsonUtils::isArray(dynamic_cast<BsonTreeItem *>(item->parent())->type());
}
bool isDocumentRoot(BsonTreeItem const *item)
bool isDocumentRoot(BsonTreeItem *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, _shell->server());
AppRegistry::instance().bus()->subscribe(this, RemoveDocumentResponse::Type, _shell->server());
AppRegistry::instance().bus()->subscribe(this, InsertDocumentResponse::Type);
AppRegistry::instance().bus()->subscribe(this, RemoveDocumentResponse::Type);
_deleteDocumentAction = new QAction("Delete Document...", wid);
VERIFY(connect(_deleteDocumentAction, SIGNAL(triggered()), SLOT(onDeleteDocument())));
@@ -158,18 +158,19 @@ 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)) 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);
}
@@ -181,12 +182,11 @@ namespace Robomongo
if (isEditable) menu->addAction(_deleteDocumentsAction);
}
void Notifier::deleteDocuments(std::vector<BsonTreeItem*> const& items, bool force)
void Notifier::deleteDocuments(std::vector<BsonTreeItem*> items, bool force)
{
bool isNeededRefresh = false;
int index = 0;
for (auto const * const documentItem : items) {
for (std::vector<BsonTreeItem*>::const_iterator it = items.begin(); it != items.end(); ++it) {
BsonTreeItem * documentItem = *it;
if (!documentItem)
break;
@@ -194,8 +194,7 @@ 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;
}
@@ -215,97 +214,44 @@ namespace Robomongo
}
isNeededRefresh = true;
RemoveDocumentCount removeCount = items.size() == 1 ? RemoveDocumentCount::ONE :
RemoveDocumentCount::MULTI;
_shell->server()->removeDocuments(query, _queryInfo._info._ns, removeCount, index);
++index;
_shell->server()->removeDocuments(query, _queryInfo._info._ns);
}
if (isNeededRefresh)
_shell->query(0, _queryInfo);
}
void Notifier::handle(InsertDocumentResponse *event)
{
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()));
if (event->isError()) {
QMessageBox::warning(NULL, "Database Error", QString::fromStdString(event->error().errorMessage()));
return;
}
// Success
_shell->query(0, _queryInfo);
}
void Notifier::handle(RemoveDocumentResponse *event)
{
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);
}
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.
void Notifier::onCopyNameDocument()
{
QModelIndex const& selectedInd = _observer->selectedIndex();
if (!selectedInd.isValid())
// QMessageBox::warning(NULL, "Database Error", QString::fromStdString(event->error().errorMessage()));
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()
@@ -317,15 +263,13 @@ namespace Robomongo
if (!detail::isMultiSelection(selectedIndexes))
return;
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) {
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) {
std::vector<BsonTreeItem*> items;
for (auto index : selectedIndexes)
items.push_back(QtUtils::item<BsonTreeItem*>(index));
for (QModelIndexList::const_iterator it = selectedIndexes.begin(); it != selectedIndexes.end(); ++it) {
BsonTreeItem *item = QtUtils::item<BsonTreeItem*>(*it);
items.push_back(item);
}
deleteDocuments(items, true);
}
}
@@ -399,7 +343,7 @@ namespace Robomongo
json, true, dynamic_cast<QWidget*>(_observer));
editor->setWindowTitle("View Document");
editor->exec();
editor->show();
}
void Notifier::onInsertDocument()
@@ -421,6 +365,8 @@ 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()
@@ -440,6 +386,53 @@ 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();
+6 -8
View File
@@ -14,14 +14,14 @@ namespace Robomongo
class MongoShell;
class BsonTreeItem;
class InsertDocumentResponse;
struct RemoveDocumentResponse;
class RemoveDocumentResponse;
namespace detail
{
bool isSimpleType(BsonTreeItem const *item);
bool isSimpleType(BsonTreeItem *item);
bool isObjectIdType(BsonTreeItem *item);
bool isMultiSelection(const QModelIndexList &indexes);
bool isDocumentType(BsonTreeItem const *item);
bool isDocumentType(BsonTreeItem *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*> const& items, bool force);
void deleteDocuments(std::vector<BsonTreeItem*> items, bool force);
void handleDeleteCommand();
public Q_SLOTS:
@@ -55,15 +55,13 @@ 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;
+18 -26
View File
@@ -57,7 +57,7 @@ namespace Robomongo
{
ScriptEngine::ScriptEngine(ConnectionSettings *connection, int timeoutSec) :
_connection(connection),
_scope(nullptr),
_scope(NULL),
_engine(NULL),
_timeoutSec(timeoutSec),
_initialized(false),
@@ -65,20 +65,26 @@ namespace Robomongo
ScriptEngine::~ScriptEngine()
{
QMutexLocker lock(&_mutex);
delete _scope;
_scope = NULL;
// delete _engine;
// _engine = NULL;
}
void ScriptEngine::init(bool isLoadMongoRcJs, const std::string& serverAddr, const std::string& dbName)
void ScriptEngine::init(bool isLoadMongoRcJs)
{
QMutexLocker lock(&_mutex);
std::string connectDatabase = dbName.empty() ? "test" : dbName;
std::string connectDatabase = "test";
if (_connection->hasEnabledPrimaryCredential())
connectDatabase = _connection->primaryCredential()->databaseName();
std::stringstream ss;
auto hostAndPort = serverAddr.empty() ? _connection->hostAndPort().toString() : serverAddr;
ss << "db = connect('" << hostAndPort << "/" << connectDatabase;
ss << "db = connect('" << _connection->info().toString() << "/" << connectDatabase;
// v0.9
// ss << "db = connect('" << _connection->serverHost() << ":" << _connection->serverPort() << _connection->sslInfo() << _connection->sshInfo() << "/" << connectDatabase;
@@ -102,23 +108,23 @@ namespace Robomongo
mongo::globalScriptEngine->setScopeInitCallback(mongo::shell_utils::initScope);
mongo::globalScriptEngine->enableJIT(true);
_scope.reset(mongo::globalScriptEngine->newScope());
mongo::Scope *scope = mongo::globalScriptEngine->newScope();
_scope = scope;
_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/
@@ -155,10 +161,8 @@ namespace Robomongo
{
QMutexLocker lock(&_mutex);
if (!_scope) {
_failedScope = true;
return MongoShellExecResult(true, "Connection error. Uninitialized mongo scope.");
}
if (!_scope)
return MongoShellExecResult();
/*
* Replace all commands ('show dbs', 'use db' etc.) with call
@@ -198,15 +202,10 @@ 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);
}
else { // failed to run script
failed = true;
_scope->exec( "__robomongoLastRes = __lastres__; shellPrintHelper( __lastres__ );" , "(shell2)" , true , true , false, _timeoutSec * 1000);
}
qint64 elapsed = timer.elapsed();
@@ -214,10 +213,6 @@ 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)
@@ -270,9 +265,6 @@ namespace Robomongo
void ScriptEngine::ping()
{
if (!_scope)
return;
QMutexLocker lock(&_mutex);
_scope->exec("if (db) { db.runCommand({ping:1}); }", "(ping)", false, false, false, 3000);
}
+2 -5
View File
@@ -18,7 +18,7 @@ namespace Robomongo
ScriptEngine(ConnectionSettings *connection, int timeoutSec);
~ScriptEngine();
void init(bool isLoadMongoJs, const std::string& serverAddr = "", const std::string& dbName = "");
void init(bool isLoadMongoJs);
MongoShellExecResult exec(const std::string &script, const std::string &dbName = std::string());
void interrupt();
@@ -29,8 +29,6 @@ namespace Robomongo
void invalidateDbCollectionsCache();
bool failedScope() const { return _failedScope; }
private:
ConnectionSettings *_connection;
@@ -44,8 +42,7 @@ namespace Robomongo
int _timeoutSec;
mongo::ScriptEngine *_engine;
std::unique_ptr<mongo::Scope> _scope;
bool _failedScope = false;
mongo::Scope *_scope;
QMutex _mutex;
bool _initialized;
};
@@ -5,11 +5,6 @@ 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)
+132 -221
View File
@@ -12,7 +12,6 @@
#include "robomongo/core/events/MongoEventsInfo.h"
#include "robomongo/core/Event.h"
#include "robomongo/core/Enums.h"
#include "robomongo/core/mongodb/ReplicaSet.h"
namespace Robomongo
{
@@ -27,118 +26,54 @@ namespace Robomongo
* @brief EstablishConnection
*/
struct EstablishConnectionRequest : public Event
class EstablishConnectionRequest : public Event
{
R_EVENT
EstablishConnectionRequest(QObject *sender, ConnectionType connectionType, std::string const& uuid) :
EstablishConnectionRequest(QObject *sender, ConnectionType connectionType) :
Event(sender),
connectionType(connectionType),
uuid(uuid) {}
connectionType(connectionType) {}
ConnectionType const connectionType;
std::string const uuid;
ConnectionType connectionType;
};
struct EstablishConnectionResponse : public Event
class EstablishConnectionResponse : public Event
{
R_EVENT
enum ErrorReason {
NoError = 0,
MongoConnection = 1,
MongoAuth = 2,
MongoSslConnection = 3
MongoConnection = 0,
MongoAuth = 1,
MongoSslConnection = 2
};
EstablishConnectionResponse(QObject *sender, const ConnectionInfo &info, ConnectionType connectionType,
const ReplicaSet& replicaSet) :
const mongo::HostAndPort& repPrimary, const std::vector<bool>& repMembersHealths) :
Event(sender),
info(info),
_info(info),
connectionType(connectionType),
replicaSet(replicaSet)
_repPrimary(repPrimary),
_repMembersHealths(repMembersHealths)
{}
EstablishConnectionResponse(QObject *sender, const EventError &error, ConnectionType connectionType,
ConnectionInfo const& info, const ReplicaSet& replicaSet, ErrorReason errorReason) :
EstablishConnectionResponse(QObject *sender, const EventError &error, ConnectionType connectionType, ErrorReason errorReason) :
Event(sender, error),
info(info),
_info(),
connectionType(connectionType),
errorReason(errorReason),
replicaSet(replicaSet)
{}
errorReason(errorReason) {}
ConnectionInfo const info;
ConnectionType const connectionType;
ErrorReason const errorReason = NoError;
ReplicaSet const 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
};
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
@@ -414,7 +349,7 @@ namespace Robomongo
InsertDocumentResponse(QObject *sender) :
Event(sender) {}
InsertDocumentResponse(QObject *sender, EventError const& error) :
InsertDocumentResponse(QObject *sender, const EventError &error) :
Event(sender, error) {}
};
@@ -422,46 +357,37 @@ 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,
RemoveDocumentCount removeCount, int index) :
RemoveDocumentRequest(QObject *sender, mongo::Query query, const MongoNamespace &ns, bool justOne = true) :
Event(sender),
_query(query),
_ns(ns),
_removeCount(removeCount),
_index(index) {}
_justOne(justOne) {}
mongo::Query query() const { return _query; }
MongoNamespace ns() const { return _ns; }
RemoveDocumentCount removeCount() const { return _removeCount; }
int index() const { return _index; }
bool justOne() const { return _justOne; }
private:
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;
mongo::Query _query;
const MongoNamespace _ns;
bool _justOne;
};
struct RemoveDocumentResponse : public Event
class RemoveDocumentResponse : public Event
{
R_EVENT
RemoveDocumentResponse(QObject *sender, RemoveDocumentCount removeCount, int index) :
Event(sender), removeCount(removeCount), index(index) {}
public:
RemoveDocumentResponse(QObject *sender) :
Event(sender) {}
RemoveDocumentResponse(QObject *sender, const EventError &error, RemoveDocumentCount removeCount, int index) :
Event(sender, error), removeCount(removeCount), index(index) {}
RemoveDocumentCount const removeCount;
int const index;
RemoveDocumentResponse(QObject *sender, const EventError &error) :
Event(sender, error) {}
};
/**
@@ -480,49 +406,52 @@ namespace Robomongo
std::string database() const { return _database; }
private:
std::string const _database;
std::string _database;
};
struct CreateDatabaseResponse : public Event
class CreateDatabaseResponse : public Event
{
R_EVENT
CreateDatabaseResponse(QObject *sender, const std::string &database) :
Event(sender), database(database) {}
public:
CreateDatabaseResponse(QObject *sender) :
Event(sender) {}
CreateDatabaseResponse(QObject *sender, const std::string &database, const EventError &error) :
Event(sender, error), database(database) {}
std::string const database;
CreateDatabaseResponse(QObject *sender, const EventError &error) :
Event(sender, error) {}
};
/**
* @brief Drop Database
*/
struct DropDatabaseRequest : public Event
{
R_EVENT
DropDatabaseRequest(QObject *sender, const std::string &database) :
Event(sender), database(database) {}
std::string const database;
};
struct DropDatabaseResponse : public Event
class DropDatabaseRequest : public Event
{
R_EVENT
public:
DropDatabaseResponse(QObject *sender, const std::string &database) :
Event(sender), database(database) {}
DropDatabaseRequest(QObject *sender, const std::string &database) :
Event(sender),
_database(database) {}
DropDatabaseResponse(QObject *sender, const std::string &database, const EventError &error) :
Event(sender, error), database(database) {}
std::string database() const { return _database; }
std::string const database;
private:
std::string _database;
};
class DropDatabaseResponse : public Event
{
R_EVENT
public:
DropDatabaseResponse(QObject *sender) :
Event(sender) {}
DropDatabaseResponse(QObject *sender, const EventError &error) :
Event(sender, error) {}
};
/**
@@ -543,31 +472,30 @@ namespace Robomongo
Event(sender), _ns(ns), _extraOptions(extraOptions),
_size(size), _capped(capped), _maxDocNum(maxDocNum) {}
MongoNamespace ns() const { return _ns; }
MongoNamespace getNs() 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:
MongoNamespace const _ns;
long long const _size;
bool const _capped;
int const _maxDocNum;
mongo::BSONObj const _extraOptions;
const MongoNamespace _ns;
const long long _size;
const bool _capped;
const int _maxDocNum;
const mongo::BSONObj _extraOptions;
};
struct CreateCollectionResponse : public Event
class CreateCollectionResponse : public Event
{
R_EVENT
CreateCollectionResponse(QObject *sender, std::string const& collection) :
Event(sender), collection(collection) {}
public:
CreateCollectionResponse(QObject *sender) :
Event(sender) {}
CreateCollectionResponse(QObject *sender, std::string const& collection, const EventError &error) :
Event(sender, error), collection(collection) {}
std::string const collection;
CreateCollectionResponse(QObject *sender, const EventError &error) :
Event(sender, error) {}
};
@@ -591,18 +519,16 @@ namespace Robomongo
MongoNamespace _ns;
};
struct DropCollectionResponse : public Event
class DropCollectionResponse : public Event
{
R_EVENT
public:
DropCollectionResponse(QObject *sender, std::string const& collection) :
Event(sender), collection(collection) {}
DropCollectionResponse(QObject *sender) :
Event(sender) {}
DropCollectionResponse(QObject *sender, std::string const& collection, const EventError &error) :
Event(sender, error), collection(collection) {}
std::string const collection;
DropCollectionResponse(QObject *sender, const EventError &error) :
Event(sender, error) {}
};
/**
@@ -623,23 +549,20 @@ namespace Robomongo
std::string newCollection() const { return _newCollection; }
private:
MongoNamespace const _ns;
std::string const _newCollection;
MongoNamespace _ns;
std::string _newCollection;
};
struct RenameCollectionResponse : public Event
class RenameCollectionResponse : public Event
{
R_EVENT
RenameCollectionResponse(QObject *sender, std::string const& oldCollection,
std::string const& newCollection) :
Event(sender), oldCollection(oldCollection), newCollection(newCollection) {}
public:
RenameCollectionResponse(QObject *sender) :
Event(sender) {}
RenameCollectionResponse(QObject *sender, const EventError &error) :
Event(sender, error) {}
std::string const oldCollection;
std::string const newCollection;
};
/**
@@ -660,24 +583,20 @@ namespace Robomongo
std::string newCollection() const { return _newCollection; }
private:
MongoNamespace const _ns;
std::string const _newCollection;
MongoNamespace _ns;
std::string _newCollection;
};
struct DuplicateCollectionResponse : public Event
class DuplicateCollectionResponse : public Event
{
R_EVENT
public:
DuplicateCollectionResponse(QObject *sender, std::string const& sourceCollection,
std::string const& duplicateCollection) :
Event(sender), sourceCollection(sourceCollection), duplicateCollection(duplicateCollection) {}
DuplicateCollectionResponse(QObject *sender) :
Event(sender) {}
DuplicateCollectionResponse(QObject *sender, std::string const& sourceCollection, const EventError &error) :
Event(sender, error), sourceCollection(sourceCollection) {}
std::string const sourceCollection;
std::string const duplicateCollection;
DuplicateCollectionResponse(QObject *sender, const EventError &error) :
Event(sender, error) {}
};
/**
@@ -742,17 +661,16 @@ namespace Robomongo
bool _overwrite;
};
struct CreateUserResponse : public Event
class CreateUserResponse : public Event
{
R_EVENT
CreateUserResponse(QObject *sender, std::string const& userName) :
Event(sender), userName(userName) {}
public:
CreateUserResponse(QObject *sender) :
Event(sender) {}
CreateUserResponse(QObject *sender, std::string const& userName, const EventError &error) :
Event(sender, error), userName(userName) {}
std::string const userName;
CreateUserResponse(QObject *sender, const EventError &error) :
Event(sender, error) {}
};
@@ -765,31 +683,29 @@ namespace Robomongo
R_EVENT
public:
DropUserRequest(QObject *sender, const std::string &database, const mongo::OID &id,
std::string const& username) :
Event(sender), _database(database), _id(id), _username(username) {}
DropUserRequest(QObject *sender, const std::string &database, const mongo::OID &id) :
Event(sender),
_database(database),
_id(id) {}
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;
};
struct DropUserResponse : public Event
class DropUserResponse : public Event
{
R_EVENT
DropUserResponse(QObject *sender, std::string const& username) :
Event(sender), username(username) {}
public:
DropUserResponse(QObject *sender) :
Event(sender) {}
DropUserResponse(QObject *sender, std::string const& username, const EventError &error) :
Event(sender, error), username(username) {}
std::string const username;
DropUserResponse(QObject *sender, const EventError &error) :
Event(sender, error) {}
};
@@ -822,21 +738,19 @@ namespace Robomongo
bool _overwrite;
};
struct CreateFunctionResponse : public Event
class CreateFunctionResponse : public Event
{
R_EVENT
enum Operation { NewFunction, NameUpdate, CodeUpdate }; // todo
CreateFunctionResponse(QObject *sender, std::string const& functionName) :
Event(sender), functionName(functionName) {}
public:
CreateFunctionResponse(QObject *sender) :
Event(sender) {}
CreateFunctionResponse(QObject *sender, std::string const& functionName, const EventError &error) :
Event(sender, error), functionName(functionName) {}
std::string const functionName;
CreateFunctionResponse(QObject *sender, const EventError &error) :
Event(sender, error) {}
};
/**
* @brief Drop Function
*/
@@ -846,30 +760,29 @@ namespace Robomongo
R_EVENT
public:
DropFunctionRequest(QObject *sender, const std::string &database, const std::string &funcName) :
DropFunctionRequest(QObject *sender, const std::string &database, const std::string &name) :
Event(sender),
_database(database),
_functionName(funcName) {}
_name(name) {}
std::string database() const { return _database; }
std::string functionName() const { return _functionName; }
std::string name() const { return _name; }
private:
std::string _database;
std::string _functionName;
std::string _name;
};
struct DropFunctionResponse : public Event
class DropFunctionResponse : public Event
{
R_EVENT
DropFunctionResponse(QObject *sender, const std::string &functionName) :
Event(sender), functionName(functionName) {}
public:
DropFunctionResponse(QObject *sender) :
Event(sender) {}
DropFunctionResponse(QObject *sender, const std::string &functionName, const EventError &error) :
Event(sender, error), functionName(functionName) {}
std::string const functionName;
DropFunctionResponse(QObject *sender, const EventError &error) :
Event(sender, error) {}
};
/**
@@ -1026,19 +939,17 @@ namespace Robomongo
// class ScriptExecute
struct ConnectionEstablishedEvent : public Event
class ConnectionEstablishedEvent : public Event
{
R_EVENT
ConnectionEstablishedEvent(MongoServer *server, ConnectionType type, ConnectionInfo connInfo) :
ConnectionEstablishedEvent(MongoServer *server, ConnectionType type) :
Event((QObject *)server),
server(server),
connectionType(type),
connInfo(connInfo) { }
connectionType(type) { }
MongoServer *server;
ConnectionType const connectionType;
ConnectionInfo const connInfo;
ConnectionType connectionType;
};
class DatabaseListLoadedEvent : public Event
@@ -17,19 +17,15 @@ namespace Robomongo
_languageOverride(languageOverride),
_textWeights(textWeights) {}
ConnectionInfo::ConnectionInfo(std::string const& uuid) :
ConnectionInfo::ConnectionInfo() :
_address(),
_databases(),
_version(0.0f),
_uuid(uuid)
{}
_version(0.0f) {}
ConnectionInfo::ConnectionInfo(const std::string &address, const std::vector<std::string> &databases,
float version, const std::string& storageEngine, std::string const& uuid) :
float version, const std::string& storageEngine) :
_address(address),
_databases(databases),
_version(version),
_storageEngineType(storageEngine),
_uuid(uuid)
{}
_storageEngineType(storageEngine) {}
}
+2 -4
View File
@@ -34,13 +34,11 @@ namespace Robomongo
struct ConnectionInfo
{
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);
ConnectionInfo();
ConnectionInfo(const std::string &address, const std::vector<std::string> &databases, float version, const std::string& storageEngine);
const std::string _address;
const std::vector<std::string> _databases;
const float _version;
const std::string _storageEngineType;
std::string const _uuid;
};
}
+25 -102
View File
@@ -108,10 +108,9 @@ namespace Robomongo
MongoNamespace ns(dbName, "system.users");
mongo::BSONObj obj = user.toBson();
if (!overwrite) { // create new user
if (!overwrite) {
_dbclient->insert(ns.toString(), obj);
}
else { // update existing user
} else {
mongo::BSONElement id = obj.getField("_id");
mongo::BSONObjBuilder builder;
builder.append(id);
@@ -120,10 +119,6 @@ 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)
@@ -136,10 +131,6 @@ 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)
@@ -302,46 +293,36 @@ 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()) { // create new function
if (existingFunctionName.empty()) { // this is insert
_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) { // update existing function code
if (existingFunctionName == name) {
mongo::BSONObjBuilder builder;
builder.append("_id", name);
mongo::BSONObj bsonQuery = builder.obj();
mongo::Query query(bsonQuery);
_dbclient->update(ns.toString(), query, obj, true, false);
std::string errorStr = _dbclient->getLastError();
if (!errorStr.empty())
throw mongo::DBException(errorStr, 0);
} else { // update function name (remove & insert)
} else {
_dbclient->insert(ns.toString(), obj);
std::string errorStr = _dbclient->getLastError();
std::string res = _dbclient->getLastError();
// if no errors
if (errorStr.empty()) {
if (res.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);
}
}
}
}
@@ -356,9 +337,6 @@ 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)
@@ -373,7 +351,7 @@ namespace Robomongo
// If <dbName>.temp already exists, stop.
if (_dbclient->exists(ns.toString()))
throw mongo::DBException(dbName + ".temp already exists.", 0);
return;
// Building { _id : "temp" } document
mongo::BSONObjBuilder builder;
@@ -382,9 +360,6 @@ 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());
@@ -392,18 +367,10 @@ namespace Robomongo
void MongoClient::dropDatabase(const std::string &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);
}
_dbclient->dropDatabase(dbName);
}
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;
@@ -423,19 +390,7 @@ namespace Robomongo
}
b.appendElements(extraOptions);
if (!_dbclient->exists(ns)) {
mongo::BSONObj result;
if (!_dbclient->runCommand(db.c_str(), b.done(), result)) {
std::string errStr = result.getStringField("errmsg");
if (errStr.empty())
errStr = "Failed to get error message.";
throw mongo::DBException(errStr, 0);
}
}
else {
throw mongo::DBException("Collection with same name already exists.", 0);
}
_dbclient->runCommand(db.c_str(), b.done(), *info);
}
void MongoClient::renameCollection(const MongoNamespace &ns, const std::string &newCollectionName)
@@ -449,38 +404,18 @@ namespace Robomongo
command.append("to", to.toString());
mongo::BSONObj result;
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);
}
_dbclient->runCommand("admin", command.obj(), result); // this command should be run against "admin" db
}
void MongoClient::duplicateCollection(const MongoNamespace &ns, const std::string &newCollectionName)
{
MongoNamespace sourceCollection(ns);
MongoNamespace newCollection(ns.databaseName(), newCollectionName);
MongoNamespace from(ns);
MongoNamespace to(ns.databaseName(), newCollectionName);
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.";
if (!_dbclient->exists(to.toString()))
_dbclient->createCollection(to.toString());
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()));
std::unique_ptr<mongo::DBClientCursor> cursor(_dbclient->query(from.toString(), mongo::Query()));
// Cursor may be NULL, it means we have connectivity problem
if (!cursor)
@@ -488,12 +423,11 @@ namespace Robomongo
while (cursor->more()) {
mongo::BSONObj bsonObj = cursor->next();
_dbclient->insert(newCollection.toString(), bsonObj);
_dbclient->insert(to.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());
@@ -512,19 +446,7 @@ namespace Robomongo
void MongoClient::dropCollection(const MongoNamespace &ns)
{
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);
}
_dbclient->dropCollection(ns.toString());
}
void MongoClient::insertDocument(const mongo::BSONObj &obj, const MongoNamespace &ns)
@@ -602,10 +524,11 @@ namespace Robomongo
std::vector<MongoCollectionInfo> MongoClient::runCollStatsCommand(const std::vector<std::string> &namespaces)
{
std::vector<MongoCollectionInfo> infos;
for (auto const& ns : namespaces) {
MongoCollectionInfo info = runCollStatsCommand(ns);
if (info.ns().isValid())
for (std::vector<std::string>::const_iterator it = namespaces.begin(); it != namespaces.end(); ++it) {
MongoCollectionInfo info = runCollStatsCommand(*it);
if (info.ns().isValid()) {
infos.push_back(info);
}
}
return infos;
}
+154 -493
View File
@@ -1,7 +1,5 @@
#include "robomongo/core/mongodb/MongoWorker.h"
#include <algorithm>
#include <QThread>
#include "mongo/client/global_conn_pool.h"
@@ -21,18 +19,17 @@
#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),
_scriptEngine(nullptr),
_connSettings(connection),
_scriptEngine(NULL),
_dbclient(nullptr),
_dbclientRepSet(nullptr),
_isAdmin(true),
_isLoadMongoRcJs(isLoadMongoRcJs),
_batchSize(batchSize),
@@ -40,10 +37,7 @@ namespace Robomongo
_dbAutocompleteCacheTimerId(-1),
_mongoTimeoutSec(mongoTimeoutSec),
_shellTimeoutSec(shellTimeoutSec),
_isQuiting(0),
_dbclient(nullptr),
_dbclientRepSet(nullptr),
_connSettings(connection)
_isQuiting(0)
{
_thread = new QThread();
moveToThread(_thread);
@@ -59,7 +53,8 @@ namespace Robomongo
return;
}
if (_dbAutocompleteCacheTimerId == event->timerId() && !_scriptEngine) {
if (_dbAutocompleteCacheTimerId == event->timerId() &&
_scriptEngine != NULL) {
_scriptEngine->invalidateDbCollectionsCache();
return;
}
@@ -69,11 +64,16 @@ namespace Robomongo
{
try {
if (_dbclient) {
pingDatabase(_dbclient.get());
}
if (_dbclientRepSet) {
pingDatabase(_dbclientRepSet.get());
// 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);
}
}
if (_scriptEngine) {
@@ -81,23 +81,21 @@ namespace Robomongo
}
} catch(std::exception &ex) {
LOG_MSG("Failed to ping the server. MongoWorker::keepAlive() failed. " + std::string(ex.what()),
mongo::logger::LogSeverity::Error());
LOG_MSG(ex.what(), mongo::logger::LogSeverity::Error());
}
}
void MongoWorker::init()
{
try {
_scriptEngine.reset(new ScriptEngine(_connSettings, _shellTimeoutSec));
_scriptEngine = 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("Failed to initialize MongoWorker. " + std::string(ex.what()),
mongo::logger::LogSeverity::Error());
LOG_MSG(ex.what(), mongo::logger::LogSeverity::Error());
}
}
@@ -121,6 +119,7 @@ namespace Robomongo
killTimer(_dbAutocompleteCacheTimerId);
delete _connSettings;
delete _scriptEngine;
// QThread "_thread" and MongoWorker itself will be deleted later
// (see MongoWorker() constructor)
@@ -135,75 +134,32 @@ namespace Robomongo
/**
* @brief Initiate connection to MongoDB
*/
bool MongoWorker::handle(EstablishConnectionRequest *event)
void 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);
// --- Connection failed for single server & replica set (no member of the set is reachable)
if (!conn)
{
if (conn == NULL) {
// Protection by default value: Logically/ideally, this error should never be seen.
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 { // 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.";
else
{
errorReason = "Network is unreachable.";
}
resetGlobalSSLparams();
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));
}
reply(event->sender(), new EstablishConnectionResponse(this,
EventError(errorReason), event->connectionType,
EstablishConnectionResponse::MongoConnection));
return;
}
if (_connSettings->hasEnabledPrimaryCredential()) {
@@ -235,45 +191,44 @@ namespace Robomongo
if (dbNames.size() == 0)
throw mongo::DBException("Failed to execute \"listdatabases\" command.", 0);
if (!_connSettings->isReplicaSet())
init(); // Init MongoWorker for single server (for replica set connections early init is used)
// 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();
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) {
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 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(), errorCode),
event->connectionType, ConnectionInfo(event->uuid), *repSetInfo.release(), errorReason));
reply(event->sender(),
new EstablishConnectionResponse(this, EventError(ex.what()), event->connectionType, 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
@@ -351,17 +306,8 @@ namespace Robomongo
reply(event->sender(), new LoadCollectionNamesResponse(this, event->databaseName(), infos));
} catch(const mongo::DBException &ex) {
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())));
reply(event->sender(), new LoadCollectionNamesResponse(this, EventError(ex.what())));
LOG_MSG(ex.what(), mongo::logger::LogSeverity::Error());
}
}
@@ -374,17 +320,8 @@ namespace Robomongo
reply(event->sender(), new LoadUsersResponse(this, event->databaseName(), users));
} catch(const mongo::DBException &ex) {
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())));
reply(event->sender(), new LoadUsersResponse(this, EventError(ex.what())));
LOG_MSG(ex.what(), mongo::logger::LogSeverity::Error());
}
}
@@ -456,17 +393,8 @@ namespace Robomongo
reply(event->sender(), new LoadFunctionsResponse(this, event->databaseName(), funs));
} catch(const mongo::DBException &ex) {
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())));
reply(event->sender(), new LoadFunctionsResponse(this, EventError(ex.what())));
LOG_MSG(ex.what(), mongo::logger::LogSeverity::Error());
}
}
@@ -481,22 +409,12 @@ namespace Robomongo
client->insertDocument(event->obj(), event->ns());
client->done();
reply(event->sender(), new InsertDocumentResponse(this));
}
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())));
}
} 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());
}
}
@@ -505,25 +423,14 @@ namespace Robomongo
try {
boost::scoped_ptr<MongoClient> client(getClient());
client->removeDocuments(event->ns(), event->query(), (event->removeCount() == RemoveDocumentCount::ONE));
client->removeDocuments(event->ns(), event->query(), event->justOne());
client->done();
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()));
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());
}
}
@@ -552,49 +459,11 @@ namespace Robomongo
return;
}
// 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;
}
}
MongoShellExecResult result = _scriptEngine->exec(event->script, event->databaseName);
reply(event->sender(), new ExecuteScriptResponse(this, result, event->script.empty()));
}
catch(const std::exception &ex) {
reply(event->sender(), new ExecuteScriptResponse(this, EventError(ex.what(), EventError::Unknown, false)));
} catch(const mongo::DBException &ex) {
reply(event->sender(), new ExecuteScriptResponse(this, EventError(ex.what())));
LOG_MSG(ex.what(), mongo::logger::LogSeverity::Error());
}
}
@@ -636,23 +505,14 @@ 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, event->database()));
reply(event->sender(), new CreateDatabaseResponse(this));
} catch(const mongo::DBException &ex) {
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())));
reply(event->sender(), new CreateDatabaseResponse(this, EventError(ex.what())));
LOG_MSG(ex.what(), mongo::logger::LogSeverity::Error());
}
}
@@ -660,76 +520,45 @@ 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, 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())));
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());
}
}
void MongoWorker::handle(CreateCollectionRequest *event)
{
std::string const& collection = event->ns().collectionName();
try {
boost::scoped_ptr<MongoClient> client(getClient());
client->createCollection(event->ns().toString(), event->getSize(), event->getCapped(),
client->createCollection(event->getNs().toString(), event->getSize(), event->getCapped(),
event->getMaxDocNum(), event->getExtraOptions());
client->done();
reply(event->sender(), new CreateCollectionResponse(this, collection));
reply(event->sender(), new CreateCollectionResponse(this));
} catch(const mongo::DBException &ex) {
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())));
reply(event->sender(), new CreateCollectionResponse(this, EventError(ex.what())));
LOG_MSG(ex.what(), mongo::logger::LogSeverity::Error());
}
}
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, collection));
reply(event->sender(), new DropCollectionResponse(this));
} catch(const mongo::DBException &ex) {
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())));
reply(event->sender(), new DropCollectionResponse(this, EventError(ex.what())));
LOG_MSG(ex.what(), mongo::logger::LogSeverity::Error());
}
}
@@ -740,46 +569,24 @@ namespace Robomongo
client->renameCollection(event->ns(), event->newCollection());
client->done();
reply(event->sender(), new RenameCollectionResponse(this, event->ns().collectionName(),
event->newCollection()));
reply(event->sender(), new RenameCollectionResponse(this));
} catch(const mongo::DBException &ex) {
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())));
reply(event->sender(), new RenameCollectionResponse(this, EventError(ex.what())));
LOG_MSG(ex.what(), mongo::logger::LogSeverity::Error());
}
}
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, 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())));
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());
}
}
@@ -805,21 +612,10 @@ namespace Robomongo
client->createUser(event->database(), event->user(), event->overwrite());
client->done();
reply(event->sender(), new CreateUserResponse(this, event->user().name()));
reply(event->sender(), new CreateUserResponse(this));
} catch(const mongo::DBException &ex) {
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())));
reply(event->sender(), new CreateUserResponse(this, EventError(ex.what())));
LOG_MSG(ex.what(), mongo::logger::LogSeverity::Error());
}
}
@@ -830,44 +626,24 @@ namespace Robomongo
client->dropUser(event->database(), event->id());
client->done();
reply(event->sender(), new DropUserResponse(this, event->username()));
reply(event->sender(), new DropUserResponse(this));
} catch(const mongo::DBException &ex) {
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())));
reply(event->sender(), new DropUserResponse(this, EventError(ex.what())));
LOG_MSG(ex.what(), mongo::logger::LogSeverity::Error());
}
}
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, functionName));
reply(event->sender(), new CreateFunctionResponse(this));
} catch(const mongo::DBException &ex) {
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())));
reply(event->sender(), new CreateFunctionResponse(this, EventError(ex.what())));
LOG_MSG(ex.what(), mongo::logger::LogSeverity::Error());
}
}
@@ -875,52 +651,42 @@ namespace Robomongo
{
try {
boost::scoped_ptr<MongoClient> client(getClient());
client->dropFunction(event->database(), event->functionName());
client->dropFunction(event->database(), event->name());
client->done();
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())));
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());
}
}
mongo::DBClientBase *MongoWorker::getConnection(bool mayReturnNull /* = false */)
{
configureSSL();
// --- 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);
}
// --- Perform connection ---
if (_connSettings->isReplicaSet()) // connection to replica set
{
if (!_dbclientRepSet)
{
init(); // Init mongoworker for early-use of _scriptEngine
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));
// 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) {
@@ -929,15 +695,17 @@ 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 = DBClientConnection(new mongo::DBClientConnection(true, _mongoTimeoutSec));
_dbclient = std::unique_ptr<mongo::DBClientConnection>(new mongo::DBClientConnection(true, _mongoTimeoutSec));
mongo::Status status = _dbclient->connect(_connSettings->hostAndPort());
if (!status.isOK() && mayReturnNull)
mongo::Status status = _dbclient->connect(_connSettings->info());
if (!status.isOK() && mayReturnNull) {
return nullptr;
}
}
return _dbclient.get();
}
@@ -948,22 +716,6 @@ 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();
@@ -995,82 +747,6 @@ 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
*/
@@ -1092,19 +768,4 @@ 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);
}
}
}
+7 -47
View File
@@ -4,7 +4,7 @@
#include <QMutex>
#include <unordered_set>
#include <mongo/client/dbclient_rs.h>
#include <mongo/client/dbclient_rs.h> // todo: move
#include "robomongo/core/events/MongoEvents.h"
@@ -24,23 +24,16 @@ 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:
protected Q_SLOTS: // handlers:
void init();
/**
* @brief Every minute we are issuing { ping : 1 } command to every used connection
* in order to avoid dropped connections.
@@ -50,13 +43,7 @@ namespace Robomongo
/**
* @brief Initiate connection to MongoDB
*/
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);
void handle(EstablishConnectionRequest *event);
/**
* @brief Load list of all database names
@@ -143,18 +130,14 @@ 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.
@@ -166,34 +149,14 @@ 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;
std::unique_ptr<ScriptEngine> _scriptEngine;
ScriptEngine *_scriptEngine;
bool _isAdmin;
const bool _isLoadMongoRcJs;
@@ -204,9 +167,6 @@ 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.
-12
View File
@@ -1,12 +0,0 @@
#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)
{}
}
-22
View File
@@ -1,22 +0,0 @@
#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,16 +2,12 @@
#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;
@@ -23,54 +19,21 @@ namespace
namespace Robomongo
{
/**
* Creates ConnectionSettings with default values
*/
ConnectionSettings::ConnectionSettings(bool isClone) : QObject(),
ConnectionSettings::ConnectionSettings() : QObject(),
_connectionName(defaultNameConnection),
_host(defaultServerHost),
_port(port),
_imported(false),
_sshSettings(new SshSettings()),
_sslSettings(new SslSettings()),
_isReplicaSet(false),
_replicaSetSettings(new ReplicaSetSettings()),
_clone(isClone)
_isReplicaSet(false)
{ }
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)));
@@ -97,13 +60,6 @@ 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
@@ -115,6 +71,9 @@ namespace Robomongo
ConnectionSettings::~ConnectionSettings()
{
clearCredentials();
delete _sshSettings;
delete _sslSettings;
delete _replicaSetSettings; // todo: unique_ptr
}
/**
@@ -122,10 +81,9 @@ namespace Robomongo
*/
ConnectionSettings *ConnectionSettings::clone() const
{
auto settings = new ConnectionSettings(true);
settings->setUuid(_uuid);
settings->apply(this);
return settings;
ConnectionSettings *record = new ConnectionSettings();
record->apply(this);
return record;
}
/**
@@ -146,9 +104,9 @@ namespace Robomongo
addCredential((*it)->clone());
}
_sshSettings.reset(source->sshSettings()->clone());
_sslSettings.reset(source->sslSettings()->clone());
_replicaSetSettings.reset(source->_replicaSetSettings->clone());
_sshSettings = source->sshSettings()->clone();
_sslSettings = source->sslSettings()->clone();
_replicaSetSettings = source->_replicaSetSettings->clone(); // todo: leak candidate
//#ifdef MONGO_SSL
// setSslInfo(source->sslInfo());
@@ -184,14 +142,13 @@ 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 = nullptr;
CredentialSettings *result = NULL;
for (QList<CredentialSettings *>::const_iterator it = _credentials.begin(); it != _credentials.end(); ++it) {
CredentialSettings *cred = *it;
if (cred->databaseName() == databaseName) {
@@ -215,7 +172,7 @@ namespace Robomongo
* @brief Checks whether this connection has primary credential
* which is also enabled.
*/
bool ConnectionSettings::hasEnabledPrimaryCredential() const
bool ConnectionSettings::hasEnabledPrimaryCredential()
{
if (_credentials.count() == 0)
return false;
@@ -226,7 +183,7 @@ namespace Robomongo
/**
* @brief Returns primary credential
*/
CredentialSettings *ConnectionSettings::primaryCredential() const
CredentialSettings *ConnectionSettings::primaryCredential()
{
if (_credentials.count() == 0) {
_credentials.append(new CredentialSettings());
@@ -246,26 +203,9 @@ namespace Robomongo
std::string ConnectionSettings::getFullAddress() const
{
return hostAndPort().toString();
return info().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,10 +3,8 @@
#include <QString>
#include <QVariant>
#include <QVariantMap>
#include <mongo/client/dbclientinterface.h>
#include <mongo/client/mongo_uri.h>
#include <mongo/util/net/hostandport.h>
#include <boost/algorithm/string.hpp>
namespace Robomongo
{
@@ -26,14 +24,9 @@ namespace Robomongo
/**
* @brief Creates ConnectionSettings with default values
*/
ConnectionSettings(bool isClone);
/**
* @brief Creates ConnectionSettings from mongo connection string URI
*/
ConnectionSettings(const mongo::MongoURI& uri, bool isClone);
ConnectionSettings();
explicit ConnectionSettings(QVariantMap map, bool isClone);
explicit ConnectionSettings(QVariantMap map);
/**
* @brief Cleanup used resources
@@ -71,7 +64,7 @@ namespace Robomongo
/**
* @brief Port of server
*/
int serverPort() const { return _port; }
unsigned short serverPort() const { return _port; }
void setServerPort(const int port) { _port = port; }
/**
@@ -100,12 +93,12 @@ namespace Robomongo
* @brief Checks whether this connection has primary credential
* which is also enabled.
*/
bool hasEnabledPrimaryCredential() const;
bool hasEnabledPrimaryCredential();
/**
* @brief Returns primary credential
*/
CredentialSettings *primaryCredential() const;
CredentialSettings *primaryCredential();
/**
* @brief Returns number of credentials
@@ -140,46 +133,45 @@ namespace Robomongo
return _connectionName;
}
mongo::HostAndPort hostAndPort() const;
SshSettings *sshSettings() const { return _sshSettings.get(); }
SslSettings *sslSettings() const { return _sslSettings.get(); }
ReplicaSetSettings *replicaSetSettings() const { return _replicaSetSettings.get(); }
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; }
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;
mutable QList<CredentialSettings *> _credentials;
std::unique_ptr<SshSettings> _sshSettings;
std::unique_ptr<SslSettings> _sslSettings;
bool _isReplicaSet;
std::unique_ptr<ReplicaSetSettings> _replicaSetSettings;
QList<CredentialSettings *> _credentials;
SshSettings *_sshSettings;
SslSettings *_sslSettings;
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,18 +6,9 @@ namespace Robomongo
{
ReplicaSetSettings::ReplicaSetSettings()
: _cachedSetName(""), _members(), _readPreference(ReadPreference::PRIMARY)
: _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);
@@ -27,8 +18,6 @@ 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));
@@ -40,11 +29,9 @@ 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
{
@@ -57,28 +44,14 @@ 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);
}
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
const 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,7 +5,6 @@
#include <QVariantMap>
#include <mongo/util/net/hostandport.h>
#include <mongo/client/mongo_uri.h>
namespace Robomongo
{
@@ -16,12 +15,9 @@ namespace Robomongo
{
PRIMARY = 0,
PRIMARY_PREFERRED = 1
// todo: Add all supported read preferences
};
ReplicaSetSettings();
ReplicaSetSettings(const mongo::MongoURI& uri);
/**
* Clones Replica Set settings.
@@ -34,33 +30,22 @@ namespace Robomongo
QVariant toVariant() const;
void fromVariant(const QVariantMap &map);
// Getters
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;
//// todo: remove
//void addMember(const std::string& str) { _members.push_back(str); }
std::set<mongo::HostAndPort> membersToHostAndPortAsSet() const
{
auto const& members = membersToHostAndPort();
return std::set<mongo::HostAndPort>(members.begin(), members.end());
}
// Getters
//
const std::vector<std::string>& members() const { return _members; }
const std::vector<mongo::HostAndPort> membersToHostAndPort() const;
ReadPreference readPreference() const { return _readPreference; }
// Setters
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 setMembers(const std::vector<std::string>& members) { _members = members; }
void setReadPreference(ReadPreference readPreference) { _readPreference = readPreference; }
private:
std::string _setNameUserEntered;
std::string _cachedSetName;
std::vector<std::string> _members;
ReadPreference _readPreference = ReadPreference::PRIMARY;
ReadPreference _readPreference;
};
}
+80 -333
View File
@@ -3,23 +3,6 @@
#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>
@@ -31,68 +14,29 @@
#include "robomongo/core/utils/QtUtils.h"
#include "robomongo/core/utils/StdUtils.h"
#include "robomongo/gui/AppStyle.h"
#include "robomongo/utils/common.h"
namespace Robomongo
namespace
{
// 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 Version of schema
*/
const QString SchemaVersion = "2.0";
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 absolute path
* (usually: /home/user/.config/robomongo/robomongo.json)
*/
const QString _configPath = QString("%1/.config/robomongo/0.9/robomongo.json").arg(QDir::homePath());
/**
* @brief Config file containing directory path
* (usually: /home/user/.config/robomongo)
*/
const QString _configDir = QString("%1/.config/robomongo/0.9").arg(QDir::homePath());
}
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
@@ -103,23 +47,18 @@ namespace Robomongo
_timeZone(Utc),
_viewMode(Robomongo::Tree),
_autocompletionMode(AutocompleteAll),
_loadMongoRcJs(false),
_minimizeToTray(false),
_lineNumbers(false),
_disableConnectionShortcuts(false),
_batchSize(50),
_disableConnectionShortcuts(false),
_textFontFamily(""),
_textFontPointSize(-1),
_lineNumbers(false),
_loadMongoRcJs(false),
_imported(false),
_mongoTimeoutSec(10),
_shellTimeoutSec(15),
_imported(false)
_shellTimeoutSec(15)
{
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);
load();
LOG_MSG("SettingsManager initialized in " + _configPath, mongo::logger::LogSeverity::Info(), false);
}
SettingsManager::~SettingsManager()
@@ -133,10 +72,10 @@ namespace Robomongo
*/
bool SettingsManager::load()
{
if (!QFile::exists(ConfigFilePath))
if (!QFile::exists(_configPath))
return false;
QFile f(ConfigFilePath);
QFile f(_configPath);
if (!f.open(QIODevice::ReadOnly))
return false;
@@ -159,14 +98,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(ConfigFilePath);
QFile f(_configPath);
if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
LOG_MSG("ERROR: Could not write settings to: " + ConfigFilePath, mongo::logger::LogSeverity::Error());
LOG_MSG("ERROR: Could not write settings to: " + _configPath, mongo::logger::LogSeverity::Error());
return false;
}
@@ -175,21 +114,11 @@ namespace Robomongo
s.setIndentMode(QJson::IndentFull);
s.serialize(map, &f, &ok);
LOG_MSG("Settings saved to: " + ConfigFilePath, mongo::logger::LogSeverity::Info());
LOG_MSG("Settings saved to: " + _configPath, 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.
*/
@@ -203,7 +132,7 @@ namespace Robomongo
if (encoding > 3 || encoding < 0)
encoding = 0;
_uuidEncoding = (UUIDEncoding)encoding;
_uuidEncoding = (UUIDEncoding) encoding;
// 3. Load view mode
@@ -211,41 +140,39 @@ 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;
_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;
_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;
// 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
}
@@ -253,10 +180,6 @@ 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;
@@ -277,48 +200,33 @@ namespace Robomongo
// 5. Load connections
_connections.clear();
QVariantList const& list = map.value("connections").toList();
for (auto const& conn : list) {
auto connSettings = new ConnectionSettings(false);
connSettings->fromVariant(conn.toMap());
addConnection(connSettings);
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);
}
/* 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;
_cacheData = map.value("cacheData").toMap();
// Load connection settings from previous versions of Robomongo
importConnections();
// Load connection settings from previous
// versions of Robomongo
importPreviousConnections();
}
/**
@@ -350,84 +258,40 @@ namespace Robomongo
// 7. Save disableConnectionShortcuts
map.insert("disableConnectionShortcuts", _disableConnectionShortcuts);
// 8. Save acceptedEulaVersions array
QJsonArray arr;
for (auto const& str : _acceptedEulaVersions)
arr.push_back(str);
map.insert("acceptedEulaVersions", arr.toVariantList());
// 9. Save batchSize
// 8. Save batchSize
map.insert("batchSize", _batchSize);
map.insert("checkForUpdates", _checkForUpdates);
map.insert("mongoTimeoutSec", _mongoTimeoutSec);
map.insert("shellTimeoutSec", _shellTimeoutSec);
// 10. Save style
// 9. Save style
map.insert("style", _currentStyle);
// 11. Save font information
// 10. Save font information
map.insert("textFontFamily", _textFontFamily);
map.insert("textFontPointSize", _textFontPointSize);
// 12. Save connections
// 11. Save connections
QVariantList list;
for (auto const conn : _connections)
list.append(conn->toVariant().toMap());
for (ConnectionSettingsContainerType::const_iterator it = _connections.begin(); it != _connections.end(); ++it) {
QVariantMap rm = (*it)->toVariant().toMap();
list.append(rm);
}
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 and set it's uniqueID
* Adds connection to the end of list
*/
void SettingsManager::addConnection(ConnectionSettings *connection)
{
@@ -446,50 +310,6 @@ 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;
@@ -514,45 +334,32 @@ namespace Robomongo
_toolbars[toolbarName] = visible;
}
void SettingsManager::importConnections()
{
void SettingsManager::importPreviousConnections() {
// 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(CONFIG_FILE_0_8_5))
return false;
if (!QFile::exists(oldConfigPath))
return;
QFile oldConfigFile(CONFIG_FILE_0_8_5);
if (!oldConfigFile.open(QIODevice::ReadOnly))
return false;
QFile f(oldConfigPath);
if (!f.open(QIODevice::ReadOnly))
return;
bool ok;
QJson::Parser parser;
QVariantMap vmap = parser.parse(oldConfigFile.readAll(), &ok).toMap();
QVariantMap vmap = parser.parse(f.readAll(), &ok).toMap();
if (!ok)
return false;
return;
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();
auto conn = new ConnectionSettings(false);
ConnectionSettings *conn = new ConnectionSettings();
conn->setImported(true);
conn->setConnectionName(QtUtils::toStdString(vconn.value("connectionName").toString()));
conn->setServerHost(QtUtils::toStdString(vconn.value("serverHost").toString().left(300)));
@@ -587,7 +394,7 @@ namespace Robomongo
for (QVariantList::const_iterator itcred = vcreds.begin(); itcred != vcreds.end(); ++itcred) {
QVariantMap vcred = (*itcred).toMap();
auto cred = new CredentialSettings();
CredentialSettings *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()));
@@ -634,46 +441,8 @@ namespace Robomongo
addConnection(conn);
}
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;
// Mark as imported
setImported(true);
}
int SettingsManager::importedConnectionsCount() {
@@ -686,26 +455,4 @@ 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("");
}
}
+7 -111
View File
@@ -2,46 +2,12 @@
#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
@@ -57,7 +23,6 @@ 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)
@@ -85,32 +50,13 @@ namespace Robomongo
* @brief Adds connection to the end of list.
* Connection now will be owned by SettingsManager.
*/
static void addConnection(ConnectionSettings *connection);
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);
@@ -119,9 +65,6 @@ 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; }
@@ -143,9 +86,6 @@ 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; }
@@ -155,12 +95,6 @@ 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; }
@@ -176,20 +110,15 @@ 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:
/**
@@ -202,21 +131,10 @@ 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 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();
void importPreviousConnections();
/**
* @brief Version of settings schema currently loaded
@@ -230,12 +148,9 @@ 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;
@@ -243,33 +158,14 @@ 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
*/
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;
*/
ConnectionSettingsContainerType _connections;
ToolbarSettingsContainerType _toolbars;
// List of config. file absolute paths and related import functions.
// Must be updated with care and with every new version. Details on cpp file.
static const std::vector<ConfigFileAndImportFunction> _configFilesAndImportFunctions;
};
}
+7 -9
View File
@@ -80,7 +80,6 @@ namespace Robomongo
s << (isArray ? "[" : "{");
BSONObjIterator i(obj);
BSONElement e = i.next();
if ( !e.eoo() ) {
while ( 1 ) {
if ( pretty ) {
@@ -92,7 +91,6 @@ namespace Robomongo
else {
s << " ";
}
s << jsonString(e, format, true, pretty ? pretty + 1 : 0, uuidEncoding, timeFormat, isArray);
e = i.next();
@@ -111,8 +109,7 @@ 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();
@@ -334,11 +331,12 @@ namespace Robomongo
break;
case bsonTimestamp:
if ( format == TenGen )
s << "Timestamp(" << elem.timestamp().getSecs() << ", " << elem.timestampInc() << ")";
else
s << "{ \"$timestamp\" : { \"t\" : " << elem.timestamp().getSecs() << ", \"i\" : "
<< elem.timestampInc() << " } }";
if ( format == TenGen ) {
s << "Timestamp(" << ( elem.timestampValue() / 1000 ) << ", " << elem.timestampInc() << ")";
}
else {
s << "{ \"$timestamp\" : { \"t\" : " << ( elem.timestampValue() / 1000 ) << ", \"i\" : " << elem.timestampInc() << " } }";
}
break;
case MinKey:
+3 -14
View File
@@ -2,16 +2,13 @@
#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;
}
}
@@ -49,15 +46,7 @@ namespace Robomongo
{
// v0.9
// LOG(level) << "[" PROJECT_NAME_TITLE "] " << QtUtils::toStdString(mess) << std::endl;
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);
}
if (notify)
emit printed(mess, level);
}
}
+6 -77
View File
@@ -44,30 +44,12 @@ 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(":/robomongo/icons/replica_set_16x16.png");
static const QIcon replicaSetIc = QIcon(":/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);
@@ -114,26 +96,14 @@ namespace Robomongo
const QIcon &GuiRegistry::maximizeIcon() const
{
static const QIcon maximizeIc(":/robomongo/icons/maximize.png");
static const QIcon maximizeIc = QIcon(":/robomongo/icons/maximize.gif");
return maximizeIc;
}
const QIcon &GuiRegistry::minimizeIcon() const
const QIcon &GuiRegistry::maximizeHighlightedIcon() const
{
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;
static const QIcon maximizeHighlightedIc = QIcon(":/robomongo/icons/maximize_highlighted_16x16.png");
return maximizeHighlightedIc;
}
const QIcon &GuiRegistry::textIcon() const
@@ -311,18 +281,6 @@ 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));
@@ -347,12 +305,6 @@ 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
{
@@ -384,36 +336,13 @@ 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();
+1 -13
View File
@@ -30,10 +30,7 @@ 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;
@@ -42,9 +39,7 @@ namespace Robomongo
const QIcon& userIcon() const;
const QIcon& functionIcon() const;
const QIcon& maximizeIcon() const;
const QIcon& minimizeIcon() const;
const QIcon& undockIcon() const;
const QIcon& dockIcon() const;
const QIcon& maximizeHighlightedIcon() const;
const QIcon& textIcon() const;
const QIcon& textHighlightedIcon() const;
const QIcon& treeIcon() const;
@@ -60,15 +55,11 @@ 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;
@@ -87,9 +78,6 @@ 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;
+47 -418
View File
@@ -17,12 +17,6 @@
#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"
@@ -36,20 +30,14 @@
#include "robomongo/gui/widgets/LogWidget.h"
#include "robomongo/gui/widgets/explorer/ExplorerWidget.h"
#include "robomongo/gui/widgets/explorer/ExplorerCollectionTreeItem.h"
#include "robomongo/gui/widgets/explorer/ExplorerTreeWidget.h"
#include "robomongo/gui/widgets/workarea/WorkAreaTabWidget.h"
#include "robomongo/gui/widgets/workarea/QueryWidget.h"
#include "robomongo/gui/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"
#include "robomongo/gui/dialogs/ExportDialog.h"
#include "robomongo/gui/GuiRegistry.h"
#include "robomongo/gui/AppStyle.h"
namespace
{
void setToolBarIconSize(QToolBar *toolBar)
@@ -80,32 +68,21 @@ 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) override
virtual void keyPressEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_F12) {
hide();
@@ -116,19 +93,12 @@ 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), // Temporarily disabling export/import feature
#if defined(Q_OS_WIN)
_trayIcon(nullptr),
#endif
_allowExit(false)
{
_app(AppRegistry::instance().app()),
_workArea(NULL),
_connectionsMenu(NULL)
{
QColor background = palette().window().color();
QString controlKey = "Ctrl";
@@ -165,7 +135,7 @@ namespace Robomongo
// Exit action
QAction *exitAction = new QAction("&Exit", this);
exitAction->setShortcuts(QKeySequence::Quit);
VERIFY(connect(exitAction, SIGNAL(triggered()), this, SLOT(exit())));
VERIFY(connect(exitAction, SIGNAL(triggered()), this, SLOT(close())));
// Connect action
_connectAction = new QAction("&Connect...", this);
@@ -196,26 +166,6 @@ 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);
@@ -227,7 +177,7 @@ namespace Robomongo
ViewMode viewMode = AppRegistry::instance().settingsManager()->viewMode();
// Text mode action
auto textModeAction = new QAction("&Text Mode", this);
QAction *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>");
@@ -432,20 +382,6 @@ 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);
@@ -498,12 +434,6 @@ 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
@@ -515,10 +445,8 @@ namespace Robomongo
windowMenu->addSeparator();
windowMenu->addAction(reloadAction);
windowMenu->addAction(duplicateAction);
windowMenu->addSeparator();
windowMenu->addAction(openWelcomeTabAction);
auto toolbarsSettings = AppRegistry::instance().settingsManager()->toolbars();
SettingsManager::ToolbarSettingsContainerType toolbarsSettings = AppRegistry::instance().settingsManager()->toolbars();
/*** About menu ***/
@@ -562,70 +490,16 @@ 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);
expImpToolBar->setMovable(false);
// Add export action
_exportAction = new QAction(this);
_exportAction->setData("Export");
_exportAction->setIcon(GuiRegistry::instance().exportIcon());
_exportAction->setDisabled(true);
VERIFY(connect(_exportAction, SIGNAL(triggered()), this, SLOT(openExportDialog())));
addToolBar(expImpToolBar);
expImpToolBar->addAction(_exportAction);
// Add import action
_importAction = new QAction(this);
_importAction->setData("Import");
_importAction->setIcon(GuiRegistry::instance().importIcon());
_importAction->setDisabled(true);
addToolBar(expImpToolBar);
expImpToolBar->addAction(_importAction);
*/
createTabs();
createStatusBar();
setWindowTitle("Robomongo 1.0.3");
setWindowTitle(PROJECT_NAME_TITLE " " PROJECT_VERSION);
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);
@@ -633,21 +507,7 @@ namespace Robomongo
AppRegistry::instance().bus()->subscribe(this, QueryWidgetUpdatedEvent::Type);
AppRegistry::instance().bus()->subscribe(this, OperationFailedEvent::Type);
restoreWindowSettings();
// Catch application windows focus changes
VERIFY(connect(qApp, SIGNAL(focusChanged(QWidget*, QWidget*)), this, SLOT(on_focusChanged())));
_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)
restoreWindowsSettings();
}
void MainWindow::createStylesMenu()
@@ -709,26 +569,22 @@ namespace Robomongo
AppRegistry::instance().settingsManager()->save();
}
void MainWindow::exit()
void MainWindow::restoreWindowsSettings()
{
_allowExit = true;
close();
}
void MainWindow::restoreWindowSettings()
{
QSettings settings("3T", "Robomongo");
QSettings settings("Paralect", "Robomongo");
// Restore settings if registery key exists, otherwise resize as app started for the first time.
if (settings.contains("MainWindow/geometry"))
{
restoreGeometry(settings.value("MainWindow/geometry").toByteArray());
restoreState(settings.value("MainWindow/windowState").toByteArray());
settings.setValue("windowState", saveState());
}
else
{
// Resize main window. We are trying to keep it "almost" maximized.
QRect screenGeometry = QApplication::desktop()->availableGeometry();
int horizontalMargin = static_cast<int>(screenGeometry.width() * 0.1);
int verticalMargin = static_cast<int>(screenGeometry.height() * 0.1);
int horizontalMargin = (int)(screenGeometry.width() * 0.1); // todo: use C++ static cast instead of C-style cast
int verticalMargin = (int)(screenGeometry.height() * 0.1);
int _width = screenGeometry.width() - horizontalMargin;
int _height = screenGeometry.height() - verticalMargin;
resize(QSize(_width, _height));
@@ -740,31 +596,11 @@ namespace Robomongo
}
}
void MainWindow::saveWindowSettings() const
void MainWindow::saveWindowsSettings() const
{
QSettings settings("3T", "Robomongo");
QSettings settings("Paralect", "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
settings.setValue("MainWindow/windowState", saveState());
}
void MainWindow::open()
@@ -773,14 +609,12 @@ namespace Robomongo
if (wid) {
wid->openFile();
}
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) {
else {
SettingsManager::ConnectionSettingsContainerType connections = AppRegistry::instance().settingsManager()->connections();
if (connections.size() == 1) {
ScriptInfo inf = ScriptInfo(QString());
if (inf.loadFromFile()) {
// todo: for now do not open new shell
_app->openShell(nullptr, connectionsCopy.at(0), inf);
_app->openShell(connections.at(0), inf);
}
}
}
@@ -802,6 +636,16 @@ namespace Robomongo
}
}
void MainWindow::keyPressEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_F12) {
_connectButton->showMenu();
return;
}
BaseClass::keyPressEvent(event);
}
void MainWindow::updateConnectionsMenu()
{
_connectionsMenu->clear();
@@ -833,17 +677,8 @@ 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();
@@ -875,10 +710,6 @@ 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();
}
@@ -944,13 +775,6 @@ 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());
@@ -1020,24 +844,6 @@ namespace Robomongo
dlg.exec();
}
void MainWindow::openWelcomeTab()
{
_workArea->openWelcomeTab();
}
/* --- Temporarily disabling export/import feature
void MainWindow::openExportDialog()
{
auto selectedItem = dynamic_cast<ExplorerCollectionTreeItem*>(_explorer->getSelectedTreeItem());
auto dbName = QString::fromStdString(selectedItem->collection()->database()->name());
auto collName = QString::fromStdString(selectedItem->collection()->name());
auto dialog = new ExportDialog(dbName, collName, this);
dialog->show(); // show it mode-less so that user can perform multiple simultaneous exports
}
*/
void MainWindow::setDefaultUuidEncoding()
{
AppRegistry::instance().settingsManager()->setUuidEncoding(DefaultEncoding);
@@ -1132,6 +938,7 @@ 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;
@@ -1139,7 +946,7 @@ namespace Robomongo
return;
lastServerHandle = event->serverHandle;
QMessageBox::critical(this, "Error", QtUtils::toQString(event->message));
QMessageBox::information(this, "Error", QtUtils::toQString(event->message));
}
void MainWindow::handle(ScriptExecutingEvent *)
@@ -1160,78 +967,13 @@ namespace Robomongo
ss << event->userFriendlyErrorMessage << std::endl << std::endl
<< "Error:" << std::endl << event->technicalErrorMessage;
QMessageBox::critical(NULL, "Operation failed", QtUtils::toQString(ss.str()));
}
void MainWindow::keyPressEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_F12) {
_connectButton->showMenu();
return;
}
BaseClass::keyPressEvent(event);
QMessageBox::information(NULL, "Operation failed", QtUtils::toQString(ss.str()));
}
void MainWindow::closeEvent(QCloseEvent *event)
{
saveWindowSettings();
#if defined(Q_OS_WIN)
if (AppRegistry::instance().settingsManager()->minimizeToTray() && !_allowExit) {
event->ignore();
hide(); // hide the window because it can be reopened with the tray icon
}
else {
if (_trayIcon->isVisible())
_trayIcon->hide();
QMainWindow::closeEvent(event);
}
#else
saveWindowsSettings();
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)
@@ -1241,14 +983,14 @@ namespace Robomongo
void MainWindow::createDatabaseExplorer()
{
_explorer = new ExplorerWidget(this);
AppRegistry::instance().bus()->subscribe(_explorer, ConnectingEvent::Type);
AppRegistry::instance().bus()->subscribe(_explorer, ConnectionFailedEvent::Type);
AppRegistry::instance().bus()->subscribe(_explorer, ConnectionEstablishedEvent::Type);
ExplorerWidget *explorer = new ExplorerWidget(this);
AppRegistry::instance().bus()->subscribe(explorer, ConnectingEvent::Type);
AppRegistry::instance().bus()->subscribe(explorer, ConnectionFailedEvent::Type);
AppRegistry::instance().bus()->subscribe(explorer, ConnectionEstablishedEvent::Type);
QDockWidget *explorerDock = new QDockWidget(tr("Database Explorer"));
explorerDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
explorerDock->setWidget(_explorer);
explorerDock->setWidget(explorer);
explorerDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable);
QWidget *titleWidget = new QWidget(this); // this lines simply remove
@@ -1275,7 +1017,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);
_logDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable);
_logDock->setVisible(false);
QAction *action = _logDock->toggleViewAction();
@@ -1291,20 +1033,8 @@ namespace Robomongo
void MainWindow::updateMenus()
{
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;
}
int contTab = _workArea->count();
bool isEnable = _workArea && contTab > 0;
_execToolBar->setEnabled(isEnable);
_openAction->setEnabled(isEnable);
@@ -1317,7 +1047,6 @@ 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);
@@ -1351,104 +1080,4 @@ namespace Robomongo
AppRegistry::instance().settingsManager()->setToolbarSettings("explorer", isVisible);
AppRegistry::instance().settingsManager()->save();
}
/* --- Temporarily disabling export/import feature
void MainWindow::onExplorerItemSelected(QTreeWidgetItem *selectedItem)
{
auto collectionItem = dynamic_cast<ExplorerCollectionTreeItem*>(selectedItem);
if (collectionItem) {
_exportAction->setEnabled(true);
}
else {
_exportAction->setDisabled(true);
}
}
*/
void MainWindow::on_tabChange()
{
auto activeTab = dynamic_cast<QueryWidget*>(_workArea->currentWidget());
if (activeTab) {
activeTab->bringDockToFront();
}
}
void MainWindow::toggleMinimize()
{
if (isHidden()) {
show();
}
else {
hide();
}
}
void MainWindow::trayActivated(QSystemTrayIcon::ActivationReason reason)
{
if (QSystemTrayIcon::DoubleClick == reason && isHidden()) {
show();
}
}
void MainWindow::toggleMinimizeToTray()
{
QAction *send = qobject_cast<QAction*>(sender());
saveMinimizeToTraySettings(send->isChecked());
}
void MainWindow::on_focusChanged()
{
// If focus is on floating output window, make it's parent (which is a QueryWidget tab) as active tab
auto const activeDock = dynamic_cast<QueryWidget::CustomDockWidget*>(qApp->activeWindow());
if (activeDock) {
_workArea->setCurrentWidget(activeDock->getParentQueryWidget());
}
}
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() + "&notify=true#");
_networkAccessManager->get(QNetworkRequest(url));
}
}
+6 -66
View File
@@ -1,17 +1,11 @@
#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
@@ -25,8 +19,6 @@ namespace Robomongo
class WorkAreaTabWidget;
class ConnectionMenu;
class App;
class ExplorerWidget;
class WelcomeTab;
class MainWindow : public QMainWindow
{
@@ -35,8 +27,7 @@ namespace Robomongo
public:
typedef QMainWindow BaseClass;
MainWindow();
WelcomeTab* getWelcomeTab();
void keyPressEvent(QKeyEvent *event);
public Q_SLOTS:
void manageConnections();
@@ -60,7 +51,6 @@ namespace Robomongo
void save();
void saveAs();
void changeStyle(QAction *);
void exit();
void setDefaultUuidEncoding();
void setJavaUuidEncoding();
@@ -81,62 +71,32 @@ namespace Robomongo
void handle(OperationFailedEvent *event);
protected:
void keyPressEvent(QKeyEvent *event) override;
void closeEvent(QCloseEvent *event) override;
void hideEvent(QHideEvent *event) override;
void showEvent(QShowEvent *event) override;
bool eventFilter(QObject *target, QEvent *event) override;
void resizeEvent(QResizeEvent* event) override;
void closeEvent(QCloseEvent *event);
private Q_SLOTS:
void updateMenus();
void setUtcTimeZone();
void setLocalTimeZone();
void openPreferences();
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);
// 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();
void createDatabaseExplorer();
void createTabs();
void createStylesMenu();
void createStatusBar();
void restoreWindowSettings();
void saveWindowSettings() const;
void adjustUpdatesBarHeight();
void restoreWindowsSettings();
void saveWindowsSettings() const;
QDockWidget *_logDock;
WorkAreaTabWidget *_workArea;
ExplorerWidget* _explorer;
App *_app;
ConnectionMenu *_connectionsMenu;
@@ -144,34 +104,14 @@ namespace Robomongo
QMenu *_viewMenu;
QMenu *_toolbarsMenu;
QAction *_connectAction;
// Open/Save tool bar
QAction *_openAction;
QAction *_saveAction;
QAction *_saveAsAction;
// Execution tool bar
QAction *_executeAction;
QAction *_stopAction;
QAction *_orientationAction;
QToolBar *_execToolBar;
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;
};
}
+16 -46
View File
@@ -4,9 +4,6 @@
#include <QGridLayout>
#include <QLabel>
#include <QPushButton>
#include <QTabWidget>
#include <QTextBrowser>
#include <QFile>
#include "robomongo/gui/GuiRegistry.h"
#include "robomongo/core/utils/QtUtils.h"
@@ -18,11 +15,11 @@ namespace
"Shell-centric MongoDB management tool."
"<br/>"
"<br/>"
"Visit " PROJECT_NAME_TITLE " website: <a href=\"https://" PROJECT_DOMAIN "\">" PROJECT_DOMAIN "</a> <br/>"
"Visit " PROJECT_NAME_TITLE " website: <a href=\"http://" PROJECT_DOMAIN "\">" PROJECT_DOMAIN "</a> <br/>"
"<br/>"
"<a href=\"https://" PROJECT_GITHUB_ISSUES "\">Submit</a> issues/proposals on GitHub. <br/>"
"<a href=\"https://" PROJECT_GITHUB_FORK "\">Fork</a> project or <a href=\"https://" PROJECT_GITHUB_ISSUES "\">submit</a> issues/proposals on GitHub. <br/>"
"<br/>"
"Copyright 2014-2017 <a href= " PROJECT_COMPANYNAME_DOMAIN " >" PROJECT_COMPANYNAME "</a>. All rights reserved.<br/>"
"Copyright 2013-2016 <a href=\"http:// " PROJECT_COMPANYNAME_DOMAIN "\">" PROJECT_COMPANYNAME "</a>. All rights reserved.<br/>"
"<br/>"
"The program is provided AS IS with NO WARRANTY OF ANY KIND, "
"INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A "
@@ -40,57 +37,30 @@ namespace Robomongo
AboutDialog::AboutDialog(QWidget *parent)
: QDialog(parent)
{
setWindowTitle("About " PROJECT_NAME_TITLE);
setWindowIcon(GuiRegistry::instance().mainWindowIcon());
//// About tab
auto aboutTab = new QWidget;
aboutTab->setWindowIcon(GuiRegistry::instance().mainWindowIcon());
aboutTab->setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
auto layout = new QGridLayout(this);
setWindowTitle("About " PROJECT_NAME_TITLE);
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
QGridLayout *layout = new QGridLayout(this);
layout->setSizeConstraint(QLayout::SetFixedSize);
auto copyRightLabel = new QLabel(description);
QLabel *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);
auto logoLabel = new QLabel;
QLabel *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);
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);
layout->addWidget(buttonBox, 4, 0, 1, 5);
}
}
@@ -3,25 +3,9 @@
#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
{
@@ -32,123 +16,23 @@ 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 behavior.");
"Leave this field empty, if you want default behaviour.");
defaultDatabaseDescriptionLabel->setWordWrap(true);
defaultDatabaseDescriptionLabel->setContentsMargins(0, -2, 0, 20);
_defaultDatabaseName = new QLineEdit(QtUtils::toQString(_settings->defaultDatabase()));
/* --- Disabling unfinished export URI connection string feature
_uriString = new QLineEdit;
_uriString->setReadOnly(true);
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);
_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);
setLayout(connectionLayout);
}
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,8 +4,6 @@
QT_BEGIN_NAMESPACE
class QLineEdit;
class QCheckBox;
class QPushButton;
QT_END_NAMESPACE
namespace Robomongo
@@ -18,24 +16,10 @@ 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(HighDpiConstants::WIN_HIGH_DPI_BUTTON_HEIGHT);
_echoModeButton->setMaximumHeight(HighDpiContants::WIN_HIGH_DPI_BUTTON_HEIGHT);
#endif
VERIFY(connect(_echoModeButton, SIGNAL(clicked()), this, SLOT(toggleEchoMode())));
+73 -184
View File
@@ -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 <QTreeWidget>
#include <QTreeWidgetItem>
#include <QMessageBox>
#include <QDialogButtonBox>
#include <QListWidget>
#include <QListWidgetItem>
#include <QShortCut>
#include <QKeySequence>
#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"));
_connectionType->addItem(tr("Replica Set or Sharded Cluster"));
_connectionType->setCurrentIndex(static_cast<int>(_settings->isReplicaSet()));
VERIFY(connect(_connectionType, SIGNAL(currentIndexChanged(int)), this, SLOT(on_ConnectionTypeChange(int))));
@@ -49,66 +49,56 @@ namespace Robomongo
_addInfoLabel->setContentsMargins(0, -2, 0, 20);
_membersLabel = new QLabel("Members:");
_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));
_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));
item->setFlags(item->flags() | Qt::ItemIsEditable);
_members->addTopLevelItem(item);
_members->addItem(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;
_addButton->setIcon(GuiRegistry::instance().plusIcon());
_removeButton = new QPushButton;
_removeButton->setIcon(GuiRegistry::instance().minusIcon());
_addButton = new QPushButton("Add");
_addButton->setFixedWidth(BUTTON_SIZE);
_removeButton = new QPushButton("Remove");
_removeButton->setFixedWidth(BUTTON_SIZE);
_discoverButton = new QPushButton("Discover");
_discoverButton->setFixedWidth(BUTTON_SIZE);
VERIFY(connect(_addButton, SIGNAL(clicked()), this, SLOT(on_addButton_clicked())));
VERIFY(connect(_removeButton, SIGNAL(clicked()), this, SLOT(on_removeButton_clicked())));
_minusPlusButtonBox = new QDialogButtonBox(this);
_minusPlusButtonBox->setOrientation(Qt::Horizontal);
#ifdef _WIN32
_minusPlusButtonBox->addButton(_addButton, QDialogButtonBox::NoRole);
_minusPlusButtonBox->addButton(_removeButton, QDialogButtonBox::NoRole);
#else
_minusPlusButtonBox->addButton(_removeButton, QDialogButtonBox::NoRole);
_minusPlusButtonBox->addButton(_addButton, QDialogButtonBox::NoRole);
#endif
auto hLayout = new QHBoxLayout;
hLayout->addWidget(_addButton);
hLayout->addWidget(_removeButton);
hLayout->addWidget(_discoverButton);
hLayout->setAlignment(Qt::AlignRight);
_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>");
_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()));
}
auto connectionLayout = new QGridLayout;
connectionLayout->setVerticalSpacing(10);
new QShortcut(QKeySequence(Qt::Key_Delete), this, SLOT(deleteItem()));
QGridLayout *connectionLayout = new QGridLayout;
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);
@@ -119,85 +109,42 @@ 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->addWidget(_minusPlusButtonBox, 8, 3, Qt::AlignRight | Qt::AlignTop);
connectionLayout->addWidget(_members, 7, 1, 1, 3);
connectionLayout->addLayout(hLayout, 8, 1, 1, 3, Qt::AlignRight);
connectionLayout->addWidget(new QLabel(""), 9, 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);
connectionLayout->addWidget(_readPrefLabel, 10, 0);
connectionLayout->addWidget(_readPreference, 10, 1, 1, 3);
connectionLayout->addWidget(new QLabel(""), 11, 0);
auto mainLayout = new QVBoxLayout;
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(connectionLayout);
setLayout(mainLayout);
_connectionName->setFocus();
on_ConnectionTypeChange(_connectionType->currentIndex());
}
bool ConnectionBasicTab::accept()
void ConnectionBasicTab::accept()
{
ReplicaSetSettings* const replicaSettings = _settings->replicaSetSettings();
_settings->setReplicaSet(static_cast<bool>(_connectionType->currentIndex()));
_settings->setConnectionName(QtUtils::toStdString(_connectionName->text()));
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());
}
_settings->setServerHost(QtUtils::toStdString(_serverAddress->text()));
_settings->setServerPort(_serverPort->text().toInt());
if (_settings->isReplicaSet()) {
// Save replica members
// save replica members
std::vector<std::string> members;
for (int i = 0; i < _members->topLevelItemCount(); ++i)
for (int i = 0; i < _members->count(); ++i)
{
QTreeWidgetItem const* item = _members->topLevelItem(i);
if (!item->text(0).isEmpty())
members.push_back(item->text(0).toStdString());
QListWidgetItem const* item = _members->item(i);
members.push_back(item->text().toStdString());
}
_settings->replicaSetSettings()->setMembers(members);
_settings->replicaSetSettings()->setSetNameUserEntered(_setNameEdit->text().toStdString());
// Clear cached set name
_settings->replicaSetSettings()->setCachedSetName("");
replicaSettings->setMembers(members);
// save read preference option
replicaSettings->setReadPreference(
static_cast<ReplicaSetSettings::ReadPreference>(_readPreference->currentIndex()));
}
return true;
}
void ConnectionBasicTab::on_ConnectionTypeChange(int index)
@@ -207,10 +154,12 @@ namespace Robomongo
// Replica set
_membersLabel->setVisible(isReplica);
_members->setVisible(isReplica);
_minusPlusButtonBox->setVisible(isReplica);
_setNameLabel->setVisible(isReplica);
_setNameEdit->setVisible(isReplica);
_addButton->setVisible(isReplica);
_removeButton->setVisible(isReplica);
_discoverButton->setVisible(isReplica);
_readPrefLabel->setVisible(isReplica);
_readPreference->setVisible(isReplica);
// Direct Connection
_addressLabel->setVisible(!isReplica);
_serverAddress->setVisible(!isReplica);
@@ -226,68 +175,8 @@ namespace Robomongo
void ConnectionBasicTab::on_addButton_clicked()
{
auto item = new QTreeWidgetItem;
// Make member addition little smarter than expected
if (_members->topLevelItemCount() < 1) {
item->setText(0, "localhost:27017");
}
else { // Add the next member using last entered hostname and incremented port by one
QString const& lastMember = _members->topLevelItem(_members->topLevelItemCount()-1)->text(0);
QStringList const& hostAndPort = lastMember.split(':');
if (hostAndPort.size() == 2) {
auto const& hostName = hostAndPort[0];
auto const& port = hostAndPort[1].toInt();
item->setText(0, hostName + ':' + QString::number(port + 1));
}
else {
item->setText(0, "localhost:" + QString::number(_members->topLevelItemCount() + 27017));
}
}
auto item = new QListWidgetItem("localhost:27017");
item->setFlags(item->flags() | Qt::ItemIsEditable);
_members->addTopLevelItem(item);
}
void ConnectionBasicTab::on_removeButton_clicked()
{
if (_members->topLevelItemCount() <= 0)
return;
if (_members->currentItem())
delete _members->currentItem();
else
delete _members->topLevelItem(_members->topLevelItemCount() - 1);
}
void ConnectionBasicTab::on_replicaMemberItemEdit(QTreeWidgetItem* item, int column)
{
if (!item)
return;
auto str = item->text(0);
// Remove white spaces
str = str.simplified();
str.remove(" ");
// Remove item from tree widget if it has empty text
if (str.isEmpty()) {
delete item;
return;
}
// Force port as integer
QStringList const& hostAndPort = str.split(':');
if (hostAndPort.size() >= 2) {
auto const& hostName = hostAndPort[0];
auto portStr = hostAndPort[1];
portStr.remove(QRegExp("[^\\d]"));
str = hostName + ':' + QString::number(portStr.toInt());
}
else
str += ":27017";
item->setText(0, str);
_members->addItem(item);
}
}
+3 -11
View File
@@ -7,9 +7,7 @@ class QLabel;
class QCheckBox;
class QPushButton;
class QComboBox;
class QTreeWidget;
class QTreeWidgetItem;
class QDialogButtonBox;
class QListWidget;
QT_END_NAMESPACE
namespace Robomongo
@@ -22,14 +20,12 @@ namespace Robomongo
public:
ConnectionBasicTab(ConnectionSettings *settings);
bool accept();
void 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;
@@ -43,16 +39,12 @@ namespace Robomongo
QLineEdit *_serverPort;
QLabel *_addInfoLabel;
QLabel *_membersLabel;
QTreeWidget *_members;
QListWidget *_members;
QPushButton *_addButton;
QPushButton *_removeButton;
QDialogButtonBox *_minusPlusButtonBox;
QLabel *_setNameLabel;
QLineEdit *_setNameEdit;
QPushButton *_discoverButton;
QLabel *_readPrefLabel;
QComboBox *_readPreference;
ConnectionSettings *const _settings;
};
}
@@ -11,7 +11,6 @@
#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"
@@ -24,11 +23,13 @@ namespace Robomongo
{
ConnectionDiagnosticDialog::ConnectionDiagnosticDialog(ConnectionSettings *connection, QWidget *parent) :
QDialog(parent),
_connSettings(connection->clone()),
_connection(NULL),
_server(NULL),
_serverHandle(0),
_continueExec(true)
{
_connection = connection->clone();
AppRegistry::instance().bus()->subscribe(this, ConnectionEstablishedEvent::Type);
AppRegistry::instance().bus()->subscribe(this, ConnectionFailedEvent::Type);
@@ -96,7 +97,7 @@ namespace Robomongo
_viewErrorLink->hide();
if (!AppRegistry::instance().app()->openServer(_connSettings, ConnectionTest)) {
if (!AppRegistry::instance().app()->openServer(_connection, ConnectionTest)) {
_continueExec = false;
return;
}
@@ -118,7 +119,7 @@ namespace Robomongo
void ConnectionDiagnosticDialog::sshStatus(State state)
{
if (!_connSettings->sshSettings()->enabled()) {
if (!_connection->sshSettings()->enabled()) {
_sshIconLabel->setVisible(false);
_sshLabel->setVisible(false);
return;
@@ -127,18 +128,18 @@ namespace Robomongo
if (state == InitialState) {
_sshIconLabel->setMovie(_loadingMovie);
_sshLabel->setText(QString("Connecting to SSH server at <b>%1:%2</b>...")
.arg(QtUtils::toQString(_connSettings->sshSettings()->host()))
.arg(_connSettings->sshSettings()->port()));
.arg(QtUtils::toQString(_connection->sshSettings()->host()))
.arg(_connection->sshSettings()->port()));
} else if (state == CompletedState) {
_sshIconLabel->setPixmap(_yesPixmap);
_sshLabel->setText(QString("Connected to SSH server at <b>%1:%2</b>")
.arg(QtUtils::toQString(_connSettings->sshSettings()->host()))
.arg(_connSettings->sshSettings()->port()));
.arg(QtUtils::toQString(_connection->sshSettings()->host()))
.arg(_connection->sshSettings()->port()));
} else if (state == FailedState) {
_sshIconLabel->setPixmap(_noPixmap);
_sshLabel->setText(QString("Unable to connect to SSH server at <b>%1:%2</b>")
.arg(QtUtils::toQString(_connSettings->sshSettings()->host()))
.arg(_connSettings->sshSettings()->port()));
.arg(QtUtils::toQString(_connection->sshSettings()->host()))
.arg(_connection->sshSettings()->port()));
}
}
@@ -146,45 +147,34 @@ namespace Robomongo
{
// Add info about tunneling if SSH or SSL is used
QString tunnelNote(""); // No tunnel info when neither SSH nor SSL enabled
if (_connSettings->sshSettings()->enabled())
if (_connection->sshSettings()->enabled())
{
tunnelNote = " via SSH tunnel";
else if (_connSettings->sslSettings()->sslEnabled())
}
else if (_connection->sslSettings()->sslEnabled())
{
tunnelNote = " via SSL tunnel";
else
tunnelNote = "";
auto replicaSetStr = QString::fromStdString(_connSettings->connectionName()) + " [Replica Set]";
replicaSetStr = (_connSettings->replicaSetSettings()->members().size() > 0)
? replicaSetStr + '(' + QString::fromStdString(
_connSettings->replicaSetSettings()->members()[0]) + ')'
: replicaSetStr + "";
QString const& serverAddress = _connSettings->isReplicaSet()
? replicaSetStr
: QString::fromStdString(_connSettings->getFullAddress());
}
// Set main info text at dialog
if (state == InitialState) {
_connectionIconLabel->setMovie(_loadingMovie);
_connectionLabel->setText(QString("Connecting to <b>%1</b>%2...").arg(serverAddress, tunnelNote));
_connectionLabel->setText(QString("Connecting to <b>%1</b>%2...").arg(QtUtils::toQString(_connection->getFullAddress()), tunnelNote));
} else if (state == CompletedState) {
_connectionIconLabel->setPixmap(_yesPixmap);
_connectionLabel->setText(QString("Connected to <b>%1</b>%2").arg(serverAddress, tunnelNote));
_connectionLabel->setText(QString("Connected to <b>%1</b>%2").arg(QtUtils::toQString(_connection->getFullAddress()), tunnelNote));
} else if (state == FailedState) {
_connectionIconLabel->setPixmap(_noPixmap);
_connectionLabel->setText(QString("Failed to connect to <b>%1</b>%2").arg(serverAddress, tunnelNote));
_connectionLabel->setText(QString("Failed to connect to <b>%1</b>%2").arg(QtUtils::toQString(_connection->getFullAddress()), tunnelNote));
} else if (state == NotPerformedState) {
_connectionIconLabel->setPixmap(_questionPixmap);
_connectionLabel->setText(QString("No chance to try connection to <b>%1</b>%2").
arg(serverAddress, tunnelNote));
_connectionLabel->setText(QString("No chance to try connection to <b>%1</b>%2").arg(QtUtils::toQString(_connection->getFullAddress()), tunnelNote));
}
}
void ConnectionDiagnosticDialog::authStatus(State state)
{
if (!_connSettings->hasEnabledPrimaryCredential()) {
if (!_connection->hasEnabledPrimaryCredential()) {
_authIconLabel->setVisible(false);
_authLabel->setVisible(false);
return;
@@ -193,18 +183,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(_connSettings->primaryCredential()->databaseName()))
.arg(QtUtils::toQString(_connSettings->primaryCredential()->userName())));
.arg(QtUtils::toQString(_connection->primaryCredential()->databaseName()))
.arg(QtUtils::toQString(_connection->primaryCredential()->userName())));
} else if (state == CompletedState) {
_authIconLabel->setPixmap(_yesPixmap);
_authLabel->setText(QString("Authorized on <b>%1</b> database as <b>%2</b>")
.arg(QtUtils::toQString(_connSettings->primaryCredential()->databaseName()))
.arg(QtUtils::toQString(_connSettings->primaryCredential()->userName())));
.arg(QtUtils::toQString(_connection->primaryCredential()->databaseName()))
.arg(QtUtils::toQString(_connection->primaryCredential()->userName())));
} else if (state == FailedState) {
_authIconLabel->setPixmap(_noPixmap);
_authLabel->setText(QString("Authorization failed on <b>%1</b> database as <b>%2</b>")
.arg(QtUtils::toQString(_connSettings->primaryCredential()->databaseName()))
.arg(QtUtils::toQString(_connSettings->primaryCredential()->userName())));
.arg(QtUtils::toQString(_connection->primaryCredential()->databaseName()))
.arg(QtUtils::toQString(_connection->primaryCredential()->userName())));
} else if (state == NotPerformedState) {
_authIconLabel->setPixmap(_questionPixmap);
_authLabel->setText(QString("No chance to authorize"));
@@ -213,7 +203,7 @@ namespace Robomongo
void ConnectionDiagnosticDialog::listStatus(State state)
{
if (_connSettings->hasEnabledPrimaryCredential()) {
if (_connection->hasEnabledPrimaryCredential()) {
_listIconLabel->setVisible(false);
_listLabel->setVisible(false);
return;
@@ -8,7 +8,7 @@ class QMovie;
namespace Robomongo
{
struct ConnectionEstablishedEvent;
class ConnectionEstablishedEvent;
class ConnectionFailedEvent;
class ConnectionSettings;
class MongoServer;
@@ -29,8 +29,7 @@ namespace Robomongo
private:
enum State
{
enum State {
InitialState,
CompletedState,
FailedState,
@@ -42,7 +41,7 @@ namespace Robomongo
void authStatus(State state);
void listStatus(State state);
ConnectionSettings *_connSettings;
ConnectionSettings *_connection;
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 (!_basicTab->accept() || !_sshTab->accept() || !_sslTab->accept())
if (!_sshTab->accept() || !_sslTab->accept())
{
return false;
}
+66 -95
View File
@@ -11,22 +11,15 @@
#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
{
@@ -52,49 +45,36 @@ namespace Robomongo
*/
void setConnection(ConnectionSettings *connection)
{
_connection = 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->isReplicaSet()) {
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()) {
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:
@@ -133,13 +113,12 @@ namespace Robomongo
_listWidget->setIndentation(5);
QStringList colums;
colums << "Name" << "Address" << "Attributes" << "Auth. Database / User";
colums << "Name" << "Address" << "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::ResizeToContents);
_listWidget->header()->setSectionResizeMode(3, QHeaderView::ResizeToContents);
_listWidget->header()->setSectionResizeMode(2, QHeaderView::Stretch);
#endif
//_listWidget->setViewMode(QListView::ListMode);
_listWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
@@ -204,19 +183,19 @@ namespace Robomongo
mainLayout->addLayout(firstColumnLayout, 1);
// Populate list with connections
std::vector<ConnectionSettings*> connectionSettings = _settingsManager->connections();
for (auto const& connSetting : connectionSettings) {
ConnectionSettings *connectionModel { connSetting };
SettingsManager::ConnectionSettingsContainerType connections = _settingsManager->connections();
for (SettingsManager::ConnectionSettingsContainerType::const_iterator it = connections.begin(); it != connections.end(); ++it) {
ConnectionSettings *connectionModel = *it;
add(connectionModel);
}
// Highlight last item
// Highlight first item
if (_listWidget->topLevelItemCount() > 0)
_listWidget->setCurrentItem(_listWidget->topLevelItem(_listWidget->topLevelItemCount()-1));
_listWidget->setCurrentItem(_listWidget->topLevelItem(0));
_listWidget->setFocus();
restoreWindowSettings();
restoreWindowsSettings();
}
/**
@@ -224,27 +203,27 @@ namespace Robomongo
*/
void ConnectionsDialog::accept()
{
auto currentItem = dynamic_cast<ConnectionListWidgetItem*>(_listWidget->currentItem());
ConnectionListWidgetItem *currentItem = (ConnectionListWidgetItem *) _listWidget->currentItem();
// Do nothing if no item selected
if (!currentItem)
return;
_selectedConnection = currentItem->connection();
saveWindowSettings();
saveWindowsSettings();
QDialog::accept();
}
void ConnectionsDialog::reject()
{
saveWindowSettings();
saveWindowsSettings();
QDialog::reject();
}
void ConnectionsDialog::closeEvent(QCloseEvent *event)
{
saveWindowSettings();
saveWindowsSettings();
QWidget::closeEvent(event);
}
@@ -264,19 +243,19 @@ namespace Robomongo
* @brief Initiate 'add' action, usually when user clicked on Add button
*/
void ConnectionsDialog::add()
{
auto newConnSettings = std::unique_ptr<ConnectionSettings>(new ConnectionSettings(false));
ConnectionDialog editDialog(newConnSettings.get());
{
ConnectionSettings *newModel = new ConnectionSettings();
ConnectionDialog editDialog(newModel);
// Do nothing if not accepted
if (editDialog.exec() != QDialog::Accepted) {
delete newModel;
return;
}
add(newConnSettings.get());
_settingsManager->addConnection(newConnSettings.release());
_settingsManager->addConnection(newModel);
_listWidget->setFocus();
add(newModel);
}
/**
@@ -284,13 +263,14 @@ namespace Robomongo
*/
void ConnectionsDialog::edit()
{
auto currentItem = dynamic_cast<ConnectionListWidgetItem*>(_listWidget->currentItem());
ConnectionListWidgetItem *currentItem =
(ConnectionListWidgetItem *) _listWidget->currentItem();
// Do nothing if no item selected
if (!currentItem)
if (currentItem == 0)
return;
auto connection = currentItem->connection();
ConnectionSettings *connection = currentItem->connection();
boost::scoped_ptr<ConnectionSettings> clonedConnection(connection->clone());
ConnectionDialog editDialog(clonedConnection.get());
@@ -322,53 +302,42 @@ namespace Robomongo
*/
void ConnectionsDialog::remove()
{
auto currentItem = dynamic_cast<ConnectionListWidgetItem*>(_listWidget->currentItem());
ConnectionListWidgetItem *currentItem =
(ConnectionListWidgetItem *)_listWidget->currentItem();
// Do nothing if no item selected
if (!currentItem)
return;
ConnectionSettings *connSettings = currentItem->connection();
ConnectionSettings *connectionModel = currentItem->connection();
// Ask user
int const answer = QMessageBox::question(this,
int answer = QMessageBox::question(this,
"Connections",
QString("Really delete \"%1\" connection?").arg(QtUtils::toQString(connSettings->getReadableName())),
QString("Really delete \"%1\" connection?").arg(QtUtils::toQString(connectionModel->getReadableName())),
QMessageBox::Yes, QMessageBox::No, QMessageBox::NoButton);
if (answer != QMessageBox::Yes)
return;
/* 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);
_settingsManager->removeConnection(connectionModel);
delete currentItem;
}
void ConnectionsDialog::clone()
{
auto currentItem = dynamic_cast<ConnectionListWidgetItem*>(_listWidget->currentItem());
ConnectionListWidgetItem *currentItem =
(ConnectionListWidgetItem *) _listWidget->currentItem();
// Do nothing if no item selected
if (!currentItem)
if (currentItem == 0)
return;
// Clone connection
ConnectionSettings *connection = currentItem->connection()->clone();
// 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();
std::string newConnectionName = "Copy of "+connection->connectionName();
connection->setConnectionName(newConnectionName);
connection->replicaSetSettings()->setCachedSetName("");
ConnectionDialog editDialog(connection);
@@ -388,13 +357,15 @@ 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 < _listWidget->topLevelItemCount(); i++)
for (int i = 0; i < count; i++)
{
auto item = (ConnectionListWidgetItem *) _listWidget->topLevelItem(i);
ConnectionListWidgetItem * item = (ConnectionListWidgetItem *) _listWidget->topLevelItem(i);
if (item->childCount() > 0) {
auto childItem = (ConnectionListWidgetItem *) item->child(0);
ConnectionListWidgetItem *childItem = (ConnectionListWidgetItem *) item->child(0);
item->removeChild(childItem);
_listWidget->insertTopLevelItem(++i, childItem);
_listWidget->setCurrentItem(childItem);
@@ -402,10 +373,11 @@ namespace Robomongo
}
}
count = _listWidget->topLevelItemCount();
SettingsManager::ConnectionSettingsContainerType items;
for (int i = 0; i < _listWidget->topLevelItemCount(); i++)
for (int i = 0; i < count; i++)
{
auto item = (ConnectionListWidgetItem *) _listWidget->topLevelItem(i);
ConnectionListWidgetItem * item = (ConnectionListWidgetItem *) _listWidget->topLevelItem(i);
items.push_back(item->connection());
}
@@ -417,9 +389,8 @@ namespace Robomongo
*/
void ConnectionsDialog::add(ConnectionSettings *connection)
{
auto item = new ConnectionListWidgetItem(connection);
ConnectionListWidgetItem *item = new ConnectionListWidgetItem(connection);
_listWidget->addTopLevelItem(item);
_listWidget->setCurrentItem(item);
_connectionItems.push_back(item);
}
@@ -445,15 +416,15 @@ namespace Robomongo
QDialog::keyPressEvent(event);
}
void ConnectionsDialog::restoreWindowSettings()
void ConnectionsDialog::restoreWindowsSettings()
{
QSettings settings("3T", "Robomongo");
QSettings settings("Paralect", "Robomongo");
resize(settings.value("ConnectionsDialog/size").toSize());
}
void ConnectionsDialog::saveWindowSettings() const
void ConnectionsDialog::saveWindowsSettings() const
{
QSettings settings("3T", "Robomongo");
QSettings settings("Paralect", "Robomongo");
settings.setValue("ConnectionsDialog/size", size());
}
+12 -12
View File
@@ -35,27 +35,27 @@ namespace Robomongo
/**
* @brief This function is called when user clicks on "Connect" button.
*/
void accept() override;
virtual void accept();
/**
* @brief Called when "Cancel" button clicked.
*/
void reject() override;
virtual void reject();
/**
* @brief Add connection to the list widget
*/
void add(ConnectionSettings *connection);
protected:
/**
* @brief Reimplementing closeEvent in order to do some pre-close actions.
*/
void closeEvent(QCloseEvent *event) override;
void closeEvent(QCloseEvent *event);
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) override;
void keyPressEvent(QKeyEvent* event);
private:
/**
* @brief Restore window settings from system registry
* @brief Restore windows settings from system registry
*/
void restoreWindowSettings();
void restoreWindowsSettings();
/**
* @brief Save windows settings into system registry
*/
void saveWindowSettings() const;
void saveWindowsSettings() const;
/**
* @brief ConnectionSettings, that was selected after pressing on
@@ -243,8 +243,7 @@ namespace Robomongo
if (state) // resize, dialog is expanding
{
setMinimumSize(560, 440);
_validateJsonButton->setHidden(_advancedOptions->currentIndex() == OPTIONS_TAB);
QSettings settings("3T", "Robomongo");
QSettings settings("Paralect", "Robomongo");
if (settings.contains("CreateCollectionDialog/size"))
{
restoreWindowSettings();
@@ -258,8 +257,7 @@ namespace Robomongo
else // resize, dialog is shrinking
{
saveWindowSettings(); // save expanded geometry first
_validateJsonButton->hide();
setFixedSize(300, 150);
setMinimumSize(300, 150);
adjustSize();
}
}
@@ -405,13 +403,13 @@ namespace Robomongo
void CreateCollectionDialog::saveWindowSettings() const
{
QSettings settings("3T", "Robomongo");
QSettings settings("Paralect", "Robomongo");
settings.setValue("CreateCollectionDialog/size", size());
}
void CreateCollectionDialog::restoreWindowSettings()
{
QSettings settings("3T", "Robomongo");
QSettings settings("Paralect", "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.
*/
void accept() override;
virtual void accept();
/**
* @brief Called when "Cancel" button clicked.
*/
void reject() override;
virtual void reject();
protected:
/**
* @brief Reimplementing closeEvent in order to do implement functionalities before close this dialog.
* @brief Reimplementing closeEvent in order to do implement functioanlites before close this dialog.
*/
void closeEvent(QCloseEvent *event) override;
void closeEvent(QCloseEvent *event);
private Q_SLOTS:
/**
@@ -1,183 +0,0 @@
#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();
}
}
@@ -1,85 +0,0 @@
#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,9 +6,7 @@
#include <QMessageBox>
#include <QDialogButtonBox>
#include <QDesktopWidget>
#include <QSettings>
#include <Qsci/qscilexerjavascript.h>
#include <mongo/client/dbclientinterface.h>
#include "robomongo/gui/editors/JSLexer.h"
@@ -36,15 +34,7 @@ namespace Robomongo
QSize size(screenGeometry.width() - horizontalMargin,
screenGeometry.height() - verticalMargin);
QSettings settings("3T", "Robomongo");
if (settings.contains("DocumentTextEditor/size"))
{
restoreWindowSettings();
}
else
{
resize(size);
}
resize(size);
setWindowFlags(Qt::Window | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint);
@@ -115,8 +105,6 @@ namespace Robomongo
if (!validate())
return;
saveWindowSettings();
QDialog::accept();
}
@@ -139,8 +127,6 @@ namespace Robomongo
return;
}
saveWindowSettings();
QDialog::reject();
}
@@ -200,12 +186,6 @@ namespace Robomongo
validate(false);
}
void DocumentTextEditor::closeEvent(QCloseEvent *event)
{
saveWindowSettings();
QWidget::closeEvent(event);
}
/*
** Configure QsciScintilla query widget
*/
@@ -224,17 +204,4 @@ 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());
}
}
+2 -19
View File
@@ -28,33 +28,16 @@ namespace Robomongo
void setCursorPosition(int line, int column);
public Q_SLOTS:
void accept() override;
void reject() override;
virtual void accept();
virtual void reject();
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;
-243
View File
@@ -1,243 +0,0 @@
#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());
}
}
-65
View File
@@ -1,65 +0,0 @@
#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;
};
}
+23 -6
View File
@@ -40,6 +40,8 @@
namespace Robomongo
{
//const QSize ExportDialog::dialogSize = QSize(500, 250); // todo: remove
namespace
{
const QString defaultDir = "D:\\exports\\"; // Default location
@@ -127,12 +129,12 @@ namespace Robomongo
VERIFY(connect(_formatComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(on_formatComboBox_change(int))));
_fieldsLabel = new QLabel("Fields:");
_fields = new QLineEdit; // todo: use textedit
_fields = new QLineEdit; // todo: make textedit
// Initially hidden
_fieldsLabel->setHidden(true);
_fields->setHidden(true);
_query = new QLineEdit("{}"); // todo: JSON frame can be used
_query = new QLineEdit("{}"); // todo: can use JSON frame
_outputFileName = new QLineEdit;
_outputDir = new QLineEdit;
_browseButton = new QPushButton("...");
@@ -152,7 +154,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(HighDpiConstants::WIN_HIGH_DPI_BUTTON_HEIGHT);
_browseButton->setMaximumHeight(HighDpiContants::WIN_HIGH_DPI_BUTTON_HEIGHT);
#endif
auto outputsInnerLay = new QGridLayout;
outputsInnerLay->addWidget(new QLabel("Format:"), 0, 0);
@@ -244,7 +246,7 @@ namespace Robomongo
layout->addLayout(hButtonBoxlayout);
setLayout(layout);
// todo: move to a function
// todo: move to function
// Help user filling inputs automatically
auto date = QDateTime::currentDateTime().toString("dd.MM.yyyy");
auto time = QDateTime::currentDateTime().toString("hh.mm.ss");
@@ -274,6 +276,13 @@ 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";
@@ -340,7 +349,17 @@ 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()
@@ -425,7 +444,6 @@ namespace Robomongo
_exportOutput->setText("Export Failed.\n");
_exportOutput->append("Output:\n" + _mongoExportOutputStr);
}
_exportOutput->moveCursor(QTextCursor::Start);
}
@@ -446,7 +464,6 @@ namespace Robomongo
_exportOutput->setText("Error: \"mongoexport\" process failed. Error code: "
+ QString::number(error));
}
_exportOutput->moveCursor(QTextCursor::Start);
}
+3 -15
View File
@@ -20,20 +20,6 @@ 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
@@ -46,10 +32,12 @@ 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();
@@ -65,7 +53,7 @@ namespace Robomongo
void on_viewOutputLink(QString);
private:
// todo: remove if unused
// todo: remove
Indicator *createDatabaseIndicator(const QString &database);
Indicator *createCollectionIndicator(const QString &collection);
+3 -3
View File
@@ -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(HighDpiConstants::WIN_HIGH_DPI_BUTTON_HEIGHT);
_passphraseEchoModeButton->setMaximumHeight(HighDpiConstants::WIN_HIGH_DPI_BUTTON_HEIGHT);
_selectPrivateFileButton->setMaximumHeight(HighDpiConstants::WIN_HIGH_DPI_BUTTON_HEIGHT);
_passwordEchoModeButton->setMaximumHeight(HighDpiContants::WIN_HIGH_DPI_BUTTON_HEIGHT);
_passphraseEchoModeButton->setMaximumHeight(HighDpiContants::WIN_HIGH_DPI_BUTTON_HEIGHT);
_selectPrivateFileButton->setMaximumHeight(HighDpiContants::WIN_HIGH_DPI_BUTTON_HEIGHT);
#endif
}
+4 -4
View File
@@ -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(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);
_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);
#endif
}
-15
View File
@@ -20,27 +20,18 @@
<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>
@@ -73,11 +64,5 @@
<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>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 518 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 334 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 550 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 189 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 388 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 384 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 168 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 509 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 192 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 446 B

After

Width:  |  Height:  |  Size: 511 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 407 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 397 B

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