Compare commits

..
186 Commits
Author SHA1 Message Date
Gokhan Simsek 27626665a1 RepSets: Adding code to check/save live status of Replica Set (health of members and who is primary) and show it in UI; showing Replica Set information/icon on ConnectionsDialog. 2016-09-06 14:38:50 +03:00
Gökhan Simsek 894f508f6c Merge pull request #1145 from flier/currentOps
Addition of db.currentOp() and db.killOp() as a database context menu items
2016-09-02 15:53:37 +03:00
Gökhan Simsek 2f2d3d584e Merge pull request #1129 from xuser86/issue-1127
#1127: Added two new context menu options in document view: "Copy Path" and "Copy Name"
2016-09-02 15:42:55 +03:00
Gokhan Simsek 67ae8a614d Merge branch 'ReplicaSets' 2016-09-02 12:21:33 +03:00
Gokhan Simsek cbba0e639d RepSets: Adding code to connect to Replica Set (using DBClientReplicaSet class) 2016-09-01 18:10:22 +03:00
Gokhan Simsek 8f3fdf3d1d RepSets: Adding "Open Direct Connection" functionality for replica member UI items 2016-08-31 13:10:31 +03:00
Gokhan Simsek 650fdd60cc RepSets: Adding double-click open shell functionality for replica member items 2016-08-31 12:39:58 +03:00
Gokhan Simsek 8b114b5122 Merge branch 'ReplicaSets' 2016-08-31 12:12:54 +03:00
Gokhan Simsek bc96addce5 RepSets: Adding various right click menu functionalities for replica set member UI items 2016-08-31 12:12:14 +03:00
Gokhan Simsek bac2c9d5ec RepSets: Adding new GUI class ExplorerReplicaSetTreeItem to represent a replica set member; adding simple icon for replica set. 2016-08-30 18:13:53 +03:00
Gokhan Simsek 94e0c37cf8 Merge branch 'ReplicaSets' 2016-08-29 19:26:21 +03:00
Gokhan Simsek 42d6181724 RepSets: Minor coding style refactoring 2016-08-26 20:50:15 +03:00
Gokhan Simsek 4c5f2e99cb RepSets: Adding code to save&load replica members to/from connection settings. 2016-08-26 17:23:12 +03:00
Gokhan Simsek 95e2fef4a6 Minor changes in CHANGELOG file. 2016-08-26 14:40:19 +03:00
Gökhan Simsek 4d8e3e61a1 Update README.md - minor corrections. 2016-08-26 14:18:27 +03:00
Gökhan Simsek c227798a4b Update README.md for RC10 release. 2016-08-26 14:15:56 +03:00
Gokhan Simsek fc5a6b9ac6 Merge branch 'master' into ReplicaSets 2016-08-25 12:03:31 +03:00
Gokhan Simsek b820f17b3c RepSets: Adding ReplicaSetSettings class and code to load&save related settings between file and UI. 2016-08-25 12:02:46 +03:00
Gokhan Simsek 079ec51dac CreateCollection: Correcting typo which was causing crash on debug mode. 2016-08-25 11:05:27 +03:00
Gökhan Simsek 88dee956f2 Adding ObjectRocket as supported platforms. 2016-08-23 12:33:44 +03:00
Gökhan Simsek 53945eb095 Adding new feature to main robomongo.md page. 2016-08-19 13:53:52 +03:00
Gökhan Simsek 0cecad49b3 Doc: Adding section to mention about new page: Robomongo Code Quality 2016-08-16 11:29:22 +03:00
Gokhan Simsek 652df93690 ReplicaSets: Adding related main UI widgets 2016-08-15 15:24:08 +03:00
Gökhan Simsek efa81c11c5 Updating link to new build pages 2016-08-12 18:12:06 +03:00
Gokhan Simsek 4af070b5b8 Adding new branch ReplicaSets 2016-08-12 17:33:16 +03:00
Rafał Jeżewski 95faf9332e Merge branch 'master' into issue-1127 2016-08-09 22:36:01 +02:00
Gökhan Simsek 7e87befd3d Updating build steps for Linux 2016-08-08 20:09:22 +03:00
Gökhan Simsek d6155f4c35 Adding mLab as supported and note about support limitation for MongoDB Atlas. 2016-08-08 14:22:06 +03:00
Gokhan Simsek 33c89eac63 Updating changelog for RC10. 2016-08-08 10:46:34 +03:00
Gökhan Simsek 335adc6bdb Adding MongoDB Atlas and Compose as supported. 2016-08-06 16:19:24 +03:00
Gokhan Simseek a107084745 UI fix to hide the password visibility by default and setting default icon for Auth and SSH tabs. 2016-08-05 22:36:47 +03:00
Gokhan Simsek 67f60b19ea Minor cosmetic fix, on MAC selecting cancel on file browse dialog results with problem where focus goes to parent of parent dialog. 2016-08-04 18:49:49 +03:00
Gokhan Simsek 734b17d0e7 SSL: Minor refactoring on MAC build which effects bin/run and bin/install & bin/pack output executable and package. 2016-08-04 13:46:27 +03:00
Gokhan Simsek 7739c92064 RC10 2016-08-03 13:04:01 +03:00
Gokhan Simsek 0ae5f12024 SSLTab: Applying Robomongo coding styles and some minor refactorings. 2016-08-03 13:00:12 +03:00
Gokhan Simsek e05dd9d1e9 SSLTab: Resetting SSL global parameters after it is being used and before every (SSL or non-SSL) connection request. 2016-08-03 09:59:16 +03:00
Gökhan Simsek ecbbeaeeca Adding extra steps for MAC to fix failing command "bin/run" 2016-08-03 09:22:08 +03:00
Flier Lu ea164e82a7 add db.killOp() as template to the context menu item 2016-08-02 02:00:04 +08:00
Flier Lu bfa1e68ce0 add db.currentOps() as a context menu item 2016-08-02 01:29:10 +08:00
Gokhan Simsek dc010092ab Adding default values for SSL settings 2016-08-01 19:32:16 +03:00
Gokhan Simsek 82c6bd6452 Merge branch 'master' into SSLTab 2016-08-01 16:03:23 +03:00
Gokhan Simsek 49a1251091 SSL: For Linux, updating find OpenSSL location and adding lib install code. 2016-08-01 16:02:26 +03:00
Gökhan Simsek 97ffad1178 Updating OpenSSL build steps for Linux 2016-08-01 15:58:35 +03:00
Gökhan Simsek 592883af29 Adding OpenSSL build helper commands for Windows 2016-08-01 12:11:16 +03:00
Gökhan Simsek 51a38c5607 Adding helper commands 2016-08-01 12:08:17 +03:00
Gokhan Simsek 5a645015a2 SSL: Fix for pack cmd line error for Windows: "destination" in cmake must be relative directory. 2016-08-01 11:39:30 +03:00
Gokhan Simsek 064094489f SSLTab: Fix for dylib and rpath problem for MAC (where OpenSSL dylibs were not copied while packing and they had absolute library paths) 2016-08-01 11:25:25 +03:00
Gökhan Simsek 218310c23c Udpating build steps for MAC in order to fix dylib and rpath problem 2016-08-01 11:10:10 +03:00
Gokhan Simsek 7c48a9e28d RC10 Beta Version 2016-07-29 09:25:39 +03:00
Gokhan Simsek 8ecffcbddb Merge remote-tracking branch 'origin/master' into SSLTab 2016-07-28 13:23:35 +03:00
Gökhan Simsek c31f8d8275 Updating build steps for Windows due to Qt 5.7 transition. 2016-07-28 13:22:26 +03:00
Gokhan Simsek 6db3fe11b9 SSLTab: Fix for minor UI flickering issue on MAC. 2016-07-28 13:18:49 +03:00
Gökhan Simsek ae7735f6bd Updating build steps for Mac and Linux due to Qt 5.7 transition. 2016-07-28 13:02:58 +03:00
Gokhan Simsek 22dc364f7e SSLTab: Removing gtk2 as an attempt to fix bin\configure error in ubuntu, reason is gtk2 has been removed in Qt 5.7. 2016-07-28 12:45:35 +03:00
Gokhan Simsek f11f22f6bb SSLTab: Minor adjustment for MAC due to new longer self-signed info text truncation. 2016-07-28 11:47:28 +03:00
Gokhan Simsek 2e07f1a9cb SSLTab: Some minor UI changes; adding more information about self-signed option. 2016-07-28 11:37:30 +03:00
Gokhan Simsek 62bc7e5ff7 SSLTab: Second major non-functional refactoring of SSLTab branch commits 2016-07-27 16:11:30 +03:00
Gokhan Simsek 6b9dd95b26 SSLTab: Window size adjustment for MAC; adding port information into SSL passphrase prompt dialog 2016-07-27 12:26:43 +03:00
Gokhan Simsek 0f3fd3ace1 SSLTab: UI horizontal spacing adjustments for MAC and HiDPI windows 2016-07-27 12:12:49 +03:00
Gokhan Simsek 10b9e91c8e SSLTab: Removing check box "PEM key encrypted..." due to unnecessary complexity. 2016-07-27 10:47:59 +03:00
Gokhan Simsek f813bc42ed Cosmetic: Cross platform pushbutton size adjustments 2016-07-26 21:30:45 +03:00
Gokhan Simsek 1c713c0e7e For related connection tabs, updating password show/hide icon with a new icon and adjusting button sizes 2016-07-26 20:15:30 +03:00
Gokhan Simsek 6b36671ad7 SSLTab: Reverting to original/removing ConnectionFailedEvent::Reason::Unknown case due to increased complexity. 2016-07-26 18:32:16 +03:00
Gokhan Simsek 720ee7981b SSLTab: Updating/adding new show/hide password icons; adding credits for icon designer company into about Robomongo window. 2016-07-26 17:43:12 +03:00
Gokhan Simsek dbed9b88a4 SSLTab: Adding tooltip (hint) helper text for users. 2016-07-26 12:01:22 +03:00
Gokhan Simsek d5afee08af SSLTab: Adding function to reset global SSL params and conditional passing of CA file. 2016-07-26 11:58:59 +03:00
Gokhan Simsek 5ae70fcf29 SSLTab: Adding warning dialog and protection for the case when both SSL and SSH are enabled. 2016-07-26 10:29:08 +03:00
Gokhan Simsek 16621d9ce1 SSLTab: Attempt to fix memory leak and resulting never closed mongod connections problem caused by an SSL connection failure exception at _dbclient->connect(_connection->info()) between manual new and delete of conn owning pointer. 2016-07-26 10:05:57 +03:00
Gokhan Simsek 32bfb590a8 SSLTab: Adding more information about failed SSL connection attempts into ConnectionDiagnosticDialog and Main error window; important change-adding reset params code into MongoWorker::writeGlobalSSLparams(). 2016-07-25 15:53:34 +03:00
Gokhan Simsek 0e2f9b427f SSLTab: Adding prompt dialog for PEM passphrase into App; adding "ask passphrase each time" feature code into SslSettings and SSLTab modules 2016-07-22 14:21:36 +03:00
Gokhan Simsek 1f8abd1be7 SSLTab: Minor UI change - top alignment adjustment required only for Windows 2016-07-21 14:11:45 +03:00
Gokhan Simsek f7e24ba46c Merge remote-tracking branch 'origin/master' into SSLTab 2016-07-21 12:53:43 +03:00
Gokhan Simsek d3b993d67e SSLTab: Adding usePemFile and useAdvancedOptions for SslSettings; adding minimum width for ConnectionDialog; adding PEM passphrase validation and some UI changes. 2016-07-21 12:53:23 +03:00
Gökhan Simsek eddd6269e7 Update README.md 2016-07-21 12:35:14 +03:00
Gokhan Simsek ad14cf6a94 SSLTab: Updating UI by removing indented style sub options, and some refactorings 2016-07-19 19:08:11 +03:00
Gökhan Simsek 53f92d91e7 Doc: Adding Qt insallation steps for MAC and Linux in BuildingRobomongo.md 2016-07-19 14:34:50 +03:00
Gokhan Simsek 58e8c708c4 SSLTab: Adding code to fix the issue on Windows High DPI button height is slightly bigger than other widgets 2016-07-15 13:16:14 +03:00
Gokhan Simsek 3b7f1f9cce SSLTab: Adding UI changes for MAC OSX issues 2016-07-15 12:23:50 +03:00
Gokhan Simsek d921975e9f Enabling Qt 5.7 cross platform High DPI support 2016-07-15 11:05:30 +03:00
Gokhan Simsek 9db35b6597 Major UI change: making SSL tab UI more simplistic and expanding; adding show/hide passphrase icons. 2016-07-15 11:02:42 +03:00
Rafał Jeżewski c6c5bd179b coding style fix 2016-07-12 12:46:04 +02:00
Rafał Jeżewski dd1d3ebdc4 #1127: Added two new context menu options in document view: "Copy Path" and "Copy Name" 2016-07-12 11:51:25 +02:00
Gokhan Simsek d5110c4ac8 Adding trivial cosmetic changes and hint strings. 2016-07-11 11:43:03 +03:00
Gokhan Simsek e08775f18a Correcting function name typo in SSLTab class 2016-07-08 18:19:56 +03:00
Gokhan Simsek a1e273a147 More trivial refactoring on SSLTab module 2016-07-08 17:54:52 +03:00
Gokhan Simsek 79a7d163c4 Adding file existence check into SSLTab accept() 2016-07-08 13:56:25 +03:00
Gokhan Simsek 4fd1a633e7 Changing size policy of ConnectionDialog to adjustSize(), trivial refactoring on MainWindow.h 2016-07-08 12:07:29 +03:00
Gökhan Simsek ce126b0f1d SSL: Updating build steps for MAC and linux according to new SSL feature. 2016-07-07 15:58:37 +03:00
Gökhan Simsek 1ccde63c31 SSL: Correction for windows build steps, set ROBOMONGO_CMAKE_PREFIX_PATH should be done after cloning robomongo-shell. 2016-07-07 14:13:49 +03:00
Gökhan Simsek 713972aed0 SSL: Updating build steps for windows in BuildingRobomongoOnWindows.md according to new SSL feature. 2016-07-06 18:46:45 +03:00
Gokhan Simsek a1abb11ec4 Merge remote-tracking branch 'origin/master' into SSLTab 2016-07-06 14:33:04 +03:00
Gokhan Simsek 2f86b628f9 First refactoring attempt of SSLTab branch - only trivial changes, no behavioral changes. 2016-07-06 14:31:34 +03:00
Gökhan Simsek 23994758dd Merge pull request #1124 from Aahanbhatt/master
Edited README.md file, changed copyrights from 2015 to 2016
2016-07-06 13:59:05 +03:00
Gökhan Simsek 1c5b6a722a Update BuildingRobomongoOnWindows.md 2016-07-06 13:06:45 +03:00
Gokhan Simsek a62d826005 Updating Linux objects files according to SSL build of mongo shell, adding OpenSSL dynamic library for linux into FindOpenSSL.cmake file. 2016-07-04 17:14:52 +03:00
Aahanbhatt c98d72f0cd Edited README.md file, changed copyrights from 2015 to 2016 2016-07-04 16:58:45 +05:30
Gokhan Simsek 55d59c1944 Removing src\third-party\openssl directory and files since Robomongo will dynamically depend on OpenSSL instead of building OpenSSL itself. 2016-06-30 18:46:26 +03:00
Gokhan Simsek 064b0b5ee1 Updating cmake script for OSX for finding version and dynamic lib path. 2016-06-29 22:19:19 +03:00
Gokhan Simsek ebf03afa75 Cmake changes for first attempt to make OpenSSL dynamically dependent (static previously). 2016-06-29 16:09:32 +03:00
Gokhan Simsek 116b2efd5f Merge branch 'SSLTab' of https://github.com/paralect/robomongo into SSLTab 2016-06-27 17:37:20 +03:00
Gokhan Simsek dfa66b8113 Merge remote-tracking branch 'origin/master' into SSLTab 2016-06-27 17:34:56 +03:00
Gokhan Simsek 620b0ff59a Updating OSX objects files according to SSL build of mongo shell, adding openssl include directory needed by robomongo project build. 2016-06-27 17:16:03 +03:00
Gokhan Simsek bc01acfef0 Updating windows objects files according to SSL build of mongo shell. 2016-06-27 17:12:13 +03:00
Gokhan Simsek f8199eaa79 Chg: Minor refactoring on MongoWorker and SSLTab classes. 2016-06-26 19:29:10 +03:00
Gokhan Simsek bb7408979a Minor re-factoring and conditional passing of sslGlobalParams.sslPEMKeyPassword 2016-06-16 13:43:46 +03:00
Gokhan Simsek 93a42e752b Adding pemKeyEncrypted into SSLSettings, switching to all grid main layout, adding slot for toggling client cert passphrase checbox. 2016-06-16 12:56:37 +03:00
Gokhan Simsek 8959bcb912 SSLTab cosmetic changes: groupbox design approach. 2016-06-15 22:21:06 +03:00
Gokhan Simsek d533e3bf8e Adding advanced options - allowInvalidHostnames and CRL file. 2016-06-15 18:57:34 +03:00
Gokhan Simsek fbdd73ec58 Adding support for allowInvalidCertificates SSL option into MongoWorker, SslSettings and SSLTab. 2016-06-14 19:58:39 +03:00
Gokhan Simsek 3c5e570e04 Adding connections with pemKeyPassPhrase option support into SSLTab, SslSettings and MongoWorker classes. 2016-06-13 17:40:32 +03:00
Gokhan Simsek 263c02eaab Adding pem key file browse button and action 2016-06-09 19:37:38 +03:00
Gokhan Simsek 44ad01b3ad Adding first fully working version from UI version with sslCAFile - details: adding allowSSL into main.cpp for global SSL initialization, adding caFile member into SslSettings class, adding caFile browse button. 2016-06-09 11:07:22 +03:00
simsekgokhan a37736eed6 Update BuildingRobomongoOnWindows.md 2016-06-06 15:04:10 +03:00
simsekgokhan a3c09047e6 Update README.md 2016-06-06 15:00:19 +03:00
simsekgokhan dc30c28c02 Update README.md - minor alignment issue 2016-06-06 14:54:30 +03:00
simsekgokhan d17f2e6eb9 Update README.md 2016-06-06 14:39:52 +03:00
simsekgokhan fdabd03924 Update README.md 2016-06-06 14:39:01 +03:00
Gokhan Simsek a8a04186ed Adding all UI widgets 2016-06-03 14:16:09 +03:00
Gokhan Simsek 164dd1d7a5 Chg: Adding SSLTab into cmakelist and into Connection Dialog UI. 2016-06-03 12:07:51 +03:00
Gokhan Simsek 4fc487348a New branch SSLTab 2016-06-03 11:43:35 +03:00
schetnikovich 0bb566869e Minor typo fix for Collections dialog 2016-06-01 15:03:11 +03:00
Gokhan Simsek 9fb495d785 Chg: Updating change log for RC9. 2016-05-30 19:15:51 +03:00
Gokhan Simsek df86e7c8ec Chg: Create Collection dialog: Hiding powerOfTwoSizeCheckBox if db version is higher than 3.0. 2016-05-27 16:26:39 +03:00
Gokhan Simsek 1e6f4d8052 Chg: Create Collection dialog: powerOfTwoSizeCheckBox is checked by default if storage engine type is MMAPV1; showing/enabling deprecated options in UI; refactoring DBVersion. 2016-05-27 11:36:13 +03:00
Gokhan Simsek bd470b1330 Minor change, removing saveState() due to runtime warnings. saveGeometry() is already enough for related feature. 2016-05-26 18:45:32 +03:00
Gokhan Simsek 60b3494207 Chg: Minor adjustment for non-high-DPI screens. 2016-05-26 17:38:45 +03:00
Gokhan Simsek 475a7fc44c #1102: Chg: Updating code to handle multiple screen condition. Saving/restoring only size, not geometry for Connections Dialog and Create Collection (advanced view) in order to show sub-windows on the same screen as (and on top of) Main Window. 2016-05-26 17:34:11 +03:00
Gokhan Simsek f19c5a0dc0 Chg: Updating fix for create collection crash to use copy of extraOptions BSON object instead of unique_ptr. 2016-05-26 16:35:52 +03:00
Gokhan Simsek 3e35f1dea3 #1102: Adding support to save geometry for more actions (connect, cancel buttons for Connections dialog and create, advanced buttons for Create Collection dialog) 2016-05-26 10:46:10 +03:00
Gokhan Simsek d8197dd00e Fix: Create Collection dialog "'usePowerOf2Sizes" option should be enabled for 2.6.11 mongodb-version. 2016-05-25 16:28:54 +03:00
Gokhan Simsek 3205248519 Fix: CreateCollection dialog "maximum number of documents" option should be optional, not required. 2016-05-25 15:55:02 +03:00
Gokhan Simsek 224801dcd9 Reverting usage of std::unique_ptr from C++14 to C++11. 2016-05-25 13:58:49 +03:00
Gokhan Simsek e8c0de07de Merge branch 'RestoreWindowSettings' 2016-05-25 13:28:02 +03:00
Gokhan Simsek cf7e061c7a Feature: For CreateCollection dialog, adding advanced button and expandable advanced option tabs and functionality to save/restore window geometry. 2016-05-25 13:27:06 +03:00
Gokhan Simsek a75becd6c5 Feature: Adding functionality to be able to restore/save window geometry/state for MainWindsow and ConnectionsDialog. 2016-05-25 11:44:18 +03:00
Gokhan Simsek e411bbe3c4 Fix: #1090: Adding informative comment. 2016-05-25 10:07:32 +03:00
Gokhan Simsek c8ca4bc945 Fix: #1090: Patch for libssh2 for ssh connection failure due to Ubuntu 16.04/OSX's OpenSSH 6.9 version have disabled obsolete form of KEX "Diffie-Hellman group exchange" algorithm. 2016-05-25 10:05:02 +03:00
Gokhan Simsek ffd9d13d79 Fixing crash related to race problem between local objects destruction and threads processing. 2016-05-19 18:38:14 +03:00
schetnikovich 4addeb5835 #1086: Imported connections compared with existing and information message is shown. 2016-05-06 19:08:03 +03:00
Dmitry Schetnikovich c086c9ab83 Update BuildingRobomongo.md 2016-05-05 18:14:07 +03:00
Dmitry Schetnikovich 44e30e4520 Update BuildingMongoDB.md 2016-05-05 18:08:12 +03:00
Dmitry Schetnikovich 8f93cd7dd6 Update BuildingRobomongo.md 2016-05-05 18:04:35 +03:00
schetnikovich a4390d348b #1086: Importing of connection settings from previous versions of Robomongo 2016-04-29 21:23:10 +03:00
schetnikovich bd72766414 Connections Dialog top link's color changed 2016-04-29 13:27:59 +03:00
schetnikovich 866828c1ba #1083 Correct handling of create/drop of databases and collections. 2016-04-28 22:09:25 +03:00
schetnikovich 8cedcc5d55 Robomongo RC9 2016-04-25 14:29:29 +03:00
gokhan.simsek 860cee5f12 #1077: Support for all options when creating new collection 2016-04-22 20:01:47 +03:00
schetnikovich a75972cf3b #1080 Shell helpers for legacy and new UUID types 2016-04-19 17:45:58 +03:00
schetnikovich 03e3592443 bin/tag 2016-04-18 13:58:36 +03:00
schetnikovich c113244319 Minor UI improvement for "Ask for password" dialog 2016-04-14 12:55:18 +03:00
schetnikovich 3a64e1310b Do not log password 2016-04-13 17:34:35 +03:00
schetnikovich 306b759b74 Fixed incorrect validation on SSH tab 2016-04-13 16:53:29 +03:00
schetnikovich 5976884b0f Details about SSH server, user name and private key file path when asked about password/passphrase. 2016-04-13 15:10:53 +03:00
schetnikovich ee907c3321 Disable and clear password and passphrase fields when "Ask password each time" checkbox is selected 2016-04-13 13:43:42 +03:00
schetnikovich 86307a724f Different text for "ask password" and "ask passphrase" checkbox 2016-04-12 23:56:58 +03:00
schetnikovich 997aa44654 #609 Robomongo should not force to save SSH passwords
Added option to always ask user for SSH password or passphrase and never store it in a plain text
2016-04-12 21:05:38 +03:00
schetnikovich feb1d277e9 Support for "~" expansion and validation of file path for SSH private key 2016-04-12 19:33:07 +03:00
schetnikovich 7e1dac26f1 SSH tab moved before Advanced and other minor UI tweaks of settings tabs 2016-04-12 19:05:47 +03:00
schetnikovich a7b95d4caf #1074 Fixed: Crash when "Copy JSON" an object within an array 2016-04-12 17:46:23 +03:00
schetnikovich bb042fe70b Brace matching turned off on the Mac because of flickering issue with Scintilla 2016-04-12 16:38:18 +03:00
schetnikovich e50ee8fbee Turn off echo of autocompletion cache setup 2016-04-12 14:41:01 +03:00
schetnikovich 55d8a6ff76 DB collections autocompletion cached for fixed number of seconds 2016-04-12 14:05:12 +03:00
schetnikovich 576fc07b5b Limit serverHost to fixed number of characters 2016-04-12 10:10:20 +03:00
schetnikovich b4978269a5 Set focus to address textbox when SSH is enabled 2016-04-11 23:10:08 +03:00
schetnikovich 24e9a74a6c Fixed two issues of blinking text and UI
1. Latest version of QScintilla has a bug with lexers that makes UI blink when editing text. We downgraded back to v2.8.1
2. Adding of a new line to the script editor in the tab no longer forces unnecessary repaint and repositioning of widgets (no more blinking)
2016-04-11 21:40:09 +03:00
schetnikovich 61e79155b4 QScintilla downgraded to v2.8.4 (from v2.9.1) because of issue with lexers 2016-04-11 21:32:46 +03:00
schetnikovich d23d9a2e7a Ping timeout set to 3 seconds for the shell 2016-04-11 17:20:27 +03:00
schetnikovich ae13e30494 #1063 Fixed: OSX Edit/View Document Popup only opens on main display 2016-04-11 17:01:10 +03:00
schetnikovich 292c664941 Config-level settings for operation timeout: both for MongoDB client and for the shell 2016-04-11 15:36:53 +03:00
schetnikovich c78845571e Separate handling of NumberInt conversion 2016-04-11 09:25:09 +03:00
schetnikovich a0d5c67e48 #1069: Fixed: Double 0.0 gets converted to Int32 0 after editing 2016-04-10 18:28:47 +03:00
schetnikovich 059a3dcc37 Code style fixes for the most horrible disuse of whitespaces 2016-04-10 16:02:36 +03:00
schetnikovich d543bb4539 Unused variable removed 2016-04-10 14:44:02 +03:00
schetnikovich 3e8f800682 Handling of edge cases with proper reporting and changes in MongoWorker
1. Detailed error message link in Diagnostic window
2. Cleanup of opened MongoServers and SSH workers in case of failure
3. Better status messages in Diagnostic window when using SSH tunnel
4. Script engine is now starts after an attempt to connect to MongoDB
2016-04-10 14:09:58 +03:00
schetnikovich 368f2abe0d Diagnostic window resizes only if needed and without layout "jumps". 2016-04-09 12:48:35 +03:00
schetnikovich 1e8af399e7 Code typo fix 2016-04-08 21:39:24 +03:00
schetnikovich 72f669e189 Merge branch 'master' of github.com:paralect/robomongo 2016-04-08 21:15:50 +03:00
schetnikovich c787b54e7c Connection diagnostic rewritten
1. Checks creation of SSH tunnel
2. Checks connection to MongoDB
3. Checks ability to list databases
4. Checks ability to authorize
2016-04-08 21:15:08 +03:00
Dmitry Schetnikovich 3512be30cf Wrong header for Windows (fixed) 2016-04-07 15:32:01 +03:00
schetnikovich 0bb279eec8 Support for domain names and IPv6 address of remote SSH server 2016-04-07 15:18:48 +03:00
Dmitry Schetnikovich 75331d9025 Warnings-free compilation for MSVC (well, almost) 2016-04-06 19:09:13 +03:00
schetnikovich 99a46eafc8 Warnings-free compilation for Clang 2016-04-06 16:41:29 +03:00
Dmitry Schetnikovich ac9300f5be Better handling of client disconnection for SSH tunnel 2016-04-06 15:30:49 +03:00
Dmitry Schetnikovich 4e4e5154f4 Compilation of RC8 under Windows 2016-04-06 13:20:05 +03:00
schetnikovich b54c096762 Robomongo RC8 2016-04-05 16:29:51 +03:00
Dmitry Schetnikovich 5d409ec1de Merge pull request #1072 from paralect/ssh
Support for SSH protocol
2016-04-05 16:24:33 +03:00
3141 changed files with 77885 additions and 810445 deletions
+60 -16
View File
@@ -1,30 +1,74 @@
0.9.0-RC6 / February 13, 2016
Most important changes: support for MongoDB 3.x and WiredTiger engine. Robomongo
now embeds MongoDB 3.2 shell. It means that you can now use the latest
shell functionality. Both MONGODB-CR and SCRAM-SHA-1 authentication mechanisms are
supprted. Also, we now allow direct connections to Replica Set members and
support IPv6 protocol. Loading of huge list of collections is now faster.
Fixed incorrect representation of floating-point numbers (fixed since RC3).
This bug was a reason of a big number of issues:
Latest Release:
Robomongo 0.9.0 RC10 (Aug 8, 2016)
http://blog.robomongo.org/robomongo-rc10/
#910, #748, #697, #699, #760, #815, #870, #892,
#894, #905, #945, #974, #978, #990, #831, #842
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.
What's new in RC7 (March 4, 2016 )
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)
What's new in RC8 (April 14, 2016)
http://blog.robomongo.org/robomongo-rc8/
What's new in RC7 (March 4, 2016)
http://blog.robomongo.org/robomongo-rc7
What's new in RC6 (February 13, 2016 )
What's new in RC6 (February 13, 2016)
http://blog.robomongo.org/ipv6-protocol
What's new in RC5 (February 8, 2016 )
What's new in RC5 (February 8, 2016)
http://blog.robomongo.org/campaign-is-over-robomongo-is-not/
What's new in RC4 (February 1, 2016 )
What's new in RC4 (February 1, 2016)
http://blog.robomongo.org/robomongo-rc4
What's new in RC3 (January 29, 2016 )
What's new in RC3 (January 29, 2016)
http://blog.robomongo.org/robomongo-0-9-0-rc3-released
What's new in RC2 (January 26, 2016)
+2 -2
View File
@@ -8,7 +8,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
set(PROJECT_VERSION_MAJOR "0")
set(PROJECT_VERSION_MINOR "9")
set(PROJECT_VERSION_PATCH "0")
set(PROJECT_VERSION_BUILD "-RC7")
set(PROJECT_VERSION_BUILD "-RC10")
set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}${PROJECT_VERSION_BUILD}")
# Modules
@@ -28,13 +28,13 @@ find_package(Qt5Gui REQUIRED)
find_package(Qt5Widgets REQUIRED)
find_package(Qt5PrintSupport REQUIRED)
find_package(MongoDB REQUIRED)
find_package(OpenSSL REQUIRED)
if (SYSTEM_MACOSX)
find_package(Qt5MacExtras REQUIRED)
endif()
# Projects
add_subdirectory(src/third-party/openssl)
add_subdirectory(src/third-party/libssh2)
add_subdirectory(src/robomongo/ssh)
add_subdirectory(src/third-party/qjson)
+63 -16
View File
@@ -7,13 +7,70 @@ Starting from version 0.9, Robomongo is compatibile with MongoDB 3.x (including
Robomongo 0.8.x embeds the **MongoDB 2.4.0** shell.
What's new in latest Robomongo 0.9.0 RC10?
====================================
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-rc10
Download: https://robomongo.org/download
Follow: https://twitter.com/Robomongo
What's Planned for the Next Release?
====================================
We are currently working towards [Robomongo 0.9.0 RC11 milestone](https://github.com/paralect/robomongo/milestone/16).
Currently in progress:
- Replica Sets Support
Plans for Future:
- User Roles
- Support execution of multiple simultaneous queries [#1161](https://github.com/paralect/robomongo/issues/1161)
- Enhancements for stability, running without crashes
- More Documentation
- Unit Tests
- Refactoring Debug Log module
Supported Platforms
===============
**Supported MongoDBs:**
| 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 |
|:---------------------- | :---------------| :---------------------------|
| Windows 64-bit 10 | Mac OS X 10.11 | Linux Ubuntu 16.04 64-bit* |
Windows 64-bit 8.1 | Mac OS X 10.10 | Linux Ubuntu 14.04 64-bit* |
| Windows 64-bit 7 | | Linux CentOS 7 64-bit* |
| | | Linux CentOS 6 64-bit* |
\* latest stable build
Download
========
You can download tested install packages for OS X, Windows, and Linux from our site: [www.robomongo.org](http://www.robomongo.org).
The latest stable release is currently **Robomongo 0.8.5**.
The latest 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
=======
@@ -29,25 +86,13 @@ You can:
Build
=====
The wiki contains prerequisites and instructions to [Build Robomongo](https://github.com/paralect/robomongo/wiki/Build-Robomongo).
The wiki contains prerequisites and instructions to [Build Robomongo](https://github.com/paralect/robomongo/wiki).
If you want to compile from source yourself, you should be able to do so cleanly from a [release branch](https://github.com/paralect/robomongo/releases).
What's Planned for the Next Release?
====================================
We are currently working toward a [0.9.0 release milestone](https://github.com/paralect/robomongo/milestones/Robomongo%200.9.0)
with some major features targeted including compatibility with MongoDB 3.0
(updated shell, SCRAM-SHA-1 auth, support for WiredTiger storage engine)
and full support for replica sets.
Contribute!
===========
### Vote for Features
Please view our [Magic Backlog](http://app.robomongo.org/backlog) to see features prioritised by donation. The small amount of donations we currently receive are put toward hosting costs.
### Suggest Features
New feature suggestions or UI improvements are always welcome.
@@ -56,7 +101,9 @@ This project is powered by open source volunteers, so we have a limited amount o
### Code Contributions
Code contributions are always welcome! Just try to follow our coding style: [Robomongo Coding Style](https://github.com/paralect/robomongo/wiki/Robomongo-Coding-Style).
Code contributions are always welcome! Just try to follow our pre-commit checks and coding style:
- [Robomongo Code Quality](https://github.com/paralect/robomongo/wiki/Robomongo-Code-Quality)
- [Robomongo Coding Style](https://github.com/paralect/robomongo/wiki/Robomongo-Coding-Style)
If you plan to contribute, please create a Github issue (or comment on the relevant existing issue) so we can help coordinate with upcoming release plans.
@@ -81,7 +128,7 @@ There are a number of issues we could use help with reproducing. They are marked
License
=======
Copyright (C) 2013-2015 [Paralect, Inc](http://www.paralect.com)
Copyright (C) 2013-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
Executable
+20
View File
@@ -0,0 +1,20 @@
#!/usr/bin/env bash
BIN_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" # Get directory of this file
PROJECT_DIR=$(dirname $BIN_DIR) # Get root directory of project
VERSION="$(echo $1 | tr '[A-Z]' '[a-z]')"
if [ -z "$VERSION" ]; then
echo "Usage: tag <name>"
echo "Example: tag 0.9.0-rc9"
exit
fi
if [ "$(git rev-parse --abbrev-ref HEAD)" != "master" ]; then
echo "Error: You are not on master branch"
exit
fi
cd $PROJECT_DIR
git tag -a "v${VERSION}" -m "${VERSION}"
git push --tags
+138
View File
@@ -0,0 +1,138 @@
##################################################
# Find and Add OpenSSL shared (dynamic) library
#
# - This module defines the following variables:
# OpenSSL_DIR
# OPENSSL_VERSION
#
# - Imported shared(dynamic) libraries "ssl" and "crypto" are created.
#
# - It is assumed that OpenSSL has been built and path to OpenSSL build directory is
# added into system(environment) variable ROBOMONGO_CMAKE_PREFIX_PATH.
#
##################################################
# Try to find OpenSSL directory (uses CMAKE_PREFIX_PATH locations)
#-------------------------------------------
find_path(
OpenSSL_DIR include/openssl/ssl.h
DOC "Path to OpenSSL (github.com/openssl/openssl) root directory"
)
# Find OpenSSL version
#-------------------------------------------
if(SYSTEM_WINDOWS)
set (OPENSSL_INCLUDE_DIR "${OpenSSL_DIR}/inc32")
else()
set (OPENSSL_INCLUDE_DIR "${OpenSSL_DIR}/include")
endif()
function(from_hex HEX DEC)
string(TOUPPER "${HEX}" HEX)
set(_res 0)
string(LENGTH "${HEX}" _strlen)
while (_strlen GREATER 0)
math(EXPR _res "${_res} * 16")
string(SUBSTRING "${HEX}" 0 1 NIBBLE)
string(SUBSTRING "${HEX}" 1 -1 HEX)
if (NIBBLE STREQUAL "A")
math(EXPR _res "${_res} + 10")
elseif (NIBBLE STREQUAL "B")
math(EXPR _res "${_res} + 11")
elseif (NIBBLE STREQUAL "C")
math(EXPR _res "${_res} + 12")
elseif (NIBBLE STREQUAL "D")
math(EXPR _res "${_res} + 13")
elseif (NIBBLE STREQUAL "E")
math(EXPR _res "${_res} + 14")
elseif (NIBBLE STREQUAL "F")
math(EXPR _res "${_res} + 15")
else()
math(EXPR _res "${_res} + ${NIBBLE}")
endif()
string(LENGTH "${HEX}" _strlen)
endwhile()
set(${DEC} ${_res} PARENT_SCOPE)
endfunction()
if (OPENSSL_INCLUDE_DIR)
if(OPENSSL_INCLUDE_DIR AND EXISTS "${OPENSSL_INCLUDE_DIR}/openssl/opensslv.h")
file(STRINGS "${OPENSSL_INCLUDE_DIR}/openssl/opensslv.h" openssl_version_str
REGEX "^#[\t ]*define[\t ]+OPENSSL_VERSION_NUMBER[\t ]+0x([0-9a-fA-F])+.*")
# The version number is encoded as 0xMNNFFPPS: major minor fix patch status
# The status gives if this is a developer or prerelease and is ignored here.
# Major, minor, and fix directly translate into the version numbers shown in
# the string. The patch field translates to the single character suffix that
# indicates the bug fix state, which 00 -> nothing, 01 -> a, 02 -> b and so
# on.
string(REGEX REPLACE "^.*OPENSSL_VERSION_NUMBER[\t ]+0x([0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F]).*$"
"\\1;\\2;\\3;\\4;\\5" OPENSSL_VERSION_LIST "${openssl_version_str}")
list(GET OPENSSL_VERSION_LIST 0 OPENSSL_VERSION_MAJOR)
list(GET OPENSSL_VERSION_LIST 1 OPENSSL_VERSION_MINOR)
from_hex("${OPENSSL_VERSION_MINOR}" OPENSSL_VERSION_MINOR)
list(GET OPENSSL_VERSION_LIST 2 OPENSSL_VERSION_FIX)
from_hex("${OPENSSL_VERSION_FIX}" OPENSSL_VERSION_FIX)
list(GET OPENSSL_VERSION_LIST 3 OPENSSL_VERSION_PATCH)
if (NOT OPENSSL_VERSION_PATCH STREQUAL "00")
from_hex("${OPENSSL_VERSION_PATCH}" _tmp)
# 96 is the ASCII code of 'a' minus 1
math(EXPR OPENSSL_VERSION_PATCH_ASCII "${_tmp} + 96")
unset(_tmp)
# Once anyone knows how OpenSSL would call the patch versions beyond 'z'
# this should be updated to handle that, too. This has not happened yet
# so it is simply ignored here for now.
string(ASCII "${OPENSSL_VERSION_PATCH_ASCII}" OPENSSL_VERSION_PATCH_STRING)
endif ()
set(OPENSSL_VERSION "${OPENSSL_VERSION_MAJOR}.${OPENSSL_VERSION_MINOR}.${OPENSSL_VERSION_FIX}${OPENSSL_VERSION_PATCH_STRING}")
endif ()
endif ()
# Add imported ssl and crypto libraries
#-------------------------------------------
# Add imported target ssl (ssleay32)
add_library(ssl SHARED IMPORTED)
# Add imported target for crypto (libeay32)
add_library(crypto SHARED IMPORTED)
if(SYSTEM_WINDOWS)
set_target_properties(ssl PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${OpenSSL_DIR}/inc32"
IMPORTED_IMPLIB "${OpenSSL_DIR}/out32dll/ssleay32.lib"
)
set_target_properties(crypto PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${OpenSSL_DIR}/inc32"
IMPORTED_IMPLIB "${OpenSSL_DIR}/out32dll/libeay32.lib"
)
elseif(SYSTEM_MACOSX)
set_target_properties(ssl PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${OpenSSL_DIR}/include"
IMPORTED_LOCATION "${OpenSSL_DIR}/libssl.dylib"
)
set_target_properties(crypto PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${OpenSSL_DIR}/include"
IMPORTED_LOCATION "${OpenSSL_DIR}/libcrypto.dylib"
)
elseif(SYSTEM_LINUX)
set_target_properties(ssl PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${OpenSSL_DIR}/include"
IMPORTED_LOCATION "${OpenSSL_DIR}/libssl.so"
)
set_target_properties(crypto PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${OpenSSL_DIR}/include"
IMPORTED_LOCATION "${OpenSSL_DIR}/libcrypto.so"
)
endif()
# End of file
+2 -1
View File
@@ -8,7 +8,8 @@ message("| Qt5 Core: ${Qt5Core_VERSION} ${Qt5Core_DIR}")
message("| Qt5 Gui: ${Qt5Gui_VERSION} ${Qt5Gui_DIR}")
message("| Qt5 Widgets: ${Qt5Widgets_VERSION} ${Qt5Widgets_DIR}")
message("| Qt5 PrintSupport: ${Qt5Widgets_VERSION} ${Qt5PrintSupport_DIR}")
message("| MongoDB: ${MongoDB_VERSION} ${MongoDB_BUILD_DIR}")
message("| MongoDB: ${MongoDB_VERSION} ${MongoDB_BUILD_DIR}")
message("| OpenSSL: ${OPENSSL_VERSION} ${OpenSSL_DIR}")
message("|")
message("|-- Summary:")
message("|")
+12
View File
@@ -52,6 +52,18 @@ elseif("${CMAKE_BUILD_TYPE}" MATCHES "MinSizeRel")
set(BUILD_MINSIZEREL TRUE)
endif()
# Compiler checks
# We check Clang using MATCH instead of strict equality, because as of CMake 3.0.0 the
# CMAKE_<LANG>_COMPILER_ID value for Apple-provided Clang is AppleClang. To test
# for both the Apple-provided Clang and the regular Clang we use MATCH.
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(COMPILER_CLANG TRUE)
elseif(CMAKE_CXX_COMPILER_ID MATCHES "^GNU$")
set(COMPILER_GCC TRUE)
elseif(CMAKE_CXX_COMPILER_ID MATCHES "^MSVC$")
set(COMPILER_MSVC TRUE)
endif()
# Link "groups" and "whole-archive" options
if(SYSTEM_LINUX OR SYSTEM_FREEBSD OR SYSTEM_OPENBSD)
set(LINK_LIBGROUP_START -Wl,--start-group)
+21 -2
View File
@@ -56,6 +56,27 @@ install(
FILES "${CMAKE_BINARY_DIR}/qt.conf"
DESTINATION "${qt_conf_dir}")
# Install OpenSSL dynamic lib files
if(SYSTEM_WINDOWS)
install(
FILES
"${OpenSSL_DIR}/out32dll/ssleay32.dll"
"${OpenSSL_DIR}/out32dll/libeay32.dll"
DESTINATION ${bin_dir})
elseif(SYSTEM_MACOSX)
install(
FILES
"${OpenSSL_DIR}/libssl.1.0.0.dylib"
"${OpenSSL_DIR}/libcrypto.1.0.0.dylib"
DESTINATION ${lib_dir}/lib)
elseif(SYSTEM_LINUX)
install(
FILES
"${OpenSSL_DIR}/libssl.so.1.0.0"
"${OpenSSL_DIR}/libcrypto.so.1.0.0"
DESTINATION ${lib_dir})
endif()
# Install binary
install(
TARGETS robomongo
@@ -79,7 +100,6 @@ install_icu_libs()
if(SYSTEM_LINUX)
install_qt_lib(XcbQpa DBus)
install_qt_plugins(
QGtk2ThemePlugin
QXcbIntegrationPlugin)
elseif(SYSTEM_MACOSX)
@@ -105,4 +125,3 @@ elseif(SYSTEM_WINDOWS)
set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION .)
include(InstallRequiredSystemLibraries)
endif()
+1
View File
@@ -10,6 +10,7 @@ elseif(SYSTEM_WINDOWS)
# The /MP option can reduce the total time to compile the source files. This option
# causes the compiler to create one or more copies of itself, each in a separate process.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
set(CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS} /MP")
set(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_CXX_STANDARD_LIBRARIES} psapi.lib bcrypt.lib Iphlpapi.lib dbghelp.lib ws2_32.lib winmm.lib version.lib")
endif()
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+7 -2
View File
@@ -6,7 +6,12 @@ Robomongo uses [modified version](https://github.com/paralect/robomongo-shell/tr
Build for Mac OS X or Linux
---------------------------
1. Clone Robomongo Shell and checkout to `roboshell-v3.2` branch:
#### Prerequisites
1. Install [Scons](http://scons.org/tag/releases.html) (2.4 or later)
2. Clone Robomongo Shell and checkout to `roboshell-v3.2` branch:
```sh
$ git clone https://github.com/paralect/robomongo-shell.git
@@ -14,7 +19,7 @@ Build for Mac OS X or Linux
$ git checkout roboshell-v3.2
```
2. Build Robomongo Shell:
3. Build Robomongo Shell:
```sh
$ bin/build
+95 -19
View File
@@ -1,28 +1,104 @@
Building Robomongo
Building Robomongo (Mac OS X and Linux)
==================
#### Step 1.
A. Prerequisites
-------------
Download and build Robomongo Shell (our fork of MongoDB).
Refer to [Building Robomongo Shell](BuildingMongoDB.md) document.
1. Install CMake (3.2 or later)
2. Install [Scons](http://scons.org/tag/releases.html) (2.4 or later)
3. Install Qt 5.7
#### Step 2.
```sh
Example installation for MAC OSX and Ubuntu:
Go to http://download.qt.io/archive/qt/5.7/5.7.0/
Download, run and install
qt-opensource-mac-x64-clang-5.7.0.dmg (for MAC OSX)
qt-opensource-linux-x64-5.7.0.run (for Linux)
After successful installation you should have
/path/to/qt-5.7.0/5.7/clang_64 (for MAC OSX)
/path/to/qt-5.7.0/5.7/gcc_64 (for Linux)
More information for installing on Ubuntu:
https://wiki.qt.io/Install_Qt_5_on_Ubuntu
```
4. Download and Build OpenSSL (1.0.1p) - (explained below in section B)
B. Building Robomongo and Dependencies
-------------
#### Step 1. Build OpenSSL (1.0.1p)
Steps to build OpenSSL on MAC:
```sh
Download openssl-1.0.1p (ftp://ftp.openssl.org/source/old/1.0.1/openssl-1.0.1p.tar.gz)
tar -xvzf openssl-1.0.1p.tar.gz
cd /Users/<user>/Downloads/openssl-1.0.1p
./Configure darwin64-x86_64-cc shared --openssldir="@rpath"
make (or sudo make)
mkdir lib
cp libssl.1.0.0.dylib libcrypto.1.0.0.dylib lib/
```
Helper Commands
```sh
// to start fresh build
make clean
```
Steps to build OpenSSL on Linux:
```sh
Download openssl-1.0.1p (ftp://ftp.openssl.org/source/old/1.0.1/openssl-1.0.1p.tar.gz)
tar -xvzf openssl-1.0.1p.tar.gz
cd /home/<user>/Downloads/openssl-1.0.1p
./config shared
make
mkdir lib
cp libssl* libcrypto* lib/
```
#### Step 2. Build Robomongo Shell (fork of MongoDB)
Clone Robomongo Shell and checkout to `roboshell-v3.2` branch:
```sh
$ git clone https://github.com/paralect/robomongo-shell.git
$ cd robomongo-shell
$ git checkout roboshell-v3.2
```
Set special environment variable `ROBOMONGO_CMAKE_PREFIX_PATH` to point to a set of
directories:
1. Location of Qt 5 SDK
2. Location of Robomongo Shell
3. Location of OpenSSL
Separate directories by semicolon `;` (not colon):
// MAC OSX example:
$ export ROBOMONGO_CMAKE_PREFIX_PATH="/path/to/qt-5.7.0/5.7/clang_64;/path/to/robomongo-shell;/path/to/openssl-1.0.1p"
// Ubuntu example:
$ export ROBOMONGO_CMAKE_PREFIX_PATH="/path/to/qt-5.7.0/5.7/gcc_64;/path/to/robomongo-shell;/path/to/openssl-1.0.1p"
Build Robomongo Shell:
```sh
$ bin/build
```
For more information refer to [Building Robomongo Shell](BuildingMongoDB.md)
#### Step 3. Build Robomongo
Download Robomongo:
$ git clone https://github.com/paralect/robomongo
$ cd robomongo
Set special environment variable `ROBOMONGO_CMAKE_PREFIX_PATH` to point to a set of
directories:
1. Location of Qt 5 SDK
2. Location of Robomongo Shell (built in "Step 1")
Separate directories by semicolon `;` (not colon):
$ export ROBOMONGO_CMAKE_PREFIX_PATH="/path/to/qt-5.5/5.5/clang_64;/path/to/robomongo-shell"
Run configuration step:
$ bin/configure
@@ -30,14 +106,14 @@ Run configuration step:
And finally, build Robomongo:
$ bin/build
<br>
## Helper commands
Execute just built Robomongo:
To run robomongo:
$ bin/run
**Helper commands**
Clean build files (in order to start build from scratch):
$ bin/clean
+74 -22
View File
@@ -1,7 +1,7 @@
Building Robomongo for Windows
Building Robomongo (Windows)
==============================
Prerequisites
A. Prerequisites
-------------
#### 0. Git
@@ -64,25 +64,70 @@ Prerequisites
command will be available from Command Prompt. For this set "Add CMake to the system
PATH for current user" in the installer wizard page.
#### 5. Qt 5.5
#### 5. Qt 5.7
Download Qt from http://www.qt.io/download-open-source/ (we use Qt Online installer)
When installing Qt, only one component is required by Robomongo:
Qt 5.5
Qt 5.7
[x] msvc2013 64-bit.
This components contains prebuilt 64-bit binaries for Visual Studio 2013.
You may uncheck all other components to download as little as possible.
#### 6. OpenSSL (1.0.1p)
Download openssl-1.0.1p (ftp://ftp.openssl.org/source/old/1.0.1/openssl-1.0.1p.tar.gz)
B. Building Robomongo and Dependencies
-------------
## Build Robomongo Shell (fork of MongoDB)
#### 1. Build OpenSSL (1.0.1p)
Steps to build OpenSSL on windows:
```sh
Open Visual Studio tool x64 Cross Tools Command prompt
cd to the directory where you have openssl sources cd c:\myPath\openssl
perl Configure VC-WIN64A
ms\do_win64a
nmake -f ms\ntdll.mak
```
**Check Point:**
After successful build, newly created sub directory out32dll should contain dynamic lib files libeay32.lib (and libeay32.dll) and ssleay32.lib (and ssleay32.dll); and associated include files will be in newly created folder "inc32".
Helper Commands:
```sh
// clean to start fresh build
nmake -f ms\ntdll.mak clean
```
Refer to OpenSSL documentation for more information:
https://wiki.openssl.org/index.php/Compilation_and_Installation#W64
#### 2. Build Robomongo Shell (fork of MongoDB)
Clone Robomongo Shell and checkout to roboshell-v3.2 branch:
```sh
$ git clone https://github.com/paralect/robomongo-shell.git
$ cd robomongo-shell
$ git checkout roboshell-v3.2
```
Set environment variable `ROBOMONGO_CMAKE_PREFIX_PATH`, required by Robomongo-Shell and Robomongo build scripts, needs to be set according to the following directories:
1. Location of Qt SDK
2. Location of Robomongo Shell
3. Location of OpenSSL
Separate directories by semicolon `;` (not colon). You can do this in Command Prompt:
> setx ROBOMONGO_CMAKE_PREFIX_PATH "d:\Qt-5\5.7\msvc2013_64;d:\Projects\robomongo-shell;c:\myPath\openssl-1.0.1p"
Refer to MongoDB documentation for additional information:
https://docs.mongodb.org/manual/contributors/tutorial/build-mongodb-from-source/#windows-specific-instructions
Open VS2013 x64 Native Tools Command Prompt and navigate to `robomongo-shell` folder.
@@ -96,20 +141,19 @@ Clean build files:
> bin\clean
Refer to MongoDB documentation for additional information:
https://docs.mongodb.org/manual/contributors/tutorial/build-mongodb-from-source/#windows-specific-instructions
## Build Robomongo
Set special environment variable `ROBOMONGO_CMAKE_PREFIX_PATH` to point to set of
directories:
#### 3. Build Robomongo
1. Location of Qt SDK
2. Location of Robomongo Shell
Clone Robomongo:
Separate directories by semicolon `;` (not colon). You can do this in Command Prompt:
> setx ROBOMONGO_CMAKE_PREFIX_PATH "d:\Qt-5\5.5\msvc2013_64;d:\Projects\robomongo-shell"
Reopen your Command Prompt to make this variable available.
```sh
$ git clone https://github.com/paralect/robomongo.git
```
Open VS2013 x64 Native Tools Command Prompt and navigate to `robomongo` folder.
Run configuration step:
@@ -118,15 +162,23 @@ Run configuration step:
And finally, build Robomongo:
> bin\build
#### Helper commands
**Run Robomongo**
Install Robomongo to `build\Release\install` folder:
> bin\install
And run Robomongo
> \robomongo\build\Release\install\Robomongo.exe
**Helper commands**
Clean build files (in order to start build from scratch):
> bin\clean
Install Robomongo to `build\Release\install` folder:
> bin\install
+9
View File
@@ -5,6 +5,10 @@ set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
#set(sanitize "-fsanitize=address")
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${sanitize}")
#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${sanitize}")
set(SOURCES
# Isolated Scope #1
@@ -62,6 +66,7 @@ set(SOURCES
gui/dialogs/ConnectionDialog.cpp
gui/dialogs/CopyCollectionDialog.cpp
gui/widgets/workarea/IndicatorLabel.cpp
gui/dialogs/CreateCollectionDialog.cpp
gui/dialogs/CreateDatabaseDialog.cpp
gui/dialogs/CreateUserDialog.cpp
gui/utils/ComboBoxUtils.cpp
@@ -81,6 +86,7 @@ set(SOURCES
# Isolated scope #6
gui/widgets/explorer/ExplorerCollectionTreeItem.cpp
gui/widgets/explorer/ExplorerTreeItem.cpp
gui/widgets/explorer/ExplorerReplicaSetTreeItem.cpp
gui/widgets/explorer/ExplorerDatabaseTreeItem.cpp
gui/widgets/explorer/ExplorerDatabaseCategoryTreeItem.cpp
gui/widgets/explorer/ExplorerUserTreeItem.cpp
@@ -117,7 +123,10 @@ set(SOURCES
gui/MainWindow.cpp
gui/dialogs/SSHTunnelTab.cpp
gui/dialogs/SSLTab.cpp
core/settings/SshSettings.cpp
core/settings/SslSettings.cpp
core/settings/ReplicaSetSettings.cpp
core/mongodb/SshTunnelWorker.cpp
gui/resources/gui.qrc
+13 -16
View File
@@ -7,14 +7,14 @@
// Header "mongo/platform/basic" is required by "sock.h" under Windows
#include <mongo/platform/basic.h>
#include <mongo/util/net/sock.h>
#include <mongo/base/initializer.h>
#include <mongo/util/net/ssl_options.h>
#include "robomongo/gui/MainWindow.h"
#include "robomongo/gui/AppStyle.h"
#include "robomongo/ssh/ssh.h"
int main(int argc, char *argv[], char** envp)
{
if (rbm_ssh_init()) {
@@ -29,6 +29,12 @@ int main(int argc, char *argv[], char** envp)
// Support for IPv6 is disabled by default. Enable it.
mongo::enableIPv6(true);
// Perform SSL-enabled mongo initialization
mongo::sslGlobalParams.sslMode.store(mongo::SSLParams::SSLMode_allowSSL);
// Cross Platform High DPI support - Qt 5.7
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
// Initialization routine for MongoDB shell
mongo::runGlobalInitializersOrDie(argc, argv, envp);
@@ -39,29 +45,20 @@ int main(int argc, char *argv[], char** envp)
// This can cause a conflict when using POSIX functions, for instance, when
// converting between data types such as floats and strings, since the notation
// may differ between locales. To get around this problem, call the POSIX
// function setlocale(LC_NUMERIC,"C") right after initializing QApplication or
// function setlocale(LC_NUMERIC, "C") right after initializing QApplication or
// QCoreApplication to reset the locale that is used for number formatting to "C"-locale.
// (http://doc.qt.io/qt-5/qcoreapplication.html#locale-settings)
setlocale(LC_NUMERIC, "C");
#ifdef Q_OS_MAC
app.setAttribute(Qt::AA_UseHighDpiPixmaps);
#endif
// Init GUI style
Robomongo::AppStyleUtils::initStyle();
// Application main window
Robomongo::MainWindow win;
// Resize main window. We are trying to keep it "almost" maximized.
QRect screenGeometry = QApplication::desktop()->availableGeometry();
int horizontalMargin = (int)(screenGeometry.width() * 0.1);
int verticalMargin = (int)(screenGeometry.height() * 0.1);
int width = screenGeometry.width() - horizontalMargin;
int height = screenGeometry.height() - verticalMargin;
win.resize(QSize(width, height));
// Center main window
int x = (screenGeometry.width() - win.width()) / 2;
int y = (screenGeometry.height() - win.height()) / 2;
win.move(x, y);
win.show();
int rc = app.exec();
+7
View File
@@ -33,12 +33,19 @@ void precisionAssert(const std::string &text, double d) {
std::stringstream s;
s.precision(std::numeric_limits<double>::digits10);
s << d;
if (d == (long long)d)
s << ".0";
std::cout << "Checking " << text << " - ";
assert(text == s.str());
std::cout << "Correct. " << std::endl;
}
void testPrecision() {
precisionAssert("-9.987654321", -9.987654321);
precisionAssert("-1.0", -1.0);
precisionAssert("-0.0", -0.0);
precisionAssert("0.0", 0.0);
precisionAssert("9.0", 9.0);
precisionAssert("9.8", 9.8);
precisionAssert("9.9", 9.9);
precisionAssert("9.98", 9.98);
+14
View File
@@ -30,4 +30,18 @@ namespace Robomongo
class MongoDocument;
typedef boost::shared_ptr<MongoDocument> MongoDocumentPtr;
enum ConnectionType {
// This type of connection is shown in Explorer and also
// opens SSH tunnel for Secondary connections (if needed)
ConnectionPrimary = 0,
// Never shown in Explorer and uses SSH tunnel from
// Primary connection (if needed)
ConnectionSecondary = 1,
// The same as Primary, but is specifically for testing
// connections.
ConnectionTest = 2
};
}
+2 -2
View File
@@ -10,9 +10,9 @@ namespace
template<typename type, int size>
inline type findTypeInArray(const char *(&arr)[size], const char *text)
{
for (int i=0; i < size;++i )
for (int i = 0; i < size; ++i )
{
if (strcmp(text,arr[i])==0)
if (strcmp(text, arr[i]) == 0)
{
return static_cast<type>(i);
}
+2 -2
View File
@@ -36,7 +36,7 @@ namespace Robomongo
char *data = new char[bytes];
const char *p = s.c_str();
for( size_t i = 0; i < bytes; i++ ) {
for (size_t i = 0; i < bytes; i++) {
data[i] = mongo::fromHex(p);
p += 2;
}
@@ -159,7 +159,7 @@ namespace Robomongo
mongo::BinDataType binType = element.binDataType();
if (binType != mongo::newUUID && binType != mongo::bdtUUID)
throw new std::invalid_argument("Binary subtype should be 3 (bdtUUID) or 4 (newUUID)");
throw std::invalid_argument("Binary subtype should be 3 (bdtUUID) or 4 (newUUID)");
int len;
const char *data = element.binData(len);
+129 -33
View File
@@ -1,11 +1,13 @@
#include "robomongo/core/domain/App.h"
#include <QHash>
#include <QInputDialog>
#include "robomongo/core/domain/MongoServer.h"
#include "robomongo/core/domain/MongoShell.h"
#include "robomongo/core/domain/MongoCollection.h"
#include "robomongo/core/settings/ConnectionSettings.h"
#include "robomongo/core/settings/SshSettings.h"
#include "robomongo/core/settings/SslSettings.h"
#include "robomongo/core/mongodb/SshTunnelWorker.h"
#include "robomongo/core/EventBus.h"
#include "robomongo/core/utils/QtUtils.h"
@@ -33,7 +35,7 @@ namespace Robomongo
}
App::App(EventBus *const bus) : QObject(),
_bus(bus) {
_bus(bus), _lastServerHandle(0) {
_bus->subscribe(this, EstablishSshConnectionResponse::Type);
_bus->subscribe(this, ListenSshConnectionResponse::Type);
_bus->subscribe(this, LogEvent::Type);
@@ -45,19 +47,74 @@ namespace Robomongo
std::for_each(_servers.begin(), _servers.end(), stdutils::default_delete<MongoServer*>());
}
bool App::openServer(ConnectionSettings *connection, ConnectionType type) {
SshSettings *ssh = connection->sshSettings();
if (ssh->enabled() && ssh->askPassword() &&
(type == ConnectionPrimary || type == ConnectionTest)) {
bool ok = false;
bool isByKey = ssh->authMethod() == "publickey";
std::string passText = isByKey ? "passphrase" : "password";
std::stringstream s;
s << "In order to continue, please provide the " << passText;
if (isByKey)
s << " for the key file";
s << "." << std::endl << std::endl;
if (ssh->authMethod() == "publickey")
s << "Private Key: " << ssh->privateKeyFile() << std::endl;
s << "Server: " << ssh->host() << std::endl;
s << "User: " << ssh->userName() << std::endl;
s << std::endl << "Enter your " << passText << " that will never be stored:";
QString userInput = QInputDialog::getText(NULL, tr("SSH Authentication"),
QtUtils::toQString(s.str()),
QLineEdit::Password, "", &ok);
if (!ok)
return false;
ssh->setAskedPassword(QtUtils::toStdString(userInput));
}
SslSettings *sslSettings = connection->sslSettings();
if (sslSettings->sslEnabled() && sslSettings->usePemFile() && sslSettings->askPassphrase()
&& (type == ConnectionPrimary || type == ConnectionTest))
{
if (!askSslPassphrasePromptDialog(connection))
{
return false;
}
}
openServerInternal(connection, type);
return true;
}
/**
* Creates and opens new server connection.
* @param connection: ConnectionSettings, that will be owned by MongoServer.
* @param visible: should this server be visible in UI (explorer) or not.
*/
MongoServer *App::openServer(ConnectionSettings *connection, bool visible) {
if (visible)
MongoServer *App::openServerInternal(ConnectionSettings const* connection, ConnectionType type) {
++_lastServerHandle;
if (type == ConnectionPrimary)
_bus->publish(new ConnectingEvent(this));
// When connection is not "visible" or do not have
// When connection is SECONDARY or do not have
// ssh settings enabled, continue without SSH Tunnel
if (!visible || !connection->sshSettings()->enabled()) {
return continueOpenServer(connection, visible);
if (type == ConnectionSecondary || !connection->sshSettings()->enabled()) {
return continueOpenServer(_lastServerHandle, connection, type);
}
// Open SSH channel and only after that open connection
@@ -67,7 +124,7 @@ namespace Robomongo
ConnectionSettings* settingsCopy = connection->clone();
SshTunnelWorker* sshWorker = new SshTunnelWorker(settingsCopy);
_bus->send(sshWorker, new EstablishSshConnectionRequest(this, sshWorker, settingsCopy, visible));
_bus->send(sshWorker, new EstablishSshConnectionRequest(this, _lastServerHandle, sshWorker, settingsCopy, type));
return NULL;
}
@@ -80,43 +137,46 @@ namespace Robomongo
_servers.erase(std::remove_if(_servers.begin(), _servers.end(), stdutils::RemoveIfFound<MongoServer*>(server)), _servers.end());
}
MongoShell *App::openShell(MongoCollection *collection,const QString &filePathToSave)
void App::openShell(MongoCollection *collection, const QString &filePathToSave)
{
ConnectionSettings *connection = collection->database()->server()->connectionRecord();
connection->setDefaultDatabase(collection->database()->name());
QString script = detail::buildCollectionQuery(collection->name(), "find({})");
return openShell(connection, ScriptInfo(script, true, CursorPosition(0, -2), QtUtils::toQString(collection->database()->name()),filePathToSave));
openShell(connection, ScriptInfo(script, true, CursorPosition(0, -2), QtUtils::toQString(collection->database()->name()), filePathToSave));
}
MongoShell *App::openShell(MongoServer *server,const QString &script, const std::string &dbName,
void App::openShell(MongoServer *server, const QString &script, const std::string &dbName,
bool execute, const QString &shellName,
const CursorPosition &cursorPosition,const QString &filePathToSave)
const CursorPosition &cursorPosition, const QString &filePathToSave)
{
ConnectionSettings *connection = server->connectionRecord();
if (!dbName.empty())
connection->setDefaultDatabase(dbName);
return openShell(connection, ScriptInfo(script, execute, cursorPosition, shellName,filePathToSave));
openShell(connection, ScriptInfo(script, execute, cursorPosition, shellName, filePathToSave));
}
MongoShell *App::openShell(MongoDatabase *database, const QString &script,
void App::openShell(MongoDatabase *database, const QString &script,
bool execute, const QString &shellName,
const CursorPosition &cursorPosition,const QString &filePathToSave)
const CursorPosition &cursorPosition, const QString &filePathToSave)
{
ConnectionSettings *connection = database->server()->connectionRecord();
connection->setDefaultDatabase(database->name());
return openShell(connection, ScriptInfo(script, execute, cursorPosition, shellName,filePathToSave));
openShell(connection, ScriptInfo(script, execute, cursorPosition, shellName, filePathToSave));
}
MongoShell *App::openShell(ConnectionSettings *connection, const ScriptInfo &scriptInfo)
void App::openShell(ConnectionSettings const* connection, const ScriptInfo &scriptInfo)
{
MongoServer *server = openServer(connection, false);
MongoShell *shell = new MongoShell(server,scriptInfo);
MongoServer *server = openServerInternal(connection, ConnectionSecondary);
if (!server)
return;
MongoShell *shell = new MongoShell(server, scriptInfo);
_shells.push_back(shell);
_bus->publish(new OpeningShellEvent(this, shell));
shell->execute();
return shell;
return;
}
/**
@@ -126,7 +186,7 @@ namespace Robomongo
void App::closeShell(MongoShell *shell)
{
// Do nothing, if this shell not owned by this App.
MongoShellsContainerType::iterator it = std::find_if(_shells.begin(),_shells.end(),std::bind1st(std::equal_to<MongoShell *>(),shell));
MongoShellsContainerType::iterator it = std::find_if(_shells.begin(), _shells.end(), std::bind1st(std::equal_to<MongoShell *>(), shell));
if (it == _shells.end())
return;
@@ -135,16 +195,17 @@ namespace Robomongo
delete shell;
}
MongoServer* App::continueOpenServer(ConnectionSettings *connection, bool visible, int localport) {
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 (visible && settings->sshSettings()->enabled()) {
if ((type == ConnectionPrimary || type == ConnectionTest)
&& settings->sshSettings()->enabled()) {
settings->setServerHost("127.0.0.1");
settings->setServerPort(localport);
}
MongoServer *server = new MongoServer(settings, visible);
MongoServer *server = new MongoServer(serverHandle, settings, type);
_servers.push_back(server);
server->runWorkerThread();
@@ -156,36 +217,71 @@ namespace Robomongo
void App::handle(EstablishSshConnectionResponse *event) {
if (event->isError()) {
_bus->publish(new ConnectionFailedEvent(this, event->error().errorMessage()));
_bus->publish(new ConnectionFailedEvent(
this, event->serverHandle, event->connectionType, event->error().errorMessage(),
ConnectionFailedEvent::SshConnection));
return;
}
LOG_MSG(QString("SSH tunnel created successfully"), mongo::logger::LogSeverity::Info());
_bus->send(event->worker, new ListenSshConnectionRequest(this));
continueOpenServer(event->settings, event->visible, event->localport);
// record event->worker and delete when needed
continueOpenServer(event->serverHandle, event->settings, event->connectionType, event->localport);
_bus->send(event->worker, new ListenSshConnectionRequest(this, event->serverHandle, event->connectionType));
}
void App::handle(LogEvent *event) {
if (event->level == LogEvent::ERROR) {
if (event->level == LogEvent::RBM_ERROR) {
LOG_MSG(event->message, mongo::logger::LogSeverity::Error());
} else if (event->level == LogEvent::WARN) {
} else if (event->level == LogEvent::RBM_WARN) {
LOG_MSG(event->message, mongo::logger::LogSeverity::Warning());
} else if (event->level == LogEvent::INFO) {
} else if (event->level == LogEvent::RBM_INFO) {
LOG_MSG(event->message, mongo::logger::LogSeverity::Info());
} else if (event->level == LogEvent::DEBUG) {
} else if (event->level == LogEvent::RBM_DEBUG) {
LOG_MSG(event->message, mongo::logger::LogSeverity::Log());
}
}
void App::handle(ListenSshConnectionResponse *event) {
if (event->isError()) {
_bus->publish(new ConnectionFailedEvent(this, event->error().errorMessage()));
_bus->publish(new ConnectionFailedEvent(this, event->serverHandle, event->connectionType, event->error().errorMessage(),
ConnectionFailedEvent::SshChannel));
return;
}
LOG_MSG(QString("SSH tunnel closed."), mongo::logger::LogSeverity::Error());
}
void App::fireConnectionFailedEvent(int serverHandle, ConnectionType type, std::string errormsg,
ConnectionFailedEvent::Reason reason) {
_bus->publish(new ConnectionFailedEvent(this, serverHandle, type, errormsg, reason));
}
bool App::askSslPassphrasePromptDialog(ConnectionSettings *connSettings) const
{
auto sslSettings = connSettings->sslSettings();
bool ok = false;
std::stringstream s;
s << "In order to continue, please provide the passphrase";
s << "." << std::endl << std::endl;
s << "Server: " << connSettings->serverHost() << ":" << connSettings->serverPort() << std::endl;
s << "PEM file: " << sslSettings->pemKeyFile() << std::endl;
s << std::endl << "Enter your PEM key passphrase (will never be stored):";
QString userInput = QInputDialog::getText(NULL, tr("SSL Authentication"),
QtUtils::toQString(s.str()),
QLineEdit::Password, "", &ok);
if (!ok)
{
return false;
}
sslSettings->setPemPassPhrase(QtUtils::toStdString(userInput));
return ok;
}
}
+24 -7
View File
@@ -47,8 +47,9 @@ namespace Robomongo
* @brief Creates and opens new server connection.
* @param connection: ConnectionSettings, that will be owned by MongoServer.
* @param visible: should this server be visible in UI (explorer) or not.
* @return Succeeded or not
*/
MongoServer *openServer(ConnectionSettings *connection, bool visible);
bool openServer(ConnectionSettings *connection, ConnectionType type);
/**
* @brief Closes MongoServer connection and frees all resources, owned
@@ -59,18 +60,18 @@ namespace Robomongo
/**
* @brief Open new shell based on specified collection
*/
MongoShell *openShell(MongoCollection *collection,const QString &filePathToSave=QString());
void openShell(MongoCollection *collection, const QString &filePathToSave = QString());
MongoShell *openShell(MongoServer *server, const QString &script, const std::string &dbName = std::string(),
void openShell(MongoServer *server, const QString &script, const std::string &dbName = std::string(),
bool execute = true, const QString &shellName = QString(),
const CursorPosition &cursorPosition = CursorPosition(), const QString &file = QString());
MongoShell *openShell(MongoDatabase *database, const QString &script,
void openShell(MongoDatabase *database, const QString &script,
bool execute = true, const QString &shellName = QString(),
const CursorPosition &cursorPosition = CursorPosition(), const QString &filePathToSave = QString());
MongoShell *openShell(ConnectionSettings *connection, const ScriptInfo &scriptInfo);
MongoServersContainerType getServers() const {return _servers; };
void openShell(ConnectionSettings const* connection, const ScriptInfo &scriptInfo);
MongoServersContainerType getServers() const { return _servers; };
/**
* @brief Closes MongoShell and frees all resources, owned by specified MongoShell.
@@ -78,13 +79,25 @@ namespace Robomongo
*/
void closeShell(MongoShell *shell);
void fireConnectionFailedEvent(int serverHandle, ConnectionType type, std::string errormsg, ConnectionFailedEvent::Reason reason);
int getLastServerHandle() const { return _lastServerHandle; };
public Q_SLOTS:
void handle(EstablishSshConnectionResponse *event);
void handle(ListenSshConnectionResponse *event);
void handle(LogEvent *event);
private:
MongoServer* continueOpenServer(ConnectionSettings *connection, bool visible, int localport = 0);
MongoServer *openServerInternal(ConnectionSettings 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
* @param connection Pointer to active connection settings
* @return true on success, false otherwise
*/
bool askSslPassphrasePromptDialog(ConnectionSettings *connSettings) const;
/**
* MongoServers, owned by this App.
@@ -97,5 +110,9 @@ namespace Robomongo
MongoShellsContainerType _shells;
EventBus *const _bus;
// Increase monotonically when new MongoServer is created
// Never decreases.
int _lastServerHandle;
};
}
+26 -2
View File
@@ -15,6 +15,13 @@ namespace Robomongo
R_REGISTER_EVENT(MongoDatabaseFunctionsLoadingEvent)
R_REGISTER_EVENT(MongoDatabaseCollectionsLoadingEvent)
const std::string MongoDatabase::StorageEngineType::WIRED_TIGER = "wiredTiger";
const std::string MongoDatabase::StorageEngineType::MMAPV1 = "mmapv1";
const float MongoDatabase::DBVersion::MONGODB_2_6 = 2.6f;
const float MongoDatabase::DBVersion::MONGODB_3_0 = 3.0f;
const float MongoDatabase::DBVersion::MONGODB_3_2 = 3.2f;
MongoDatabase::MongoDatabase(MongoServer *server, const std::string &name) :
QObject(),
_system(name == "admin" || name == "local"),
@@ -44,9 +51,11 @@ namespace Robomongo
_bus->publish(new MongoDatabaseFunctionsLoadingEvent(this));
_bus->send(_server->client(), new LoadFunctionsRequest(this, _name));
}
void MongoDatabase::createCollection(const std::string &collection)
void MongoDatabase::createCollection(const std::string &collection, long long size, bool capped, int maxDocNum, const mongo::BSONObj& extraOptions)
{
_bus->send(_server->client(), new CreateCollectionRequest(this, MongoNamespace(_name, collection)));
_bus->send(_server->client(),
new CreateCollectionRequest(this, MongoNamespace(_name, collection), extraOptions, size, capped, maxDocNum));
}
void MongoDatabase::dropCollection(const std::string &collection)
@@ -149,4 +158,19 @@ namespace Robomongo
{
_collections.push_back(collection);
}
void MongoDatabase::handle(CreateCollectionResponse *event) {
genericResponseHandler(event, "Failed to create collection.");
}
void MongoDatabase::handle(DropCollectionResponse *event) {
genericResponseHandler(event, "Failed to drop collection.");
}
void MongoDatabase::genericResponseHandler(Event *event, const std::string &userFriendlyMessage) {
if (!event->isError())
return;
_bus->publish(new OperationFailedEvent(this, event->error().errorMessage(), userFriendlyMessage));
}
}
+23 -1
View File
@@ -18,6 +18,25 @@ namespace Robomongo
Q_OBJECT
public:
/**
* @brief Database storage engine type
*/
struct StorageEngineType
{
static const std::string WIRED_TIGER;
static const std::string MMAPV1;
};
/**
* @brief MongoDB version
*/
struct DBVersion
{
static const float MONGODB_2_6;
static const float MONGODB_3_0;
static const float MONGODB_3_2;
};
/**
* @brief MongoDatabase
* @param server: pointer to parent MongoServer
@@ -37,7 +56,7 @@ namespace Robomongo
void loadFunctions();
void createCollection(const std::string &collection);
void createCollection(const std::string &collection, long long size, bool capped, int maxDocNum, const mongo::BSONObj& extraOptions);
void dropCollection(const std::string &collection);
void renameCollection(const std::string &collection, const std::string &newCollection);
void duplicateCollection(const std::string &collection, const std::string &newCollection);
@@ -65,10 +84,13 @@ namespace Robomongo
void handle(LoadUsersResponse *event);
void handle(LoadFunctionsResponse *event);
void handle(CreateUserResponse *event);
void handle(CreateCollectionResponse *event);
void handle(DropCollectionResponse *event);
private:
void clearCollections();
void addCollection(MongoCollection *collection);
void genericResponseHandler(Event *event, const std::string &userFriendlyMessage);
private:
MongoServer *_server;
+1 -1
View File
@@ -38,7 +38,7 @@ namespace Robomongo
std::vector<MongoDocumentPtr> MongoDocument::fromBsonObj(const std::vector<mongo::BSONObj> &bsonObjs)
{
std::vector<MongoDocumentPtr> list;
for (std::vector<mongo::BSONObj>::const_iterator it=bsonObjs.begin(); it!=bsonObjs.end(); ++it) {
for (std::vector<mongo::BSONObj>::const_iterator it = bsonObjs.begin(); it != bsonObjs.end(); ++it) {
list.push_back(fromBsonObj(*it));
}
+1 -1
View File
@@ -6,7 +6,7 @@ namespace Robomongo
{
MongoFunction::MongoFunction(const mongo::BSONObj &obj)
{
_name = BsonUtils::getField<mongo::String>(obj,"_id");
_name = BsonUtils::getField<mongo::String>(obj, "_id");
_code = obj.getField("value")._asCode();
}
+3 -3
View File
@@ -9,8 +9,8 @@ namespace Robomongo
std::string prepareServerAddress(const std::string &address)
{
size_t pos = address.find_first_of("{");
if (pos!=std::string::npos){
return address.substr(0,pos);
if (pos != std::string::npos) {
return address.substr(0, pos);
}
return address;
}
@@ -20,7 +20,7 @@ namespace Robomongo
CollectionInfo::CollectionInfo(const std::string &server, const std::string &database, const std::string &collection)
:_serverAddress(server),
_ns(database,collection)
_ns(database, collection)
{}
bool CollectionInfo::isValid() const
+71 -20
View File
@@ -7,19 +7,22 @@
#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/utils/QtUtils.h"
namespace Robomongo {
R_REGISTER_EVENT(MongoServerLoadingDatabasesEvent)
MongoServer::MongoServer(ConnectionSettings *settings, bool visible) : QObject(),
MongoServer::MongoServer(int handle, ConnectionSettings *settings, ConnectionType connectionType) : QObject(),
_version(0.0f),
_visible(visible),
_connectionType(connectionType),
_client(NULL),
_isConnected(false),
_settings(settings),
_bus(AppRegistry::instance().bus()) { }
_handle(handle),
_bus(AppRegistry::instance().bus()),
_app(AppRegistry::instance().app()) { }
bool MongoServer::isConnected() const {
return _isConnected;
@@ -49,7 +52,7 @@ namespace Robomongo {
* @throws MongoException, if fails
*/
void MongoServer::tryConnect() {
_bus->send(_client, new EstablishConnectionRequest(this));
_bus->send(_client, new EstablishConnectionRequest(this, _connectionType));
}
QStringList MongoServer::getDatabasesNames() const {
@@ -62,7 +65,7 @@ namespace Robomongo {
}
void MongoServer::createDatabase(const std::string &dbName) {
_bus->send(_client,new CreateDatabaseRequest(this, dbName));
_bus->send(_client, new CreateDatabaseRequest(this, dbName));
}
MongoDatabase *MongoServer::findDatabaseByName(const std::string &dbName) const {
@@ -76,7 +79,7 @@ namespace Robomongo {
}
void MongoServer::dropDatabase(const std::string &dbName) {
_bus->send(_client,new DropDatabaseRequest(this, dbName));
_bus->send(_client, new DropDatabaseRequest(this, dbName));
}
void MongoServer::insertDocuments(const std::vector<mongo::BSONObj> &objCont,
@@ -87,12 +90,12 @@ namespace Robomongo {
}
void MongoServer::insertDocument(const mongo::BSONObj &obj, const MongoNamespace &ns) {
_bus->send(_client,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) {
for (std::vector<mongo::BSONObj>::const_iterator it = objCont.begin(); it != objCont.end(); it++) {
saveDocument(*it,ns);
saveDocument(*it, ns);
}
}
@@ -122,26 +125,57 @@ namespace Robomongo {
if (event->isError()) {
_isConnected = false;
std::stringstream ss;
ss << "Cannot connect to the MongoDB at " << connectionRecord()->getFullAddress()
<< ".\n\nError:\n" << event->error().errorMessage();
std::stringstream ss("Unknown error");
auto eventErrorReason = event->errorReason;
if (EstablishConnectionResponse::ErrorReason::MongoSslConnection == eventErrorReason)
{
auto reason = ConnectionFailedEvent::SslConnection;
ss.clear();
ss << "Cannot connect to the MongoDB at " << connectionRecord()->getFullAddress()
<< ".\n\nError:\n" << "SSL connection failure: " << event->error().errorMessage();
_app->fireConnectionFailedEvent(_handle, _connectionType, ss.str(), reason);
}
else
{
auto reason = (EstablishConnectionResponse::ErrorReason::MongoAuth == eventErrorReason) ?
ConnectionFailedEvent::MongoAuth : ConnectionFailedEvent::MongoConnection;
ss.clear();
ss << "Cannot connect to the MongoDB at " << connectionRecord()->getFullAddress()
<< ".\n\nError:\n" << event->error().errorMessage();
_app->fireConnectionFailedEvent(_handle, _connectionType, ss.str(), reason);
}
// When connection cannot be established, we should cleanup this instance of MongoServer if it wasn't
// shown in UI (i.e. it is not a Secondary connection that is used for shells tab)
if (_connectionType == ConnectionPrimary || _connectionType == ConnectionTest)
{
_app->closeServer(this);
}
_bus->publish(new ConnectionFailedEvent(this, ss.str()));
return;
}
// Save various information after successful connection
const ConnectionInfo &info = event->info();
_version = info._version;
_storageEngineType = info._storageEngineType;
_isConnected = true;
// Do nothing if this is not "visible" connection
if (!_visible) {
return;
// 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));
_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) {
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);
@@ -155,7 +189,7 @@ namespace Robomongo {
}
clearDatabases();
for(std::vector<std::string>::iterator it = event->databaseNames.begin(); it != event->databaseNames.end(); ++it) {
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);
@@ -175,6 +209,23 @@ namespace Robomongo {
void MongoServer::runWorkerThread() {
_client = new MongoWorker(_settings->clone(),
AppRegistry::instance().settingsManager()->loadMongoRcJs(),
AppRegistry::instance().settingsManager()->batchSize());
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));
}
}
+26 -8
View File
@@ -8,10 +8,15 @@ namespace Robomongo
{
class MongoWorker;
class MongoDatabase;
class EventBus;
class App;
// Messages
class EstablishConnectionResponse;
class LoadDatabaseNamesResponse;
class InsertDocumentResponse;
class EventBus;
class CreateDatabaseResponse;
class DropDatabaseResponse;
/**
* @brief MongoServer represents active connection to MongoDB server.
@@ -29,7 +34,7 @@ namespace Robomongo
* @param visible
* @param defaultDatabase
*/
MongoServer(ConnectionSettings *connectionRecord, bool visible);
MongoServer(int handle, ConnectionSettings *connectionRecord, ConnectionType connectionType);
~MongoServer();
void runWorkerThread();
@@ -52,7 +57,7 @@ namespace Robomongo
void saveDocument(const mongo::BSONObj &obj, const MongoNamespace &ns);
void removeDocuments(mongo::Query query, const MongoNamespace &ns, bool justOne = true);
float version() const{ return _version; }
const std::string& getStorageEngineType() const { return _storageEngineType; }
/**
* @brief Returns associated connection record
@@ -63,28 +68,41 @@ namespace Robomongo
* @brief Loads databases of this server asynchronously.
*/
void loadDatabases();
bool visible() const { return _visible; }
MongoWorker *const client() const { return _client; }
// --- 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(LoadDatabaseNamesResponse *event);
void handle(InsertDocumentResponse *event);
void handle(RemoveDocumentResponse *event);
void handle(CreateDatabaseResponse *event);
void handle(DropDatabaseResponse *event);
private:
void clearDatabases();
void addDatabase(MongoDatabase *database);
void genericResponseHandler(Event *event, const std::string &userFriendlyMessage);
MongoWorker* _client;
ConnectionSettings* _settings;
EventBus* _bus;
MongoWorker *_client;
ConnectionSettings *_settings;
EventBus *_bus;
App *_app;
float _version;
bool _visible;
std::string _storageEngineType;
ConnectionType _connectionType;
bool _isConnected;
int _handle;
QList<MongoDatabase *> _databases;
// Replica Set Status
mongo::HostAndPort _repPrimary;
std::vector<bool> _repMembersHealths; // todo: vector of pairs of host and health
};
class MongoServerLoadingDatabasesEvent : public Event
+2 -2
View File
@@ -38,12 +38,12 @@ namespace Robomongo
if (_scriptInfo.execute()) {
AppRegistry::instance().bus()->publish(new ScriptExecutingEvent(this));
AppRegistry::instance().bus()->send(_server->client(), new ExecuteScriptRequest(this, query(), dbName));
if(!_scriptInfo.script().isEmpty())
if (!_scriptInfo.script().isEmpty())
LOG_MSG(_scriptInfo.script(), mongo::logger::LogSeverity::Info());
} else {
AppRegistry::instance().bus()->publish(new ScriptExecutingEvent(this));
_scriptInfo.setScript("");
AppRegistry::instance().bus()->send(_server->client(), new ExecuteScriptRequest(this,query() , dbName));
AppRegistry::instance().bus()->send(_server->client(), new ExecuteScriptRequest(this, query() , dbName));
}
}
+2 -2
View File
@@ -10,7 +10,7 @@ namespace Robomongo
Q_OBJECT
public:
MongoShell(MongoServer *server,const ScriptInfo &scriptInfo);
MongoShell(MongoServer *server, const ScriptInfo &scriptInfo);
void open(const std::string &script, const std::string &dbName = std::string());
void query(int resultIndex, const MongoQueryInfo &info);
@@ -19,7 +19,7 @@ namespace Robomongo
MongoServer *server() const { return _server; }
std::string query() const;
void execute(const std::string &dbName = std::string());
bool isExecutable() const {return _scriptInfo.execute(); }
bool isExecutable() const { return _scriptInfo.execute(); }
const QString &title() const { return _scriptInfo.title(); }
const CursorPosition &cursor() const { return _scriptInfo.cursor(); }
void setScript(const QString &script) { return _scriptInfo.setScript(script); }
+7 -7
View File
@@ -12,15 +12,15 @@ namespace Robomongo
_version(version), _readOnly(false)
{
_id = obj.getField("_id").OID();
_name = BsonUtils::getField<mongo::String>(obj,"user");
_passwordHash = BsonUtils::getField<mongo::String>(obj,"pwd");
_name = BsonUtils::getField<mongo::String>(obj, "user");
_passwordHash = BsonUtils::getField<mongo::String>(obj, "pwd");
if (_version<minimumSupportedVersion) {
_readOnly = BsonUtils::getField<mongo::Bool>(obj,"readOnly");
_readOnly = BsonUtils::getField<mongo::Bool>(obj, "readOnly");
}
else {
_userSource = BsonUtils::getField<mongo::String>(obj,"userSource");
_userSource = BsonUtils::getField<mongo::String>(obj, "userSource");
std::vector<mongo::BSONElement> roles = BsonUtils::getField<mongo::Array>(obj, "roles");
for (std::vector<mongo::BSONElement>::const_iterator it = roles.begin(); it!= roles.end(); ++it)
for (std::vector<mongo::BSONElement>::const_iterator it = roles.begin(); it != roles.end(); ++it)
{
_role.push_back(BsonUtils::bsonelement_cast<std::string>(*it));
}
@@ -28,7 +28,7 @@ namespace Robomongo
}
MongoUser::MongoUser(const float version) :
_version(version),_readOnly(false),_role() {}
_version(version), _readOnly(false), _role() {}
mongo::BSONObj MongoUser::toBson() const
{
@@ -50,7 +50,7 @@ namespace Robomongo
builder.append("userSource", _userSource);
mongo::BSONArrayBuilder builderA;
for (RoleType::const_iterator it = _role.begin(); it!=_role.end(); ++it)
for (RoleType::const_iterator it = _role.begin(); it != _role.end(); ++it)
{
builderA << *it;
}
+91 -18
View File
@@ -38,7 +38,7 @@ namespace Robomongo
bool isMultiSelection(const QModelIndexList &indexes)
{
return indexes.count()>1;
return indexes.count() > 1;
}
bool isDocumentType(BsonTreeItem *item)
@@ -46,6 +46,16 @@ namespace Robomongo
return Robomongo::BsonUtils::isDocument(item->type());
}
bool isArrayChild(BsonTreeItem *item)
{
return Robomongo::BsonUtils::isArray(dynamic_cast<BsonTreeItem *>(item->parent())->type());
}
bool isDocumentRoot(BsonTreeItem *item)
{
return ( item == item->superParent() );
}
/**
*
* @param QModelIndexList indexes
@@ -55,20 +65,20 @@ namespace Robomongo
QModelIndexList uniqueRows(QModelIndexList indexes, bool returnSuperParents)
{
QModelIndexList result;
for (QModelIndexList::const_iterator it = indexes.begin(); it!=indexes.end(); ++it)
for (QModelIndexList::const_iterator it = indexes.begin(); it != indexes.end(); ++it)
{
QModelIndex isUnique = *it;
Robomongo::BsonTreeItem *item = Robomongo::QtUtils::item<Robomongo::BsonTreeItem*>(isUnique);
if(item){
for (QModelIndexList::const_iterator jt = result.begin(); jt!=result.end(); ++jt)
if (item) {
for (QModelIndexList::const_iterator jt = result.begin(); jt != result.end(); ++jt)
{
Robomongo::BsonTreeItem *jItem = Robomongo::QtUtils::item<Robomongo::BsonTreeItem*>(*jt);
if (jItem && jItem->superParent() == item->superParent()){
if (jItem && jItem->superParent() == item->superParent()) {
isUnique = QModelIndex();
break;
}
}
if (isUnique.isValid()){
if (isUnique.isValid()) {
if (returnSuperParents) {
// Move index onto "super parent" element before pushing it into result set
QModelIndex parent = isUnique.parent();
@@ -113,6 +123,12 @@ namespace Robomongo
_copyValueAction = new QAction("Copy Value", wid);
VERIFY(connect(_copyValueAction, SIGNAL(triggered()), SLOT(onCopyDocument())));
_copyValueNameAction = new QAction("Copy Name", wid);
VERIFY(connect(_copyValueNameAction, SIGNAL(triggered()), SLOT(onCopyNameDocument())));
_copyValuePathAction = new QAction("Copy Path", wid);
VERIFY(connect(_copyValuePathAction, SIGNAL(triggered()), SLOT(onCopyPathDocument())));
_copyTimestampAction = new QAction("Copy Timestamp from ObjectId", wid);
VERIFY(connect(_copyTimestampAction, SIGNAL(triggered()), SLOT(onCopyTimestamp())));
@@ -128,11 +144,15 @@ namespace Robomongo
bool isSimple = false;
bool isDocument = false;
bool isObjectId = false;
bool isNotArrayChild = false;
bool isRoot = false;
if (item) {
isSimple = detail::isSimpleType(item);
isDocument = detail::isDocumentType(item);
isObjectId = detail::isObjectIdType(item);
isNotArrayChild = !detail::isArrayChild(item);
isRoot = detail::isDocumentRoot(item);
}
if (onItem && isEditable) menu->addAction(_editDocumentAction);
@@ -142,9 +162,15 @@ namespace Robomongo
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);
}
@@ -187,7 +213,7 @@ namespace Robomongo
break;
}
isNeededRefresh=true;
isNeededRefresh = true;
_shell->server()->removeDocuments(query, _queryInfo._info._ns);
}
@@ -240,11 +266,11 @@ namespace Robomongo
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 (QModelIndexList::const_iterator it = selectedIndexes.begin(); it!= selectedIndexes.end(); ++it) {
for (QModelIndexList::const_iterator it = selectedIndexes.begin(); it != selectedIndexes.end(); ++it) {
BsonTreeItem *item = QtUtils::item<BsonTreeItem*>(*it);
items.push_back(item);
}
deleteDocuments(items,true);
deleteDocuments(items, true);
}
}
@@ -260,7 +286,7 @@ namespace Robomongo
BsonTreeItem *documentItem = QtUtils::item<BsonTreeItem*>(selectedIndex);
std::vector<BsonTreeItem*> vec;
vec.push_back(documentItem);
return deleteDocuments(vec,false);
return deleteDocuments(vec, false);
}
void Notifier::onEditDocument()
@@ -360,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();
@@ -376,19 +449,19 @@ namespace Robomongo
QClipboard *clipboard = QApplication::clipboard();
// new Date(parseInt(this.valueOf().slice(0,8), 16)*1000);
QString hexTimestamp = documentItem->value().mid(10,8);
QString hexTimestamp = documentItem->value().mid(10, 8);
bool ok;
long long milliTimestamp = (long long)hexTimestamp.toLongLong(&ok,16)*1000;
long long milliTimestamp = (long long)hexTimestamp.toLongLong(&ok, 16)*1000;
bool isSupportedDate = (miutil::minDate < milliTimestamp) && (milliTimestamp < miutil::maxDate);
boost::posix_time::ptime epoch(boost::gregorian::date(1970,1,1));
boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1));
boost::posix_time::time_duration diff = boost::posix_time::millisec(milliTimestamp);
boost::posix_time::ptime time = epoch + diff;
if(isSupportedDate)
if (isSupportedDate)
{
std::string date = miutil::isotimeString(time,false,false);
std::string date = miutil::isotimeString(time, false, false);
clipboard->setText("ISODate(\""+QString::fromStdString(date)+"\")");
}
else {
@@ -411,13 +484,13 @@ namespace Robomongo
QClipboard *clipboard = QApplication::clipboard();
mongo::BSONObj obj = documentItem->root();
if (documentItem != documentItem->superParent()){
obj = obj[QtUtils::toStdString(documentItem->key())].Obj();
if (documentItem != documentItem->superParent()) {
obj = obj[documentItem->fieldName()].Obj();
}
bool isArray = BsonUtils::isArray(documentItem->type());
std::string str = BsonUtils::jsonString(obj, mongo::TenGen, 1,
AppRegistry::instance().settingsManager()->uuidEncoding(),
AppRegistry::instance().settingsManager()->timeZone(),isArray);
AppRegistry::instance().settingsManager()->timeZone(), isArray);
const QString &json = QtUtils::toQString(str);
clipboard->setText(json);
+4
View File
@@ -55,6 +55,8 @@ namespace Robomongo
void onViewDocument();
void onInsertDocument();
void onCopyDocument();
void onCopyNameDocument();
void onCopyPathDocument();
void onCopyTimestamp();
void onCopyJson();
void handle(InsertDocumentResponse *event);
@@ -67,6 +69,8 @@ namespace Robomongo
QAction *_viewDocumentAction;
QAction *_insertDocumentAction;
QAction *_copyValueAction;
QAction *_copyValueNameAction;
QAction *_copyValuePathAction;
QAction *_copyTimestampAction;
QAction *_copyJsonAction;
const MongoQueryInfo _queryInfo;
+4 -4
View File
@@ -62,7 +62,7 @@ namespace
namespace Robomongo
{
ScriptInfo::ScriptInfo(const QString &script, bool execute,const CursorPosition &position,
ScriptInfo::ScriptInfo(const QString &script, bool execute, const CursorPosition &position,
const QString &title, const QString &filePath) :
_script(script),
_execute(execute),
@@ -73,10 +73,10 @@ namespace Robomongo
bool ScriptInfo::loadFromFile(const QString &filePath)
{
bool result = false;
QString filepath = QFileDialog::getOpenFileName(QApplication::activeWindow(), filePath,QString(), filterForScripts);
QString filepath = QFileDialog::getOpenFileName(QApplication::activeWindow(), filePath, QString(), filterForScripts);
if (!filepath.isEmpty()) {
QString out;
if (loadFromFileText(filepath,out)) {
if (loadFromFileText(filepath, out)) {
_script = out;
_filePath = filepath;
result = true;
@@ -95,7 +95,7 @@ namespace Robomongo
QString filepath = QFileDialog::getSaveFileName(QApplication::activeWindow(),
QObject::tr("Save As"), _filePath, filterForScripts);
if (saveToFileText(filepath,_script)) {
if (saveToFileText(filepath, _script)) {
_filePath = filepath;
return true;
}
+50 -15
View File
@@ -55,10 +55,12 @@ namespace mongo {
namespace Robomongo
{
ScriptEngine::ScriptEngine(ConnectionSettings *connection) :
ScriptEngine::ScriptEngine(ConnectionSettings *connection, int timeoutSec) :
_connection(connection),
_scope(NULL),
_engine(NULL),
_timeoutSec(timeoutSec),
_initialized(false),
_mutex(QMutex::Recursive) { }
ScriptEngine::~ScriptEngine()
@@ -126,28 +128,40 @@ namespace Robomongo
}
// Esprima ECMAScript parser: http://esprima.org/
QFile file(":/robomongo/scripts/esprima.js");
if (!file.open(QIODevice::ReadOnly))
throw std::runtime_error("Unable to read esprima.js ");
std::string esprima = loadFile(":/robomongo/scripts/esprima.js", true);
_scope->exec(esprima, "(esprima)", false, true, true);
QTextStream in(&file);
QString esprima = in.readAll();
// Inject Esprima into Javascript scope
_scope->exec(QtUtils::toStdString(esprima), "(esprima)", true, true, true);
// UUID helpers
std::string uuidhelpers = loadFile(":/robomongo/scripts/uuidhelpers.js", true);
_scope->exec(uuidhelpers, "(uuidhelpers)", false, true, true);
// Enable verbose shell reporting
_scope->exec("_verboseShell = true;", "(verboseShell)", false, false, false);
// Save original autocomplete function so it can be restored if overwritten by user preference
_scope->exec("DB.autocompleteOriginal = DB.autocomplete;", "(saveOriginalAutocomplete)", false, false, false);
// Cache result of original "DB.autocomplete"
// Cache invalidated by the invalidateDbCollectionsCache() method.
std::string cacheAutocompletion =
"__robomongoAutocompletionCache = null;"
"DB.autocompleteCached = function(obj) { "
" if (__robomongoAutocompletionCache == null) {"
" __robomongoAutocompletionCache = DB.autocompleteOriginal(obj);"
" }"
" return __robomongoAutocompletionCache;"
"}";
_scope->exec(cacheAutocompletion, "", false, false, false);
_initialized = true;
}
MongoShellExecResult ScriptEngine::exec(const std::string &originalScript, const std::string &dbName)
{
QMutexLocker lock(&_mutex);
if(!_scope)
if (!_scope)
return MongoShellExecResult();
/*
@@ -177,7 +191,7 @@ namespace Robomongo
use(dbName);
for(std::vector<std::string>::const_iterator it = statements.begin(); it != statements.end(); ++it)
for (std::vector<std::string>::const_iterator it = statements.begin(); it != statements.end(); ++it)
{
std::string statement = *it;
// clear global objects
@@ -190,8 +204,8 @@ namespace Robomongo
try {
QElapsedTimer timer;
timer.start();
if ( _scope->exec( statement , "(shell)" , false , true , false, 0 ) ) {
_scope->exec( "__robomongoLastRes = __lastres__; shellPrintHelper( __lastres__ );" , "(shell2)" , true , true , false, 0);
if ( _scope->exec( statement , "(shell)" , false , true , false, _timeoutSec * 1000) ) {
_scope->exec( "__robomongoLastRes = __lastres__; shellPrintHelper( __lastres__ );" , "(shell2)" , true , true , false, _timeoutSec * 1000);
}
qint64 elapsed = timer.elapsed();
@@ -252,7 +266,7 @@ namespace Robomongo
void ScriptEngine::ping()
{
QMutexLocker lock(&_mutex);
_scope->exec("if (db) { db.runCommand({ping:1}); }", "(ping)", false, false, false);
_scope->exec("if (db) { db.runCommand({ping:1}); }", "(ping)", false, false, false, 3000);
}
QStringList ScriptEngine::complete(const std::string &prefix, const AutocompletionMode mode)
@@ -262,7 +276,7 @@ namespace Robomongo
try {
if (mode == AutocompleteAll)
_scope->exec("DB.autocomplete = DB.autocompleteOriginal;", "", false, false, false);
_scope->exec("DB.autocomplete = DB.autocompleteCached;", "", false, false, false);
else if (mode == AutocompleteNoCollectionNames)
_scope->exec("DB.autocomplete = function(obj){return [];}", "", false, false, false);
@@ -413,4 +427,25 @@ namespace Robomongo
return true;
}
void ScriptEngine::invalidateDbCollectionsCache() {
if (!_initialized)
return;
_scope->exec("__robomongoAutocompletionCache = null;", "", false, false, false);
}
std::string ScriptEngine::loadFile(const QString &path, bool throwOnError) {
QFile file(path);
if (!file.open(QIODevice::ReadOnly)) {
if (throwOnError)
throw std::runtime_error("Unable to load file");
return "";
}
QTextStream in(&file);
QString content = in.readAll();
return QtUtils::toStdString(content);
}
}
+6 -3
View File
@@ -15,7 +15,7 @@ namespace Robomongo
{
public:
ScriptEngine(ConnectionSettings *connection);
ScriptEngine(ConnectionSettings *connection, int timeoutSec);
~ScriptEngine();
void init(bool isLoadMongoJs);
@@ -27,20 +27,23 @@ namespace Robomongo
void ping();
QStringList complete(const std::string &prefix, const AutocompletionMode mode);
void invalidateDbCollectionsCache();
private:
ConnectionSettings *_connection;
MongoShellResult prepareResult(const std::string &type, const std::string &output, const std::vector<MongoDocumentPtr> &objects, qint64 elapsedms);
MongoShellExecResult prepareExecResult(const std::vector<MongoShellResult> &results);
std::string loadFile(const QString &path, bool throwOnError);
std::string getString(const char *fieldName);
bool statementize(const std::string &script, std::vector<std::string> &outList, std::string &outError);
std::vector<std::string> statementize2(const std::string &script);
// void parseTree(JSParseNode * root, int indent, const std::string &script, std::vector<std::string> &list, bool topList);
int _timeoutSec;
mongo::ScriptEngine *_engine;
mongo::Scope *_scope;
QMutex _mutex;
bool _initialized;
};
}
@@ -66,4 +66,5 @@ namespace Robomongo
R_REGISTER_EVENT(ListenSshConnectionResponse)
R_REGISTER_EVENT(LogEvent)
R_REGISTER_EVENT(StopScriptRequest)
R_REGISTER_EVENT(OperationFailedEvent)
}
+133 -53
View File
@@ -30,25 +30,48 @@ namespace Robomongo
{
R_EVENT
EstablishConnectionRequest(QObject *sender) :
Event(sender) {}
EstablishConnectionRequest(QObject *sender, ConnectionType connectionType) :
Event(sender),
connectionType(connectionType) {}
ConnectionType connectionType;
};
class EstablishConnectionResponse : public Event
{
R_EVENT
EstablishConnectionResponse(QObject *sender, const ConnectionInfo &info) :
enum ErrorReason {
MongoConnection = 0,
MongoAuth = 1,
MongoSslConnection = 2
};
EstablishConnectionResponse(QObject *sender, const ConnectionInfo &info, ConnectionType connectionType,
const mongo::HostAndPort& repPrimary, const std::vector<bool>& repMembersHealths) :
Event(sender),
_info(info) {}
_info(info),
connectionType(connectionType),
_repPrimary(repPrimary),
_repMembersHealths(repMembersHealths)
{}
EstablishConnectionResponse(QObject *sender, const EventError &error) :
Event(sender, error),_info() {}
EstablishConnectionResponse(QObject *sender, const EventError &error, ConnectionType connectionType, ErrorReason errorReason) :
Event(sender, error),
_info(),
connectionType(connectionType),
errorReason(errorReason) {}
// 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() const{
return _info;
}
const ConnectionInfo _info;
ConnectionType connectionType;
ErrorReason errorReason;
mongo::HostAndPort _repPrimary;
std::vector<bool> _repMembersHealths; // todo: vector of host and health pair
};
@@ -109,7 +132,7 @@ namespace Robomongo
_databaseName(databaseName),
_collectionInfos(collectionInfos) { }
LoadCollectionNamesResponse(QObject *sender,const EventError &error) :
LoadCollectionNamesResponse(QObject *sender, const EventError &error) :
Event(sender, error) {}
std::string databaseName() const { return _databaseName; }
@@ -125,7 +148,7 @@ namespace Robomongo
R_EVENT
public:
LoadCollectionIndexesRequest(QObject *sender, const MongoCollectionInfo &collection) :
Event(sender),_collection(collection) {}
Event(sender), _collection(collection) {}
MongoCollectionInfo collection() const { return _collection; }
private:
const MongoCollectionInfo _collection;
@@ -148,7 +171,7 @@ namespace Robomongo
class EnsureIndexRequest : public Event
{
R_EVENT
EnsureIndexRequest(QObject *sender,const EnsureIndexInfo &oldInfo,const EnsureIndexInfo &newInfo) : Robomongo::Event(sender),oldInfo_(oldInfo),newInfo_(newInfo) {}
EnsureIndexRequest(QObject *sender, const EnsureIndexInfo &oldInfo, const EnsureIndexInfo &newInfo) : Robomongo::Event(sender), oldInfo_(oldInfo), newInfo_(newInfo) {}
const EnsureIndexInfo &oldInfo() const
{
return oldInfo_;
@@ -172,7 +195,7 @@ namespace Robomongo
_name(name) {}
MongoCollectionInfo collection() const { return _collection; }
std::string name() const {return _name;}
std::string name() const { return _name; }
private:
const MongoCollectionInfo _collection;
std::string _name;
@@ -182,14 +205,14 @@ namespace Robomongo
{
R_EVENT
public:
DeleteCollectionIndexResponse(QObject *sender, const MongoCollectionInfo &collection,const std::string &index) :
Event(sender),_collection(collection),_index(index) {}
DeleteCollectionIndexResponse(QObject *sender, const MongoCollectionInfo &collection, const std::string &index) :
Event(sender), _collection(collection), _index(index) {}
DeleteCollectionIndexResponse(QObject *sender, const EventError &error) :
Event(sender, error) {}
MongoCollectionInfo collection() const { return _collection; }
std::string index() const {return _index;}
std::string index() const { return _index; }
private:
MongoCollectionInfo _collection;
std::string _index;
@@ -199,11 +222,11 @@ namespace Robomongo
{
R_EVENT
public:
EditIndexRequest(QObject *sender, const MongoCollectionInfo &collection,const std::string &oldIndex,const std::string &newIndex) :
Event(sender),_collection(collection),_oldIndex(oldIndex),_newIndex(newIndex) {}
EditIndexRequest(QObject *sender, const MongoCollectionInfo &collection, const std::string &oldIndex, const std::string &newIndex) :
Event(sender), _collection(collection), _oldIndex(oldIndex), _newIndex(newIndex) {}
MongoCollectionInfo collection() const { return _collection; }
std::string oldIndex() const {return _oldIndex;}
std::string newIndex() const {return _newIndex;}
std::string oldIndex() const { return _oldIndex; }
std::string newIndex() const { return _newIndex; }
private:
const MongoCollectionInfo _collection;
std::string _oldIndex;
@@ -240,7 +263,7 @@ namespace Robomongo
_databaseName(databaseName),
_users(users) { }
LoadUsersResponse(QObject *sender,const EventError &error) :
LoadUsersResponse(QObject *sender, const EventError &error) :
Event(sender, error) {}
std::string databaseName() const { return _databaseName; }
@@ -282,7 +305,7 @@ namespace Robomongo
_databaseName(databaseName),
_functions(functions) { }
LoadFunctionsResponse(QObject *sender,const EventError &error) :
LoadFunctionsResponse(QObject *sender, const EventError &error) :
Event(sender, error) {}
std::string databaseName() const { return _databaseName; }
@@ -440,14 +463,27 @@ namespace Robomongo
R_EVENT
public:
CreateCollectionRequest(QObject *sender, const MongoNamespace &ns) :
Event(sender),
_ns(ns) {}
CreateCollectionRequest(QObject *sender, const MongoNamespace &ns,
const mongo::BSONObj& extraOptions,
long long size = 0,
bool capped = false,
int maxDocNum = 0
) :
Event(sender), _ns(ns), _extraOptions(extraOptions),
_size(size), _capped(capped), _maxDocNum(maxDocNum) {}
MongoNamespace ns() const { return _ns; }
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:
const MongoNamespace _ns;
const long long _size;
const bool _capped;
const int _maxDocNum;
const mongo::BSONObj _extraOptions;
};
class CreateCollectionResponse : public Event
@@ -580,7 +616,7 @@ namespace Robomongo
_to(databaseTo, collection) {}
MongoWorker *worker() const { return _worker; }
MongoNamespace from() const { return _from;}
MongoNamespace from() const { return _from; }
MongoNamespace to() const { return _to; }
private:
MongoWorker *_worker;
@@ -806,7 +842,7 @@ namespace Robomongo
{
R_EVENT
AutocompleteResponse(QObject *sender,const QStringList &list, const std::string &prefix) :
AutocompleteResponse(QObject *sender, const QStringList &list, const std::string &prefix) :
Event(sender),
list(list),
prefix(prefix) {}
@@ -879,14 +915,26 @@ namespace Robomongo
{
R_EVENT
ConnectionFailedEvent(QObject *sender, const std::string& message) :
Event(sender),
message(message) {}
enum Reason {
MongoConnection = 1,
MongoAuth = 2,
SshConnection = 3,
SshChannel = 4,
SslConnection = 5
};
ConnectionFailedEvent(QObject *sender, const EventError &error) :
Event(sender, error) {}
ConnectionFailedEvent(QObject *sender, int serverHandle, ConnectionType connectionType,
const std::string& message, Reason reason) :
Event(sender),
message(message),
reason(reason),
serverHandle(serverHandle),
connectionType(connectionType) {}
std::string message;
Reason reason;
ConnectionType connectionType;
int serverHandle;
};
// class ScriptExecute
@@ -895,11 +943,13 @@ namespace Robomongo
{
R_EVENT
ConnectionEstablishedEvent(MongoServer *server) :
ConnectionEstablishedEvent(MongoServer *server, ConnectionType type) :
Event((QObject *)server),
server(server) { }
server(server),
connectionType(type) { }
MongoServer *server;
ConnectionType connectionType;
};
class DatabaseListLoadedEvent : public Event
@@ -973,6 +1023,20 @@ namespace Robomongo
Event(sender) { }
};
class OperationFailedEvent : public Event
{
R_EVENT
public:
OperationFailedEvent(QObject *sender, const std::string &technicalErrorMessage, const std::string &userFriendlyErrorMessage) :
technicalErrorMessage(technicalErrorMessage),
userFriendlyErrorMessage(userFriendlyErrorMessage),
Event(sender) { }
std::string technicalErrorMessage;
std::string userFriendlyErrorMessage;
};
class QueryWidgetUpdatedEvent : public Event
{
R_EVENT
@@ -996,38 +1060,43 @@ namespace Robomongo
{
R_EVENT
EstablishSshConnectionRequest(QObject *sender, SshTunnelWorker* worker, ConnectionSettings* settings, bool visible) :
EstablishSshConnectionRequest(QObject *sender, int serverHandle, SshTunnelWorker* worker, ConnectionSettings* settings, ConnectionType connectionType) :
Event(sender),
worker(worker),
settings(settings),
visible(visible) {}
serverHandle(serverHandle),
connectionType(connectionType) {}
ConnectionSettings* settings;
bool visible;
ConnectionType connectionType;
SshTunnelWorker* worker;
int serverHandle;
};
class EstablishSshConnectionResponse : public Event
{
R_EVENT
EstablishSshConnectionResponse(QObject *sender, SshTunnelWorker* worker, ConnectionSettings* settings, bool visible, int localport) :
EstablishSshConnectionResponse(QObject *sender, int serverHandle, SshTunnelWorker* worker, ConnectionSettings* settings, ConnectionType connectionType, int localport) :
Event(sender),
worker(worker),
settings(settings),
visible(visible),
connectionType(connectionType),
serverHandle(serverHandle),
localport(localport) {}
EstablishSshConnectionResponse(QObject *sender, const EventError &error, SshTunnelWorker* worker, ConnectionSettings* settings, bool visible) :
EstablishSshConnectionResponse(QObject *sender, int serverHandle, const EventError &error, SshTunnelWorker* worker, ConnectionSettings* settings, ConnectionType connectionType) :
Event(sender, error),
worker(worker),
settings(settings),
visible(visible) {}
serverHandle(serverHandle),
connectionType(connectionType) {}
ConnectionSettings* settings;
bool visible;
ConnectionType connectionType;
SshTunnelWorker* worker;
int localport;
int serverHandle;
};
/**
@@ -1038,23 +1107,34 @@ namespace Robomongo
{
R_EVENT
ListenSshConnectionRequest(QObject *sender) :
Event(sender) {}
ListenSshConnectionRequest(QObject *sender, int serverHandle, ConnectionType connectionType) :
Event(sender),
serverHandle(serverHandle),
connectionType(connectionType) {}
ConnectionType connectionType;
int serverHandle;
};
class ListenSshConnectionResponse : public Event
{
R_EVENT
ListenSshConnectionResponse(QObject *sender, ConnectionSettings* settings) :
ListenSshConnectionResponse(QObject *sender, int serverHandle, ConnectionSettings* settings, ConnectionType connectionType) :
Event(sender),
settings(settings) {}
settings(settings),
serverHandle(serverHandle),
connectionType(connectionType) {}
ListenSshConnectionResponse(QObject *sender, const EventError &error, ConnectionSettings* settings) :
ListenSshConnectionResponse(QObject *sender, const EventError &error, int serverHandle, ConnectionSettings* settings, ConnectionType connectionType) :
Event(sender, error),
settings(settings) {}
settings(settings),
serverHandle(serverHandle),
connectionType(connectionType) {}
ConnectionSettings* settings;
ConnectionType connectionType;
int serverHandle;
};
class LogEvent : public Event
@@ -1062,10 +1142,10 @@ namespace Robomongo
R_EVENT
enum LogLevel {
ERROR = 1,
WARN = 2,
INFO = 3,
DEBUG = 100 // log as much as possible
RBM_ERROR = 1,
RBM_WARN = 2,
RBM_INFO = 3,
RBM_DEBUG = 100 // log as much as possible
};
LogEvent(QObject *sender, const std::string& message, LogLevel level) :
@@ -22,8 +22,10 @@ namespace Robomongo
_databases(),
_version(0.0f) {}
ConnectionInfo::ConnectionInfo(const std::string &address, const std::vector<std::string> &databases, float version) :
ConnectionInfo::ConnectionInfo(const std::string &address, const std::vector<std::string> &databases,
float version, const std::string& storageEngine) :
_address(address),
_databases(databases),
_version(version) {}
_version(version),
_storageEngineType(storageEngine) {}
}
+2 -1
View File
@@ -35,9 +35,10 @@ namespace Robomongo
struct ConnectionInfo
{
ConnectionInfo();
ConnectionInfo(const std::string &address, const std::vector<std::string> &databases, float version);
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;
};
}
+39 -12
View File
@@ -1,5 +1,7 @@
#include "robomongo/core/mongodb/MongoClient.h"
#include "mongo/db/namespace_string.h"
#include "robomongo/core/domain/MongoDocument.h"
#include "robomongo/core/utils/BsonUtils.h"
#include "robomongo/shell/bson/json.h"
@@ -13,7 +15,7 @@ namespace
using namespace Robomongo::BsonUtils;
Robomongo::EnsureIndexInfo info(collection);
info._name = getField<mongo::String>(obj, "name");
mongo::BSONObj keyObj = getField<mongo::Object>(obj,"key");
mongo::BSONObj keyObj = getField<mongo::Object>(obj, "key");
if (keyObj.isValid()) {
info._request = jsonString(keyObj, mongo::TenGen, 1, Robomongo::DefaultEncoding, Robomongo::Utc);
}
@@ -56,11 +58,18 @@ namespace Robomongo
float result = 0.0f;
mongo::BSONObj resultObj;
_dbclient->runCommand("db", BSON("buildInfo" << "1"), resultObj);
std::string resultStr = BsonUtils::getField<mongo::String>(resultObj,"version");
std::string resultStr = BsonUtils::getField<mongo::String>(resultObj, "version");
result = atof(resultStr.c_str());
return result;
}
std::string MongoClient::getStorageEngineType() const
{
mongo::BSONObj resultObj;
_dbclient->runCommand("db", BSON("serverStatus" << "1"), resultObj);
return resultObj.getObjectField("storageEngine").getStringField("name");
}
std::vector<std::string> MongoClient::getDatabaseNames() const
{
typedef std::list<std::string> cont_string_t;
@@ -87,7 +96,7 @@ namespace Robomongo
float ver = getVersion();
while (cursor->more()) {
mongo::BSONObj bsonObj = cursor->next();
MongoUser user(ver,bsonObj);
MongoUser user(ver, bsonObj);
users.push_back(user);
}
@@ -157,13 +166,13 @@ namespace Robomongo
for (std::list<mongo::BSONObj>::iterator it = indexes.begin(); it != indexes.end(); ++it) {
mongo::BSONObj bsonObj = *it;
result.push_back(makeEnsureIndexInfoFromBsonObj(collection,bsonObj));
result.push_back(makeEnsureIndexInfoFromBsonObj(collection, bsonObj));
}
return result;
}
void MongoClient::ensureIndex(const EnsureIndexInfo &oldInfo,const EnsureIndexInfo &newInfo) const
void MongoClient::ensureIndex(const EnsureIndexInfo &oldInfo, const EnsureIndexInfo &newInfo) const
{
std::string ns = newInfo._collection.ns().toString();
@@ -172,8 +181,8 @@ namespace Robomongo
mongo::BSONObj keys = mongo::Robomongo::fromjson(newInfo._request);
mongo::BSONObjBuilder toSave;
bool cache=true;
int version =-1;
bool cache = true;
int version = -1;
toSave.append( "ns" , ns );
toSave.append( "key" , keys );
@@ -361,9 +370,27 @@ namespace Robomongo
_dbclient->dropDatabase(dbName);
}
void MongoClient::createCollection(const MongoNamespace &ns)
void MongoClient::createCollection(const std::string& ns, long long size, bool capped, int max, const mongo::BSONObj& extraOptions, mongo::BSONObj* info)
{
_dbclient->createCollection(ns.toString());
verify(!capped || size);
mongo::BSONObj o;
if (info == 0)
info = &o;
mongo::BSONObjBuilder b;
std::string db = mongo::nsToDatabase(ns);
b.append("create", ns.c_str() + db.length() + 1);
if (size) {
b.append("size", size);
}
if (capped) {
b.append("capped", true);
}
if (max) {
b.append("max", max);
}
b.appendElements(extraOptions);
_dbclient->runCommand(db.c_str(), b.done(), *info);
}
void MongoClient::renameCollection(const MongoNamespace &ns, const std::string &newCollectionName)
@@ -400,7 +427,7 @@ namespace Robomongo
}
}
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());
@@ -497,9 +524,9 @@ namespace Robomongo
std::vector<MongoCollectionInfo> MongoClient::runCollStatsCommand(const std::vector<std::string> &namespaces)
{
std::vector<MongoCollectionInfo> infos;
for (std::vector<std::string>::const_iterator it = namespaces.begin(); it!=namespaces.end(); ++it) {
for (std::vector<std::string>::const_iterator it = namespaces.begin(); it != namespaces.end(); ++it) {
MongoCollectionInfo info = runCollStatsCommand(*it);
if (info.ns().isValid()){
if (info.ns().isValid()) {
infos.push_back(info);
}
}
+5 -2
View File
@@ -1,6 +1,8 @@
#pragma once
#include <mongo/client/dbclientinterface.h>
#include <mongo/bson/bsonobj.h>
#include "robomongo/core/Core.h"
#include "robomongo/core/domain/MongoQueryInfo.h"
#include "robomongo/core/domain/MongoUser.h"
@@ -17,6 +19,7 @@ namespace Robomongo
std::vector<std::string> getCollectionNames(const std::string &dbname) const;
std::vector<std::string> getDatabaseNames() const;
float getVersion() const;
std::string getStorageEngineType() const;
std::vector<MongoUser> getUsers(const std::string &dbName);
void createUser(const std::string &dbName, const MongoUser &user, bool overwrite);
@@ -37,11 +40,11 @@ namespace Robomongo
void createDatabase(const std::string &dbName);
void dropDatabase(const std::string &dbName);
void createCollection(const MongoNamespace &ns);
void createCollection(const std::string &ns, long long size, bool capped, int max, const mongo::BSONObj& extraOptions, mongo::BSONObj* info = nullptr);
void renameCollection(const MongoNamespace &ns, const std::string &newCollectionName);
void duplicateCollection(const MongoNamespace &ns, const std::string &newCollectionName);
void dropCollection(const MongoNamespace &ns);
void copyCollectionToDiffServer(mongo::DBClientBase *const,const MongoNamespace &from, const MongoNamespace &to);
void copyCollectionToDiffServer(mongo::DBClientBase *const, const MongoNamespace &from, const MongoNamespace &to);
void insertDocument(const mongo::BSONObj &obj, const MongoNamespace &ns);
void saveDocument(const mongo::BSONObj &obj, const MongoNamespace &ns);
+204 -38
View File
@@ -2,34 +2,45 @@
#include <QThread>
#include "mongo/client/global_conn_pool.h"
#include "mongo/client/replica_set_monitor.h"
#include <mongo/util/net/ssl_manager.h>
#include <mongo/util/net/ssl_options.h>
#include "robomongo/core/events/MongoEvents.h"
#include "robomongo/core/engine/ScriptEngine.h"
#include "robomongo/core/EventBus.h"
#include "robomongo/core/AppRegistry.h"
#include "robomongo/core/mongodb/MongoClient.h"
#include "robomongo/core/settings/ConnectionSettings.h"
#include "robomongo/core/settings/ReplicaSetSettings.h"
#include "robomongo/core/domain/MongoShellResult.h"
#include "robomongo/core/domain/MongoCollectionInfo.h"
#include "robomongo/core/settings/CredentialSettings.h"
#include "robomongo/core/settings/SettingsManager.h"
#include "robomongo/core/settings/SslSettings.h"
#include "robomongo/core/utils/Logger.h"
#include "robomongo/core/utils/QtUtils.h"
namespace Robomongo
{
MongoWorker::MongoWorker(ConnectionSettings *connection, bool isLoadMongoRcJs, int batchSize, QObject *parent) : QObject(parent),
_connection(connection),
MongoWorker::MongoWorker(ConnectionSettings *connection, bool isLoadMongoRcJs, int batchSize,
int mongoTimeoutSec, int shellTimeoutSec, QObject *parent) : QObject(parent),
_connSettings(connection),
_scriptEngine(NULL),
_dbclient(NULL),
_dbclient(nullptr),
_dbclientRepSet(nullptr),
_isAdmin(true),
_isLoadMongoRcJs(isLoadMongoRcJs),
_batchSize(batchSize),
_timerId(-1),
_dbAutocompleteCacheTimerId(-1),
_mongoTimeoutSec(mongoTimeoutSec),
_shellTimeoutSec(shellTimeoutSec),
_isQuiting(0)
{
{
_thread = new QThread();
moveToThread(_thread);
VERIFY(connect( _thread, SIGNAL(started()), this, SLOT(init()) ));
VERIFY(connect( _thread, SIGNAL(finished()), _thread, SLOT(deleteLater()) ));
VERIFY(connect( _thread, SIGNAL(finished()), this, SLOT(deleteLater()) ));
_thread->start();
@@ -39,6 +50,13 @@ namespace Robomongo
{
if (_timerId == event->timerId()) {
keepAlive();
return;
}
if (_dbAutocompleteCacheTimerId == event->timerId() &&
_scriptEngine != NULL) {
_scriptEngine->invalidateDbCollectionsCache();
return;
}
}
@@ -70,11 +88,12 @@ namespace Robomongo
void MongoWorker::init()
{
try {
_scriptEngine = new ScriptEngine(_connection);
_scriptEngine = new ScriptEngine(_connSettings, _shellTimeoutSec);
_scriptEngine->init(_isLoadMongoRcJs);
_scriptEngine->use(_connection->defaultDatabase());
_scriptEngine->use(_connSettings->defaultDatabase());
_scriptEngine->setBatchSize(_batchSize);
_timerId = startTimer(pingTimeMs);
_dbAutocompleteCacheTimerId = startTimer(30000);
} catch (const std::exception &ex) {
LOG_MSG(ex.what(), mongo::logger::LogSeverity::Error());
}
@@ -93,9 +112,13 @@ namespace Robomongo
MongoWorker::~MongoWorker()
{
killTimer(_timerId);
delete _dbclient;
delete _connection;
if (_timerId != -1)
killTimer(_timerId);
if (_dbAutocompleteCacheTimerId != -1)
killTimer(_dbAutocompleteCacheTimerId);
delete _connSettings;
delete _scriptEngine;
// QThread "_thread" and MongoWorker itself will be deleted later
@@ -116,10 +139,31 @@ namespace Robomongo
QMutexLocker lock(&_firstConnectionMutex);
try {
mongo::DBClientBase *conn = getConnection();
bool hasPrimary = _connection->hasEnabledPrimaryCredential();
if (hasPrimary) {
CredentialSettings *credentials = _connection->primaryCredential();
mongo::DBClientBase *conn = getConnection(true);
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())
{
// Note: Currently mongo-shell does not provide any interface to fetch actual error details
// for some SSL connection failures that's why we are unable to show exact error here.
errorReason = "SSL tunnel failure: Network is unreachable or SSL connection rejected by server.";
}
else
{
errorReason = "Network is unreachable.";
}
resetGlobalSSLparams();
reply(event->sender(), new EstablishConnectionResponse(this,
EventError(errorReason), event->connectionType,
EstablishConnectionResponse::MongoConnection));
return;
}
if (_connSettings->hasEnabledPrimaryCredential()) {
CredentialSettings *credentials = _connSettings->primaryCredential();
// Building BSON object:
mongo::BSONObj authParams(mongo::BSONObjBuilder()
@@ -138,21 +182,60 @@ namespace Robomongo
if (dbName.compare("admin") != 0) // dbName is NOT "admin"
_isAdmin = false;
}
boost::scoped_ptr<MongoClient> client(getClient());
std::vector<std::string> dbNames = getDatabaseNamesSafe();
reply(event->sender(), new EstablishConnectionResponse(this, ConnectionInfo(_connection->getFullAddress(), dbNames, client->getVersion()) ));
// If we do not have databases, it means that we are unable to
// execute "listdatabases" command and we have nothing to show.
if (dbNames.size() == 0)
throw mongo::DBException("Failed to execute \"listdatabases\" command.", 0);
// todo: move to func. getRepSetStatus();
mongo::HostAndPort repPrimary;
std::vector<bool> repMembersHealths;
if (_connSettings->isReplicaSet())
{
// Refresh view of Replica Set Monitor to get live data
auto repSetMonitor = mongo::globalRSMonitorManager.getMonitor(_dbclientRepSet->getSetName());
repSetMonitor->startOrContinueRefresh().refreshAll();
// Update connection settings with primary(master) address and port
repPrimary = repSetMonitor->getMasterOrUassert();
_connSettings->setServerHost(repPrimary.host());
_connSettings->setServerPort(repPrimary.port());
// Save health of members
for (auto const& member : _connSettings->replicaSetSettings()->membersToHostAndPort())
{
repMembersHealths.push_back(repSetMonitor->isHostUp(member));
}
}
// Init MongoWorker
init();
resetGlobalSSLparams();
reply(event->sender(), new EstablishConnectionResponse(this, ConnectionInfo(_connSettings->getFullAddress(),
dbNames, client->getVersion(), client->getStorageEngineType()), event->connectionType, repPrimary, repMembersHealths));
} catch(const std::exception &ex) {
reply(event->sender(), new EstablishConnectionResponse(this, EventError(ex.what())));
LOG_MSG(ex.what(), mongo::logger::LogSeverity::Error());
resetGlobalSSLparams();
auto errorReason = _connSettings->sslSettings()->sslEnabled() ?
EstablishConnectionResponse::ErrorReason::MongoSslConnection :
EstablishConnectionResponse::ErrorReason::MongoAuth;
reply(event->sender(),
new EstablishConnectionResponse(this, EventError(ex.what()), event->connectionType, errorReason));
}
}
std::string MongoWorker::getAuthBase() const
{
bool hasPrimary = _connection->hasEnabledPrimaryCredential();
if(hasPrimary){
return _connection->primaryCredential()->databaseName();
}
if (_connSettings->hasEnabledPrimaryCredential())
return _connSettings->primaryCredential()->databaseName();
return std::string();
}
@@ -161,17 +244,16 @@ namespace Robomongo
DatabasesContainerType result;
std::string authBase = getAuthBase();
if (!_isAdmin && !authBase.empty()) {
result.push_back(_connection->primaryCredential()->databaseName());
result.push_back(_connSettings->primaryCredential()->databaseName());
return result;
}
try {
boost::scoped_ptr<MongoClient> client(getClient());
result = client->getDatabaseNames();
} catch(const std::exception &ex) {
} catch(const std::exception &) {
if (!authBase.empty())
result.push_back(authBase);
LOG_MSG(ex.what(), mongo::logger::LogSeverity::Error());
}
return result;
}
@@ -202,7 +284,7 @@ namespace Robomongo
if (dbNames.size()) {
reply(event->sender(), new LoadDatabaseNamesResponse(this, dbNames));
} else {
reply(event->sender(), new LoadDatabaseNamesResponse(this, EventError("Unable to load database names.")));
reply(event->sender(), new LoadDatabaseNamesResponse(this, EventError("Failed to execute \"listdatabases\" command.")));
}
} catch(const mongo::DBException &ex) {
reply(event->sender(), new LoadDatabaseNamesResponse(this, EventError(ex.what())));
@@ -263,7 +345,7 @@ namespace Robomongo
const EnsureIndexInfo &oldInfo = event->oldInfo();
try {
boost::scoped_ptr<MongoClient> client(getClient());
client->ensureIndex(oldInfo,newInfo);
client->ensureIndex(oldInfo, newInfo);
const std::vector<EnsureIndexInfo> &ind = client->getIndexes(newInfo._collection);
client->done();
@@ -278,7 +360,7 @@ namespace Robomongo
{
try {
boost::scoped_ptr<MongoClient> client(getClient());
client->dropIndexFromCollection(event->collection(),event->name());
client->dropIndexFromCollection(event->collection(), event->name());
client->done();
reply(event->sender(), new DeleteCollectionIndexResponse(this, event->collection(), event->name()));
} catch(const mongo::DBException &ex) {
@@ -291,7 +373,7 @@ namespace Robomongo
{
try {
boost::scoped_ptr<MongoClient> client(getClient());
client->renameIndexFromCollection(event->collection(),event->oldIndex(),event->newIndex());
client->renameIndexFromCollection(event->collection(), event->oldIndex(), event->newIndex());
const std::vector<EnsureIndexInfo> &ind = client->getIndexes(event->collection());
client->done();
@@ -372,6 +454,11 @@ namespace Robomongo
void MongoWorker::handle(ExecuteScriptRequest *event)
{
try {
if (!_scriptEngine) {
reply(event->sender(), new ExecuteScriptResponse(this, EventError("MongoDB Shell was not initialized")));
return;
}
MongoShellExecResult result = _scriptEngine->exec(event->script, event->databaseName);
reply(event->sender(), new ExecuteScriptResponse(this, result, event->script.empty()));
} catch(const mongo::DBException &ex) {
@@ -386,6 +473,10 @@ namespace Robomongo
void MongoWorker::handle(StopScriptRequest *)
{
try {
if (!_scriptEngine) {
return;
}
_scriptEngine->interrupt();
} catch(const mongo::DBException &ex) {
LOG_MSG(ex.what(), mongo::logger::LogSeverity::Error());
@@ -395,6 +486,11 @@ namespace Robomongo
void MongoWorker::handle(AutocompleteRequest *event)
{
try {
if (!_scriptEngine) {
reply(event->sender(), new AutocompleteResponse(this, EventError("MongoDB Shell was not initialized")));
return;
}
QStringList list = _scriptEngine->complete(event->prefix, event->mode);
reply(event->sender(), new AutocompleteResponse(this, list, event->prefix));
} catch(const mongo::DBException &ex) {
@@ -441,7 +537,8 @@ namespace Robomongo
{
try {
boost::scoped_ptr<MongoClient> client(getClient());
client->createCollection(event->ns());
client->createCollection(event->getNs().toString(), event->getSize(), event->getCapped(),
event->getMaxDocNum(), event->getExtraOptions());
client->done();
reply(event->sender(), new CreateCollectionResponse(this));
@@ -498,7 +595,7 @@ namespace Robomongo
try {
boost::scoped_ptr<MongoClient> client(getClient());
MongoWorker *cl = event->worker();
client->copyCollectionToDiffServer(cl->_dbclient,event->from(),event->to());
client->copyCollectionToDiffServer(cl->_dbclient.get(), event->from(), event->to());
client->done();
reply(event->sender(), new CopyCollectionToDiffServerResponse(this));
@@ -564,16 +661,54 @@ namespace Robomongo
}
}
mongo::DBClientBase *MongoWorker::getConnection()
mongo::DBClientBase *MongoWorker::getConnection(bool mayReturnNull /* = false */)
{
if (!_dbclient) {
// Timeout for operations is 10 seconds.
// Connect timeout is fixed, but short, at 5 seconds (see headers for DBClientConnection)
mongo::DBClientConnection *conn = new mongo::DBClientConnection(true, 10);
conn->connect(_connection->info());
_dbclient = conn;
// --- 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) {
_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));
bool const connStatus = _dbclientRepSet->connect();
if (!connStatus) {
return nullptr;
}
}
return _dbclientRepSet.get();
}
else { // connection to single server
if (!_dbclient) {
// Timeout for operations
// Connect timeout is fixed, but short, at 5 seconds (see headers for DBClientConnection)
_dbclient = std::unique_ptr<mongo::DBClientConnection>(new mongo::DBClientConnection(true, _mongoTimeoutSec));
mongo::Status status = _dbclient->connect(_connSettings->info());
if (!status.isOK() && mayReturnNull) {
return nullptr;
}
}
return _dbclient.get();
}
return _dbclient;
}
MongoClient *MongoWorker::getClient()
@@ -581,6 +716,37 @@ namespace Robomongo
return new MongoClient(getConnection());
}
void MongoWorker::updateGlobalSSLparams() const
{
resetGlobalSSLparams();
const SslSettings * const sslSettings = _connSettings->sslSettings();
mongo::sslGlobalParams.sslAllowInvalidCertificates = sslSettings->allowInvalidCertificates();
if (!mongo::sslGlobalParams.sslAllowInvalidCertificates)
{
mongo::sslGlobalParams.sslCAFile = sslSettings->caFile();
}
if (sslSettings->usePemFile())
{
mongo::sslGlobalParams.sslPEMKeyFile = sslSettings->pemKeyFile();
mongo::sslGlobalParams.sslPEMKeyPassword = sslSettings->pemPassPhrase();
}
if (sslSettings->useAdvancedOptions())
{
mongo::sslGlobalParams.sslCRLFile = sslSettings->crlFile();
mongo::sslGlobalParams.sslAllowInvalidHostnames = sslSettings->allowInvalidHostnames();
}
}
void MongoWorker::resetGlobalSSLparams() const
{
mongo::sslGlobalParams.sslAllowInvalidCertificates = false;
mongo::sslGlobalParams.sslCAFile = "";
mongo::sslGlobalParams.sslPEMKeyFile = "";
mongo::sslGlobalParams.sslPEMKeyPassword = "";
mongo::sslGlobalParams.sslCRLFile = "";
mongo::sslGlobalParams.sslAllowInvalidHostnames = false;
}
/**
* @brief Send event to this MongoWorker
*/
+23 -5
View File
@@ -4,6 +4,8 @@
#include <QMutex>
#include <unordered_set>
#include <mongo/client/dbclient_rs.h> // todo: move
#include "robomongo/core/events/MongoEvents.h"
QT_BEGIN_NAMESPACE
@@ -23,7 +25,8 @@ namespace Robomongo
public:
typedef std::vector<std::string> DatabasesContainerType;
explicit MongoWorker(ConnectionSettings *connection, bool isLoadMongoRcJs, int batchSize, QObject *parent = NULL);
explicit MongoWorker(ConnectionSettings *connection, bool isLoadMongoRcJs, int batchSize,
int mongoTimeoutSec, int shellTimeoutSec, QObject *parent = NULL);
~MongoWorker();
enum { pingTimeMs = 60 * 1000 };
void interrupt();
@@ -130,10 +133,22 @@ namespace Robomongo
DatabasesContainerType getDatabaseNamesSafe();
std::string getAuthBase() const;
mongo::DBClientBase *_dbclient;
mongo::DBClientBase *getConnection();
std::unique_ptr<mongo::DBClientConnection> _dbclient;
std::unique_ptr<mongo::DBClientReplicaSet> _dbclientRepSet;
mongo::DBClientBase *getConnection(bool mayReturnNull = false);
MongoClient *getClient();
/**
*@brief Update global mongo SSL settings (mongo::sslGlobalParams) according to active connection
* request's SSL settings.
*/
void updateGlobalSSLparams() const;
/**
*@brief Reset global mongo SSL settings (mongo::sslGlobalParams) into default zero state
*/
void resetGlobalSSLparams() const;
/**
* @brief Send reply event to object 'obj'
*/
@@ -147,13 +162,16 @@ namespace Robomongo
const bool _isLoadMongoRcJs;
const int _batchSize;
int _timerId;
int _dbAutocompleteCacheTimerId;
int _mongoTimeoutSec;
int _shellTimeoutSec;
QAtomicInteger<int> _isQuiting;
ConnectionSettings *_connection;
ConnectionSettings *_connSettings;
// Collection of created databases.
// Starting from 3.0, MongoDB drops empty databases.
// It means, we do not find a way to create "empty" database.
// It means, we did not find a way to create "empty" database.
// We save all created databases in this collection and merge with
// list of real databases returned from MongoDB server.
std::unordered_set<std::string> _createdDbs;
+101 -70
View File
@@ -1,6 +1,8 @@
#include "robomongo/core/mongodb/SshTunnelWorker.h"
#include <QThread>
#include <QElapsedTimer>
#include "robomongo/core/utils/QtUtils.h"
#include "robomongo/core/utils/Logger.h"
#include "robomongo/core/settings/ConnectionSettings.h"
@@ -15,50 +17,23 @@ namespace Robomongo
{
SshTunnelWorker::SshTunnelWorker(ConnectionSettings *settings) : QObject(),
_settings(settings),
_sshConfig(NULL),
_sshSession(NULL)
_sshSession(NULL),
_configCreator(settings)
{
_thread = new QThread();
moveToThread(_thread);
VERIFY(connect(_thread, SIGNAL(started()), this, SLOT(init())));
VERIFY(connect(_thread, SIGNAL(finished()), _thread, SLOT(deleteLater())));
VERIFY(connect(_thread, SIGNAL(finished()), this, SLOT(deleteLater())));
_thread->start();
}
void SshTunnelWorker::init()
{
try {
SshSettings *ssh = _settings->sshSettings();
// We are going to access raw char* buffers, so prepare copies
// of strings (and rest of the things for symmetry)
_sshhost = ssh->host();
_sshport = ssh->port();
_remotehost = _settings->serverHost();
_remoteport = _settings->serverPort();
_localip = "127.0.0.1";
_localport = 27040;
_userName = ssh->userName();
_userPassword = ssh->userPassword();
_privateKeyFile = ssh->privateKeyFile();
_publicKeyFile = ssh->publicKeyFile();
_passphrase = ssh->passphrase();
_authMethod = ssh->authMethod(); // "password" or "publickey"
}
catch (const std::exception &ex) {
LOG_MSG(ex.what(), mongo::logger::LogSeverity::Error());
}
}
SshTunnelWorker::~SshTunnelWorker()
{
// QThread "_thread" and MongoWorker itself will be deleted later
// (see MongoWorker() constructor)
delete _settings;
delete _sshConfig;
printf("SSH tunnel closed.\n");
}
void SshTunnelWorker::stopAndDelete() {
@@ -71,30 +46,12 @@ namespace Robomongo
if (_isQuiting)
return;
_sshConfig = new rbm_ssh_tunnel_config;
_sshConfig->sshserverip = const_cast<char*>(_sshhost.c_str());
_sshConfig->sshserverport = static_cast<unsigned int>(_sshport);
_sshConfig->remotehost = const_cast<char*>(_remotehost.c_str());
_sshConfig->remoteport = static_cast<unsigned int>(_remoteport);
_sshConfig->localip = const_cast<char*>(_localip.c_str());
_sshConfig->localport = static_cast<unsigned int>(_localport);
_sshConfig->username = const_cast<char*>(_userName.c_str());
_sshConfig->password = const_cast<char*>(_userPassword.c_str());
_sshConfig->privatekeyfile = const_cast<char*>(_privateKeyFile.c_str());
_sshConfig->publickeyfile = _publicKeyFile.empty() ? NULL : const_cast<char*>(_publicKeyFile.c_str());
_sshConfig->passphrase = const_cast<char*>(_passphrase.c_str());
_sshConfig->authtype = (_authMethod == "publickey") ? RBM_SSH_AUTH_TYPE_PUBLICKEY : RBM_SSH_AUTH_TYPE_PASSWORD;
_sshConfig->logcontext = this;
_sshConfig->logcallback = &SshTunnelWorker::logCallbackHandler;
_sshConfig->loglevel = (rbm_ssh_log_type) _settings->sshSettings()->logLevel(); // RBM_SSH_LOG_TYPE_DEBUG;
if ((_sshSession = rbm_ssh_session_create(_sshConfig)) == 0) {
// Cleanup config structure
delete _sshConfig;
_sshConfig = NULL;
// Additionally configure "rbm_ssh_tunnel_config"
_configCreator.config()->logcontext = this;
_configCreator.config()->logcallback = &SshTunnelWorker::logCallbackHandler;
_configCreator.config()->loglevel = (rbm_ssh_log_type) _settings->sshSettings()->logLevel(); // RBM_SSH_LOG_TYPE_DEBUG;
if ((_sshSession = rbm_ssh_session_create(_configCreator.config())) == 0) {
// Not much we can say about this error
throw std::runtime_error("Failed to create SSH session");
}
@@ -113,20 +70,16 @@ namespace Robomongo
rbm_ssh_session_close(_sshSession);
_sshSession = NULL;
// Cleanup config
delete _sshConfig;
_sshConfig = NULL;
throw std::runtime_error(ss.str());
}
reply(event->sender(), new EstablishSshConnectionResponse(
this, event->worker, event->settings, event->visible, _sshConfig->localport));
this, event->serverHandle, event->worker, event->settings, event->connectionType, _configCreator.config()->localport));
} catch (const std::exception& ex) {
reply(event->sender(),
new EstablishSshConnectionResponse(this, EventError(ex.what()),
event->worker, event->settings, event->visible));
new EstablishSshConnectionResponse(this, event->serverHandle, EventError(ex.what()),
event->worker, event->settings, event->connectionType));
// In case of error in connection, we should cleanup SshTunnelWorker
stopAndDelete();
@@ -134,28 +87,50 @@ namespace Robomongo
}
void SshTunnelWorker::handle(ListenSshConnectionRequest *event) {
// do it here, i.e. cycle
try {
if (_isQuiting)
return;
if (_sshSession == NULL || _sshConfig == NULL)
if (_sshSession == NULL)
return;
// We are running this timer in order to distinguish between two
// types of errors:
// 1) SSH tunnel wasn't successfully created
// 2) SSH tunnel was disconnected
// This is used now only for UI error message, that we show to user.
QElapsedTimer timer;
timer.start();
// This function will block until all TCP connections disconnects.
// Initially, it will wait for at least one such connection.
if (rbm_ssh_open_tunnel(_sshSession) != 0) {
qint64 elapsed = timer.elapsed();
bool wasDisconnected = elapsed > 20000; // More than 20 seconds passed
// Prepare copy of error message (if any)
std::string error(_sshSession->lasterror);
std::stringstream ss;
ss << "You are disconnected from SSH tunnel ("
<< _settings->sshSettings()->host() << ":"
<< _settings->sshSettings()->port() << "). "
<< "Please initiate a new connection and reopen all tabs.\n\nError:\n"
<< error;
if (wasDisconnected) {
ss << "You are disconnected from SSH tunnel ("
<< _settings->sshSettings()->host() << ":"
<< _settings->sshSettings()->port() << "). "
<< "Please initiate a new connection and reopen all tabs.\n\nError:\n"
<< error;
} else {
ss << "Cannot establish SSH tunnel ("
<< _settings->sshSettings()->host() << ":"
<< _settings->sshSettings()->port() << "). "
<< "\n\nError:\n"
<< error;
}
// Cleanup session
rbm_ssh_session_close(_sshSession);
_sshSession = NULL;
throw std::runtime_error(ss.str());
}
@@ -164,7 +139,7 @@ namespace Robomongo
} catch (const std::exception& ex) {
reply(event->sender(),
new ListenSshConnectionResponse(this, EventError(ex.what()), _settings));
new ListenSshConnectionResponse(this, EventError(ex.what()), event->serverHandle, _settings, event->connectionType));
}
// When we done with SSH (i.e. rbm_ssh_open_tunnel exits) regardless
@@ -195,4 +170,60 @@ namespace Robomongo
void SshTunnelWorker::logCallbackHandler(void *context, char *message, int level) {
static_cast<SshTunnelWorker*>(context)->log(message, level);
}
/*
* SshTunnelConfigCreator
*/
SshTunnelConfigCreator::SshTunnelConfigCreator(ConnectionSettings *settings) {
SshSettings *ssh = settings->sshSettings();
// We are going to access raw char* buffers, so prepare copies
// of strings (and rest of the things for symmetry)
_sshhost = ssh->host();
_sshport = ssh->port();
_remotehost = settings->serverHost();
_remoteport = settings->serverPort();
_localip = "127.0.0.1";
_localport = 27040;
_userName = ssh->userName();
_userPassword = ssh->userPassword();
_privateKeyFile = ssh->privateKeyFile();
_publicKeyFile = ssh->publicKeyFile();
_passphrase = ssh->passphrase();
_authMethod = ssh->authMethod(); // "password" or "publickey"
// Use "askedPassword" for both passphrase and password if required
if (ssh->askPassword()) {
_passphrase = ssh->askedPassword();
_userPassword = ssh->askedPassword();
}
_sshConfig = new rbm_ssh_tunnel_config;
_sshConfig->sshserverhost = const_cast<char*>(_sshhost.c_str());
_sshConfig->sshserverport = static_cast<unsigned int>(_sshport);
_sshConfig->remotehost = const_cast<char*>(_remotehost.c_str());
_sshConfig->remoteport = static_cast<unsigned int>(_remoteport);
_sshConfig->localip = const_cast<char*>(_localip.c_str());
_sshConfig->localport = static_cast<unsigned int>(_localport);
_sshConfig->username = const_cast<char*>(_userName.c_str());
_sshConfig->password = const_cast<char*>(_userPassword.c_str());
_sshConfig->privatekeyfile = const_cast<char*>(_privateKeyFile.c_str());
_sshConfig->publickeyfile = _publicKeyFile.empty() ? NULL : const_cast<char*>(_publicKeyFile.c_str());
_sshConfig->passphrase = const_cast<char*>(_passphrase.c_str());
_sshConfig->authtype = (_authMethod == "publickey") ? RBM_SSH_AUTH_TYPE_PUBLICKEY : RBM_SSH_AUTH_TYPE_PASSWORD;
// Default settings
_sshConfig->logcontext = NULL;
_sshConfig->logcallback = NULL;
_sshConfig->loglevel = RBM_SSH_LOG_TYPE_ERROR;
}
SshTunnelConfigCreator::~SshTunnelConfigCreator() {
delete _sshConfig;
}
}
+31 -15
View File
@@ -1,6 +1,7 @@
#pragma once
#include <QObject>
#include <robomongo/ssh/ssh.h>
#include "robomongo/core/events/MongoEvents.h"
@@ -17,6 +18,34 @@ namespace Robomongo
class EstablishSshConnectionRequest;
class ListenSshConnectionRequest;
/*
* Helper class for creating rbm_ssh_tunnel_config from ConnectionSettings
* Automatically deletes allocated structure
*/
class SshTunnelConfigCreator {
public:
explicit SshTunnelConfigCreator(ConnectionSettings *settings);
~SshTunnelConfigCreator();
rbm_ssh_tunnel_config *config() { return _sshConfig; }
private:
std::string _sshhost;
int _sshport;
std::string _remotehost;
int _remoteport;
std::string _localip;
int _localport;
std::string _userName;
std::string _userPassword;
std::string _privateKeyFile;
std::string _publicKeyFile;
std::string _passphrase;
std::string _authMethod; // "password" or "publickey"
rbm_ssh_tunnel_config* _sshConfig;
};
class SshTunnelWorker : public QObject
{
Q_OBJECT
@@ -31,7 +60,6 @@ namespace Robomongo
void stopAndDelete();
protected Q_SLOTS: // handlers:
void init();
void handle(EstablishSshConnectionRequest *event);
void handle(ListenSshConnectionRequest *event);
@@ -42,21 +70,9 @@ namespace Robomongo
QThread *_thread;
QAtomicInteger<int> _isQuiting;
ConnectionSettings* _settings;
rbm_ssh_tunnel_config* _sshConfig;
rbm_ssh_session* _sshSession;
std::string _sshhost;
int _sshport;
std::string _remotehost;
int _remoteport;
std::string _localip;
int _localport;
std::string _userName;
std::string _userPassword;
std::string _privateKeyFile;
std::string _publicKeyFile;
std::string _passphrase;
std::string _authMethod; // "password" or "publickey"
SshTunnelConfigCreator _configCreator;
};
}
@@ -3,7 +3,9 @@
#include <stdio.h>
#include "robomongo/core/settings/CredentialSettings.h"
#include "robomongo/core/settings/ReplicaSetSettings.h"
#include "robomongo/core/settings/SshSettings.h"
#include "robomongo/core/settings/SslSettings.h"
#include "robomongo/core/utils/QtUtils.h"
namespace
@@ -11,6 +13,8 @@ namespace
const unsigned port = 27017;
const char *defaultServerHost = "localhost";
const char *defaultNameConnection = "New Connection";
const int maxLength = 300;
}
namespace Robomongo
@@ -23,16 +27,22 @@ namespace Robomongo
_connectionName(defaultNameConnection),
_host(defaultServerHost),
_port(port),
_sshSettings(new SshSettings()) { }
_imported(false),
_sshSettings(new SshSettings()),
_sslSettings(new SslSettings()),
_replicaSetSettings(new ReplicaSetSettings()),
_isReplicaSet(false)
{ }
void ConnectionSettings::fromVariant(const QVariantMap &map) {
setConnectionName(QtUtils::toStdString(map.value("connectionName").toString()));
setServerHost(QtUtils::toStdString(map.value("serverHost").toString()));
setServerHost(QtUtils::toStdString(map.value("serverHost").toString().left(maxLength)));
setServerPort(map.value("serverPort").toInt());
setDefaultDatabase(QtUtils::toStdString(map.value("defaultDatabase").toString()));
setReplicaSet(map.value("isReplicaSet").toBool());
QVariantList list = map.value("credentials").toList();
for(QVariantList::const_iterator it = list.begin(); it != list.end(); ++it) {
for (QVariantList::const_iterator it = list.begin(); it != list.end(); ++it) {
QVariant var = *it;
CredentialSettings *credential = new CredentialSettings(var.toMap());
addCredential(credential);
@@ -42,6 +52,14 @@ namespace Robomongo
_sshSettings->fromVariant(map.value("ssh").toMap());
}
if (map.contains("ssl")) {
_sslSettings->fromVariant(map.value("ssl").toMap());
}
if (isReplicaSet()) {
_replicaSetSettings->fromVariant(map.value("replicaSet").toMap());
}
//#ifdef MONGO_SSL
// ,SSLInfo(map.value("sslEnabled").toBool(),QtUtils::toStdString(map.value("sslPemKeyFile").toString()))
//#endif
@@ -54,6 +72,8 @@ namespace Robomongo
{
clearCredentials();
delete _sshSettings;
delete _sslSettings;
delete _replicaSetSettings; // todo: unique_ptr
}
/**
@@ -75,6 +95,8 @@ namespace Robomongo
setServerHost(source->serverHost());
setServerPort(source->serverPort());
setDefaultDatabase(source->defaultDatabase());
setImported(source->imported());
setReplicaSet(source->isReplicaSet());
clearCredentials();
QList<CredentialSettings *> cred = source->credentials();
@@ -82,8 +104,9 @@ namespace Robomongo
addCredential((*it)->clone());
}
// Clone SSH settings
_sshSettings = source->sshSettings()->clone();
_sslSettings = source->sslSettings()->clone();
_replicaSetSettings = source->_replicaSetSettings->clone(); // todo: leak candidate
//#ifdef MONGO_SSL
// setSslInfo(source->sslInfo());
@@ -100,19 +123,25 @@ namespace Robomongo
map.insert("serverHost", QtUtils::toQString(serverHost()));
map.insert("serverPort", serverPort());
map.insert("defaultDatabase", QtUtils::toQString(defaultDatabase()));
map.insert("isReplicaSet", isReplicaSet());
if (isReplicaSet()) {
map.insert("replicaSet", _replicaSetSettings->toVariant());
}
#ifdef MONGO_SSL
SSLInfo infl = _info.sslInfo();
map.insert("sslEnabled", infl._sslSupport);
map.insert("sslPemKeyFile", QtUtils::toQString(infl._sslPEMKeyFile));
#endif
QVariantList list;
for(QList<CredentialSettings *>::const_iterator it = _credentials.begin(); it != _credentials.end(); ++it) {
for (QList<CredentialSettings *>::const_iterator it = _credentials.begin(); it != _credentials.end(); ++it) {
CredentialSettings *credential = *it;
list.append(credential->toVariant());
}
map.insert("credentials", list);
map.insert("ssh", _sshSettings->toVariant());
map.insert("ssl", _sslSettings->toVariant());
return map;
}
@@ -120,7 +149,7 @@ namespace Robomongo
CredentialSettings *ConnectionSettings::findCredential(const std::string &databaseName) const
{
CredentialSettings *result = NULL;
for(QList<CredentialSettings *>::const_iterator it = _credentials.begin(); it != _credentials.end(); ++it) {
for (QList<CredentialSettings *>::const_iterator it = _credentials.begin(); it != _credentials.end(); ++it) {
CredentialSettings *cred = *it;
if (cred->databaseName() == databaseName) {
result = cred;
@@ -152,12 +181,13 @@ namespace Robomongo
}
/**
* @brief Returns primary credential, or NULL if no credentials exists.
* @brief Returns primary credential
*/
CredentialSettings *ConnectionSettings::primaryCredential()
{
if (_credentials.count() == 0)
return NULL;
if (_credentials.count() == 0) {
_credentials.append(new CredentialSettings());
}
return _credentials.at(0);
}
@@ -10,6 +10,9 @@ namespace Robomongo
{
class CredentialSettings;
class SshSettings;
class SslSettings;
class ReplicaSetSettings;
/**
* @brief Represents connection record
*/
@@ -70,6 +73,12 @@ namespace Robomongo
std::string defaultDatabase() const { return _defaultDatabase; }
void setDefaultDatabase(const std::string &defaultDatabase) { _defaultDatabase = defaultDatabase; }
/**
* Was this connection imported from somewhere?
*/
bool imported() const { return _imported; }
void setImported(bool imported) { _imported = imported; }
/**
* @brief Adds credential to this connection
*/
@@ -87,7 +96,7 @@ namespace Robomongo
bool hasEnabledPrimaryCredential();
/**
* @brief Returns primary credential, or NULL if no credentials exists.
* @brief Returns primary credential
*/
CredentialSettings *primaryCredential();
@@ -143,15 +152,26 @@ namespace Robomongo
}
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; }
private:
CredentialSettings *findCredential(const std::string &databaseName) const;
bool _isReplicaSet;
std::string _connectionName;
std::string _host;
int _port;
std::string _defaultDatabase;
QList<CredentialSettings *> _credentials;
SshSettings *_sshSettings;
SslSettings *_sslSettings;
ReplicaSetSettings *_replicaSetSettings;
// Was this connection imported from somewhere?
bool _imported;
};
}
@@ -57,7 +57,7 @@ namespace Robomongo
std::string _userName;
std::string _userPassword;
std::string _databaseName;
std::string _mechanism; // authentication mechanism
std::string _mechanism; // authentication mechanism (MONGODB-CR or SCRAM-SHA-1)
/**
* @brief Flag, indecating whether we should use this
@@ -0,0 +1,58 @@
#include "robomongo/core/settings/ReplicaSetSettings.h"
#include "robomongo/core/utils/QtUtils.h"
namespace Robomongo
{
ReplicaSetSettings::ReplicaSetSettings()
: _members(), _readPreference(ReadPreference::PRIMARY)
{}
ReplicaSetSettings *ReplicaSetSettings::clone() const
{
ReplicaSetSettings *cloned = new ReplicaSetSettings(*this);
return cloned;
}
QVariant ReplicaSetSettings::toVariant() const
{
QVariantMap map;
int idx = 0;
for (std::string const& str : _members) {
map.insert(QString::number(idx), QtUtils::toQString(str));
++idx;
}
map.insert("readPreference", static_cast<int>(readPreference()));
return map;
}
void ReplicaSetSettings::fromVariant(const QVariantMap &map)
{
// Extract and set replica members
std::vector<std::string> vec;
auto& itr = map.begin();
int idx = 0;
do
{
itr = map.find(QString::number(idx));
if (map.end() == itr) break;
vec.push_back(itr->toString().toStdString());
++idx;
} while (map.end() != itr);
setMembers(vec);
// Extract and set read reference
setReadPreference(static_cast<ReadPreference>(map.value("readPreference").toInt()));
}
const std::vector<mongo::HostAndPort> ReplicaSetSettings::membersToHostAndPort() const
{
std::vector<mongo::HostAndPort> membersHostAndPort;
for (auto const& member : _members)
{
membersHostAndPort.push_back(mongo::HostAndPort(member));
}
return membersHostAndPort;
}
}
@@ -0,0 +1,51 @@
#pragma once
#include <QString>
#include <QVariant>
#include <QVariantMap>
#include <mongo/util/net/hostandport.h>
namespace Robomongo
{
class ReplicaSetSettings
{
public:
enum class ReadPreference
{
PRIMARY = 0,
PRIMARY_PREFERRED = 1
};
ReplicaSetSettings();
/**
* Clones Replica Set settings.
*/
ReplicaSetSettings* clone() const;
/**
* Converts to QVariantMap
*/
QVariant toVariant() const;
void fromVariant(const QVariantMap &map);
//// todo: remove
//void addMember(const std::string& str) { _members.push_back(str); }
// Getters
//
const std::vector<std::string>& members() const { return _members; }
const std::vector<mongo::HostAndPort> membersToHostAndPort() const;
ReadPreference readPreference() const { return _readPreference; }
// Setters
//
void setMembers(const std::vector<std::string>& members) { _members = members; }
void setReadPreference(ReadPreference readPreference) { _readPreference = readPreference; }
private:
std::vector<std::string> _members;
ReadPreference _readPreference;
};
}
+153 -6
View File
@@ -7,7 +7,11 @@
#include <serializer.h>
#include "robomongo/core/settings/ConnectionSettings.h"
#include "robomongo/core/settings/CredentialSettings.h"
#include "robomongo/core/settings/SshSettings.h"
#include "robomongo/core/settings/SslSettings.h"
#include "robomongo/core/utils/Logger.h"
#include "robomongo/core/utils/QtUtils.h"
#include "robomongo/core/utils/StdUtils.h"
#include "robomongo/gui/AppStyle.h"
@@ -48,7 +52,10 @@ namespace Robomongo
_textFontFamily(""),
_textFontPointSize(-1),
_lineNumbers(false),
_loadMongoRcJs(false)
_loadMongoRcJs(false),
_imported(false),
_mongoTimeoutSec(10),
_shellTimeoutSec(15)
{
load();
LOG_MSG("SettingsManager initialized in " + _configPath, mongo::logger::LogSeverity::Info(), false);
@@ -56,7 +63,7 @@ namespace Robomongo
SettingsManager::~SettingsManager()
{
std::for_each(_connections.begin() ,_connections.end(), stdutils::default_delete<ConnectionSettings *>());
std::for_each(_connections.begin(), _connections.end(), stdutils::default_delete<ConnectionSettings *>());
}
/**
@@ -147,6 +154,9 @@ namespace Robomongo
_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)
@@ -179,6 +189,14 @@ namespace Robomongo
_textFontFamily = map.value("textFontFamily").toString();
_textFontPointSize = map.value("textFontPointSize").toInt();
if (map.contains("mongoTimeoutSec")) {
_mongoTimeoutSec = map.value("mongoTimeoutSec").toInt();
}
if (map.contains("shellTimeoutSec")) {
_shellTimeoutSec = map.value("shellTimeoutSec").toInt();
}
// 5. Load connections
_connections.clear();
@@ -186,7 +204,7 @@ namespace Robomongo
for (QVariantList::iterator it = list.begin(); it != list.end(); ++it) {
ConnectionSettings *record = new ConnectionSettings();
record->fromVariant((*it).toMap());
_connections.push_back(record);
addConnection(record);
}
_toolbars = map.value("toolbars").toMap();
@@ -205,6 +223,10 @@ namespace Robomongo
it = _toolbars.find("logs");
if (_toolbars.end() == it)
_toolbars["logs"] = false;
// Load connection settings from previous
// versions of Robomongo
importPreviousConnections();
}
/**
@@ -239,6 +261,8 @@ namespace Robomongo
// 8. Save batchSize
map.insert("batchSize", _batchSize);
map.insert("mongoTimeoutSec", _mongoTimeoutSec);
map.insert("shellTimeoutSec", _shellTimeoutSec);
// 9. Save style
map.insert("style", _currentStyle);
@@ -250,7 +274,7 @@ namespace Robomongo
// 11. Save connections
QVariantList list;
for (ConnectionSettingsContainerType::const_iterator it = _connections.begin(); it!=_connections.end(); ++it) {
for (ConnectionSettingsContainerType::const_iterator it = _connections.begin(); it != _connections.end(); ++it) {
QVariantMap rm = (*it)->toVariant().toMap();
list.append(rm);
}
@@ -261,6 +285,8 @@ namespace Robomongo
map.insert("toolbars", _toolbars);
map.insert("imported", _imported);
return map;
}
@@ -277,8 +303,8 @@ namespace Robomongo
*/
void SettingsManager::removeConnection(ConnectionSettings *connection)
{
ConnectionSettingsContainerType::iterator it = std::find(_connections.begin(),_connections.end(),connection);
if (it!=_connections.end()) {
ConnectionSettingsContainerType::iterator it = std::find(_connections.begin(), _connections.end(), connection);
if (it != _connections.end()) {
_connections.erase(it);
delete connection;
}
@@ -308,4 +334,125 @@ namespace Robomongo
_toolbars[toolbarName] = visible;
}
void SettingsManager::importPreviousConnections() {
// Skip when settings already imported
if (_imported)
return;
// Load old configuration file (used till version 0.8.5)
const QString oldConfigPath = QString("%1/.config/robomongo/robomongo.json").arg(QDir::homePath());
if (!QFile::exists(oldConfigPath))
return;
QFile f(oldConfigPath);
if (!f.open(QIODevice::ReadOnly))
return;
bool ok;
QJson::Parser parser;
QVariantMap vmap = parser.parse(f.readAll(), &ok).toMap();
if (!ok)
return;
QVariantList vconns = vmap.value("connections").toList();
for (QVariantList::iterator itconn = vconns.begin(); itconn != vconns.end(); ++itconn) {
QVariantMap vconn = (*itconn).toMap();
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)));
conn->setServerPort(vconn.value("serverPort").toInt());
conn->setDefaultDatabase(QtUtils::toStdString(vconn.value("defaultDatabase").toString()));
// SSH settings
if (vconn.contains("sshAuthMethod")) {
SshSettings *ssh = conn->sshSettings();
ssh->setHost(QtUtils::toStdString(vconn.value("sshHost").toString()));
ssh->setUserName(QtUtils::toStdString(vconn.value("sshUserName").toString()));
ssh->setPort(vconn.value("sshPort").toInt());
ssh->setUserPassword(QtUtils::toStdString(vconn.value("sshUserPassword").toString()));
ssh->setPublicKeyFile(QtUtils::toStdString(vconn.value("sshPublicKey").toString()));
ssh->setPrivateKeyFile(QtUtils::toStdString(vconn.value("sshPrivateKey").toString()));
ssh->setPassphrase(QtUtils::toStdString(vconn.value("sshPassphrase").toString()));
int auth = vconn.value("sshAuthMethod").toInt();
ssh->setEnabled(auth == 1 || auth == 2);
ssh->setAuthMethod(auth == 2 ? "publickey" : "password");
}
// SSL settings
if (vconn.contains("sshEnabled")) {
SslSettings *ssl = conn->sslSettings();
ssl->enableSSL(vconn.value("enabled").toBool());
ssl->setPemKeyFile(QtUtils::toStdString(vconn.value("sslPemKeyFile").toString()));
}
// Credentials
QVariantList vcreds = vconn.value("credentials").toList();
for (QVariantList::const_iterator itcred = vcreds.begin(); itcred != vcreds.end(); ++itcred) {
QVariantMap vcred = (*itcred).toMap();
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()));
cred->setMechanism("MONGODB-CR");
cred->setEnabled(vcred.value("enabled").toBool());
conn->addCredential(cred);
}
// Check that we didn't have similar connection
bool matched = false;
for (std::vector<ConnectionSettings*>::const_iterator it = _connections.begin(); it != _connections.end(); ++it) {
ConnectionSettings *econn = *it; // Existing connection
if (conn->serverPort() != econn->serverPort() ||
conn->serverHost() != econn->serverHost() ||
conn->defaultDatabase() != econn->defaultDatabase())
continue;
CredentialSettings *cred = conn->primaryCredential();
CredentialSettings *ecred = econn->primaryCredential();
if (cred->databaseName() != ecred->databaseName() ||
cred->userName() != ecred->userName() ||
cred->userPassword() != ecred->userPassword() ||
cred->enabled() != ecred->enabled())
continue;
SshSettings *ssh = conn->sshSettings();
SshSettings *essh = econn->sshSettings();
if (ssh->enabled() != essh->enabled() ||
ssh->port() != essh->port() ||
ssh->host() != essh->host() ||
ssh->privateKeyFile() != essh->privateKeyFile() ||
ssh->userPassword() != essh->userPassword() ||
ssh->userName() != essh->userName())
continue;
matched = true;
break;
}
// Import connection only if we didn't find similar one
if (!matched)
addConnection(conn);
}
// Mark as imported
setImported(true);
}
int SettingsManager::importedConnectionsCount() {
int count = 0;
for (std::vector<ConnectionSettings*>::const_iterator it = _connections.begin(); it != _connections.end(); ++it) {
ConnectionSettings *conn = *it;
if (conn->imported())
++count;
}
return count;
}
}
+28 -10
View File
@@ -72,7 +72,7 @@ namespace Robomongo
UUIDEncoding uuidEncoding() const { return _uuidEncoding; }
void setTimeZone(SupportedTimes timeZ) { _timeZone = timeZ; }
SupportedTimes timeZone() const { return _timeZone;}
SupportedTimes timeZone() const { return _timeZone; }
void setViewMode(ViewMode viewMode) { _viewMode = viewMode; }
ViewMode viewMode() const { return _viewMode; }
@@ -98,7 +98,7 @@ namespace Robomongo
void setBatchSize(int batchSize) { _batchSize = batchSize; }
int batchSize() const { return _batchSize; }
QString currentStyle() const {return _currentStyle; }
QString currentStyle() const { return _currentStyle; }
void setCurrentStyle(const QString& style);
QString textFontFamily() const { return _textFontFamily; }
@@ -107,32 +107,42 @@ namespace Robomongo
int textFontPointSize() const { return _textFontPointSize; }
void setTextFontPointSize(int pointSize);
int mongoTimeoutSec() const { return _mongoTimeoutSec; }
int shellTimeoutSec() const { return _shellTimeoutSec; }
// True when settings from previous versions of
// Robomongo are imported
void setImported(bool imported) { _imported = imported; }
bool imported() const { return _imported; }
/**
* Returns number of imported connections
*/
int importedConnectionsCount();
private:
/**
* @brief Load settings from the map. Existings settings will be overwritten.
* Load settings from the map. Existing settings will be overwritten.
*/
void loadFromMap(QVariantMap &map);
/**
* @brief Save all settings to map.
* Save all settings to map.
*/
QVariantMap convertToMap() const;
/**
* Load connection settings from previous versions of Robomongo
*/
void importPreviousConnections();
/**
* @brief Version of settings schema currently loaded
*/
QString _version;
/**
* @brief UUID encoding
*/
UUIDEncoding _uuidEncoding;
SupportedTimes _timeZone;
/**
* @brief view mode
*/
ViewMode _viewMode;
AutocompletionMode _autocompletionMode;
bool _loadMongoRcJs;
@@ -144,6 +154,14 @@ namespace Robomongo
QString _currentStyle;
QString _textFontFamily;
int _textFontPointSize;
int _mongoTimeoutSec;
int _shellTimeoutSec;
// True when settings from previous versions of
// Robomongo are imported
bool _imported;
/**
* @brief List of connections
*/
+5 -1
View File
@@ -7,7 +7,9 @@ namespace Robomongo
SshSettings::SshSettings() :
_port(22),
_authMethod("publickey"),
_enabled(false) {
_enabled(false),
_askPassword(false),
_logLevel(1) {
}
@@ -27,6 +29,7 @@ namespace Robomongo
map.insert("passphrase", QtUtils::toQString(passphrase()));
map.insert("method", QtUtils::toQString(authMethod()));
map.insert("enabled", enabled());
map.insert("askPassword", askPassword());
return map;
}
@@ -40,5 +43,6 @@ namespace Robomongo
setPassphrase(QtUtils::toStdString(map.value("passphrase").toString()));
setAuthMethod(QtUtils::toStdString(map.value("method").toString()));
setEnabled(map.value("enabled").toBool());
setAskPassword(map.value("askPassword").toBool());
}
}
+17 -2
View File
@@ -22,6 +22,9 @@ namespace Robomongo
QVariant toVariant() const;
void fromVariant(const QVariantMap &map);
/*
* Domain or IPv4 or IPv6 address
*/
std::string host() const { return _host; }
void setHost(const std::string &host) { _host = host; }
@@ -43,6 +46,7 @@ namespace Robomongo
std::string passphrase() const { return _passphrase; }
void setPassphrase(const std::string &passphrase) { _passphrase = passphrase; }
// "password" or "publickey"
std::string authMethod() const { return _authMethod.empty() ? "publickey" : _authMethod; }
void setAuthMethod(const std::string &method) { _authMethod = method; }
@@ -53,11 +57,17 @@ namespace Robomongo
bool enabled() const { return _enabled; }
void setEnabled(bool enabled) { _enabled = enabled; }
bool askPassword() const { return _askPassword; }
void setAskPassword(bool ask) { _askPassword = ask; }
std::string askedPassword() const { return _askedPassword; }
void setAskedPassword(const std::string &asked) { _askedPassword = asked; }
int logLevel() const { return _logLevel; }
void setLogLevel(const int logLevel) { _logLevel = logLevel; }
private:
std::string _host;
std::string _host; // domain or IPv4/v6
int _port;
std::string _userName;
std::string _userPassword;
@@ -66,11 +76,16 @@ namespace Robomongo
std::string _passphrase;
std::string _authMethod; // "password" or "publickey"
// Should we ask user about password or passphrase
// each time when we try to connect
bool _askPassword;
/**
* Flag, indicating whether we should use
* this SSH tunnel or not.
*/
bool _enabled;
int _logLevel; // this property is not persisted
int _logLevel; // this property is not persisted
std::string _askedPassword; // this property is not persisted
};
}
@@ -0,0 +1,49 @@
#include "robomongo/core/settings/SslSettings.h"
#include "robomongo/core/utils/QtUtils.h"
namespace Robomongo
{
SslSettings::SslSettings() :
_sslEnabled(false), _caFile(""), _pemKeyFile(""), _pemPassPhrase(""), _crlFile(""),
_allowInvalidHostnames(false), _allowInvalidCertificates(false), _usePemFile(false),
_useAdvancedOptions(false), _askPassphrase(false)
{}
SslSettings *SslSettings::clone() const
{
SslSettings *cloned = new SslSettings(*this);
return cloned;
}
QVariant SslSettings::toVariant() const
{
QVariantMap map;
map.insert("sslEnabled", sslEnabled());
map.insert("caFile", QtUtils::toQString(caFile()));
map.insert("usePemFile", usePemFile());
map.insert("pemKeyFile", QtUtils::toQString(pemKeyFile()));
map.insert("pemPassPhrase", QtUtils::toQString(pemPassPhrase()));
map.insert("useAdvancedOptions", useAdvancedOptions());
map.insert("crlFile", QtUtils::toQString(crlFile()));
map.insert("allowInvalidHostnames", allowInvalidHostnames());
map.insert("allowInvalidCertificates", allowInvalidCertificates());
map.insert("askPassphrase", askPassphrase());
return map;
}
void SslSettings::fromVariant(const QVariantMap &map)
{
enableSSL(map.value("sslEnabled").toBool());
setCaFile(QtUtils::toStdString(map.value("caFile").toString()));
setUsePemFile(map.value("usePemFile").toBool());
setPemKeyFile(QtUtils::toStdString(map.value("pemKeyFile").toString()));
setPemPassPhrase(QtUtils::toStdString(map.value("pemPassPhrase").toString()));
setUseAdvancedOptions(map.value("useAdvancedOptions").toBool());
setCrlFile(QtUtils::toStdString(map.value("crlFile").toString()));
setAllowInvalidHostnames(map.value("allowInvalidHostnames").toBool());
setAllowInvalidCertificates(map.value("allowInvalidCertificates").toBool());
setAskPassphrase(map.value("askPassphrase").toBool());
}
}
+79
View File
@@ -0,0 +1,79 @@
#pragma once
#include <QString>
#include <QVariant>
#include <QVariantMap>
namespace Robomongo
{
class SslSettings
{
public:
SslSettings();
/**
* Clones credential settings.
*/
SslSettings *clone() const;
/**
* Converts to QVariantMap
*/
QVariant toVariant() const;
void fromVariant(const QVariantMap &map);
// Getters for mongo:: SSLGlobalParams related settings
std::string pemKeyFile() const { return _pemKeyFile; }
std::string caFile() const { return _caFile; }
std::string pemPassPhrase() const { return _pemPassPhrase; }
std::string crlFile() const { return _crlFile; }
bool allowInvalidHostnames() const { return _allowInvalidHostnames; }
bool allowInvalidCertificates() const { return _allowInvalidCertificates; }
// Getters for helper SSL settings
bool usePemFile() const { return _usePemFile; }
bool useAdvancedOptions() const { return _useAdvancedOptions; }
bool askPassphrase() const { return _askPassphrase; }
// Setters for mongo:: SSLGlobalParams related settings
void setPemKeyFile(const std::string &path) { _pemKeyFile = path; }
void setCaFile(const std::string &path) { _caFile = path; }
void setPemPassPhrase(const std::string &pemPassPhrase) { _pemPassPhrase = pemPassPhrase; }
void setCrlFile(const std::string &path) { _crlFile = path; }
void setAllowInvalidHostnames(const bool state) { _allowInvalidHostnames = state; }
void setAllowInvalidCertificates(const bool state) { _allowInvalidCertificates = state; }
// Setters for helper SSL settings
void setUsePemFile(const bool state) { _usePemFile = state; }
void setUseAdvancedOptions(const bool state) { _useAdvancedOptions = state; }
void setAskPassphrase(const bool state) { _askPassphrase = state; }
/**
* Flag, indicating whether we should use
* this SSL settings or not.
*/
bool sslEnabled() const { return _sslEnabled; }
void enableSSL(bool enabled) { _sslEnabled = enabled; }
private:
// mongo:: SSL Global params related settings
std::string _caFile;
std::string _pemKeyFile;
std::string _pemPassPhrase;
std::string _crlFile;
bool _allowInvalidHostnames;
bool _allowInvalidCertificates;
// Helper settings indirectly effecting what to pass to SSL global params
bool _usePemFile;
bool _useAdvancedOptions;
bool _askPassphrase;
/**
* Flag, indicating whether SSL enabled for related connection
*/
bool _sslEnabled;
};
}
+36 -25
View File
@@ -80,23 +80,23 @@ namespace Robomongo
s << (isArray ? "[" : "{");
BSONObjIterator i(obj);
BSONElement e = i.next();
if ( !e.eoo() ){
if ( !e.eoo() ) {
while ( 1 ) {
if ( pretty ) {
s << '\n';
for( int x = 0; x < pretty; x++ ){
for( int x = 0; x < pretty; x++ ) {
s << " ";
}
}
else {
s << " ";
}
s << jsonString(e, format, true, pretty?pretty+1:0, uuidEncoding, timeFormat, isArray);
s << jsonString(e, format, true, pretty ? pretty + 1 : 0, uuidEncoding, timeFormat, isArray);
e = i.next();
if (e.eoo()) {
s << '\n';
for( int x = 0; x < pretty - 1; x++ ){
for( int x = 0; x < pretty - 1; x++ ) {
s << " ";
}
s << (isArray ? "]" : "}");
@@ -130,15 +130,21 @@ namespace Robomongo
s << "NumberLong(" << elem._numberLong() << ")";
break;
case NumberInt:
s << elem._numberInt();
break;
case NumberDouble:
{
int sign=0;
int sign = 0;
if ( elem.number() >= -std::numeric_limits< double >::max() &&
elem.number() <= std::numeric_limits< double >::max() ) {
// was 16
s.precision(std::numeric_limits<double>::digits10);
s << elem.number();
// Leave trailing zero if needed
if (elem.number() == (long long)elem.number())
s << ".0";
}
else if (std::isnan(elem.number()) ) {
s << "NaN";
@@ -166,7 +172,7 @@ namespace Robomongo
break;
case Object: {
BSONObj obj = elem.embeddedObject();
s << jsonString(obj, format, pretty, uuidEncoding,timeFormat);
s << jsonString(obj, format, pretty, uuidEncoding, timeFormat);
}
break;
case mongo::Array: {
@@ -180,7 +186,7 @@ namespace Robomongo
if ( !e.eoo() ) {
int count = 0;
while ( 1 ) {
if( pretty ) {
if ( pretty ) {
s << '\n';
for( int x = 0; x < pretty; x++ )
s << " ";
@@ -190,7 +196,7 @@ namespace Robomongo
s << "undefined";
}
else {
s << jsonString(e, format, false, pretty?pretty+1:0, uuidEncoding, timeFormat, true);
s << jsonString(e, format, false, pretty ? pretty + 1 : 0, uuidEncoding, timeFormat, true);
e = i.next();
}
count++;
@@ -260,13 +266,13 @@ namespace Robomongo
case mongo::Date:
{
Date_t d = elem.date();
long long ms = d.toMillisSinceEpoch();//static_cast<long long>(d.millis);
long long ms = d.toMillisSinceEpoch(); //static_cast<long long>(d.millis);
bool isSupportedDate = miutil::minDate < ms && ms < miutil::maxDate;
if ( format == Strict )
s << "{ \"$date\" : ";
else{
if(isSupportedDate){
if (isSupportedDate) {
s << "ISODate(";
}
else{
@@ -275,7 +281,7 @@ namespace Robomongo
}
if ( pretty && isSupportedDate) {
boost::posix_time::ptime epoch(boost::gregorian::date(1970,1,1));
boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1));
boost::posix_time::time_duration diff = boost::posix_time::millisec(ms);
boost::posix_time::ptime time = epoch + diff;
std::string timestr = miutil::isotimeString(time, true, timeFormat == LocalTime);
@@ -563,13 +569,13 @@ namespace Robomongo
con.append(e.fieldName());
con.append("\"");
con.append(" : ");
buildJsonString(e,con,uuid,tz);
buildJsonString(e, con, uuid, tz);
con.append(", \n");
}
con.append("\n}\n\n");
}
void buildJsonString(const mongo::BSONElement &elem,std::string &con, UUIDEncoding uuid, SupportedTimes tz)
void buildJsonString(const mongo::BSONElement &elem, std::string &con, UUIDEncoding uuid, SupportedTimes tz)
{
switch (elem.type())
{
@@ -578,6 +584,11 @@ namespace Robomongo
std::stringstream s;
s.precision(std::numeric_limits<double>::digits10);
s << elem.Double();
// Leave trailing zero if needed
if (elem.Double() == (long long)elem.Double())
s << ".0";
con.append(s.str());
}
break;
@@ -626,13 +637,13 @@ namespace Robomongo
long long ms = (long long) elem.Date().toMillisSinceEpoch();
bool isSupportedDate = miutil::minDate < ms && ms < miutil::maxDate;
boost::posix_time::ptime epoch(boost::gregorian::date(1970,1,1));
boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1));
boost::posix_time::time_duration diff = boost::posix_time::millisec(ms);
boost::posix_time::ptime time = epoch + diff;
std::string date;
if(isSupportedDate)
date = miutil::isotimeString(time,false,tz==LocalTime);
if (isSupportedDate)
date = miutil::isotimeString(time, false, tz == LocalTime);
else
date = boost::lexical_cast<std::string>(ms);
@@ -652,7 +663,7 @@ namespace Robomongo
case 'g':
case 'i':
case 'm':
con+=*f;
con += *f;
default:
break;
}
@@ -678,15 +689,15 @@ namespace Robomongo
break;
case NumberInt:
{
char num[16]={0};
sprintf(num,"%d",elem.Int());
char num[16] = {0};
sprintf(num, "%d", elem.Int());
con.append(num);
break;
}
case bsonTimestamp:
{
Date_t date = elem.timestampTime();
unsigned long long millis = date.toMillisSinceEpoch();// millis;
unsigned long long millis = date.toMillisSinceEpoch(); // millis;
if ((long long)millis >= 0 &&
((long long)millis/1000) < (std::numeric_limits<time_t>::max)()) {
con.append(date.toString());
@@ -695,8 +706,8 @@ namespace Robomongo
}
case NumberLong:
{
char num[32]={0};
sprintf(num,"%lld",elem.Long());
char num[32] = {0};
sprintf(num, "%lld", elem.Long());
con.append(num);
break;
}
@@ -706,13 +717,13 @@ namespace Robomongo
}
}
mongo::BSONElement indexOf(const mongo::BSONObj &doc,int index)
mongo::BSONElement indexOf(const mongo::BSONObj &doc, int index)
{
mongo::BSONObjIterator iterator(doc);
for (int i = 0; iterator.more(); ++i)
{
mongo::BSONElement element = iterator.next();
if(i==index){
if (i == index) {
return element;
}
}
@@ -722,7 +733,7 @@ namespace Robomongo
int elementsCount(const mongo::BSONObj &doc)
{
mongo::BSONObjIterator iterator(doc);
int i=0;
int i = 0;
for (; iterator.more(); ++i)
{
iterator.next();
+2 -2
View File
@@ -100,8 +100,8 @@ namespace Robomongo
const char* BSONTypeToString(mongo::BSONType type, mongo::BinDataType binDataType, UUIDEncoding uuidEncoding);
void buildJsonString(const mongo::BSONObj &obj,std::string &con, UUIDEncoding uuid, SupportedTimes tz);
void buildJsonString(const mongo::BSONElement &elem,std::string &con, UUIDEncoding uuid, SupportedTimes tz);
void buildJsonString(const mongo::BSONObj &obj, std::string &con, UUIDEncoding uuid, SupportedTimes tz);
void buildJsonString(const mongo::BSONElement &elem, std::string &con, UUIDEncoding uuid, SupportedTimes tz);
mongo::BSONElement indexOf(const mongo::BSONObj &doc, int index);
int elementsCount(const mongo::BSONObj &doc);
+1 -1
View File
@@ -38,7 +38,7 @@ namespace Robomongo
void cleanUpThread(QThread *const thread)
{
if (thread&&thread->isRunning()) {
if (thread && thread->isRunning()) {
//thread->stop();
thread->wait();
}
+1 -1
View File
@@ -20,6 +20,6 @@ namespace Robomongo
static const QString StyleName;
virtual void drawControl(ControlElement element, const QStyleOption * option, QPainter * painter, const QWidget * widget) const;
virtual void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const;
virtual QRect subElementRect( SubElement element, const QStyleOption * option, const QWidget * widget=0 ) const;
virtual QRect subElementRect( SubElement element, const QStyleOption * option, const QWidget * widget = 0 ) const;
};
}
+39 -3
View File
@@ -38,6 +38,18 @@ namespace Robomongo
return serverIc;
}
const QIcon &GuiRegistry::serverImportedIcon() const
{
static const QIcon importedIcon = QIcon(":/robomongo/icons/server_imported_16x16.png");
return importedIcon;
}
const QIcon &GuiRegistry::replicaSetIcon() const
{
static const QIcon replicaSetIc = QIcon(":/robomongo/icons/replica_set_16x16.png");
return replicaSetIc;
}
const QIcon &GuiRegistry::openIcon() const
{
static const QIcon openIc = qApp->style()->standardIcon(QStyle::SP_DialogOpenButton);
@@ -223,16 +235,28 @@ namespace Robomongo
const QIcon &GuiRegistry::noMarkIcon() const
{
static const QIcon noMarkIc = QIcon(":/robomongo/icons/no_mark_16x16.png");
static const QIcon noMarkIc = QIcon(":/robomongo/icons/no_mark_24x24.png");
return noMarkIc;
}
const QIcon &GuiRegistry::yesMarkIcon() const
{
static const QIcon yesMarkIc = QIcon(":/robomongo/icons/yes_mark_16x16.png");
static const QIcon yesMarkIc = QIcon(":/robomongo/icons/yes_mark_24x24.png");
return yesMarkIc;
}
const QIcon &GuiRegistry::skipMarkIcon() const
{
static const QIcon skipMarkIc = QIcon(":/robomongo/icons/skip_mark_24x24.png");
return skipMarkIc;
}
const QIcon &GuiRegistry::questionMarkIcon() const
{
static const QIcon questionMarkIc = QIcon(":/robomongo/icons/question_mark_24x24.png");
return questionMarkIc;
}
const QIcon &GuiRegistry::timeIcon() const
{
static const QIcon timeIc = QIcon(":/robomongo/icons/time_16x16.png");
@@ -245,9 +269,21 @@ namespace Robomongo
return keyIc;
}
const QIcon &GuiRegistry::showIcon() const
{
static const QIcon icon = QIcon(":/robomongo/icons/show_64x64.png");
return icon;
}
const QIcon &GuiRegistry::hideIcon() const
{
static const QIcon icon = QIcon(":/robomongo/icons/hide_64x64.png");
return icon;
}
const QBrush &GuiRegistry::typeBrush() const
{
static const QBrush typeBrush = QBrush(QColor(150,150, 150));
static const QBrush typeBrush = QBrush(QColor(150, 150, 150));
return typeBrush;
}
+51 -45
View File
@@ -17,7 +17,7 @@ namespace Robomongo
/**
* @brief Returns single instance of GuiRegistry
*/
static GuiRegistry &instance()
static GuiRegistry& instance()
{
static GuiRegistry _instance;
return _instance;
@@ -28,52 +28,58 @@ namespace Robomongo
/**
* @brief Functions that provide access to various icons
*/
const QIcon &serverIcon() const;
const QIcon &saveIcon() const;
const QIcon &openIcon() const;
const QIcon &databaseIcon() const;
const QIcon &collectionIcon() const;
const QIcon &indexIcon() const;
const QIcon &userIcon() const;
const QIcon &functionIcon() const;
const QIcon &maximizeIcon() const;
const QIcon &maximizeHighlightedIcon() const;
const QIcon &textIcon() const;
const QIcon &textHighlightedIcon() const;
const QIcon &treeIcon() const;
const QIcon &treeHighlightedIcon() const;
const QIcon &tableIcon() const;
const QIcon &tableHighlightedIcon() const;
const QIcon &customIcon() const;
const QIcon &customHighlightedIcon() const;
const QIcon &rotateIcon() const;
const QIcon &visualIcon() const;
const QIcon &folderIcon() const;
const QIcon &circleIcon() const;
const QIcon &leftIcon() const;
const QIcon &rightIcon() const;
const QIcon &mongodbIcon() const;
const QIcon &connectIcon() const;
const QIcon &executeIcon() const;
const QIcon &stopIcon() const;
const QIcon &mainWindowIcon() const;
const QIcon &bsonObjectIcon() const;
const QIcon &bsonArrayIcon() const;
const QIcon &bsonStringIcon() const;
const QIcon &bsonIntegerIcon() const;
const QIcon &bsonDoubleIcon() const;
const QIcon &bsonDateTimeIcon() const;
const QIcon &bsonBinaryIcon() const;
const QIcon &bsonNullIcon() const;
const QIcon &bsonBooleanIcon() const;
const QIcon &noMarkIcon() const;
const QIcon &yesMarkIcon() const;
const QIcon &timeIcon() const;
const QIcon &keyIcon() const;
const QIcon& serverIcon() const;
const QIcon& serverImportedIcon() const;
const QIcon& replicaSetIcon() const;
const QIcon& saveIcon() const;
const QIcon& openIcon() const;
const QIcon& databaseIcon() const;
const QIcon& collectionIcon() const;
const QIcon& indexIcon() const;
const QIcon& userIcon() const;
const QIcon& functionIcon() const;
const QIcon& maximizeIcon() const;
const QIcon& maximizeHighlightedIcon() const;
const QIcon& textIcon() const;
const QIcon& textHighlightedIcon() const;
const QIcon& treeIcon() const;
const QIcon& treeHighlightedIcon() const;
const QIcon& tableIcon() const;
const QIcon& tableHighlightedIcon() const;
const QIcon& customIcon() const;
const QIcon& customHighlightedIcon() const;
const QIcon& rotateIcon() const;
const QIcon& visualIcon() const;
const QIcon& folderIcon() const;
const QIcon& circleIcon() const;
const QIcon& leftIcon() const;
const QIcon& rightIcon() const;
const QIcon& mongodbIcon() const;
const QIcon& connectIcon() const;
const QIcon& executeIcon() const;
const QIcon& stopIcon() const;
const QIcon& mainWindowIcon() const;
const QIcon& bsonObjectIcon() const;
const QIcon& bsonArrayIcon() const;
const QIcon& bsonStringIcon() const;
const QIcon& bsonIntegerIcon() const;
const QIcon& bsonDoubleIcon() const;
const QIcon& bsonDateTimeIcon() const;
const QIcon& bsonBinaryIcon() const;
const QIcon& bsonNullIcon() const;
const QIcon& bsonBooleanIcon() const;
const QIcon& noMarkIcon() const;
const QIcon& yesMarkIcon() const;
const QIcon& skipMarkIcon() const;
const QIcon& questionMarkIcon() const;
const QIcon& timeIcon() const;
const QIcon& keyIcon() const;
const QIcon& showIcon() const;
const QIcon& hideIcon() const;
const QBrush &typeBrush() const;
const QBrush& typeBrush() const;
const QFont &font() const;
const QFont& font() const;
private:
/**
* @brief Private, because this is singleton
+77 -13
View File
@@ -16,6 +16,7 @@
#include <QLabel>
#include <QStatusBar>
#include <QHBoxLayout>
#include <QSettings>
#include <mongo/logger/log_severity.h>
#include "robomongo/core/settings/SettingsManager.h"
@@ -323,17 +324,17 @@ namespace Robomongo
AutocompletionMode autocompletionMode = AppRegistry::instance().settingsManager()->autocompletionMode();
// Autocompletion
QAction *autocompletionAllAction = new QAction("All",this);
QAction *autocompletionAllAction = new QAction("All", this);
autocompletionAllAction->setCheckable(true);
autocompletionAllAction->setChecked(autocompletionMode == AutocompleteAll);
VERIFY(connect(autocompletionAllAction, SIGNAL(triggered()), this, SLOT(setShellAutocompletionAll())));
QAction *autocompletionNoCollectionNamesAction = new QAction("All (Except Collection Names)",this);
QAction *autocompletionNoCollectionNamesAction = new QAction("All (Except Collection Names)", this);
autocompletionNoCollectionNamesAction->setCheckable(true);
autocompletionNoCollectionNamesAction->setChecked(autocompletionMode == AutocompleteNoCollectionNames);
VERIFY(connect(autocompletionNoCollectionNamesAction, SIGNAL(triggered()), this, SLOT(setShellAutocompletionNoCollectionNames())));
QAction *autocompletionNoneAction = new QAction("None",this);
QAction *autocompletionNoneAction = new QAction("None", this);
autocompletionNoneAction->setCheckable(true);
autocompletionNoneAction->setChecked(autocompletionMode == AutocompleteNone);
VERIFY(connect(autocompletionNoneAction, SIGNAL(triggered()), this, SLOT(setShellAutocompletionNone())));
@@ -348,7 +349,7 @@ namespace Robomongo
autocompletionGroup->addAction(autocompletionNoCollectionNamesAction);
autocompletionGroup->addAction(autocompletionNoneAction);
QAction *loadMongoRcJs = new QAction("Load .mongorc.js",this);
QAction *loadMongoRcJs = new QAction("Load .mongorc.js", this);
loadMongoRcJs->setCheckable(true);
loadMongoRcJs->setChecked(AppRegistry::instance().settingsManager()->loadMongoRcJs());
VERIFY(connect(loadMongoRcJs, SIGNAL(triggered()), this, SLOT(setLoadMongoRcJs())));
@@ -369,19 +370,19 @@ namespace Robomongo
VERIFY(connect(showLineNumbers, SIGNAL(triggered()), this, SLOT(toggleLineNumbers())));
optionsMenu->addAction(showLineNumbers);
QAction *disabelConnectionShortcuts = new QAction("Disable Connection Shortcuts",this);
QAction *disabelConnectionShortcuts = new QAction("Disable Connection Shortcuts", this);
disabelConnectionShortcuts->setCheckable(true);
disabelConnectionShortcuts->setChecked(AppRegistry::instance().settingsManager()->disableConnectionShortcuts());
VERIFY(connect(disabelConnectionShortcuts, SIGNAL(triggered()), this, SLOT(setDisableConnectionShortcuts())));
optionsMenu->addAction(disabelConnectionShortcuts);
QAction *autoExec = new QAction(tr("Automatically execute code in new tab"),this);
QAction *autoExec = new QAction(tr("Automatically execute code in new tab"), this);
autoExec->setCheckable(true);
autoExec->setChecked(AppRegistry::instance().settingsManager()->autoExec());
VERIFY(connect(autoExec, SIGNAL(triggered()), this, SLOT(toggleAutoExec())));
optionsMenu->addAction(autoExec);
QAction *preferencesAction = new QAction("Preferences",this);
QAction *preferencesAction = new QAction("Preferences", this);
VERIFY(connect(preferencesAction, SIGNAL(triggered()), this, SLOT(openPreferences())));
preferencesAction->setVisible(false);
optionsMenu->addAction(preferencesAction);
@@ -504,6 +505,9 @@ namespace Robomongo
AppRegistry::instance().bus()->subscribe(this, ScriptExecutedEvent::Type);
AppRegistry::instance().bus()->subscribe(this, ScriptExecutingEvent::Type);
AppRegistry::instance().bus()->subscribe(this, QueryWidgetUpdatedEvent::Type);
AppRegistry::instance().bus()->subscribe(this, OperationFailedEvent::Type);
restoreWindowsSettings();
}
void MainWindow::createStylesMenu()
@@ -516,7 +520,7 @@ namespace Robomongo
const QString &currentStyle = AppRegistry::instance().settingsManager()->currentStyle();
for (QStringList::const_iterator it = supportedStyles.begin(); it != supportedStyles.end(); ++it) {
const QString &style = *it;
QAction *styleAction = new QAction(style,this);
QAction *styleAction = new QAction(style, this);
styleAction->setCheckable(true);
styleAction->setChecked(style == currentStyle);
styleGroup->addAction(styleAction);
@@ -565,6 +569,37 @@ namespace Robomongo
AppRegistry::instance().settingsManager()->save();
}
void MainWindow::restoreWindowsSettings()
{
QSettings settings("Paralect", "Robomongo");
// Restore settings if registery key exists, otherwise resize as app started for the first time.
if (settings.contains("MainWindow/geometry"))
{
restoreGeometry(settings.value("MainWindow/geometry").toByteArray());
}
else
{
// Resize main window. We are trying to keep it "almost" maximized.
QRect screenGeometry = QApplication::desktop()->availableGeometry();
int horizontalMargin = (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));
// Center main window
int x = (screenGeometry.width() - width()) / 2;
int y = (screenGeometry.height() - height()) / 2;
move(x, y);
}
}
void MainWindow::saveWindowsSettings() const
{
QSettings settings("Paralect", "Robomongo");
settings.setValue("MainWindow/geometry", saveGeometry());
}
void MainWindow::open()
{
QueryWidget *wid = _workArea->currentQueryWidget();
@@ -614,7 +649,7 @@ namespace Robomongo
int number = 1;
// Populate list with connections
SettingsManager::ConnectionSettingsContainerType connections = AppRegistry::instance().settingsManager()->connections();
for(SettingsManager::ConnectionSettingsContainerType::const_iterator it = connections.begin(); it!= connections.end(); ++it) {
for (SettingsManager::ConnectionSettingsContainerType::const_iterator it = connections.begin(); it != connections.end(); ++it) {
ConnectionSettings *connection = *it;
QAction *action = new QAction(QtUtils::toQString(connection->getReadableName()), this);
action->setData(QVariant::fromValue(connection));
@@ -641,8 +676,10 @@ namespace Robomongo
void MainWindow::manageConnections()
{
ConnectionsDialog dialog(AppRegistry::instance().settingsManager(), this);
static bool checkForImported = true;
ConnectionsDialog dialog(AppRegistry::instance().settingsManager(), checkForImported, this);
int result = dialog.exec();
checkForImported = false;
// save settings and update connection menu
AppRegistry::instance().settingsManager()->save();
@@ -663,7 +700,7 @@ namespace Robomongo
}
try {
_app->openServer(selected, true);
_app->openServer(selected, ConnectionPrimary);
} catch(const std::exception &) {
QString message = QString("Cannot connect to MongoDB (%1)").arg(QtUtils::toQString(selected->getFullAddress()));
QMessageBox::information(this, "Error", message);
@@ -788,7 +825,7 @@ namespace Robomongo
void MainWindow::refreshConnections()
{
QToolTip::showText(QPoint(0,0),
QToolTip::showText(QPoint(0, 0),
QString("Refresh not working yet... : <br/> <b>Ctrl+D</b> : push Button"));
}
@@ -883,7 +920,7 @@ namespace Robomongo
QVariant data = connectionAction->data();
ConnectionSettings *ptr = data.value<ConnectionSettings *>();
try {
_app->openServer(ptr, true);
_app->openServer(ptr, ConnectionPrimary);
}
catch(const std::exception &) {
QString message = QString("Cannot connect to the MongoDB at %1.")
@@ -894,6 +931,18 @@ namespace Robomongo
void MainWindow::handle(ConnectionFailedEvent *event)
{
// Handle only Primary connections
if (event->connectionType != ConnectionPrimary)
return;
// Very temporary solution to prevent multiple connection error messages
// from both SshTunnelWorker and MongoWorker
static int lastServerHandle = -1;
if (event->serverHandle <= lastServerHandle)
return;
lastServerHandle = event->serverHandle;
QMessageBox::information(this, "Error", QtUtils::toQString(event->message));
}
@@ -909,6 +958,21 @@ namespace Robomongo
_executeAction->setDisabled(false);
}
void MainWindow::handle(OperationFailedEvent *event)
{
std::stringstream ss;
ss << event->userFriendlyErrorMessage << std::endl << std::endl
<< "Error:" << std::endl << event->technicalErrorMessage;
QMessageBox::information(NULL, "Operation failed", QtUtils::toQString(ss.str()));
}
void MainWindow::closeEvent(QCloseEvent *event)
{
saveWindowsSettings();
QMainWindow::closeEvent(event);
}
void MainWindow::handle(QueryWidgetUpdatedEvent *event)
{
_orientationAction->setEnabled(event->numOfResults() > 1);
+15 -5
View File
@@ -13,6 +13,8 @@ namespace Robomongo
class ConnectionFailedEvent;
class ScriptExecutingEvent;
class ScriptExecutedEvent;
class OperationFailedEvent;
class QueryWidgetUpdatedEvent;
class WorkAreaTabWidget;
class ConnectionMenu;
@@ -66,6 +68,11 @@ namespace Robomongo
void handle(ScriptExecutingEvent *event);
void handle(ScriptExecutedEvent *event);
void handle(QueryWidgetUpdatedEvent *event);
void handle(OperationFailedEvent *event);
protected:
void closeEvent(QCloseEvent *event);
private Q_SLOTS:
void updateMenus();
void setUtcTimeZone();
@@ -78,6 +85,14 @@ namespace Robomongo
void onExplorerVisibilityChanged(bool isVisisble);
private:
void updateConnectionsMenu();
void createDatabaseExplorer();
void createTabs();
void createStylesMenu();
void createStatusBar();
void restoreWindowsSettings();
void saveWindowsSettings() const;
QDockWidget *_logDock;
WorkAreaTabWidget *_workArea;
@@ -97,11 +112,6 @@ namespace Robomongo
QAction *_orientationAction;
QToolBar *_execToolBar;
void updateConnectionsMenu();
void createDatabaseExplorer();
void createTabs();
void createStylesMenu();
void createStatusBar();
};
}
+7 -1
View File
@@ -23,7 +23,13 @@ namespace
"<br/>"
"The program is provided AS IS with NO WARRANTY OF ANY KIND, "
"INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A "
"PARTICULAR PURPOSE.<br/>");
"PARTICULAR PURPOSE.<br/>"
"<br>"
"Credits: "
"<br/>"
"Some icons are designed by Freepik from <a href=http://www.flaticon.com>www.flaticon.com</a>"
"<br/>"
);
}
namespace Robomongo
+28 -19
View File
@@ -10,6 +10,8 @@
#include "robomongo/core/settings/ConnectionSettings.h"
#include "robomongo/core/settings/CredentialSettings.h"
#include "robomongo/core/utils/QtUtils.h"
#include "robomongo/gui/GuiRegistry.h"
#include "robomongo/gui/utils/GuiConstants.h"
namespace Robomongo
{
@@ -43,7 +45,17 @@ namespace Robomongo
_useAuth->setStyleSheet("margin-bottom: 7px");
VERIFY(connect(_useAuth, SIGNAL(toggled(bool)), this, SLOT(authChecked(bool))));
_echoModeButton = new QPushButton("Show");
_echoModeButton = new QPushButton;
_echoModeButton->setIcon(GuiRegistry::instance().hideIcon());
#ifdef Q_OS_MAC
_echoModeButton->setMaximumWidth(50);
#else
_echoModeButton->setMinimumWidth(50);
#endif
// Attempt to fix the issue for Windows High DPI button height is slightly taller than other widgets
#ifdef Q_OS_WIN
_echoModeButton->setMaximumHeight(HighDpiContants::WIN_HIGH_DPI_BUTTON_HEIGHT);
#endif
VERIFY(connect(_echoModeButton, SIGNAL(clicked()), this, SLOT(toggleEchoMode())));
_useAuth->setChecked(_settings->hasEnabledPrimaryCredential());
@@ -57,23 +69,20 @@ namespace Robomongo
_mechanismComboBox->setCurrentText(QtUtils::toQString(primaryCredential->mechanism()));
}
QGridLayout *_authLayout = new QGridLayout;
_authLayout->addWidget(_useAuth, 0, 0, 1, 3);
_authLayout->addWidget(_databaseNameLabel, 1, 0);
_authLayout->addWidget(_databaseName, 1, 1, 1, 2);
_authLayout->addWidget(_databaseNameDescriptionLabel, 2, 1, 1, 2);
_authLayout->addWidget(_userNameLabel, 3, 0);
_authLayout->addWidget(_userName, 3, 1, 1, 2);
_authLayout->addWidget(_userPasswordLabel, 4, 0);
_authLayout->addWidget(_userPassword, 4, 1);
_authLayout->addWidget(_echoModeButton, 4, 2);
_authLayout->addWidget(_mechanismLabel, 5, 0);
_authLayout->addWidget(_mechanismComboBox, 5, 1, 1, 2);
_authLayout->setAlignment(Qt::AlignTop);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(_authLayout);
setLayout(mainLayout);
QGridLayout *authLayout = new QGridLayout;
authLayout->addWidget(_useAuth, 0, 0, 1, 3);
authLayout->addWidget(_databaseNameLabel, 1, 0);
authLayout->addWidget(_databaseName, 1, 1, 1, 2);
authLayout->addWidget(_databaseNameDescriptionLabel, 2, 1, 1, 2);
authLayout->addWidget(_userNameLabel, 3, 0);
authLayout->addWidget(_userName, 3, 1, 1, 2);
authLayout->addWidget(_userPasswordLabel, 4, 0);
authLayout->addWidget(_userPassword, 4, 1);
authLayout->addWidget(_echoModeButton, 4, 2);
authLayout->addWidget(_mechanismLabel, 5, 0);
authLayout->addWidget(_mechanismComboBox, 5, 1, 1, 2);
authLayout->setAlignment(Qt::AlignTop);
setLayout(authLayout);
}
void ConnectionAuthTab::accept()
@@ -99,7 +108,7 @@ namespace Robomongo
{
bool isPassword = _userPassword->echoMode() == QLineEdit::Password;
_userPassword->setEchoMode(isPassword ? QLineEdit::Normal: QLineEdit::Password);
_echoModeButton->setText(isPassword ? "Hide": "Show");
_echoModeButton->setIcon(isPassword ? GuiRegistry::instance().showIcon() : GuiRegistry::instance().hideIcon());
}
void ConnectionAuthTab::authChecked(bool checked)
+141 -19
View File
@@ -7,54 +7,176 @@
#include <QCheckBox>
#include <QPushButton>
#include <QFileDialog>
#include <QComboBox>
#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"
namespace Robomongo
{
ConnectionBasicTab::ConnectionBasicTab(ConnectionSettings *settings) :
_settings(settings)
{
QLabel *connectionDescriptionLabel = new QLabel(
"Choose any connection name that will help you to identify this connection.");
connectionDescriptionLabel->setWordWrap(true);
connectionDescriptionLabel->setContentsMargins(0, -2, 0, 20);
QLabel *serverDescriptionLabel = new QLabel(
"Specify host and port of MongoDB server. Host can be either IPv4, IPv6 or domain name.");
serverDescriptionLabel->setWordWrap(true);
serverDescriptionLabel->setContentsMargins(0, -2, 0, 20);
const ReplicaSetSettings* const replicaSettings = _settings->replicaSetSettings();
_typeLabel = new QLabel("Type:");
_connectionType = new QComboBox;
_connectionType->addItem(tr("Direct Connection"));
_connectionType->addItem(tr("Replica Set or Sharded Cluster"));
_connectionType->setCurrentIndex(static_cast<int>(_settings->isReplicaSet()));
VERIFY(connect(_connectionType, SIGNAL(currentIndexChanged(int)), this, SLOT(on_ConnectionTypeChange(int))));
_nameLabel = new QLabel("Name:");
_connectionName = new QLineEdit(QtUtils::toQString(_settings->connectionName()));
_connInfoLabel = new QLabel("Choose any connection name that will help you to identify this connection.");
_connInfoLabel->setWordWrap(true);
_connInfoLabel->setContentsMargins(0, -2, 0, 20);
_addressLabel = new QLabel("Address:");
_serverAddress = new QLineEdit(QtUtils::toQString(_settings->serverHost()));
_colon = new QLabel(":");
_serverPort = new QLineEdit(QString::number(_settings->serverPort()));
_serverPort->setFixedWidth(80);
QRegExp rx("\\d+");//(0-65554)
_serverPort->setValidator(new QRegExpValidator(rx, this));
QRegExp rx("\\d+"); //(0-65554)
_serverPort->setValidator(new QRegExpValidator(rx, this));
_addInfoLabel = new QLabel("Specify host and port of MongoDB server. Host can be either IPv4, IPv6 or domain name.");
_addInfoLabel->setWordWrap(true);
_addInfoLabel->setContentsMargins(0, -2, 0, 20);
_membersLabel = new QLabel("Members:");
_members = new QListWidget;
if (_settings->isReplicaSet())
{
if (replicaSettings->members().empty())
{
auto item = new QListWidgetItem("localhost:27017");
item->setFlags(item->flags() | Qt::ItemIsEditable);
_members->addItem(item);
}
else
{
for (const std::string& str : replicaSettings->members())
{
auto item = new QListWidgetItem(QString::fromStdString(str));
item->setFlags(item->flags() | Qt::ItemIsEditable);
_members->addItem(item);
}
}
}
int const BUTTON_SIZE = 60;
_addButton = new QPushButton("Add");
_addButton->setFixedWidth(BUTTON_SIZE);
_removeButton = new QPushButton("Remove");
_removeButton->setFixedWidth(BUTTON_SIZE);
_discoverButton = new QPushButton("Discover");
_discoverButton->setFixedWidth(BUTTON_SIZE);
VERIFY(connect(_addButton, SIGNAL(clicked()), this, SLOT(on_addButton_clicked())));
auto hLayout = new QHBoxLayout;
hLayout->addWidget(_addButton);
hLayout->addWidget(_removeButton);
hLayout->addWidget(_discoverButton);
hLayout->setAlignment(Qt::AlignRight);
_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()));
}
new QShortcut(QKeySequence(Qt::Key_Delete), this, SLOT(deleteItem()));
QGridLayout *connectionLayout = new QGridLayout;
connectionLayout->setAlignment(Qt::AlignTop);
connectionLayout->addWidget(new QLabel("Name:"), 1, 0);
connectionLayout->addWidget(_connectionName, 1, 1, 1, 3);
connectionLayout->addWidget(connectionDescriptionLabel, 2, 1, 1, 3);
connectionLayout->addWidget(new QLabel("Address:"), 3, 0);
connectionLayout->addWidget(_serverAddress, 3, 1);
connectionLayout->addWidget(new QLabel(":"), 3, 2);
connectionLayout->addWidget(_serverPort, 3, 3);
connectionLayout->addWidget(serverDescriptionLabel, 4, 1, 1, 3);
connectionLayout->addWidget(_typeLabel, 1, 0);
connectionLayout->addWidget(_connectionType, 1, 1, 1, 3);
connectionLayout->addWidget(new QLabel(""), 2, 0);
connectionLayout->addWidget(_nameLabel, 3, 0);
connectionLayout->addWidget(_connectionName, 3, 1, 1, 3);
connectionLayout->addWidget(_connInfoLabel, 4, 1, 1, 3);
connectionLayout->addWidget(_addressLabel, 5, 0);
connectionLayout->addWidget(_serverAddress, 5, 1);
connectionLayout->addWidget(_colon, 5, 2);
connectionLayout->addWidget(_serverPort, 5, 3);
connectionLayout->addWidget(_addInfoLabel, 6, 1, 1, 3);
connectionLayout->addWidget(_membersLabel, 7, 0, Qt::AlignTop);
connectionLayout->addWidget(_members, 7, 1, 1, 3);
connectionLayout->addLayout(hLayout, 8, 1, 1, 3, Qt::AlignRight);
connectionLayout->addWidget(new QLabel(""), 9, 0);
connectionLayout->addWidget(_readPrefLabel, 10, 0);
connectionLayout->addWidget(_readPreference, 10, 1, 1, 3);
connectionLayout->addWidget(new QLabel(""), 11, 0);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(connectionLayout);
setLayout(mainLayout);
_connectionName->setFocus();
on_ConnectionTypeChange(_connectionType->currentIndex());
}
void ConnectionBasicTab::accept()
{
ReplicaSetSettings* const replicaSettings = _settings->replicaSetSettings();
_settings->setReplicaSet(static_cast<bool>(_connectionType->currentIndex()));
_settings->setConnectionName(QtUtils::toStdString(_connectionName->text()));
_settings->setServerHost(QtUtils::toStdString(_serverAddress->text()));
_settings->setServerPort(_serverPort->text().toInt());
if (_settings->isReplicaSet()) {
// save replica members
std::vector<std::string> members;
for (int i = 0; i < _members->count(); ++i)
{
QListWidgetItem const* item = _members->item(i);
members.push_back(item->text().toStdString());
}
replicaSettings->setMembers(members);
// save read preference option
replicaSettings->setReadPreference(
static_cast<ReplicaSetSettings::ReadPreference>(_readPreference->currentIndex()));
}
}
void ConnectionBasicTab::on_ConnectionTypeChange(int index)
{
bool const isReplica = static_cast<bool>(index);
// Replica set
_membersLabel->setVisible(isReplica);
_members->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);
_serverPort->setVisible(!isReplica);
_colon->setVisible(!isReplica);
_addInfoLabel->setVisible(!isReplica);
}
void ConnectionBasicTab::deleteItem()
{
delete _members->currentItem(); // todo: refactor
}
void ConnectionBasicTab::on_addButton_clicked()
{
auto item = new QListWidgetItem("localhost:27017");
item->setFlags(item->flags() | Qt::ItemIsEditable);
_members->addItem(item);
}
}
@@ -3,8 +3,11 @@
#include <QWidget>
QT_BEGIN_NAMESPACE
class QLineEdit;
class QLabel;
class QCheckBox;
class QPushButton;
class QComboBox;
class QListWidget;
QT_END_NAMESPACE
namespace Robomongo
@@ -19,10 +22,29 @@ namespace Robomongo
ConnectionBasicTab(ConnectionSettings *settings);
void accept();
private Q_SLOTS:
void on_ConnectionTypeChange(int index);
void deleteItem();
void on_addButton_clicked();
private:
QLabel *_typeLabel;
QComboBox *_connectionType;
QLabel *_nameLabel;
QLineEdit *_connectionName;
QLabel *_connInfoLabel;
QLabel *_addressLabel;
QLineEdit *_serverAddress;
QLabel *_colon;
QLineEdit *_serverPort;
QLabel *_addInfoLabel;
QLabel *_membersLabel;
QListWidget *_members;
QPushButton *_addButton;
QPushButton *_removeButton;
QPushButton *_discoverButton;
QLabel *_readPrefLabel;
QComboBox *_readPreference;
ConnectionSettings *const _settings;
};
}
@@ -5,169 +5,275 @@
#include <QLabel>
#include <QPushButton>
#include <QMovie>
#include <QMessageBox>
#include <boost/scoped_ptr.hpp>
#include <mongo/client/dbclientinterface.h>
#include "robomongo/core/settings/ConnectionSettings.h"
#include "robomongo/core/settings/CredentialSettings.h"
#include "robomongo/core/settings/SshSettings.h"
#include "robomongo/core/settings/SslSettings.h"
#include "robomongo/gui/GuiRegistry.h"
#include "robomongo/core/AppRegistry.h"
#include "robomongo/core/domain/App.h"
#include "robomongo/core/domain/MongoServer.h"
#include "robomongo/core/EventBus.h"
#include "robomongo/core/utils/QtUtils.h"
#include "robomongo/core/mongodb/SshTunnelWorker.h"
namespace Robomongo
{
const QSize ConnectionDiagnosticDialog::dialogSize = QSize(300,200);
ConnectionDiagnosticDialog::ConnectionDiagnosticDialog(ConnectionSettings *connection, QWidget *parent) :
BaseClass(parent),
_connection(connection),
_connectionStatusReceived(false),
_authStatusReceived(false)
QDialog(parent),
_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);
setWindowTitle("Diagnostic");
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); // Remove help button (?)
//setFixedSize(dialogSize);
_yesIcon = GuiRegistry::instance().yesMarkIcon();
_noIcon = GuiRegistry::instance().noMarkIcon();
_yesPixmap = _yesIcon.pixmap(24, 24);
_noPixmap = _noIcon.pixmap(24, 24);
_questionIcon = GuiRegistry::instance().questionMarkIcon();
_yesPixmap = _yesIcon.pixmap(20, 20);
_noPixmap = _noIcon.pixmap(20, 20);
_questionPixmap = _questionIcon.pixmap(20, 20);
QPushButton *closeButton = new QPushButton("&Close");
VERIFY(connect(closeButton, SIGNAL(clicked()), this, SLOT(accept())));
_connectionIconLabel = new QLabel;
_authIconLabel = new QLabel;
_connectionLabel = new QLabel;
_authIconLabel = new QLabel;
_authLabel = new QLabel;
_sshIconLabel = new QLabel;
_sshLabel = new QLabel;
_listIconLabel = new QLabel;
_listLabel = new QLabel;
_loadingMovie = new QMovie(":robomongo/icons/loading.gif", QByteArray(), this);
_viewErrorLink = new QLabel("<a href='error' style='color: #777777;'>Show error details</a>");
VERIFY(connect(_viewErrorLink, SIGNAL(linkActivated(QString)), this, SLOT(errorLinkActivated(QString))));
_loadingMovie = new QMovie(":robomongo/icons/loading_ticks_40x40.gif", QByteArray(), this);
_loadingMovie->setScaledSize(QSize(20, 20));
_loadingMovie->start();
_connectionIconLabel->setMovie(_loadingMovie);
_sshIconLabel->setMovie(_loadingMovie);
_authIconLabel->setMovie(_loadingMovie);
_connectionLabel->setText(QString("Connecting to <b>%1</b>...").arg(QtUtils::toQString(_connection->getFullAddress())));
if (_connection->hasEnabledPrimaryCredential()) {
_authLabel->setText(QString("Authorizing on <b>%1</b> database as <b>%2</b>...")
.arg(QtUtils::toQString(_connection->primaryCredential()->databaseName()))
.arg(QtUtils::toQString(_connection->primaryCredential()->userName())));
} else {
_authLabel->setText("Authorization skipped by you");
}
_listIconLabel->setMovie(_loadingMovie);
QGridLayout *layout = new QGridLayout();
layout->setContentsMargins(20, 20, 20, 10);
layout->addWidget(_connectionIconLabel, 0, 0);
layout->addWidget(_connectionLabel, 0, 1, Qt::AlignLeft);
layout->addWidget(_authIconLabel, 1, 0);
layout->addWidget(_authLabel, 1, 1, Qt::AlignLeft);
layout->setSizeConstraint(QLayout::SetFixedSize);
layout->addWidget(_sshIconLabel, 0, 0);
layout->addWidget(_sshLabel, 0, 1, Qt::AlignLeft);
layout->addWidget(_connectionIconLabel, 1, 0);
layout->addWidget(_connectionLabel, 1, 1, Qt::AlignLeft);
layout->addWidget(_authIconLabel, 2, 0);
layout->addWidget(_authLabel, 2, 1, Qt::AlignLeft);
layout->addWidget(_listIconLabel, 3, 0);
layout->addWidget(_listLabel, 3, 1, Qt::AlignLeft);
layout->setColumnStretch(0, 0) ; // Give column 0 no stretch ability
layout->setColumnStretch(1, 1) ; // Give column 1 stretch ability of ratio 1
QHBoxLayout *hbox = new QHBoxLayout;
hbox->addSpacing(21);
hbox->addWidget(_viewErrorLink, 1, Qt::AlignLeft);
hbox->addWidget(closeButton, 0, Qt::AlignRight);
QVBoxLayout *box = new QVBoxLayout;
box->addLayout(layout);
box->addSpacing(10);
box->addWidget(closeButton, 0, Qt::AlignRight);
box->addLayout(hbox);
setLayout(box);
ConnectionDiagnosticThread *thread = new ConnectionDiagnosticThread(_connection);
VERIFY(connect(thread, SIGNAL(connectionStatus(QString, bool)), this, SLOT(connectionStatus(QString, bool))));
VERIFY(connect(thread, SIGNAL(authStatus(QString, bool)), this, SLOT(authStatus(QString, bool))));
VERIFY(connect(thread, SIGNAL(completed()), this, SLOT(completed())));
VERIFY(connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())));
thread->start();
}
sshStatus(InitialState);
connectionStatus(InitialState);
authStatus(InitialState);
listStatus(InitialState);
void ConnectionDiagnosticDialog::connectionStatus(QString error, bool connected)
{
_connectionStatusReceived = true;
_viewErrorLink->hide();
if (connected) {
_connectionIconLabel->setPixmap(_yesPixmap);
_connectionLabel->setText(QString("Connected to <b>%1</b>").arg(QtUtils::toQString(_connection->getFullAddress())));
} else {
_connectionIconLabel->setPixmap(_noPixmap);
_connectionLabel->setText(QString("Unable to connect to <b>%1</b>").arg(QtUtils::toQString(_connection->getFullAddress())));
}
layout()->activate();
setFixedSize(minimumSizeHint());
}
void ConnectionDiagnosticDialog::authStatus(QString error, bool authed)
{
_authStatusReceived = true;
if (authed) {
_authIconLabel->setPixmap(_yesPixmap);
_authLabel->setText(QString("Authorized as <b>%1</b>").arg(QtUtils::toQString(_connection->primaryCredential()->userName())));
} else {
_authIconLabel->setPixmap(_noPixmap);
if (_connection->hasEnabledPrimaryCredential())
_authLabel->setText(QString("Authorization failed"));
else
_authLabel->setText(QString("Authorization skipped by you"));
}
layout()->activate();
setFixedSize(minimumSizeHint());
}
void ConnectionDiagnosticDialog::completed()
{
if (!_connectionStatusReceived)
connectionStatus("", false);
if (!_authStatusReceived)
authStatus("", false);
// seems that it is wrong to call any method on thread,
// because thread already can be disposed.
// QThread *thread = static_cast<QThread *>(sender());
// thread->quit();
}
ConnectionDiagnosticThread::ConnectionDiagnosticThread(ConnectionSettings *connection) :
_connection(connection) { }
void ConnectionDiagnosticThread::run()
{
boost::scoped_ptr<mongo::DBClientConnection> connection;
try {
connection.reset(new mongo::DBClientConnection);
connection->connect(_connection->info());
emit connectionStatus("", true);
}
catch(const mongo::UserException &ex) {
const char *what = ex.what();
emit connectionStatus(QString(what), false);
emit completed();
if (!AppRegistry::instance().app()->openServer(_connection, ConnectionTest)) {
_continueExec = false;
return;
}
try {
if (_connection->hasEnabledPrimaryCredential())
{
CredentialSettings *credentials = _connection->primaryCredential();
_serverHandle = AppRegistry::instance().app()->getLastServerHandle();
}
// Building BSON object:
mongo::BSONObj authParams(mongo::BSONObjBuilder()
.append("user", credentials->userName())
.append("db", credentials->databaseName())
.append("pwd", credentials->userPassword())
.append("mechanism", credentials->mechanism())
.obj());
ConnectionDiagnosticDialog::~ConnectionDiagnosticDialog() {
if (_server)
AppRegistry::instance().app()->closeServer(_server);
}
connection->auth(authParams);
emit authStatus("", true);
}
} catch (const mongo::UserException &ex) {
emit authStatus("", false);
void ConnectionDiagnosticDialog::errorLinkActivated(const QString &link) {
if (_lastErrorMessage.empty())
return;
QMessageBox::information(this, "Error details", QtUtils::toQString(_lastErrorMessage));
}
void ConnectionDiagnosticDialog::sshStatus(State state)
{
if (!_connection->sshSettings()->enabled()) {
_sshIconLabel->setVisible(false);
_sshLabel->setVisible(false);
return;
}
emit completed();
if (state == InitialState) {
_sshIconLabel->setMovie(_loadingMovie);
_sshLabel->setText(QString("Connecting to SSH server at <b>%1:%2</b>...")
.arg(QtUtils::toQString(_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(_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(_connection->sshSettings()->host()))
.arg(_connection->sshSettings()->port()));
}
}
void ConnectionDiagnosticDialog::connectionStatus(State state)
{
// Add info about tunneling if SSH or SSL is used
QString tunnelNote(""); // No tunnel info when neither SSH nor SSL enabled
if (_connection->sshSettings()->enabled())
{
tunnelNote = " via SSH tunnel";
}
else if (_connection->sslSettings()->sslEnabled())
{
tunnelNote = " via SSL tunnel";
}
// Set main info text at dialog
if (state == InitialState) {
_connectionIconLabel->setMovie(_loadingMovie);
_connectionLabel->setText(QString("Connecting to <b>%1</b>%2...").arg(QtUtils::toQString(_connection->getFullAddress()), tunnelNote));
} else if (state == CompletedState) {
_connectionIconLabel->setPixmap(_yesPixmap);
_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(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(QtUtils::toQString(_connection->getFullAddress()), tunnelNote));
}
}
void ConnectionDiagnosticDialog::authStatus(State state)
{
if (!_connection->hasEnabledPrimaryCredential()) {
_authIconLabel->setVisible(false);
_authLabel->setVisible(false);
return;
}
if (state == InitialState) {
_authIconLabel->setMovie(_loadingMovie);
_authLabel->setText(QString("Authorizing on <b>%1</b> database as <b>%2</b>...")
.arg(QtUtils::toQString(_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(_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(_connection->primaryCredential()->databaseName()))
.arg(QtUtils::toQString(_connection->primaryCredential()->userName())));
} else if (state == NotPerformedState) {
_authIconLabel->setPixmap(_questionPixmap);
_authLabel->setText(QString("No chance to authorize"));
}
}
void ConnectionDiagnosticDialog::listStatus(State state)
{
if (_connection->hasEnabledPrimaryCredential()) {
_listIconLabel->setVisible(false);
_listLabel->setVisible(false);
return;
}
if (state == InitialState) {
_listIconLabel->setMovie(_loadingMovie);
_listLabel->setText(QString("Loading list of databases..."));
} else if (state == CompletedState) {
_listIconLabel->setPixmap(_yesPixmap);
_listLabel->setText(QString("Access to databases is available"));
} else if (state == FailedState) {
_listIconLabel->setPixmap(_noPixmap);
_listLabel->setText(QString("Failed to load list of databases"));
} else if (state == NotPerformedState) {
_listIconLabel->setPixmap(_questionPixmap);
_listLabel->setText(QString("No chance to load list of databases"));
}
}
void ConnectionDiagnosticDialog::handle(ConnectionEstablishedEvent *event) {
if (event->connectionType != ConnectionTest)
return;
sshStatus(CompletedState);
connectionStatus(CompletedState);
authStatus(CompletedState);
listStatus(CompletedState);
// Remember in order to delete on dialog close
_server = static_cast<MongoServer*>(event->sender());
}
void ConnectionDiagnosticDialog::handle(ConnectionFailedEvent *event) {
if (event->connectionType != ConnectionTest || event->serverHandle != _serverHandle)
return;
sshStatus(CompletedState);
connectionStatus(CompletedState);
authStatus(CompletedState);
listStatus(CompletedState);
switch (event->reason) {
case ConnectionFailedEvent::SshConnection:
sshStatus(FailedState);
connectionStatus(NotPerformedState);
authStatus(NotPerformedState);
listStatus(NotPerformedState);
break;
case ConnectionFailedEvent::SshChannel:
case ConnectionFailedEvent::MongoConnection:
connectionStatus(FailedState);
authStatus(NotPerformedState);
listStatus(NotPerformedState);
break;
case ConnectionFailedEvent::MongoAuth:
authStatus(FailedState);
listStatus(FailedState);
break;
case ConnectionFailedEvent::SslConnection:
connectionStatus(FailedState);
authStatus(NotPerformedState);
listStatus(NotPerformedState);
break;
}
// Show link for additional error details
if (!event->message.empty()) {
_lastErrorMessage = event->message;
_viewErrorLink->show();
}
}
}
@@ -2,63 +2,69 @@
#include <QDialog>
#include <QIcon>
#include <QThread>
QT_BEGIN_NAMESPACE
class QLabel;
class QMovie;
QT_END_NAMESPACE
namespace Robomongo
{
class ConnectionEstablishedEvent;
class ConnectionFailedEvent;
class ConnectionSettings;
class MongoServer;
class ConnectionDiagnosticDialog : public QDialog
{
Q_OBJECT
public:
typedef QDialog BaseClass;
static const QSize dialogSize;
ConnectionDiagnosticDialog(ConnectionSettings *connection, QWidget *parent = 0);
~ConnectionDiagnosticDialog();
bool continueExec() const { return _continueExec; }
protected Q_SLOTS:
void connectionStatus(QString error, bool connected);
void authStatus(QString error, bool authed);
void completed();
void handle(ConnectionEstablishedEvent *event);
void handle(ConnectionFailedEvent *event);
void errorLinkActivated(const QString &link);
private:
enum State {
InitialState,
CompletedState,
FailedState,
NotPerformedState
};
void sshStatus(State state);
void connectionStatus(State state);
void authStatus(State state);
void listStatus(State state);
ConnectionSettings *_connection;
QIcon _yesIcon;
QIcon _noIcon;
QIcon _waitIcon;
QIcon _questionIcon;
QMovie *_loadingMovie;
QPixmap _yesPixmap;
QPixmap _noPixmap;
QPixmap _questionPixmap;
QLabel *_sshIconLabel;
QLabel *_sshLabel;
QLabel *_connectionIconLabel;
QLabel *_connectionLabel;
QLabel *_authIconLabel;
QLabel *_authLabel;
QLabel *_listIconLabel; // List database names
QLabel *_listLabel;
bool _connectionStatusReceived;
bool _authStatusReceived;
};
QLabel *_viewErrorLink;
std::string _lastErrorMessage;
class ConnectionDiagnosticThread : public QThread
{
Q_OBJECT
public:
ConnectionDiagnosticThread(ConnectionSettings *connection);
Q_SIGNALS:
void connectionStatus(QString error, bool connected);
void authStatus(QString error, bool authed);
void completed();
protected:
void run();
private:
ConnectionSettings *_connection;
MongoServer *_server;
int _serverHandle;
bool _continueExec;
};
}
+28 -21
View File
@@ -15,6 +15,7 @@
#include "robomongo/gui/dialogs/ConnectionSslTab.h"
#include "robomongo/gui/dialogs/ConnectionSslTab.h"
#include "robomongo/gui/dialogs/SSHTunnelTab.h"
#include "robomongo/gui/dialogs/SSLTab.h"
#include "robomongo/gui/dialogs/ConnectionDiagnosticDialog.h"
#include "robomongo/core/utils/QtUtils.h"
@@ -50,18 +51,14 @@ namespace Robomongo
_basicTab = new ConnectionBasicTab(_connection);
_authTab = new ConnectionAuthTab(_connection);
_advancedTab = new ConnectionAdvancedTab(_connection);
_sshTab = new SshTunnelTab(_connection);
// v0.9
//_sslTab = new ConnectionSslTab(_connection);
_sshTab = new SshTunnelTab(_connection);
_sslTab = new SSLTab(_connection);
tabWidget->addTab(_basicTab, "Connection");
tabWidget->addTab(_authTab, "Authentication");
tabWidget->addTab(_advancedTab, "Advanced");
tabWidget->addTab(_sshTab, "SSH");
// v0.9
//tabWidget->addTab(_sslTab, "SSL");
tabWidget->addTab(_sslTab, "SSL");
tabWidget->addTab(_advancedTab, "Advanced");
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addWidget(tabWidget);
@@ -69,6 +66,10 @@ namespace Robomongo
setLayout(mainLayout);
_basicTab->setFocus();
adjustSize();
// Set minimum width - adjustment after adding SSLTab
setMinimumWidth(550);
}
/**
@@ -76,27 +77,29 @@ namespace Robomongo
*/
void ConnectionDialog::accept()
{
if (validateAndApply()){
if (validateAndApply()) {
QDialog::accept();
}
}
bool ConnectionDialog::validateAndApply()
{
#ifdef SSH_SUPPORT_ENABLED
bool isSshAndSsl = _sslTab->isSslSupported() && _sshTab->isSshSupported();
if (isSshAndSsl) {
QMessageBox::warning(this, "Invalid Transport", "SSH and SSL cannot be enabled simultaneously. Please uncheck one of them.");
bool sshAndSslChecked = (_sslTab->sslEnabled() && _sshTab->sshEnabled());
if (sshAndSslChecked)
{
QMessageBox::warning(this, "Invalid Configuration", "SSH and SSL cannot be enabled simultaneously. "
"Please use one of them.");
return false;
}
#endif
_basicTab->accept();
_authTab->accept();
_advancedTab->accept();
_sshTab->accept();
// v0.9
// _sslTab->accept();
if (!_sshTab->accept() || !_sslTab->accept())
{
return false;
}
return true;
}
@@ -105,9 +108,13 @@ namespace Robomongo
*/
void ConnectionDialog::testConnection()
{
if(validateAndApply()){
ConnectionDiagnosticDialog diag(_connection,this);
diag.exec();
}
if (!validateAndApply())
return;
ConnectionDiagnosticDialog diag(_connection, this);
if (!diag.continueExec())
return;
diag.exec();
}
}
+2 -4
View File
@@ -10,7 +10,7 @@ namespace Robomongo
class ConnectionAuthTab;
class ConnectionBasicTab;
class ConnectionAdvancedTab;
class ConnectionSslTab;
class SSLTab;
class SshTunnelTab;
/**
* @brief This Dialog allows to edit single connection
@@ -44,9 +44,7 @@ namespace Robomongo
ConnectionBasicTab *_basicTab;
ConnectionAdvancedTab *_advancedTab;
SshTunnelTab *_sshTab;
// v0.9
//ConnectionSslTab *_sslTab;
SSLTab *_sslTab;
#ifdef SSH_SUPPORT_ENABLED
SshTunnelTab *_sshTab;
@@ -36,7 +36,7 @@ namespace Robomongo
_sslSupport->setChecked(_settings->sslInfo()._sslSupport);
_selectPemFileButton = new QPushButton("...");
_selectPemFileButton->setFixedSize(20,20);
_selectPemFileButton->setFixedSize(20, 20);
VERIFY(connect(_selectPemFileButton, SIGNAL(clicked()), this, SLOT(setSslPEMKeyFile())));
_sslPemLabel = new QLabel("SSL Certificate:");
+80 -15
View File
@@ -9,8 +9,11 @@
#include <QDialogButtonBox>
#include <QTreeWidgetItem>
#include <QKeyEvent>
#include <QApplication>
#include <QSettings>
#include "robomongo/core/settings/ConnectionSettings.h"
#include "robomongo/core/settings/ReplicaSetSettings.h"
#include "robomongo/core/settings/CredentialSettings.h"
#include "robomongo/core/settings/SettingsManager.h"
#include "robomongo/core/utils/QtUtils.h"
@@ -42,8 +45,15 @@ namespace Robomongo
*/
void setConnection(ConnectionSettings *connection)
{
setText(0, QtUtils::toQString(connection->connectionName()));
setText(1, QtUtils::toQString(connection->getFullAddress()));
if (connection->isReplicaSet()) {
setText(0, QtUtils::toQString(connection->connectionName()) + " [Replica Set]");
auto const repSetSize = connection->replicaSetSettings()->members().size();
setText(1, QString::number(repSetSize) + " Servers (" + QtUtils::toQString(connection->getFullAddress()) + ")");
}
else {
setText(0, QtUtils::toQString(connection->connectionName()));
setText(1, QtUtils::toQString(connection->getFullAddress()));
}
if (connection->hasEnabledPrimaryCredential()) {
QString authString = QString("%1 / %2").arg(QtUtils::toQString(connection->primaryCredential()->databaseName())).arg(QtUtils::toQString(connection->primaryCredential()->userName()));
@@ -55,7 +65,16 @@ namespace Robomongo
}
_connection = connection;
setIcon(0, GuiRegistry::instance().serverIcon());
if (connection->isReplicaSet()) {
setIcon(0, GuiRegistry::instance().replicaSetIcon());
}
else {
setIcon(0, GuiRegistry::instance().serverIcon());
}
if (connection->imported()) {
setIcon(0, GuiRegistry::instance().serverImportedIcon());
}
}
private:
@@ -65,8 +84,8 @@ namespace Robomongo
/**
* @brief Creates dialog
*/
ConnectionsDialog::ConnectionsDialog(SettingsManager *settingsManager, QWidget *parent)
: QDialog(parent), _settingsManager(settingsManager)
ConnectionsDialog::ConnectionsDialog(SettingsManager *settingsManager, bool checkForImported, QWidget *parent)
: QDialog(parent), _settingsManager(settingsManager), _checkForImported(checkForImported)
{
setWindowIcon(GuiRegistry::instance().connectIcon());
setWindowTitle("MongoDB Connections");
@@ -124,13 +143,33 @@ namespace Robomongo
VERIFY(connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())));
QHBoxLayout *bottomLayout = new QHBoxLayout;
bottomLayout->addWidget(buttonBox);
QLabel *intro = new QLabel(
"<a href='create'>Create</a>, "
"<a href='edit'>edit</a>, "
"<a href='remove'>remove</a>, "
"<a href='clone'>clone</a> or reorder connections via drag'n'drop.");
// Information message is shown when connection
// settings are imported from previous version of Robomongo
int importedCount = _settingsManager->importedConnectionsCount();
if (_checkForImported && importedCount > 0) {
QIcon importIcon = qApp->style()->standardIcon(QStyle::SP_MessageBoxInformation);
QPixmap importPixmap = importIcon.pixmap(20, 20);
QLabel *importLabelIcon = new QLabel;
importLabelIcon->setPixmap(importPixmap);
QString importedRecords = importedCount > 1 ? "records" : "record";
QLabel *importLabelMessage = new QLabel(QString(
"<span style='color: #777777;'>"
"Connection settings have been imported (%1 %2)"
"</span>").arg(importedCount).arg(importedRecords));
bottomLayout->addWidget(importLabelIcon, 0, Qt::AlignLeft);
bottomLayout->addWidget(importLabelMessage, 1, Qt::AlignLeft);
}
bottomLayout->addWidget(buttonBox, 0, Qt::AlignRight);
QLabel *intro = new QLabel(QString(
"<a style='color: %1' href='create'>Create</a>, "
"<a style='color: %1' href='edit'>edit</a>, "
"<a style='color: %1' href='remove'>remove</a>, "
"<a style='color: %1' href='clone'>clone</a> "
"or reorder connections via drag'n'drop.").arg("#106CD6"));
intro->setWordWrap(true);
VERIFY(connect(intro, SIGNAL(linkActivated(QString)), this, SLOT(linkActivated(QString))));
@@ -155,6 +194,8 @@ namespace Robomongo
_listWidget->setCurrentItem(_listWidget->topLevelItem(0));
_listWidget->setFocus();
restoreWindowsSettings();
}
/**
@@ -169,10 +210,23 @@ namespace Robomongo
return;
_selectedConnection = currentItem->connection();
saveWindowsSettings();
QDialog::accept();
}
void ConnectionsDialog::reject()
{
saveWindowsSettings();
QDialog::reject();
}
void ConnectionsDialog::closeEvent(QCloseEvent *event)
{
saveWindowsSettings();
QWidget::closeEvent(event);
}
void ConnectionsDialog::linkActivated(const QString &link)
{
if (link == "create")
@@ -233,7 +287,7 @@ namespace Robomongo
activateWindow();
int size = _connectionItems.size();
for (int i=0; i<size; ++i)
for (int i = 0; i<size; ++i)
{
ConnectionListWidgetItem *item = _connectionItems[i];
if (_connectionItems[i]->connection() == connection) {
@@ -281,7 +335,7 @@ namespace Robomongo
// Clone connection
ConnectionSettings *connection = currentItem->connection()->clone();
std::string newConnectionName="Copy of "+connection->connectionName();
std::string newConnectionName = "Copy of "+connection->connectionName();
connection->setConnectionName(newConnectionName);
@@ -307,7 +361,7 @@ namespace Robomongo
// Make childrens toplevel again. This is a bad, but quickiest item reordering
// implementation.
for(int i = 0; i < count; i++)
for (int i = 0; i < count; i++)
{
ConnectionListWidgetItem * item = (ConnectionListWidgetItem *) _listWidget->topLevelItem(i);
if (item->childCount() > 0) {
@@ -321,7 +375,7 @@ namespace Robomongo
count = _listWidget->topLevelItemCount();
SettingsManager::ConnectionSettingsContainerType items;
for(int i = 0; i < count; i++)
for (int i = 0; i < count; i++)
{
ConnectionListWidgetItem * item = (ConnectionListWidgetItem *) _listWidget->topLevelItem(i);
items.push_back(item->connection());
@@ -362,6 +416,17 @@ namespace Robomongo
QDialog::keyPressEvent(event);
}
void ConnectionsDialog::restoreWindowsSettings()
{
QSettings settings("Paralect", "Robomongo");
resize(settings.value("ConnectionsDialog/size").toSize());
}
void ConnectionsDialog::saveWindowsSettings() const
{
QSettings settings("Paralect", "Robomongo");
settings.setValue("ConnectionsDialog/size", size());
}
ConnectionsTreeWidget::ConnectionsTreeWidget()
{
+26 -1
View File
@@ -23,7 +23,7 @@ namespace Robomongo
/**
* @brief Creates dialog
*/
ConnectionsDialog(SettingsManager *manager, QWidget *parent = 0);
ConnectionsDialog(SettingsManager *manager, bool checkForImported, QWidget *parent = 0);
/**
* @brief ConnectionSettings, that was selected after pressing on
@@ -31,11 +31,23 @@ namespace Robomongo
*/
ConnectionSettings *selectedConnection() const { return _selectedConnection; }
public Q_SLOTS:
/**
* @brief This function is called when user clicks on "Connect" button.
*/
virtual void accept();
/**
* @brief Called when "Cancel" button clicked.
*/
virtual void reject();
protected:
/**
* @brief Reimplementing closeEvent in order to do some pre-close actions.
*/
void closeEvent(QCloseEvent *event);
private Q_SLOTS:
void linkActivated(const QString &link);
@@ -75,6 +87,17 @@ namespace Robomongo
void keyPressEvent(QKeyEvent* event);
private:
/**
* @brief Restore windows settings from system registry
*/
void restoreWindowsSettings();
/**
* @brief Save windows settings into system registry
*/
void saveWindowsSettings() const;
/**
* @brief ConnectionSettings, that was selected after pressing on
* "Connect" button
@@ -96,6 +119,8 @@ namespace Robomongo
* ConnectionListWidgetItem*
*/
ConnectionListItemContainerType _connectionItems;
bool _checkForImported;
};
class ConnectionsTreeWidget : public QTreeWidget
@@ -18,7 +18,7 @@
namespace Robomongo
{
const QSize CopyCollection::minimumSize = QSize(300,150);
const QSize CopyCollection::minimumSize = QSize(300, 150);
CopyCollection::CopyCollection(const QString &serverName, const QString &database,
const QString &collection, QWidget *parent) :
@@ -28,9 +28,9 @@ namespace Robomongo
{
App::MongoServersContainerType servers = AppRegistry::instance().app()->getServers();
QSet<QString> uniqueConnectionsNames;
for(App::MongoServersContainerType::const_iterator it = servers.begin(); it != servers.end(); ++it){
for (App::MongoServersContainerType::const_iterator it = servers.begin(); it != servers.end(); ++it) {
MongoServer *server = *it;
if (server->isConnected() ){
if (server->isConnected()) {
_servers.push_back(server);
uniqueConnectionsNames.insert(QtUtils::toQString(server->connectionRecord()->connectionName()));
}
@@ -109,7 +109,7 @@ namespace Robomongo
}
}
_databaseComboBox->addItems(server->getDatabasesNames());
if (_currentServerName==QtUtils::toQString(server->connectionRecord()->getFullAddress())) {
if (_currentServerName == QtUtils::toQString(server->connectionRecord()->getFullAddress())) {
_databaseComboBox->removeItem(_databaseComboBox->findText(_currentDatabase));
}
}
@@ -0,0 +1,579 @@
#include "robomongo/gui/dialogs/CreateCollectionDialog.h"
#include <mongo/bson/bsonobjbuilder.h>
#include <QApplication>
#include <QCheckBox>
#include <QComboBox>
#include <QDialogButtonBox>
#include <QHBoxLayout>
#include <QLineEdit>
#include <QLabel>
#include <QMessageBox>
#include <QPushButton>
#include <QSettings>
#include <Qsci/qscilexerjavascript.h>
#include <Qsci/qsciscintilla.h>
#include <QVBoxLayout>
#include "robomongo/gui/widgets/workarea/IndicatorLabel.h"
#include "robomongo/gui/GuiRegistry.h"
#include "robomongo/core/domain/MongoDatabase.h"
#include "robomongo/core/utils/QtUtils.h"
#include "robomongo/gui/editors/FindFrame.h"
#include "robomongo/gui/editors/JSLexer.h"
#include "robomongo/gui/editors/PlainJavaScriptEditor.h"
#include "robomongo/shell/bson/json.h"
namespace Robomongo
{
enum
{
OPTIONS_TAB = 0,
STORAGE_ENGINE_TAB = 1,
VALIDATOR_TAB = 2,
INDEX_OPTION_DEFAULTS_TAB = 3,
};
const QString CreateCollectionDialog::STORAGE_ENGINE_TAB_HINT = "Option available for WiredTiger storage engine only and database version 3.0 and higher.";
const QString CreateCollectionDialog::VALIDATOR_TAB_HINT = "Option available for database version 3.2 and higher.";
const QString CreateCollectionDialog::INDEX_OPTION_DEFAULTS_TAB_HINT = "Option available for database version 3.2 and higher.";
const QString CreateCollectionDialog::NO_PADDING_HINT = "Option available for MMAPv1 storage engine only and database version 3.0 and higher.";
const QString CreateCollectionDialog::USE_POWEROFTWO_HINT = "Option available for MMAPv1 storage engine only and deprecated since database version 3.0";
const QString CreateCollectionDialog::AUTO_INDEXID_HINT = "Option deprecated since database version 3.2";
CreateCollectionDialog::CreateCollectionDialog(const QString &serverName, const float dbVersion, const std::string& storageEngine,
const QString &database, const QString &collection, QWidget *parent) :
QDialog(parent), _dbVersion(dbVersion), _storageEngine(storageEngine),
_activeFrame(nullptr), _activeObj(&_storageEngineObj)
{
setWindowTitle(tr("Create Collection"));
setMinimumSize(300,150);
Indicator *serverIndicator = new Indicator(GuiRegistry::instance().serverIcon(), serverName);
QFrame *hline = new QFrame();
hline->setFrameShape(QFrame::HLine);
hline->setFrameShadow(QFrame::Sunken);
_inputEdit = new QLineEdit();
_inputLabel = new QLabel(tr("Collection Name:"));
_inputEdit->setMaxLength(60);
_buttonBox = new QDialogButtonBox(this);
_buttonBox->setOrientation(Qt::Horizontal);
_buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Save);
_buttonBox->button(QDialogButtonBox::Save)->setText(tr("C&reate"));
_buttonBox->button(QDialogButtonBox::Save)->setEnabled(false);
VERIFY(connect(_buttonBox, SIGNAL(accepted()), this, SLOT(accept())));
VERIFY(connect(_buttonBox, SIGNAL(rejected()), this, SLOT(reject())));
VERIFY(connect(_inputEdit, SIGNAL(textChanged(const QString &)),
this, SLOT(enableCreateButton(const QString &))));
_validateJsonButton = new QPushButton(tr("Validate JSON"));
_validateJsonButton->setIcon(qApp->style()->standardIcon(QStyle::SP_MessageBoxInformation));
_validateJsonButton->hide();
VERIFY(connect(_validateJsonButton, SIGNAL(clicked()), this, SLOT(onValidateButtonClicked())));
// Create advanced options tabs
_advancedOptions = new QTabWidget(this);
_advancedOptions->addTab(createOptionsTab(), tr("Options"));
_advancedOptions->addTab(createStorageEngineTab(), tr("Storage Engine"));
_advancedOptions->addTab(createValidatorTab(), tr("Validator"));
_advancedOptions->addTab(createIndexOptionDefaultsTab(), tr("Index Option Defaults"));
_advancedOptions->setTabsClosable(false);
VERIFY(connect(_advancedOptions, SIGNAL(currentChanged(int)), this, SLOT(onTabChanged(int))));
// Create Advanced Button
_advancedButton = new QPushButton(tr("Advanced"));
_advancedButton->setCheckable(true);
VERIFY(connect(_advancedButton, SIGNAL(toggled(bool)), this, SLOT(onAdvancedButtonToggled(bool))));
// Check mongodb version and storage engine type to enable/disable UI options
disableUnsupportedOptions();
QHBoxLayout *buttomHlayout = new QHBoxLayout();
buttomHlayout->addWidget(_validateJsonButton);
buttomHlayout->addStretch(1);
buttomHlayout->addWidget(_buttonBox);
buttomHlayout->addWidget(_advancedButton);
QHBoxLayout *vlayout = new QHBoxLayout();
if (!serverName.isEmpty())
vlayout->addWidget(serverIndicator, 0, Qt::AlignLeft);
if (!database.isEmpty())
vlayout->addWidget(createDatabaseIndicator(database), 0, Qt::AlignLeft);
if (!collection.isEmpty())
vlayout->addWidget(createCollectionIndicator(collection), 0, Qt::AlignLeft);
vlayout->addStretch(1);
QVBoxLayout *namelayout = new QVBoxLayout();
namelayout->setContentsMargins(0, 7, 0, 7);
namelayout->addWidget(_inputLabel);
namelayout->addWidget(_inputEdit);
QVBoxLayout *mainLayout = new QVBoxLayout();
mainLayout->addLayout(vlayout);
mainLayout->addWidget(hline);
mainLayout->addLayout(namelayout);
mainLayout->addWidget(_advancedOptions);
mainLayout->addLayout(buttomHlayout);
mainLayout->setSizeConstraint(QLayout::SetMaximumSize);
setLayout(mainLayout);
_advancedOptions->hide();
_inputEdit->setFocus();
}
const mongo::BSONObj CreateCollectionDialog::getExtraOptions() const
{
return _extraOptionsObj;
}
QString CreateCollectionDialog::getCollectionName() const
{
return _inputEdit->text();
}
bool CreateCollectionDialog::isCapped() const
{
return (_cappedCheckBox->checkState() == Qt::Checked);
}
long long CreateCollectionDialog::getSizeInputValue() const
{
return (_sizeInputEdit->text().toLongLong());
}
int CreateCollectionDialog::getMaxDocNumberInputValue() const
{
return (_maxDocNumberInputEdit->text().toInt());
}
bool CreateCollectionDialog::isCheckedAutoIndexid() const
{
return (_autoIndexCheckBox->checkState() == Qt::Checked);
}
bool CreateCollectionDialog::isCheckedUsePowerOfTwo() const
{
return (_usePowerOfTwoSizeCheckBox->checkState() == Qt::Checked);
}
bool CreateCollectionDialog::isCheckedNoPadding() const
{
return (_noPaddingCheckBox->checkState() == Qt::Checked);
}
void CreateCollectionDialog::accept()
{
if ( _inputEdit->text().isEmpty()
|| !validateOptionDependencies()
|| !validate(_storageEngineFrame, _storageEngineObj)
|| !validate(_validatorFrame, _validatorObj)
|| !validate(_indexOptionDefaultsFrame, _indexOptionDefaultsObj))
{
return;
}
makeExtraOptionsObj();
saveWindowSettings();
QDialog::accept();
}
void CreateCollectionDialog::reject()
{
saveWindowSettings();
QDialog::reject();
}
void CreateCollectionDialog::closeEvent(QCloseEvent *event)
{
if (_advancedOptions->isVisible())
{
saveWindowSettings();
}
QWidget::closeEvent(event);
}
void CreateCollectionDialog::onFrameTextChanged()
{
_activeFrame->sciScintilla()->clearIndicatorRange(0, 0, _activeFrame->sciScintilla()->lines(), 40, 0);
}
void CreateCollectionDialog::onValidateButtonClicked()
{
validate(_activeFrame, *_activeObj, false);
}
void CreateCollectionDialog::enableCreateButton(const QString &text)
{
_buttonBox->button(QDialogButtonBox::Save)->setEnabled(!text.isEmpty());
}
void CreateCollectionDialog::onCappedCheckBoxChanged(int newState)
{
_sizeInputEdit->setEnabled((Qt::Checked == static_cast<Qt::CheckState>(newState)));
_maxDocNumberInputEdit->setEnabled((Qt::Checked == static_cast<Qt::CheckState>(newState)));
}
void CreateCollectionDialog::onTabChanged(int index)
{
if (OPTIONS_TAB == index) {
_validateJsonButton->hide();
}
else {
_validateJsonButton->show();
if (STORAGE_ENGINE_TAB == index) {
_activeFrame = _storageEngineFrame;
_activeObj = &_storageEngineObj;
}
else if (VALIDATOR_TAB == index) {
_activeFrame = _validatorFrame;
_activeObj = &_validatorObj;
}
else if (INDEX_OPTION_DEFAULTS_TAB == index) {
_activeFrame = _indexOptionDefaultsFrame;
_activeObj = &_indexOptionDefaultsObj;
}
}
};
void CreateCollectionDialog::onAdvancedButtonToggled(bool state)
{
_advancedOptions->setVisible(state);
if (state) // resize, dialog is expanding
{
setMinimumSize(560, 440);
QSettings settings("Paralect", "Robomongo");
if (settings.contains("CreateCollectionDialog/size"))
{
restoreWindowSettings();
}
else
{
resize(560, 440);
adjustSize();
}
}
else // resize, dialog is shrinking
{
saveWindowSettings(); // save expanded geometry first
setMinimumSize(300, 150);
adjustSize();
}
}
Indicator *CreateCollectionDialog::createDatabaseIndicator(const QString &database)
{
return new Indicator(GuiRegistry::instance().databaseIcon(), database);
}
Indicator *CreateCollectionDialog::createCollectionIndicator(const QString &collection)
{
return new Indicator(GuiRegistry::instance().collectionIcon(), collection);
}
QWidget* CreateCollectionDialog::createOptionsTab()
{
QWidget *optionsTab = new QWidget(this);
_cappedCheckBox = new QCheckBox(tr("Create capped collection"), optionsTab);
_sizeInputLabel = new QLabel(tr("Maximum size in bytes: "));
_sizeInputLabel->setContentsMargins(22, 0, 0, 0);
_sizeInputEdit = new QLineEdit();
_sizeInputEdit->setEnabled(false);
_maxDocNumberInputLabel = new QLabel(tr("Maximum number of documents: "));
_maxDocNumberInputLabel->setContentsMargins(22, 0, 0, 0);
_maxDocNumberInputEdit = new QLineEdit();
_maxDocNumberInputEdit->setEnabled(false);
_autoIndexCheckBox = new QCheckBox(tr("Auto index _id"), optionsTab);
_autoIndexCheckBox->setChecked(true);
_usePowerOfTwoSizeCheckBox = new QCheckBox(tr("Use power-of-2 sizes"), optionsTab);
// Note: For mongodb 2.6 does not have storageEngine string due to the fact that it uses MMAPV1 only.
if (MongoDatabase::StorageEngineType::MMAPV1 == _storageEngine || "" == _storageEngine)
{
_usePowerOfTwoSizeCheckBox->setChecked(true);
}
_noPaddingCheckBox = new QCheckBox(tr("No Padding"), optionsTab);
VERIFY(connect(_cappedCheckBox, SIGNAL(stateChanged(int)),
this, SLOT(onCappedCheckBoxChanged(int))));
QGridLayout *layout = new QGridLayout;
layout->addWidget(_cappedCheckBox, 0, 0, 1, 2);
layout->addWidget(_sizeInputLabel, 1, 0);
layout->addWidget(_sizeInputEdit, 1, 1);
layout->addWidget(_maxDocNumberInputLabel, 2, 0);
layout->addWidget(_maxDocNumberInputEdit, 2, 1);
layout->addWidget(_autoIndexCheckBox, 3, 0);
layout->addWidget(_usePowerOfTwoSizeCheckBox, 4, 0);
layout->addWidget(_noPaddingCheckBox, 5, 0);
layout->setAlignment(Qt::AlignTop);
optionsTab->setLayout(layout);
return optionsTab;
}
QWidget* CreateCollectionDialog::createStorageEngineTab()
{
QWidget *storageEngineTab = new QWidget(this);
_storageEngineFrameLabel = new QLabel(tr("Enter the configuration for the storage engine: "));
_storageEngineFrame = new JSONFrame(this);
configureFrameText(_storageEngineFrame);
_storageEngineFrame->sciScintilla()->setText("{\n \n}");
// clear modification state after setting the content
_storageEngineFrame->sciScintilla()->setModified(false);
VERIFY(connect(_storageEngineFrame->sciScintilla(), SIGNAL(textChanged()),
this, SLOT(onFrameTextChanged())));
QGridLayout *layout = new QGridLayout;
layout->addWidget(_storageEngineFrameLabel, 0, 0);
layout->addWidget(_storageEngineFrame, 1, 0);
layout->setAlignment(Qt::AlignTop);
storageEngineTab->setLayout(layout);
return storageEngineTab;
}
QWidget* CreateCollectionDialog::createValidatorTab()
{
QWidget *validatorEngineTab = new QWidget(this);
_validatorLevelLabel = new QLabel(tr("Validation Level: "));
_validatorLevelComboBox = new QComboBox();
_validatorLevelComboBox->addItem(tr("off"));
_validatorLevelComboBox->addItem(tr("strict"));
_validatorLevelComboBox->addItem(tr("moderate"));
_validatorLevelComboBox->setCurrentIndex(1);
_validatorActionLabel = new QLabel(tr("Validation Action: "));
_validatorActionComboBox = new QComboBox();
_validatorActionComboBox->addItem(tr("error"));
_validatorActionComboBox->addItem(tr("warn"));
_validatorActionComboBox->setCurrentIndex(0);
_validatorFrameLabel = new QLabel(tr("Enter the validator document for this collection: "));
_validatorFrame = new JSONFrame(this);
configureFrameText(_validatorFrame);
_validatorFrame->sciScintilla()->setText("{\n \n}");
// clear modification state after setting the content
_validatorFrame->sciScintilla()->setModified(false);
VERIFY(connect(_validatorFrame->sciScintilla(), SIGNAL(textChanged()),
this, SLOT(onFrameTextChanged())));
QHBoxLayout *validationOptionslayout = new QHBoxLayout();
validationOptionslayout->addWidget(_validatorLevelLabel);
validationOptionslayout->addWidget(_validatorLevelComboBox, Qt::AlignLeft);
validationOptionslayout->addWidget(_validatorActionLabel);
validationOptionslayout->addWidget(_validatorActionComboBox, Qt::AlignLeft);
QVBoxLayout *vlayout = new QVBoxLayout();
vlayout->addWidget(_validatorFrameLabel);
vlayout->addWidget(_validatorFrame);
QVBoxLayout *layout = new QVBoxLayout();
layout->addLayout(validationOptionslayout);
layout->addLayout(vlayout);
validatorEngineTab->setLayout(layout);
return validatorEngineTab;
}
QWidget* CreateCollectionDialog::createIndexOptionDefaultsTab()
{
QWidget *indexOptionDefaultsTab = new QWidget(this);
_indexOptionDefaultsFrameLabel = new QLabel(tr("Enter a default configuration for indexes when creating a collection: "));
_indexOptionDefaultsFrame = new JSONFrame(this);
configureFrameText(_indexOptionDefaultsFrame);
_indexOptionDefaultsFrame->sciScintilla()->setText("{\n \n}");
// clear modification state after setting the content
_indexOptionDefaultsFrame->sciScintilla()->setModified(false);
VERIFY(connect(_indexOptionDefaultsFrame->sciScintilla(), SIGNAL(textChanged()),
this, SLOT(onFrameTextChanged())));
QGridLayout *layout = new QGridLayout;
layout->addWidget(_indexOptionDefaultsFrameLabel, 0, 0);
layout->addWidget(_indexOptionDefaultsFrame, 1, 0);
layout->setAlignment(Qt::AlignTop);
indexOptionDefaultsTab->setLayout(layout);
return indexOptionDefaultsTab;
}
void CreateCollectionDialog::saveWindowSettings() const
{
QSettings settings("Paralect", "Robomongo");
settings.setValue("CreateCollectionDialog/size", size());
}
void CreateCollectionDialog::restoreWindowSettings()
{
QSettings settings("Paralect", "Robomongo");
resize(settings.value("CreateCollectionDialog/size").toSize());
}
void CreateCollectionDialog::configureFrameText(JSONFrame* frame)
{
QsciLexerJavaScript *javaScriptLexer = new JSLexer(this);
QFont font = GuiRegistry::instance().font();
javaScriptLexer->setFont(font);
frame->sciScintilla()->setBraceMatching(QsciScintilla::StrictBraceMatch);
frame->sciScintilla()->setFont(font);
frame->sciScintilla()->setPaper(QColor(255, 0, 0, 127));
frame->sciScintilla()->setLexer(javaScriptLexer);
frame->sciScintilla()->setWrapMode((QsciScintilla::WrapMode)QsciScintilla::SC_WRAP_WORD);
frame->sciScintilla()->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
frame->sciScintilla()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
frame->sciScintilla()->setStyleSheet("QFrame { background-color: rgb(73, 76, 78); border: 1px solid #c7c5c4; border-radius: 4px; margin: 0px; padding: 0px;}");
}
bool CreateCollectionDialog::validate(JSONFrame* frame, mongo::BSONObj& bsonObj, bool silentOnSuccess /* = true */)
{
try {
bsonObj = mongo::Robomongo::fromjson(jsonText(frame).toStdString());
}
catch (const mongo::Robomongo::ParseMsgAssertionException &ex) {
// v0.9
QString message = QtUtils::toQString(ex.reason());
int offset = ex.offset();
int line = 0, pos = 0;
frame->sciScintilla()->lineIndexFromPosition(offset, &line, &pos);
frame->sciScintilla()->setCursorPosition(line, pos);
int lineHeight = frame->sciScintilla()->lineLength(line);
frame->sciScintilla()->fillIndicatorRange(line, pos, line, lineHeight, 0);
message = QString(tr("Unable to parse JSON:<br /> <b>%1</b>, at (%2, %3)."))
.arg(message).arg(line + 1).arg(pos + 1);
QMessageBox::critical(NULL, tr("Parsing error"), message);
frame->setFocus();
activateWindow();
return false;
}
if (!silentOnSuccess) {
QMessageBox::information(NULL, tr("Validation"), tr("JSON is valid!"));
frame->setFocus();
activateWindow();
}
return true;
}
void CreateCollectionDialog::makeExtraOptionsObj()
{
mongo::BSONObjBuilder builder;
if (_autoIndexCheckBox->isEnabled()) builder.append("autoIndexId", isCheckedAutoIndexid());
if (_noPaddingCheckBox->isEnabled() && _usePowerOfTwoSizeCheckBox->isEnabled()) {
builder.append("flags", (isCheckedNoPadding()*2 + isCheckedUsePowerOfTwo()*1));
}
else if (_noPaddingCheckBox->isEnabled()) {
builder.append("flags", (isCheckedNoPadding()*2));
}
else if (_usePowerOfTwoSizeCheckBox->isEnabled()) {
builder.append("flags", (isCheckedUsePowerOfTwo()*1));
}
if (_advancedOptions->isTabEnabled(STORAGE_ENGINE_TAB) ) {
validate(_storageEngineFrame, _storageEngineObj);
if(!_storageEngineObj.isEmpty()) builder.append("storageEngine", _storageEngineObj);
}
if (_advancedOptions->isTabEnabled(VALIDATOR_TAB)) {
validate(_validatorFrame, _validatorObj);
if (!_validatorObj.isEmpty()) {
builder.append("validator", _validatorObj);
builder.append("validationLevel", _validatorLevelComboBox->currentText().toStdString());
builder.append("validationAction", _validatorActionComboBox->currentText().toStdString());
}
}
if (_advancedOptions->isTabEnabled(INDEX_OPTION_DEFAULTS_TAB)) {
validate(_indexOptionDefaultsFrame, _indexOptionDefaultsObj);
if (!_indexOptionDefaultsObj.isEmpty()) builder.append("indexOptionDefaults", _indexOptionDefaultsObj);
}
// Complete and get resulting BSONObj
_extraOptionsObj = builder.obj();
}
bool CreateCollectionDialog::validateOptionDependencies() const
{
bool result(false);
// Validate capped options
if (isCapped()) {
if (!(getSizeInputValue() > 0)) {
QMessageBox::critical(NULL, tr("Error"), tr("Maximum size is required for capped collections"));
return false;
}
result = true;
}
else {
result = true;
}
// Completed checking all dependendencies.
return result;
}
void CreateCollectionDialog::disableUnsupportedOptions() const
{
// Handle WIRED_TIGER engine type
if (MongoDatabase::StorageEngineType::WIRED_TIGER == _storageEngine) {
disableOption(_usePowerOfTwoSizeCheckBox, USE_POWEROFTWO_HINT);
disableOption(_noPaddingCheckBox, NO_PADDING_HINT);
if (MongoDatabase::DBVersion::MONGODB_3_0 > _dbVersion) {
disableTab(STORAGE_ENGINE_TAB, STORAGE_ENGINE_TAB_HINT);
}
}
else {
disableTab(STORAGE_ENGINE_TAB, STORAGE_ENGINE_TAB_HINT);
}
// Handle MMAPV1 engine type
// Note: For mongodb 2.6 does not have storageEngine string due to the fact that it uses MMAPV1 only.
if (MongoDatabase::StorageEngineType::MMAPV1 == _storageEngine || "" == _storageEngine) {
if (MongoDatabase::DBVersion::MONGODB_3_0 < _dbVersion) {
_usePowerOfTwoSizeCheckBox->hide();
}
if (MongoDatabase::DBVersion::MONGODB_3_0 > _dbVersion) {
disableOption(_noPaddingCheckBox, NO_PADDING_HINT);
}
}
if (MongoDatabase::DBVersion::MONGODB_3_2 < _dbVersion) {
disableOption(_autoIndexCheckBox, AUTO_INDEXID_HINT);
}
if (MongoDatabase::DBVersion::MONGODB_3_0 > _dbVersion) {
disableTab(STORAGE_ENGINE_TAB, STORAGE_ENGINE_TAB_HINT);
}
if (MongoDatabase::DBVersion::MONGODB_3_2 > _dbVersion) {
disableTab(VALIDATOR_TAB, VALIDATOR_TAB_HINT);
disableTab(INDEX_OPTION_DEFAULTS_TAB, INDEX_OPTION_DEFAULTS_TAB_HINT);
}
}
void CreateCollectionDialog::disableOption(QWidget* option, const QString& hint) const
{
option->setDisabled(true);
option->setToolTip(hint);
}
void CreateCollectionDialog::disableTab(int index, const QString& hint) const
{
_advancedOptions->setTabEnabled(index, false);
_advancedOptions->setTabToolTip(index, hint);
}
void CreateCollectionDialog::setCursorPosition(int line, int column)
{
_activeFrame->sciScintilla()->setCursorPosition(line, column);
}
QString CreateCollectionDialog::jsonText(JSONFrame* frame) const
{
return frame->sciScintilla()->text().trimmed();
}
}
@@ -0,0 +1,309 @@
#pragma once
#include <mongo/bson/bsonobj.h>
#include <QDialog>
QT_BEGIN_NAMESPACE
class QLabel;
class QDialogButtonBox;
class QLineEdit;
class QTabWidget;
class QCheckBox;
class QComboBox;
QT_END_NAMESPACE
class QsciScintilla;
namespace Robomongo
{
class FindFrame;
class Indicator;
/**
* @brief This Dialog allows to create collection with selective options.
*/
class CreateCollectionDialog : public QDialog
{
Q_OBJECT
public:
using JSONFrame = FindFrame;
/**
* @brief Construct dialog with parameters
* @param serverName: Database name
* @param dbVersion: Database version
* @param storageEngine: Storage engine type
* @param database: Name of the database
* @param collection: Name of the collection
*/
explicit CreateCollectionDialog(const QString &serverName, const float dbVersion, const std::string& storageEngine,
const QString &database = QString(), const QString &collection = QString(), QWidget *parent = 0);
/**
* @brief Used if dialog exec() method returns QDialog::Accepted
* @return Extra options as BSONObj
*/
const mongo::BSONObj getExtraOptions() const;
/**
* @return Collection name
*/
QString getCollectionName() const;
/**
* @return true if capped option checked, false otherwise
*/
bool isCapped() const;
/**
* @return Size option input
*/
long long getSizeInputValue() const;
/**
* @return Max number of documents option input
*/
int getMaxDocNumberInputValue() const;
/**
* @return true if auto index_id option checked, false otherwise
*/
bool isCheckedAutoIndexid() const;
/**
* @return true if use power-of-2 sizes option checked, false otherwise
*/
bool isCheckedUsePowerOfTwo() const;
/**
* @return true if no padding option checked, false otherwise
*/
bool isCheckedNoPadding() const;
public Q_SLOTS:
/**
* @brief Validate all input fields and make extraOptions object if all valid when create button clicked.
*/
virtual void accept();
/**
* @brief Called when "Cancel" button clicked.
*/
virtual void reject();
protected:
/**
* @brief Reimplementing closeEvent in order to do implement functioanlites before close this dialog.
*/
void closeEvent(QCloseEvent *event);
private Q_SLOTS:
/**
* @brief Set indicator position when active JSON frame text changed
*/
void onFrameTextChanged();
/**
* @brief Validate active JSON frame text when validate button clicked
*/
void onValidateButtonClicked();
/**
* @brief Enable create button if input edit has text
* @param text : string from _inputEdit widget
*/
void enableCreateButton(const QString &text);
/**
* @brief Enable/disable capped options when capped checkbox state changed
* @param newState : New state of capped checkbox
*/
void onCappedCheckBoxChanged(int newState);
/**
* @brief Show/hide validate JSON button, update active JSON frame and object according to active tab
* @param Index of active tab (i.e. index = 0 for the first tab)
*/
void onTabChanged(int index);
/**
* @brief Show/hide advanced options menu (_tabwidget) when advancedButton toggled.
* @param state : true if dialog is expanding, false if shrinking.
*/
void onAdvancedButtonToggled(bool state);
private:
/**
* @brief Tool tip strings for disabled options and tabs
*/
static const QString STORAGE_ENGINE_TAB_HINT;
static const QString VALIDATOR_TAB_HINT;
static const QString INDEX_OPTION_DEFAULTS_TAB_HINT;
static const QString NO_PADDING_HINT;
static const QString USE_POWEROFTWO_HINT;
static const QString AUTO_INDEXID_HINT;
/**
* @brief Create database indicator widget
* @param database : Name of the database
* @return : Pointer to newly created Indicator
*/
Indicator* createDatabaseIndicator(const QString &database);
/**
* @brief Create collection indicator widget
* @param database : Name of the collection
* @return : Pointer to newly created Indicator
*/
Indicator* createCollectionIndicator(const QString &collection);
/**
* @brief Creators for the tabs
* @return : Pointer to newly created tab
*/
QWidget* createOptionsTab();
QWidget* createStorageEngineTab();
QWidget* createValidatorTab();
QWidget* createIndexOptionDefaultsTab();
/**
* @brief Save windows settings into system registry
*/
void saveWindowSettings() const;
/**
* @brief Restore windows settings from system registry
*/
void restoreWindowSettings();
/**
* @brief Do initial configuration of given JSON frame.
* @param frame : Pointer to newly created tab
*/
void configureFrameText(JSONFrame* frame);
/**
* @brief Build extraOptions BSON Object
*/
void makeExtraOptionsObj();
/**
* @brief Validate dependencies and requirements between all inputs
* @return : true if all options valid, false at least one dependency not met
*/
bool validateOptionDependencies() const;
/**
* @brief Disable options if they are not supported by database version or storage engine type
*/
void disableUnsupportedOptions() const;
/**
* @brief Disable widget if unsupported
* @param option : Widget to disable
* @param hint : Hint string to be shown
*/
void disableOption(QWidget* option, const QString& hint) const;
/**
* @brief Disable tab if unsupported
* @param index : Tab index
* @param hint : Hint string to be shown
*/
void disableTab(int index, const QString& hint) const;
/**
* @brief Set cursor position of the active JSON frame
* @param line : Line number
* @param column : Column number
*/
void setCursorPosition(int line, int column);
/**
* @return : String from given JSON frame
* @param frame : Pointer to JSON frame
*/
QString jsonText(JSONFrame* frame) const;
/**
* @brief Do JSON validation/creation for given frame text
* @param frame : Pointer to JSON frame
* @param bsonObj : BSON object to be validated
* @param silentOnSuccess : true for silent validation, false for message box on validation
* @return true if text is valid JSON, false otherwise
*/
bool validate(JSONFrame* frame, mongo::BSONObj& bsonObj, bool silentOnSuccess = true);
/**
* @brief Main Window
*/
QLineEdit *_inputEdit;
QLabel *_inputLabel;
QTabWidget *_advancedOptions;
QPushButton *_validateJsonButton;
QDialogButtonBox *_buttonBox;
QPushButton *_advancedButton;
/**
* @brief Options Tab
*/
QCheckBox *_cappedCheckBox;
QLabel *_sizeInputLabel;
QLineEdit *_sizeInputEdit;
QLabel *_maxDocNumberInputLabel;
QLineEdit *_maxDocNumberInputEdit;
QCheckBox *_autoIndexCheckBox;
QCheckBox *_usePowerOfTwoSizeCheckBox;
QCheckBox *_noPaddingCheckBox;
/**
* @brief Storage Engine Tab
*/
JSONFrame *_storageEngineFrame;
QLabel * _storageEngineFrameLabel;
/**
* @brief Validator Tab
*/
QLabel * _validatorLevelLabel;
QComboBox * _validatorLevelComboBox;
QLabel * _validatorActionLabel;
QComboBox * _validatorActionComboBox;
JSONFrame *_validatorFrame;
QLabel * _validatorFrameLabel;
/**
* @brief Index Options Defaults Tab
*/
JSONFrame *_indexOptionDefaultsFrame;
QLabel * _indexOptionDefaultsFrameLabel;
/**
* @brief Pointer to active tab's frame
*/
JSONFrame *_activeFrame;
/**
* @brief Pointer to active tab's BSON obj.
*/
mongo::BSONObj *_activeObj;
/**
* @brief Main BSON obj. with all extra options
*/
mongo::BSONObj _extraOptionsObj;
/**
* @brief Sub objects for each JSON tab to create extraOptions object finally.
*/
mongo::BSONObj _storageEngineObj;
mongo::BSONObj _validatorObj;
mongo::BSONObj _indexOptionDefaultsObj;
/**
* @brief Database version
*/
const float _dbVersion;
/**
* @brief Storage engine type
*/
const std::string _storageEngine;
};
}
@@ -13,7 +13,7 @@
namespace Robomongo
{
const QSize CreateDatabaseDialog::dialogSize = QSize(300,150);
const QSize CreateDatabaseDialog::dialogSize = QSize(300, 150);
CreateDatabaseDialog::CreateDatabaseDialog(const QString &serverName, const QString &database,
const QString &collection, QWidget *parent) :
@@ -31,7 +31,7 @@ namespace Robomongo
hline->setFrameShadow(QFrame::Sunken);
_inputEdit = new QLineEdit();
_inputLabel= new QLabel("Database Name:");
_inputLabel = new QLabel("Database Name:");
_inputEdit->setMaxLength(maxLenghtName);
_buttonBox = new QDialogButtonBox(this);
+23 -15
View File
@@ -17,8 +17,19 @@
namespace Robomongo
{
const char * rolesText[CreateUserDialog::RolesCount] = {"read","readWrite","dbAdmin","userAdmin","clusterAdmin","readAnyDatabase","readWriteAnyDatabase","userAdminAnyDatabase","dbAdminAnyDatabase"};
const QSize CreateUserDialog::minimumSize = QSize(400,200);
const char * rolesText[CreateUserDialog::RolesCount] = {
"read",
"readWrite",
"dbAdmin",
"userAdmin",
"clusterAdmin",
"readAnyDatabase",
"readWriteAnyDatabase",
"userAdminAnyDatabase",
"dbAdminAnyDatabase"
};
const QSize CreateUserDialog::minimumSize = QSize(400, 200);
bool containsWord(const std::string& sentence, const std::string& word)
{
@@ -31,7 +42,7 @@ namespace Robomongo
if (pos) {
isFirstAlpha = isalpha(sentence[pos - 1]);
}
if (!isFirstAlpha&&!isLastAlpha)
if (!isFirstAlpha && !isLastAlpha)
return true;
}
pos++;
@@ -39,13 +50,11 @@ namespace Robomongo
return false;
}
CreateUserDialog::CreateUserDialog(const QStringList &databases,const QString &serverName,
CreateUserDialog::CreateUserDialog(const QStringList &databases, const QString &serverName,
const QString &database, const MongoUser &user,
QWidget *parent) : QDialog(parent),
_user(user)
{
VERIFY(!user.version() < MongoUser::minimumSupportedVersion);
setWindowTitle("Add User");
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); // Remove help button (?)
setMinimumSize(minimumSize);
@@ -57,10 +66,10 @@ namespace Robomongo
hline->setFrameShape(QFrame::HLine);
hline->setFrameShadow(QFrame::Sunken);
_userNameLabel= new QLabel("Name:");
_userNameLabel = new QLabel("Name:");
_userNameEdit = new QLineEdit();
_userNameEdit->setText(QtUtils::toQString(user.name()));
_userPassLabel= new QLabel("Password:");
_userPassLabel = new QLabel("Password:");
_userPassEdit = new QLineEdit();
_userPassEdit->setEchoMode(QLineEdit::Password);
_userSourceLabel = new QLabel("UserSource:");
@@ -70,14 +79,14 @@ namespace Robomongo
QGridLayout *gridRoles = new QGridLayout();
MongoUser::RoleType userRoles = user.role();
for (unsigned i=0; i<RolesCount; ++i)
for (unsigned i = 0; i<RolesCount; ++i)
{
int row = i%3;
int col = i/3;
_rolesArray[i] = new QCheckBox(rolesText[i],this);
MongoUser::RoleType::const_iterator it = std::find(userRoles.begin(),userRoles.end(),rolesText[i]);
_rolesArray[i]->setChecked(it!=userRoles.end());
gridRoles->addWidget(_rolesArray[i],row,col);
_rolesArray[i] = new QCheckBox(rolesText[i], this);
MongoUser::RoleType::const_iterator it = std::find(userRoles.begin(), userRoles.end(), rolesText[i]);
_rolesArray[i]->setChecked(it!= userRoles.end());
gridRoles->addWidget(_rolesArray[i], row, col);
}
QDialogButtonBox *buttonBox = new QDialogButtonBox(this);
@@ -120,7 +129,6 @@ namespace Robomongo
QWidget *parent) : QDialog(parent),
_user(user)
{
VERIFY(user.version() < MongoUser::minimumSupportedVersion);
setWindowTitle("Add User");
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); // Remove help button (?)
setMinimumSize(minimumSize);
@@ -132,7 +140,7 @@ namespace Robomongo
hline->setFrameShape(QFrame::HLine);
hline->setFrameShadow(QFrame::Sunken);
_userNameLabel= new QLabel("Name:");
_userNameLabel = new QLabel("Name:");
_userNameEdit = new QLineEdit();
_userNameEdit->setText(QtUtils::toQString(user.name()));
_userPassLabel= new QLabel("Password:");
@@ -36,10 +36,6 @@ namespace Robomongo
resize(size);
int x = (screenGeometry.width() - width()) / 2;
int y = (screenGeometry.height() - height()) / 2;
move(x, y);
setWindowFlags(Qt::Window | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint);
Indicator *collectionIndicator = new Indicator(GuiRegistry::instance().collectionIcon(), QtUtils::toQString(_info._ns.collectionName()));
@@ -144,18 +140,18 @@ namespace Robomongo
int jsonLen = textString.length();
int offset = 0;
_obj.clear();
while(offset!=jsonLen)
while (offset != jsonLen)
{
mongo::BSONObj doc = mongo::Robomongo::fromjson(json+offset,&len);
mongo::BSONObj doc = mongo::Robomongo::fromjson(json+offset, &len);
_obj.push_back(doc);
offset+=len;
offset += len;
}
} catch (const mongo::Robomongo::ParseMsgAssertionException &ex) {
// v0.9
QString message = QtUtils::toQString(ex.reason());
int offset = ex.offset();
int line=0, pos=0;
int line = 0, pos = 0;
_queryText->sciScintilla()->lineIndexFromPosition(offset, &line, &pos);
_queryText->sciScintilla()->setCursorPosition(line, pos);
@@ -198,7 +194,7 @@ namespace Robomongo
QsciLexerJavaScript *javaScriptLexer = new JSLexer(this);
QFont font = GuiRegistry::instance().font();
javaScriptLexer->setFont(font);
_queryText->sciScintilla()->setBraceMatching(QsciScintilla::StrictBraceMatch);
_queryText->sciScintilla()->setAppropriateBraceMatching();
_queryText->sciScintilla()->setFont(font);
_queryText->sciScintilla()->setPaper(QColor(255, 0, 0, 127));
_queryText->sciScintilla()->setLexer(javaScriptLexer);
@@ -97,7 +97,7 @@ namespace Robomongo
const QFont &textFont = GuiRegistry::instance().font();
QsciLexerJavaScript *javaScriptLexer = new JSLexer(this);
javaScriptLexer->setFont(textFont);
_queryText->sciScintilla()->setBraceMatching(QsciScintilla::StrictBraceMatch);
_queryText->sciScintilla()->setAppropriateBraceMatching();
_queryText->sciScintilla()->setFont(textFont);
_queryText->sciScintilla()->setPaper(QColor(255, 0, 0, 127));
_queryText->sciScintilla()->setLexer(javaScriptLexer);
@@ -24,7 +24,7 @@ namespace Robomongo
setWindowTitle("Preferences " PROJECT_NAME_TITLE);
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
setFixedSize(height,width);
setFixedSize(height, width);
QVBoxLayout *layout = new QVBoxLayout(this);
@@ -33,7 +33,7 @@ namespace Robomongo
defLayout->addWidget(defDisplayModeLabel);
_defDisplayModeComboBox = new QComboBox();
QStringList modes;
for (int i = Text;i<=Custom;++i)
for (int i = Text; i <= Custom; ++i)
{
modes.append(convertViewModeToString(static_cast<ViewMode>(i)));
}
@@ -46,7 +46,7 @@ namespace Robomongo
timeZoneLayout->addWidget(timeZoneLabel);
_timeZoneComboBox = new QComboBox();
QStringList times;
for (int i = Utc;i<=LocalTime;++i)
for (int i = Utc; i <= LocalTime; ++i)
{
times.append(convertTimesToString(static_cast<SupportedTimes>(i)));
}
@@ -59,7 +59,7 @@ namespace Robomongo
uuidEncodingLayout->addWidget(uuidEncodingLabel);
_uuidEncodingComboBox = new QComboBox();
QStringList uuids;
for (int i = DefaultEncoding;i<=PythonLegacy;++i)
for (int i = DefaultEncoding; i <= PythonLegacy; ++i)
{
uuids.append(convertUUIDEncodingToString(static_cast<UUIDEncoding>(i)));
}
+112 -34
View File
@@ -9,12 +9,25 @@
#include <QPushButton>
#include <QFileDialog>
#include <QComboBox>
#include <QMessageBox>
#include <QFileInfo>
#include <QFrame>
#include "robomongo/core/utils/QtUtils.h"
#include "robomongo/gui/GuiRegistry.h"
#include "robomongo/gui/utils/ComboBoxUtils.h"
#include "robomongo/core/settings/ConnectionSettings.h"
#include "robomongo/core/settings/SshSettings.h"
#include "robomongo/gui/utils/GuiConstants.h"
namespace {
const QString askPasswordText = "Ask for password each time";
const QString askPassphraseText = "Ask for passphrase each time";
bool isFileExists(const QString &path) {
QFileInfo fileInfo(path);
return fileInfo.exists() && fileInfo.isFile();
}
}
namespace Robomongo
{
@@ -26,12 +39,16 @@ namespace Robomongo
_sshSupport->setStyleSheet("margin-bottom: 7px");
_sshSupport->setChecked(info->enabled());
_askForPassword = new QCheckBox(askPasswordText);
_askForPassword->setChecked(info->askPassword());
VERIFY(connect(_askForPassword, SIGNAL(stateChanged(int)), this, SLOT(askForPasswordStateChanged(int))));
_sshHostName = new QLineEdit(QtUtils::toQString(info->host()));
_userName = new QLineEdit(QtUtils::toQString(info->userName()));
_sshPort = new QLineEdit(QString::number(info->port()));
_sshPort->setFixedWidth(80);
QRegExp rx("\\d+");//(0-65554)
_sshPort->setFixedWidth(40);
QRegExp rx("\\d+"); //(0-65554)
_sshPort->setValidator(new QRegExpValidator(rx, this));
_security = new QComboBox();
@@ -40,14 +57,16 @@ namespace Robomongo
_passwordBox = new QLineEdit(QtUtils::toQString(info->userPassword()));
_passwordBox->setEchoMode(QLineEdit::Password);
_passwordEchoModeButton = new QPushButton(tr("Show"));
_passwordEchoModeButton = new QPushButton;
_passwordEchoModeButton->setIcon(GuiRegistry::instance().hideIcon());
VERIFY(connect(_passwordEchoModeButton, SIGNAL(clicked()), this, SLOT(togglePasswordEchoMode())));
_privateKeyBox = new QLineEdit(QtUtils::toQString(info->privateKeyFile()));
_passphraseBox = new QLineEdit(QtUtils::toQString(info->passphrase()));
_passphraseBox->setEchoMode(QLineEdit::Password);
_passphraseEchoModeButton = new QPushButton(tr("Show"));
_passphraseEchoModeButton = new QPushButton;
_passphraseEchoModeButton->setIcon(GuiRegistry::instance().hideIcon());
VERIFY(connect(_passphraseEchoModeButton, SIGNAL(clicked()), this, SLOT(togglePassphraseEchoMode())));
_passwordLabel = new QLabel("User Password:");
@@ -103,6 +122,7 @@ namespace Robomongo
connectionLayout->addWidget(_sshPassphraseLabel, 8, 0);
connectionLayout->addWidget(_passphraseBox, 8, 1);
connectionLayout->addWidget(_passphraseEchoModeButton, 8, 2);
connectionLayout->addWidget(_askForPassword, 9, 1, 1, 2);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addWidget(_sshSupport);
@@ -122,32 +142,70 @@ namespace Robomongo
VERIFY(connect(_sshSupport, SIGNAL(stateChanged(int)), this, SLOT(sshSupportStateChange(int))));
_sshHostName->setFocus();
#ifdef Q_OS_MAC
_passwordEchoModeButton->setMaximumWidth(_selectPrivateFileButton->width());
_passphraseEchoModeButton->setMaximumWidth(_selectPrivateFileButton->width());
#else
_passwordEchoModeButton->setMinimumWidth(_selectPrivateFileButton->width());
_passphraseEchoModeButton->setMinimumWidth(_selectPrivateFileButton->width());
#endif
// Attempt to fix the issue for Windows High DPI button height is slightly taller than other widgets
#ifdef Q_OS_WIN
_passwordEchoModeButton->setMaximumHeight(HighDpiContants::WIN_HIGH_DPI_BUTTON_HEIGHT);
_passphraseEchoModeButton->setMaximumHeight(HighDpiContants::WIN_HIGH_DPI_BUTTON_HEIGHT);
_selectPrivateFileButton->setMaximumHeight(HighDpiContants::WIN_HIGH_DPI_BUTTON_HEIGHT);
#endif
}
bool SshTunnelTab::isSshSupported() const
bool SshTunnelTab::sshEnabled() const
{
return _sshSupport->isChecked();
}
void SshTunnelTab::sshSupportStateChange(int value)
void SshTunnelTab::setPasswordFieldsEnabled(bool enabled)
{
_sshHostName->setEnabled(value);
_userName->setEnabled(value);
_sshPort->setEnabled(value);
_security->setEnabled(value);
_sshPassphraseLabel->setEnabled(enabled);
_passphraseBox->setEnabled(enabled);
_passwordBox->setEnabled(enabled);
_passwordLabel->setEnabled(enabled);
_passphraseEchoModeButton->setEnabled(enabled);
_passwordEchoModeButton->setEnabled(enabled);
}
_sshPrivateKeyLabel->setEnabled(value);
_privateKeyBox->setEnabled(value);
_selectPrivateFileButton->setEnabled(value);
_sshAddressLabel->setEnabled(value);
_sshUserNameLabel->setEnabled(value);
_sshAuthMethodLabel->setEnabled(value);
void SshTunnelTab::askForPasswordStateChanged(int state)
{
bool checked = state == Qt::Checked;
if (checked) {
_passphraseBox->setText("");
_passwordBox->setText("");
}
_sshPassphraseLabel->setEnabled(value);
_passphraseBox->setEnabled(value);
setPasswordFieldsEnabled(!checked && _sshSupport->isChecked());
}
_passwordBox->setEnabled(value);
_passwordLabel->setEnabled(value);
void SshTunnelTab::sshSupportStateChange(int state)
{
bool checked = state == Qt::Checked;
_sshHostName->setEnabled(checked);
_userName->setEnabled(checked);
_sshPort->setEnabled(checked);
_security->setEnabled(checked);
_sshPrivateKeyLabel->setEnabled(checked);
_privateKeyBox->setEnabled(checked);
_selectPrivateFileButton->setEnabled(checked);
_sshAddressLabel->setEnabled(checked);
_sshUserNameLabel->setEnabled(checked);
_sshAuthMethodLabel->setEnabled(checked);
_askForPassword->setEnabled(checked);
askForPasswordStateChanged(_askForPassword->checkState());
if (checked)
_sshHostName->setFocus();
}
void SshTunnelTab::securityChange(const QString& method)
@@ -165,6 +223,7 @@ namespace Robomongo
_passwordBox->setVisible(!isKey);
_passwordLabel->setVisible(!isKey);
_passwordEchoModeButton->setVisible(!isKey);
_askForPassword->setText(isKey ? askPassphraseText : askPasswordText);
}
void SshTunnelTab::setPrivateFile()
@@ -196,37 +255,56 @@ namespace Robomongo
// return SSHInfo::PASSWORD;
// }
void SshTunnelTab::accept()
bool SshTunnelTab::accept()
{
bool sshEnabled = _sshSupport->isChecked();
QString authMethod = _security->currentText() == "Private Key" ? "publickey" : "password";
// Check for existence of the private key file name
// and try to expand "~" character when needed
QString privateKey = _privateKeyBox->text();
if (sshEnabled && authMethod == "publickey" && !isFileExists(privateKey)) {
bool failed = true;
// Try to expand "~" if available
if (privateKey.startsWith ("~/")) {
privateKey.replace (0, 1, QDir::homePath());
if (isFileExists(privateKey)) {
failed = false;
}
}
if (failed) {
QString message = QString("Private key file \"%1\" doesn't exist").arg(privateKey);
QMessageBox::information(this, "Settings are incomplete", message);
return false;
}
}
SshSettings *info = _settings->sshSettings();
info->setHost(QtUtils::toStdString(_sshHostName->text()));
info->setPort(_sshPort->text().toInt());
info->setUserName(QtUtils::toStdString(_userName->text()));
info->setUserPassword(QtUtils::toStdString(_passwordBox->text()));
// info->setPublicKeyFile("");
info->setPrivateKeyFile(QtUtils::toStdString(_privateKeyBox->text()));
info->setAskPassword(_askForPassword->isChecked());
info->setPrivateKeyFile(QtUtils::toStdString(privateKey));
info->setPassphrase(QtUtils::toStdString(_passphraseBox->text()));
if (_security->currentText() == "Private Key") {
info->setAuthMethod("publickey");
} else {
info->setAuthMethod("password");
}
info->setEnabled(_sshSupport->isChecked());
info->setAuthMethod(QtUtils::toStdString(authMethod));
info->setEnabled(sshEnabled);
return true;
}
void SshTunnelTab::togglePasswordEchoMode()
{
bool isPassword = _passwordBox->echoMode() == QLineEdit::Password;
_passwordBox->setEchoMode(isPassword ? QLineEdit::Normal: QLineEdit::Password);
_passwordEchoModeButton->setText(isPassword ? tr("Hide"): tr("Show"));
_passwordEchoModeButton->setIcon(isPassword ? GuiRegistry::instance().showIcon() : GuiRegistry::instance().hideIcon());
}
void SshTunnelTab::togglePassphraseEchoMode()
{
bool isPassword = _passphraseBox->echoMode() == QLineEdit::Password;
_passphraseBox->setEchoMode(isPassword ? QLineEdit::Normal: QLineEdit::Password);
_passphraseEchoModeButton->setText(isPassword ? tr("Hide"): tr("Show"));
_passphraseEchoModeButton->setIcon(isPassword ? GuiRegistry::instance().showIcon() : GuiRegistry::instance().hideIcon());
}
}
+11 -5
View File
@@ -23,21 +23,27 @@ namespace Robomongo
public:
SshTunnelTab(ConnectionSettings *settings);
void accept();
bool isSshSupported() const;
bool accept();
/**
* @return true if use SSH checkbox is checked, false otherwise
*/
bool sshEnabled() const;
private Q_SLOTS:
void sshSupportStateChange(int val);
void sshSupportStateChange(int checked);
void askForPasswordStateChanged(int checked);
void securityChange(const QString& val);
void setPrivateFile();
void togglePasswordEchoMode();
void togglePassphraseEchoMode();
private:
// SSHInfo::SupportedAuthenticationMetods selectedAuthMethod();
void setPasswordFieldsEnabled(bool enabled);
private:
QCheckBox *_sshSupport;
QCheckBox *_askForPassword;
QLineEdit *_sshHostName;
QLineEdit *_userName;
QLineEdit *_sshPort;
+410
View File
@@ -0,0 +1,410 @@
#include "robomongo/gui/dialogs/SSLTab.h"
#include <QApplication>
#include <QLabel>
#include <QLineEdit>
#include <QGridLayout>
#include <QCheckBox>
#include <QPushButton>
#include <QFileDialog>
#include <QComboBox>
#include <QRadioButton>
#include <QFileInfo>
#include <QMessageBox>
#include <mongo/util/net/ssl_options.h>
#include <mongo/util/net/ssl_manager.h>
#include "robomongo/core/utils/QtUtils.h"
#include "robomongo/core/settings/ConnectionSettings.h"
#include "robomongo/core/settings/SshSettings.h"
#include "robomongo/core/settings/SslSettings.h"
#include "robomongo/gui/GuiRegistry.h"
#include "robomongo/gui/utils/ComboBoxUtils.h"
#include "robomongo/gui/utils/GuiConstants.h"
namespace
{
// Helper function: Check file existence, return true if file exists, false otherwise
bool fileExists(const QString &path)
{
QFileInfo fileInfo(path);
return (fileInfo.exists() && fileInfo.isFile());
}
// Helper hint strings
QString const CA_FILE_HINT = " mongo --sslCAFile : Certificate Authority file for SSL";
QString const PEM_FILE_HINT = " mongo --sslPEMKeyFile : PEM certificate/key file for SSL";
QString const PEM_PASS_HINT = " mongo --sslPEMKeyPassword : password for key in PEM file for SSL";
QString const ALLOW_INVALID_HOSTNAME_HINT = " mongo --sslAllowInvalidHostnames : allow connections to servers"
" with non-matching hostnames";
QString const ALLOW_INVALID_CERTIFICATES_HINT = " mongo --sslAllowInvalidCertificates : allow connections to servers"
" with invalid certificates";
QString const CRL_FILE_HINT = " mongo --sslCRLFile : Certificate Revocation List file for SSL";
}
namespace Robomongo
{
SSLTab::SSLTab(ConnectionSettings *connSettings)
: _connSettings(connSettings)
{
const SslSettings* const sslSettings = _connSettings->sslSettings();
// Use SSL section
_useSslCheckBox = new QCheckBox("Use SSL protocol");
_useSslCheckBox->setStyleSheet("margin-bottom: 7px");
VERIFY(connect(_useSslCheckBox, SIGNAL(stateChanged(int)), this, SLOT(useSslCheckBoxStateChange(int))));
// Auth. Method section
_authMethodLabel = new QLabel("Authentication Method: ");
_authMethodComboBox = new QComboBox;
_authMethodComboBox->addItem("Self-signed Certificate");
_authMethodComboBox->addItem("Use CA Certificate");
_authMethodComboBox->setItemData(0, ALLOW_INVALID_CERTIFICATES_HINT, Qt::ToolTipRole);
_authMethodComboBox->setItemData(1, CA_FILE_HINT, Qt::ToolTipRole);
_selfSignedInfoStr = new QLabel("In general, avoid using self-signed certificates unless the network is trusted. "
"If self-signed certificate is used, the communications channel will be encrypted however there will be "
"no validation of server identity.");
_selfSignedInfoStr->setWordWrap(true);
_selfSignedInfoStr->setToolTip(ALLOW_INVALID_CERTIFICATES_HINT);
_caFileLabel = new QLabel("CA Certificate:");
_caFileLabel->setToolTip(CA_FILE_HINT);
_caFilePathLineEdit = new QLineEdit;
_caFileBrowseButton = new QPushButton("...");
_caFileBrowseButton->setMaximumWidth(50);
VERIFY(connect(_caFileBrowseButton, SIGNAL(clicked()), this, SLOT(on_caFileBrowseButton_clicked())));
VERIFY(connect(_authMethodComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(on_authModeComboBox_change(int))));
// PEM file section
_usePemFileCheckBox = new QCheckBox("Use PEM Cert./Key: ");
_usePemFileCheckBox->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
_pemFileInfoStr =
new QLabel("Enable this option to connect to a MongoDB that requires CA-signed client certificates/key file.");
_pemFileInfoStr->setWordWrap(true);
#ifdef Q_OS_WIN
_pemFileInfoStr->setContentsMargins(0,2,0,0); // Top alignment adjustment required only for Windows
#endif
_pemFileLabel = new QLabel("PEM Certificate/Key: ");
_pemFileLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
_pemFileLabel->setToolTip(PEM_FILE_HINT);
_pemFilePathLineEdit = new QLineEdit;
_pemFileBrowseButton = new QPushButton("...");
_pemFileBrowseButton->setMaximumWidth(50);
VERIFY(connect(_usePemFileCheckBox, SIGNAL(toggled(bool)), this, SLOT(on_usePemFileCheckBox_toggle(bool))));
VERIFY(connect(_pemFileBrowseButton, SIGNAL(clicked()), this, SLOT(on_pemKeyFileBrowseButton_clicked())));
// PEM Passphrase section
_pemPassLabel = new QLabel("Passphrase: ");
_pemPassLabel->setToolTip(PEM_PASS_HINT);
_pemPassLineEdit = new QLineEdit;
_pemPassShowButton = new QPushButton;
// Fix for MAC OSX: PEM pass show button was created bigger, making it same size as other pushbuttons
_pemPassShowButton->setMaximumWidth(_pemFileBrowseButton->width());
VERIFY(connect(_pemPassShowButton, SIGNAL(clicked()), this, SLOT(togglePassphraseShowMode())));
togglePassphraseShowMode();
_askPemPassCheckBox = new QCheckBox("Ask for passphrase each time");
_askPemPassCheckBox->setChecked(sslSettings->askPassphrase());
VERIFY(connect(_askPemPassCheckBox, SIGNAL(toggled(bool)), this, SLOT(on_askPemPassCheckBox_toggle(bool))));
// Advanced options
_useAdvancedOptionsCheckBox = new QCheckBox("Advanced Options");
_useAdvancedOptionsCheckBox->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
VERIFY(connect(_useAdvancedOptionsCheckBox, SIGNAL(toggled(bool)), this, SLOT(on_useAdvancedOptionsCheckBox_toggle(bool))));
_crlFileLabel = new QLabel("CRL (Revocation List): ");
_crlFileLabel->setToolTip(CRL_FILE_HINT);
_crlFilePathLineEdit = new QLineEdit;
_crlFileBrowseButton = new QPushButton("...");
_crlFileBrowseButton->setMaximumWidth(50);
VERIFY(connect(_crlFileBrowseButton, SIGNAL(clicked()), this, SLOT(on_crlFileBrowseButton_clicked())));
_allowInvalidHostnamesLabel = new QLabel("Invalid Hostnames: ");
_allowInvalidHostnamesLabel->setToolTip(ALLOW_INVALID_HOSTNAME_HINT);
_allowInvalidHostnamesComboBox = new QComboBox;
_allowInvalidHostnamesComboBox->addItem("Not Allowed");
_allowInvalidHostnamesComboBox->addItem("Allowed");
_allowInvalidHostnamesComboBox->setCurrentIndex(sslSettings->allowInvalidHostnames());
// Layouts
// Auth. method section
QGridLayout* gridLayout = new QGridLayout;
gridLayout->addWidget(_authMethodLabel, 0 ,0);
gridLayout->addWidget(_authMethodComboBox, 0 ,1, 1, 2);
gridLayout->addWidget(_selfSignedInfoStr, 1, 1, 1, 2);
gridLayout->addWidget(_caFileLabel, 2, 0);
gridLayout->addWidget(_caFilePathLineEdit, 2, 1);
gridLayout->addWidget(_caFileBrowseButton, 2, 2);
gridLayout->addWidget(new QLabel(""), 3, 0);
// PEM File Section
gridLayout->addWidget(_usePemFileCheckBox, 4, 0, Qt::AlignTop);
gridLayout->addWidget(_pemFileInfoStr, 4, 1, 1, 2);
gridLayout->addWidget(_pemFileLabel, 5, 0);
gridLayout->addWidget(_pemFilePathLineEdit, 5, 1);
gridLayout->addWidget(_pemFileBrowseButton, 5, 2);
gridLayout->addWidget(_pemPassLabel, 6, 0);
gridLayout->addWidget(_pemPassLineEdit, 6, 1);
gridLayout->addWidget(_pemPassShowButton, 6, 2);
gridLayout->addWidget(_askPemPassCheckBox, 7, 1, 1, 2);
gridLayout->addWidget(new QLabel(""), 8, 0);
// Advanced section
gridLayout->addWidget(_useAdvancedOptionsCheckBox, 9, 0, Qt::AlignTop);
gridLayout->addWidget(_crlFileLabel, 10, 0);
gridLayout->addWidget(_crlFilePathLineEdit, 10, 1);
gridLayout->addWidget(_crlFileBrowseButton, 10, 2);
gridLayout->addWidget(_allowInvalidHostnamesLabel, 11, 0);
gridLayout->addWidget(_allowInvalidHostnamesComboBox, 11, 1, Qt::AlignLeft);
gridLayout->addWidget(new QLabel(""), 12, 0);
QLabel* spacingAfterFirstRow = new QLabel("");
spacingAfterFirstRow->setFixedHeight(5);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->setAlignment(Qt::AlignTop);
mainLayout->addWidget(_useSslCheckBox);
mainLayout->addWidget(spacingAfterFirstRow);
mainLayout->addLayout(gridLayout);
setLayout(mainLayout);
// Load SSL settings to update UI states
_useSslCheckBox->setChecked(sslSettings->sslEnabled());
_authMethodComboBox->setCurrentIndex(!sslSettings->allowInvalidCertificates());
_caFilePathLineEdit->setText(QString::fromStdString(sslSettings->caFile()));
_usePemFileCheckBox->setChecked(sslSettings->usePemFile());
_pemFilePathLineEdit->setText(QString::fromStdString(sslSettings->pemKeyFile()));
_askPemPassCheckBox->setChecked(sslSettings->askPassphrase());
// Load passphrase only if askPassphrase is false
if (!sslSettings->askPassphrase())
{
_pemPassLineEdit->setText(QString::fromStdString(sslSettings->pemPassPhrase()));
}
_useAdvancedOptionsCheckBox->setChecked(sslSettings->useAdvancedOptions());
_allowInvalidHostnamesComboBox->setCurrentIndex(sslSettings->allowInvalidHostnames());
_crlFilePathLineEdit->setText(QString::fromStdString(sslSettings->crlFile()));
// Update UI inter-connected (signal-slot) widget states
on_authModeComboBox_change(_authMethodComboBox->currentIndex());
on_usePemFileCheckBox_toggle(_usePemFileCheckBox->isChecked());
on_askPemPassCheckBox_toggle(_askPemPassCheckBox->isChecked());
on_useAdvancedOptionsCheckBox_toggle(_useAdvancedOptionsCheckBox->isChecked());
// Enable/disable all SSL tab widgets
useSslCheckBoxStateChange(_useSslCheckBox->checkState());
// Attempt to fix issue for Windows High DPI button height is slightly taller than other widgets
#ifdef Q_OS_WIN
_caFileBrowseButton->setMaximumHeight(HighDpiContants::WIN_HIGH_DPI_BUTTON_HEIGHT);
_pemFileBrowseButton->setMaximumHeight(HighDpiContants::WIN_HIGH_DPI_BUTTON_HEIGHT);
_pemPassShowButton->setMaximumHeight(HighDpiContants::WIN_HIGH_DPI_BUTTON_HEIGHT);
_crlFileBrowseButton->setMaximumHeight(HighDpiContants::WIN_HIGH_DPI_BUTTON_HEIGHT);
#endif
}
bool SSLTab::accept()
{
saveSslSettings();
return validate();
}
bool SSLTab::sslEnabled() const
{
return _useSslCheckBox->isChecked();
}
void SSLTab::useSslCheckBoxStateChange(int state)
{
bool isChecked = static_cast<bool>(state);
_authMethodLabel->setDisabled(!isChecked);
_authMethodComboBox->setDisabled(!isChecked);
_selfSignedInfoStr->setDisabled(!isChecked);
_caFileLabel->setDisabled(!isChecked);
_caFilePathLineEdit->setDisabled(!isChecked);
_caFileBrowseButton->setDisabled(!isChecked);
_usePemFileCheckBox->setDisabled(!isChecked);
_pemFileInfoStr->setDisabled(!isChecked);
_pemFileLabel->setDisabled(!isChecked);
_pemFilePathLineEdit->setDisabled(!isChecked);
_pemFileBrowseButton->setDisabled(!isChecked);
_pemPassLabel->setDisabled(!isChecked);
_pemPassLineEdit->setDisabled(!isChecked);
_pemPassShowButton->setDisabled(!isChecked);
_askPemPassCheckBox->setDisabled(!isChecked);
_useAdvancedOptionsCheckBox->setDisabled(!isChecked);
_crlFileLabel->setDisabled(!isChecked);
_crlFilePathLineEdit->setDisabled(!isChecked);
_crlFileBrowseButton->setDisabled(!isChecked);
_allowInvalidHostnamesLabel->setDisabled(!isChecked);
_allowInvalidHostnamesComboBox->setDisabled(!isChecked);
if (isChecked) // Update some widgets only if SSL enabled
{
on_usePemFileCheckBox_toggle(_usePemFileCheckBox->isChecked());
on_askPemPassCheckBox_toggle(_askPemPassCheckBox->isChecked());
}
}
void SSLTab::on_authModeComboBox_change(int index)
{
bool const isCaSigned = static_cast<bool>(index);
_selfSignedInfoStr->setVisible(!isCaSigned);
_caFileLabel->setVisible(isCaSigned);
_caFilePathLineEdit->setVisible(isCaSigned);
_caFileBrowseButton->setVisible(isCaSigned);
if (!isCaSigned)
{
setMinimumHeight(330);
}
}
void SSLTab::on_usePemFileCheckBox_toggle(bool isChecked)
{
_pemFileInfoStr->setVisible(!isChecked);
_pemFileLabel->setVisible(isChecked);
_pemFilePathLineEdit->setVisible(isChecked);
_pemFileBrowseButton->setVisible(isChecked);
_pemPassLabel->setVisible(isChecked);
_pemPassLineEdit->setVisible(isChecked);
_pemPassShowButton->setVisible(isChecked);
_askPemPassCheckBox->setVisible(isChecked);
if(isChecked)
{
setMinimumHeight(440);
}
}
void SSLTab::on_useAdvancedOptionsCheckBox_toggle(bool isChecked)
{
_crlFileLabel->setVisible(isChecked);
_crlFilePathLineEdit->setVisible(isChecked);
_crlFileBrowseButton->setVisible(isChecked);
_allowInvalidHostnamesLabel->setVisible(isChecked);
_allowInvalidHostnamesComboBox->setVisible(isChecked);
if(isChecked)
{
setMinimumHeight(500);
}
}
void SSLTab::on_caFileBrowseButton_clicked()
{
QString const& fileName = openFileBrowseDialog(_caFilePathLineEdit->text());
if (!fileName.isEmpty()) {
_caFilePathLineEdit->setText(fileName);
}
}
void SSLTab::on_pemKeyFileBrowseButton_clicked()
{
QString const& fileName = openFileBrowseDialog(_pemFilePathLineEdit->text());
if (!fileName.isEmpty()) {
_pemFilePathLineEdit->setText(fileName);
}
}
void SSLTab::on_crlFileBrowseButton_clicked()
{
QString const& fileName = openFileBrowseDialog(_crlFilePathLineEdit->text());
if (!fileName.isEmpty()) {
_crlFilePathLineEdit->setText(fileName);
}
}
void SSLTab::togglePassphraseShowMode()
{
bool isPassword = _pemPassLineEdit->echoMode() == QLineEdit::Password;
_pemPassLineEdit->setEchoMode(isPassword ? QLineEdit::Normal : QLineEdit::Password);
_pemPassShowButton->setIcon(isPassword ? GuiRegistry::instance().showIcon() : GuiRegistry::instance().hideIcon());
}
void SSLTab::on_askPemPassCheckBox_toggle(bool checked)
{
//if (_usePemFileCheckBox->isChecked())
//{
_pemPassLabel->setDisabled(checked);
_pemPassLineEdit->setDisabled(checked);
_pemPassShowButton->setDisabled(checked);
if (checked)
{
_pemPassLineEdit->setText(""); // clear passphrase on UI
}
//}
}
bool SSLTab::validate()
{
// Validate existence of files
auto const& resultAndFileName = checkExistenseOfFiles();
if (!resultAndFileName.first)
{
QString const& nonExistingFile = resultAndFileName.second;
QMessageBox errorBox;
errorBox.critical(this, "Error", ("Error: " + nonExistingFile + " file does not exist"));
errorBox.adjustSize();
return false;
}
return true;
}
std::pair<bool,QString> SSLTab::checkExistenseOfFiles() const
{
if (_caFilePathLineEdit->isEnabled() && _caFilePathLineEdit->isVisible()) {
if (!fileExists(_caFilePathLineEdit->text())) {
return {false, "CA-signed certificate"};
}
}
if (_pemFilePathLineEdit->isVisible() && _pemFilePathLineEdit->isEnabled()) {
if (!fileExists(_pemFilePathLineEdit->text())) {
return {false, "PEM Certificate/Key"};
}
}
if (!_crlFilePathLineEdit->text().isEmpty()) {
if (!fileExists(_crlFilePathLineEdit->text())) {
return {false, "CRL (Revocation List)"};
}
}
return {true, ""};
}
void SSLTab::saveSslSettings() const
{
SslSettings* sslSettings = _connSettings->sslSettings();
sslSettings->enableSSL(_useSslCheckBox->isChecked());
sslSettings->setAllowInvalidCertificates(!static_cast<bool>(_authMethodComboBox->currentIndex()));
sslSettings->setCaFile(QtUtils::toStdString(_caFilePathLineEdit->text()));
sslSettings->setUsePemFile(_usePemFileCheckBox->isChecked());
sslSettings->setPemKeyFile(QtUtils::toStdString(_pemFilePathLineEdit->text()));
sslSettings->setAskPassphrase(_askPemPassCheckBox->isChecked());
// save passphrase only if _askPemPassCheckBox is not checked; otherwise don't save and clear saved passphrase
if (!_askPemPassCheckBox->isChecked())
{
sslSettings->setPemPassPhrase(QtUtils::toStdString(_pemPassLineEdit->text()));
}
else
{
sslSettings->setPemPassPhrase("");
}
sslSettings->setUseAdvancedOptions(_useAdvancedOptionsCheckBox->isChecked());
sslSettings->setCrlFile(QtUtils::toStdString(_crlFilePathLineEdit->text()));
sslSettings->setAllowInvalidHostnames(static_cast<bool>(_allowInvalidHostnamesComboBox->currentIndex()));
}
QString SSLTab::openFileBrowseDialog(const QString& initialPath)
{
QString filePath = initialPath;
// If user has previously selected a file, initialize file dialog with that file's
// path and name; otherwise, use user's home directory.
if (filePath.isEmpty())
{
filePath = QDir::homePath();
}
QString fileName = QFileDialog::getOpenFileName(this, tr("Choose File"), filePath);
// Some strange behaviour at least on Mac happens when you close QFileDialog. Focus switched to a different modal
// dialog, not the one that was active before openning QFileDialog.
// http://stackoverflow.com/questions/17998811/window-modal-qfiledialog-pushing-parent-to-background-after-exec
QApplication::activeModalWidget()->raise();
QApplication::activeModalWidget()->activateWindow();
return QDir::toNativeSeparators(fileName);
}
}
+151
View File
@@ -0,0 +1,151 @@
#pragma once
#include <QWidget>
#include "robomongo/core/settings/ConnectionSettings.h"
QT_BEGIN_NAMESPACE
class QLabel;
class QLineEdit;
class QCheckBox;
class QPushButton;
class QRadioButton;
class QComboBox;
QT_END_NAMESPACE
namespace Robomongo
{
class ConnectionSettings;
class SSLTab : public QWidget
{
Q_OBJECT
public:
/**
* @brief Construct SSL tab of ConnectionDialog
* @param settings: Pointer to active connection's ConnectionsSettings member
*/
SSLTab(ConnectionSettings *settings);
/**
* @brief Save dialog input into connection's ssl settings
* @return true on success, false otherwise
*/
bool accept();
/**
* @return true if use SSL checkbox is checked, false otherwise
*/
bool sslEnabled() const;
private Q_SLOTS :
/**
* @brief Disable/enable widgets according to SSL check box state
*/
void useSslCheckBoxStateChange(int checked);
/**
* @brief Disable/enable widgets according to state of authentication method combo box
*/
void on_authModeComboBox_change(int index);
/**
* @brief Disable/enable widgets according to state of use PEM file combo box
*/
void on_usePemFileCheckBox_toggle(bool checked);
/**
* @brief Disable/enable widgets according to state of advanced options combo box
*/
void on_useAdvancedOptionsCheckBox_toggle(bool checked);
/**
* @brief File browsers for SSL related cert/key files
*/
void on_caFileBrowseButton_clicked();
void on_pemKeyFileBrowseButton_clicked();
void on_crlFileBrowseButton_clicked();
/**
* @brief Show/hide client cert's passphrase on Show/Hide button pressed
*/
void togglePassphraseShowMode();
/**
* @brief Enable/disable/clean PEM passphrase widgets section
*/
void on_askPemPassCheckBox_toggle(bool checked);
private:
/**
* @brief Do validation according to user input in UI
* @return true on success, false otherwise
*/
bool validate();
/**
* @brief Check existence of files: CA cert, Client Cert and CRL file
* @return true if all files exist, false any of them does not exist
* @return QString Lable of file which does not exist, empty string if all files exist
*/
std::pair<bool, QString> checkExistenseOfFiles() const;
/**
* @brief Save dialog input into connection's ssl settings
*/
void saveSslSettings() const;
/**
* @brief
* @param initialPath Previously selected file path
* @return Selected file absolute path and file name
*/
QString openFileBrowseDialog(const QString& initialPath);
/**
* @brief Main checkbox to disable/enable all other SSL tab widgets
*/
QCheckBox *_useSslCheckBox;
/**
* @brief Auth. Method widgets
*/
QLabel *_authMethodLabel;
QComboBox *_authMethodComboBox;
QLabel *_selfSignedInfoStr;
QLabel *_caFileLabel;
QLineEdit *_caFilePathLineEdit;
QPushButton *_caFileBrowseButton;
/**
* @brief PEM file widgets
*/
QCheckBox* _usePemFileCheckBox;
QLabel* _pemFileInfoStr;
QLabel* _pemFileLabel;
QLineEdit *_pemFilePathLineEdit;
QPushButton *_pemFileBrowseButton;
QLabel* _pemPassLabel;
QLineEdit* _pemPassLineEdit;
QPushButton* _pemPassShowButton;
QCheckBox* _askPemPassCheckBox;
/**
* @brief Advanced options widgets
*/
QCheckBox* _useAdvancedOptionsCheckBox;
QLabel *_crlFileLabel;
QLineEdit *_crlFilePathLineEdit;
QPushButton *_crlFileBrowseButton;
QLabel *_allowInvalidHostnamesLabel;
QComboBox *_allowInvalidHostnamesComboBox;
/**
* @brief Pointer to active connection's settings
*/
ConnectionSettings *const _connSettings;
};
} /* end of Robomongo namespace */
+11 -7
View File
@@ -33,12 +33,13 @@ namespace Robomongo
_close->setIcon(QIcon(":/robomongo/icons/close_2_16x16.png"));
_close->setToolButtonStyle(Qt::ToolButtonIconOnly);
_close->setIconSize(QSize(16, 16));
_close->hide(); // We do not need close button because ESC works
_findLine->setAlignment(Qt::AlignLeft | Qt::AlignAbsolute);
QHBoxLayout *layout = new QHBoxLayout();
layout->setContentsMargins(6, 0, 6, 0);
layout->setSpacing(4);
layout->addWidget(_close);
layout->setContentsMargins(2, 0, 6, 0);
layout->setSpacing(7);
layout->addWidget(_findLine);
layout->addWidget(_next);
layout->addWidget(_prev);
@@ -50,8 +51,8 @@ namespace Robomongo
QVBoxLayout *mainLayout = new QVBoxLayout();
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->setSpacing(0);
mainLayout->addWidget(_scin);
mainLayout->addWidget(_findPanel);
mainLayout->addWidget(_scin, 1);
mainLayout->addWidget(_findPanel, 0, Qt::AlignBottom);
setLayout(mainLayout);
_findPanel->hide();
@@ -71,14 +72,17 @@ namespace Robomongo
bool isFocusScin = _scin->isActiveWindow();
bool isShowFind = _findPanel->isVisible();
if (Qt::Key_Escape == keyEvent->key() && isFocusScin && isShowFind) {
// Hide & Show of Scintilla widget solves problem of UI blinking
_scin->hide();
_findPanel->hide();
_scin->setFocus();
_scin->show();
return keyEvent->accept();
} else if (Qt::Key_Return == keyEvent->key() && (keyEvent->modifiers() & Qt::ShiftModifier) && isFocusScin && isShowFind) {
goToPrevElement();
} else if (Qt::Key_Return == keyEvent->key() && isFocusScin && isShowFind) {
goToNextElement();
} else if (((keyEvent->modifiers() & Qt::ControlModifier) && keyEvent->key()==Qt::Key_F) && isFocusScin) {
} else if (((keyEvent->modifiers() & Qt::ControlModifier) && keyEvent->key() == Qt::Key_F) && isFocusScin) {
_findPanel->show();
_findLine->setFocus();
_findLine->selectAll();
@@ -122,7 +126,7 @@ namespace Robomongo
_scin->ensureCursorVisible();
}
else {
QMessageBox::warning(this, tr("Search"),tr("The specified text was not found."));
QMessageBox::warning(this, tr("Search"), tr("The specified text was not found."));
}
}
}
+1 -1
View File
@@ -21,7 +21,7 @@ namespace Robomongo
public:
enum
{
HeightFindPanel = 40
HeightFindPanel = 38
};
typedef QFrame BaseClass;
explicit FindFrame(QWidget *parent);
@@ -155,13 +155,13 @@ namespace Robomongo
}
if (((keyEvent->modifiers() & Qt::ControlModifier) &&
(keyEvent->key()==Qt::Key_F4 || keyEvent->key()==Qt::Key_W ||
keyEvent->key()==Qt::Key_T || keyEvent->key()==Qt::Key_Space ||
keyEvent->key()==Qt::Key_F || keyEvent->key()==Qt::Key_Slash))
(keyEvent->key() == Qt::Key_F4 || keyEvent->key() == Qt::Key_W ||
keyEvent->key() == Qt::Key_T || keyEvent->key() == Qt::Key_Space ||
keyEvent->key() == Qt::Key_F || keyEvent->key() == Qt::Key_Slash))
|| keyEvent->key() == Qt::Key_Escape /*|| keyEvent->key() == Qt::Key_Return*/
|| ((keyEvent->modifiers() & Qt::ControlModifier) && (keyEvent->modifiers() & Qt::AltModifier) && keyEvent->key()==Qt::Key_Left)
|| ((keyEvent->modifiers() & Qt::ControlModifier) && (keyEvent->modifiers() & Qt::AltModifier) && keyEvent->key()==Qt::Key_Right)
|| ((keyEvent->modifiers() & Qt::ControlModifier) && (keyEvent->modifiers() & Qt::ShiftModifier) && keyEvent->key()==Qt::Key_C)
|| ((keyEvent->modifiers() & Qt::ControlModifier) && (keyEvent->modifiers() & Qt::AltModifier) && keyEvent->key() == Qt::Key_Left)
|| ((keyEvent->modifiers() & Qt::ControlModifier) && (keyEvent->modifiers() & Qt::AltModifier) && keyEvent->key() == Qt::Key_Right)
|| ((keyEvent->modifiers() & Qt::ControlModifier) && (keyEvent->modifiers() & Qt::ShiftModifier) && keyEvent->key() == Qt::Key_C)
)
{
keyEvent->ignore();
@@ -182,4 +182,18 @@ namespace Robomongo
setMarginWidth(0, _lineNumberMarginWidth);
}
}
void RoboScintilla::setAppropriateBraceMatching() {
#ifdef Q_OS_MAC
// On Mac OS when brace matching is enabled, text
// will blink when you move cursor to some brace or
// when inside braces. This behaviour is not fully fixed
// in QScintilla 2.9.1 and 2.8.4
setBraceMatching(QsciScintilla::NoBraceMatch);
#else
setBraceMatching(QsciScintilla::StrictBraceMatch);
#endif
}
}
@@ -19,6 +19,7 @@ namespace Robomongo
void setIgnoreTabKey(bool ignore) { _ignoreTabKey = ignore; }
int lineNumberMarginWidth() const;
int textWidth(int style, const QString &text);
void setAppropriateBraceMatching();
protected:
void wheelEvent(QWheelEvent *e);
+14 -2
View File
@@ -16,7 +16,9 @@
<file>icons/bson_object_16x16.png</file>
<file>icons/database_16x16.png</file>
<file>icons/loading.gif</file>
<file>icons/loading_ticks_40x40.gif</file>
<file>scripts/esprima.js</file>
<file>scripts/uuidhelpers.js</file>
<file>icons/maximize.gif</file>
<file>icons/text_16x16.png</file>
<file>icons/tree_16x16.png</file>
@@ -28,6 +30,8 @@
<file>icons/mongodb_16x16.png</file>
<file>icons/connect_24x24.png</file>
<file>icons/server_16x16.png</file>
<file>icons/server_imported_16x16.png</file>
<file>icons/replica_set_16x16.png</file>
<file>icons/execute_24x24.png</file>
<file>icons/main_window_icon_old.png</file>
<file>icons/logo-256x256.png</file>
@@ -38,8 +42,14 @@
<file>icons/maximize_highlighted_16x16.png</file>
<file>icons/table_16x16.png</file>
<file>icons/table_highlighted_16x16.png</file>
<file>icons/no_mark_16x16.png</file>
<file>icons/yes_mark_16x16.png</file>
<file>icons/no_mark_24x24.png</file>
<file>icons/no_mark_24x24@2x.png</file>
<file>icons/yes_mark_24x24.png</file>
<file>icons/yes_mark_24x24@2x.png</file>
<file>icons/skip_mark_24x24.png</file>
<file>icons/skip_mark_24x24@2x.png</file>
<file>icons/question_mark_24x24.png</file>
<file>icons/question_mark_24x24@2x.png</file>
<file>icons/time_16x16.png</file>
<file>icons/key_16x16.png</file>
<file>icons/progress_bar.gif</file>
@@ -50,5 +60,7 @@
<file>icons/stop_24x24.png</file>
<file>icons/function_16x16.png</file>
<file>icons/index_16x16.png</file>
<file>icons/hide_64x64.png</file>
<file>icons/show_64x64.png</file>
</qresource>
</RCC>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

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