Compare commits

...
459 Commits
Author SHA1 Message Date
Gokhan Simsek 8b812ffba2 Adding patch versioning changes 2017-06-07 09:18:38 +03:00
Gokhan Simsek 14d3caaa7a Attempt to prevent MAC OS Sierra crash related to updates notification bar 2017-06-07 08:57:30 +03:00
Gokhan Simsek 1124ec22c6 Merge branch 'master' into Rebranding 2017-04-26 11:01:25 +03:00
Gokhan Simsek ab367fc890 Change to fix a merge error 2017-04-26 11:01:04 +03:00
Gokhan Simsek 22c632794f Merge branch 'master' of https://github.com/paralect/robomongo 2017-04-26 10:40:39 +03:00
Gökhan Simsek 0fefd6d443 Update README.md
For version 1.0
2017-04-21 17:15:29 +03:00
Gokhan Simsek 89f24ea07c Updating changelog for 1.0 release 2017-04-21 09:04:07 +03:00
Gokhan Simsek d2a11bc0f2 UpdatesBar: Making the height of bar %30 bigger for MAC 2017-04-20 15:08:21 +03:00
Gokhan Simsek 575455b10e Removing the changes done in the previous commit "e858c68: Setting unified light blue color for all links in the application" 2017-04-20 12:45:58 +03:00
Gokhan Simsek e858c68fa6 Setting unified light blue color for all links in the application 2017-04-20 10:55:49 +03:00
Gokhan Simsek 2df26740c6 WelcomeTab: Changing background color from grey to base (white) 2017-04-20 10:52:15 +03:00
Gokhan Simsek 39209ee2b8 WelcomeTab: Making left (What's New) section's header dynamically read from remote server. 2017-04-20 10:50:31 +03:00
Gokhan Simsek 3e9cd11aa3 WelcomeTab: Fix to solve low resolution image problem 2017-04-12 09:43:54 +03:00
Gokhan Simsek f91b2c4a2f UpdatesBar: Adding fix for the problem where some menu items were still shown when focus changes to welcome tab from other shell tabs. Updating softwareID to 8 to receive Robomongo related updates. 2017-04-11 16:02:18 +03:00
Gökhan Simsek a2b1a07a4f Update RuntimeDependencies.md
Adding more debugging commands and options for Linux
2017-04-05 11:25:50 +03:00
Gokhan Simsek 82b5f434f9 Adding fix to solve problem where program was failing to start due to relocation errors during runtime shared library search on Linux 2017-04-05 09:33:46 +03:00
Gokhan Simsek a55b7398da EulaDialog: Adding first launch size which is almost the half of primary screen width and height 2017-03-30 12:54:44 +03:00
Gokhan Simsek d9b37b2094 Rebranding from RoboM to Robomongo 2017-03-30 11:26:50 +03:00
Gokhan Simsek 230c5f37bc ConnectionBasicTab: Adding fix for the cosmetic issue when creating new replica set connection, plus and minus buttons alignment issue for MAC and Linux. 2017-03-29 16:08:18 +03:00
Gokhan Simsek 561bdbf2b4 Changing shortcut for "Open/Refresh Welcome Tab" to "CTRL+SHIFT+W". Disabling File, Open, Save, Save As menu items when there is only Welcome Tab. 2017-03-27 09:29:10 +03:00
Gokhan Simsek 0bafec7ce5 To fix the vendor string on Windows programs list 2017-03-27 09:23:28 +03:00
Gokhan Simsek 34dca72a09 Fix: #501: Display Dates in - Local Timezone shows wrong date 2017-03-27 09:22:36 +03:00
Gokhan Simsek 428b576acf Fix: #1309: Time zone shown as "000:00" when UTC +00:00 is used as local time zone 2017-03-23 14:26:06 +03:00
Gokhan Simsek f26a08d9bc Fix: #1159: Timestamp value is represented incorrectly 2017-03-23 14:17:39 +03:00
Gokhan Simsek 92ae9affd6 ReplicaSets: #1302: Some enhancements and more informational error messages for the issue where same set name is used with different members. 2017-03-22 12:06:30 +03:00
Gokhan Simsek d52cd2e0c0 ReplicaSets: Adding "Set Name" option into connection basic tab UI and "setNameUserEntered" parameter into config file. 2017-03-21 18:41:32 +03:00
Gokhan Simsek e11cbd1a26 ReplicaSets: Renaming config file parameter "setName" as "cachedSetName" 2017-03-21 14:53:00 +03:00
Gokhan Simsek 4e58a6a6b4 EulaDialog: Re-enabling first license page for windows, disabling second email form page for linux 2017-03-21 09:44:32 +03:00
Gokhan Simsek e01de676fa ReplicaSets: #1302 - To avoid the problem when same set name is used with different members, which is not supported configuration, Robomongo might connect to wrong set. 2017-03-20 09:35:25 +03:00
Gokhan Simsek 9e43802e2a WelcomeTab: Using http for file downloads on Linux due to crashes and unstable behaviors 2017-03-17 10:09:12 +03:00
Gokhan Simsek f02f4f6116 Re-enabling first start connections dialog which was disabled for development purposes 2017-03-16 19:03:39 +03:00
Gokhan Simsek 306689f30d EulaDialog: Removing email form page for Linux 2017-03-16 18:39:43 +03:00
Gökhan Simsek 43bf28968b Update BuildingRobomongo.md 2017-03-15 13:13:13 +03:00
Gökhan Simsek df6e9a57d8 Updating Centos build steps 2017-03-13 11:51:31 +03:00
Gokhan Simsek cbcafd28c9 Minor re-factoring 2017-03-10 14:57:55 +03:00
Gokhan Simsek 4f181ec460 UpdatesBar: Adding functionality to auto-adjust height for updates label to fix issues with set word wrap problems. EulaDialog: Adding one space before main body text. 2017-03-10 11:39:27 +03:00
Gokhan Simsek a72583df76 Code Review: All changes between 1.0.0 (this commit) and 1.0.0-RC1 (commit 496f5c2, 1 Feb 2017). No behavioral changes. 2017-03-09 18:27:45 +03:00
Gokhan Simsek 8f508e5e0f Removing connection setting's program-lifetime uniqueID solution which was replaced with persistent (config. file) UUID solution 2017-03-06 19:49:38 +03:00
Gokhan Simsek 536f75fdb4 WelcomeTab: Making 'all logs button' fully left aligned for MAC 2017-03-06 16:00:13 +03:00
Gokhan Simsek f37a070eda WelcomeTab: Showing blog section widgets after loading what's new section widgets. 2017-03-06 12:47:41 +03:00
Gokhan Simsek 51c1267bf4 WelcomeTab: Disabling Recent Connections feature on UI, settings file and code. 2017-03-04 16:48:28 +03:00
Gokhan Simsek 6a0b972aac WelcomeTab: Adding 'Connect' tool button plus the clear all action instead of clear all button 2017-03-04 15:05:07 +03:00
Gokhan Simsek 94168f02ca Rebranding: Renaming config folder to robom and config file to robom.json 2017-03-03 19:51:48 +03:00
Gokhan Simsek 88c8dfa26e WelcomeTab: Fixing major problem which was causing cache files not to be saved 2017-03-03 19:49:58 +03:00
Gokhan Simsek c8050f0edd Rebranding: Major renaming to RoboM and 3T Software Labs Ltd 2017-03-03 19:04:16 +03:00
Gokhan Simsek e3d90e63e8 CallHome: Adding "os" parameter to send operating system information 2017-03-03 15:19:14 +03:00
Gokhan Simsek ec20a3e4c9 EulaDialog: Adding code for posting email, name and last name. Removing two checkboxes. 2017-03-03 15:01:34 +03:00
Gokhan Simsek 83bac6b8aa EulaDialog: Not showing first page(license) which is already shown in installer and hiding some buttons on second page on Windows.
Updates Bar: Not showing the bar if the string sent from server is empty.
2017-03-01 15:59:35 +03:00
Gokhan Simsek 34025743a1 Some refactoring for 1.0.0 changes, no functional changes. 2017-03-01 11:26:37 +03:00
Gokhan Simsek ff02a303c7 SettingsManager: Change of config. file path due to re-branding to 3T. 2017-02-28 14:10:53 +03:00
Gokhan Simsek 4552576fa3 Minor change: Fixing MAC build error. Disabling debug code. 2017-02-28 11:33:25 +03:00
Gokhan Simsek cc2c834359 WelcomeTab: Adding dynamic resizing functionality for text, image and blogs sections which are shown 60, 50 and 28 percent of tab respectively. Resizing happens any time tab or main window size is changed by user. 2017-02-28 10:35:45 +03:00
Gokhan Simsek b746aeb729 WelcomeTab: Adding code to show main text and image 50-60 percent and blogs section 30 percent of whole tab width which is calculated and adjusted every time the tab is opened or refreshed. 2017-02-27 20:16:20 +03:00
Gokhan Simsek 686fc08cef Not showing main window updates bar if there is network error. Disabling welcome tab's right click menu. 2017-02-27 15:18:16 +03:00
Gokhan Simsek 9d74aae3f5 WelcomeTab: Polishing left layout when there is no image or/and text is available (no cache, no internet). Not showing "What's New" header when there is no image and no text. 2017-02-27 11:14:51 +03:00
Gokhan Simsek b3fd99eb1b WelcomeTab: Fixing cropped image and text layout problems. Refactoring picture download handler. 2017-02-25 15:43:07 +03:00
Gokhan Simsek bf111c97f1 AboutDialog: Fixing broken robomongo link 2017-02-25 15:40:13 +03:00
Gokhan Simsek ac8d4cf899 Disabling welcome tab's icon for MAC. Adding initial size for setup wizard. 2017-02-24 16:41:33 +03:00
Gokhan Simsek 04bd7fbffa WelcomeTab: Adding a better adjusted icon. 2017-02-24 12:18:48 +03:00
Gokhan Simsek 8167e8d944 Adding functionality import connections to version 1.0.0 2017-02-24 11:49:57 +03:00
Gokhan Simsek 6c3174599a WelcomeTab: Adding functionality when 'Open/Refresh Welcome Tab' menu item is clicked, show/focus and reload welcome tab if tab is not closed; if tab is closed, insert it as first tab. 2017-02-24 11:28:23 +03:00
Gokhan Simsek 76aca637e8 Adding config file directory automatically created with PROJECT_VERSION.
WelcomeTab: Adding load/save to cache for RSS feed XML.
2017-02-24 09:05:39 +03:00
Gokhan Simsek 832d843ea1 WelcomeTab: To achieve much faster loading, adding cache mechanism for what's new section's main picture and text 2017-02-23 11:50:06 +03:00
Gokhan Simsek 9aa702efcd WelcomeTab: Adding underline on mouse hover functionality for blog links 2017-02-22 12:20:04 +03:00
Gokhan Simsek cde6d9eaae Doing unzipping/reading file content in-memory instead of extracting/reading from file. Fixing build error related to qzipreader.h for MAC/Linux. 2017-02-22 10:41:01 +03:00
Gokhan Simsek 91db033e3a WelcomeTab: Adding menu action 'Window > Open Welcome Tab' to be able to re-open/refresh welcome tab when closed. 2017-02-21 16:51:51 +03:00
Gokhan Simsek 7d6c0af7e7 WelcomeTab: Adding functionality to be able to download/load tab's main text, picture and blogs info rss from network. Changing maximum shown blog count to 10. 2017-02-21 16:29:23 +03:00
Gokhan Simsek d0c2345044 CallHome: Adding functionality to unzip 3T zip files (more specifically *.dat files) 2017-02-21 15:27:33 +03:00
Gokhan Simsek 51c4b93bc7 Adding one space at the beginning of updates bar's text. Making updates bar non-movable. Minor white space change in about dialog text. 2017-02-20 12:44:15 +03:00
Gokhan Simsek 242acbcd9d CallHome: Adding "checkForUpdates" flag into config file. Adding related "Check For Udates" menu action into main window "Options" menu. Not checking updates if the related flag is false. 2017-02-19 01:53:36 +03:00
Gokhan Simsek 3079801e0f CallHome: Adding code for first update check (30secs after program start) and hourly periodic update checks. 2017-02-19 01:24:11 +03:00
Gokhan Simsek fdc02400b9 CallHome: Adding updates bar into main window 2017-02-19 00:52:38 +03:00
Gokhan Simsek 499b0da006 CallHome: Adding functionality to find & save AnonymousID from the settings files inside .3T folder 2017-02-18 19:01:18 +03:00
Gokhan Simsek a7caf02ec6 Adding Qt XML module into the project 2017-02-18 18:57:03 +03:00
Gokhan Simsek 70b819700d CallHome: Renaming uuid to anonymousID in config file 2017-02-18 14:24:56 +03:00
Gokhan Simsek b1aca90888 SetupWizard: Adding acceptedEulaVersions array of strings into the config file. and the related functionality. 2017-02-17 18:34:08 +03:00
Gokhan Simsek 73ccc5d3ad SetupWizard: Converting eula dialog into a two page-setup wizard. Minor changes on about dialog. 2017-02-17 10:56:40 +03:00
Gokhan Simsek 47d3fd7db9 AboutDialog: Adding GNU license agreement tab. Updating names according to rebranding. 2017-02-15 17:33:30 +03:00
Gokhan Simsek 56d2d7cb12 Rebranding: Changing executable name to "studio-3t-robo" for Windows and Linux. Updating bin/run for Linux. 2017-02-15 11:55:22 +03:00
Gokhan Simsek ac3c078141 Rebranding: Name changes for MAC build effecting run, install and packing. 2017-02-15 09:54:09 +03:00
Gokhan Simsek 0110a0df8d Rebranding: Renaming from "Studio 3T Robo" and "3T Software Labs" in UI and cmake files. 2017-02-14 16:56:40 +03:00
Gokhan Simsek c1d6b51075 WelcomeTab: Minor change to fix a build error for MAC/Linux. 2017-02-14 14:02:58 +03:00
Gokhan Simsek bc6c6a36da WelcomeTab: Adding left and right vertical layouts. Moving blog posts section to the right side of the page. 2017-02-14 10:45:04 +03:00
Gokhan Simsek 806d76d9a2 WelcomeTab: For much faster image loading, adding capability to save/use cached image files instead of downloading from internet. 2017-02-13 20:16:55 +03:00
Gokhan Simsek a0a476b57d Minor change to fix MAC/Linux build errors. 2017-02-13 19:00:22 +03:00
Gokhan Simsek d5dd24fa51 WelcomeTab: Adding functionality to be able to delete recent connections one by one. 2017-02-12 19:41:08 +03:00
Gokhan Simsek 42561be37e WelcomeTab: Adding underline on mouse hover feature for recent connection items only. 2017-02-09 20:09:55 +03:00
Gokhan Simsek c2fdee9cf8 WelcomeTab: Adding functionality where removing a connection from connections list dialog will remove related recent connection label and related entry from config. file. 2017-02-09 14:02:06 +03:00
Gokhan Simsek 9972ee4113 Improving connection setting's program-lifetime uniqueID by replacing with persistent (config. file) UUID solution used in WelcomeTab and replica set cached set name feature. 2017-02-09 12:45:30 +03:00
Gokhan Simsek 846b0ce9fd WelcomeTab: Adding functionality to load/save recent connections from/into config. file. 2017-02-08 19:57:43 +03:00
Gokhan Simsek fe78fe4416 WelcomeTab: Adding first working version of Recent Connections feature without saving into config. file. 2017-02-08 12:33:54 +03:00
Gokhan Simsek 54d03ae788 WelcomeTab: Adding and showing an image downloaded from an internet URL. 2017-02-06 20:59:55 +03:00
Gokhan Simsek 55f895a7f5 Adding Qt Network module into cmake project to use in Welcome Tab. 2017-02-06 20:55:32 +03:00
Gokhan Simsek 22b557605b WelcomeTab: Adding first basic working version with static data and with 'What's New' and 'Blog Posts' sections 2017-02-06 12:25:45 +03:00
Gokhan Simsek 16ed6e844a EulaDialog: Adding UUID generation, eulaAccepted flag & UUID string into config file and related functionality. 2017-02-04 14:29:05 +03:00
Gokhan Simsek d96b3de97a Updating version to 1.0.0 and main window title to 1.0 2017-02-04 10:30:20 +03:00
Gokhan Simsek bd0436ffa0 EulaDialog: Adding first basic working version 2017-02-04 10:21:16 +03:00
Gokhan Simsek 496f5c256f Minor update for change log. 2017-02-01 16:43:51 +03:00
Gökhan Simsek 6617d07e1e Update README.md 2017-02-01 16:42:20 +03:00
Gökhan Simsek 99837f0b8f Update README.md 2017-02-01 16:39:53 +03:00
Gokhan Simsek 6ed27b7b83 Updating change log for 1.0-RC1 2017-02-01 15:24:05 +03:00
Gokhan Simsek 3247fd77d6 Replica Set: To fix the problem where shell tab does not update primary address when checked after 10 minutes primary changed. 2017-02-01 13:21:29 +03:00
Gokhan Simsek f9b9679b5e ReplicaSet: Minor text adjustment for test connection (diagnostics) dialog 2017-01-30 11:56:32 +03:00
Gokhan Simsek 685d264ab7 Updating icon for 'Open Shell' menu item for MAC due to alignment problems. 2017-01-26 18:03:18 +03:00
Gokhan Simsek 2355f0cb54 Updating main window title into short version "Robomongo 1.0-RC1" 2017-01-26 11:28:29 +03:00
Gokhan Simsek 68674fb1c2 ReplicaSet: Updating member add/remove buttons to support MAC style, making them absolute right aligned, decreasing vertical spacing between columns for better look. 2017-01-25 13:25:25 +03:00
Gokhan Simsek 0e1a942ecd Removing one improvement because it breaks 'show log' functionality. 2017-01-24 11:28:34 +03:00
Gokhan Simsek d01ce11e47 ReplicaSet: Clearing cached set name when a connections settings is changed & saved. Adding set's first member's hostname & port info into 'test' connection dialog. 2017-01-24 09:13:25 +03:00
Gokhan Simsek eee9e1cbd4 ReplicaSet: Always expand replica set folder, if an operation failed due unreachable primary. 2017-01-22 18:43:53 +03:00
Gokhan Simsek 54e2da3dc4 ReplicaSet: Adding additional check to detect & give error if any of the replica set member items does not contain ":" character between hostname and port after user clicks save on Connection Settings dialog. 2017-01-20 19:06:10 +03:00
Gokhan Simsek 6887850eae ReplicaSet: For cloned connections from connections dialog, cached set name should be set to empty string. 2017-01-20 18:50:35 +03:00
Gokhan Simsek ab16885112 ReplicaSet: Set replica set folder not-expanded after first connection, not-expanded after connection refresh, expanded after replica set folder user-manual-extending and after replica set folder context menu 'refresh' action. 2017-01-20 18:33:48 +03:00
Gokhan Simsek 5f3ff06403 Code Review: All changes between 1.0.0-RC1 (this commit) and 0.9.0 Final (commit 0786489, 15 october). No behavioral changes. 2017-01-20 16:35:06 +03:00
Gokhan Simsek 81f872fdaf Disabling unfinished export URI connection string feature 2017-01-17 18:38:55 +03:00
Gokhan Simsek 0ec739a2b7 Fix for the problem where all delete/remove document related operations were causing all related/unrelated tabs to re-run. 2017-01-17 18:09:11 +03:00
Gokhan Simsek 752bc51452 Implementing more maintainable solution to handle import using const static list of config file & import function pairs. 2017-01-17 17:37:29 +03:00
Gokhan Simsek 3a23c62070 Changing import behavior to find & import only from the latest version instead of importing from all old versions one by one. 2017-01-17 17:30:50 +03:00
Gokhan Simsek 4512b4c998 ReplicaSet: Adding more handlers for replica set item edit. Auto-remove white space; auto-delete item from widget if item text is empty; auto-remove non-numeric characters from port sub string; if there is no numeric character in port string, auto-add 27017 as default port number. 2017-01-17 10:39:26 +03:00
Gokhan Simsek 684d2abcdc ReplicaSet: Adding duplicate member checker & error message and adding plus and minus icons for replica set member add/remove. Adding 'Attributes' header into connections list dialog. Removing find set button and read preference from connection basic tab. 2017-01-16 16:16:39 +03:00
Gokhan Simsek db3eb8872c Updating Robomongo version to 1.0.0-RC1. Adding plus and minus sign icons for replica set member add/remove. 2017-01-16 15:29:29 +03:00
Gokhan Simsek 885f388201 Minor changes to clear MAC build warnings. 2017-01-12 11:17:53 +03:00
Gokhan Simsek 9431f7f2a8 ReplicaSet: Minor changes to fix some build errors for Linux. 2017-01-11 10:52:37 +03:00
Gokhan Simsek f7752fd2b7 ReplicaSet: Handle long lasting (e.g. overnight) connections which causes replica set monitor to be null. 2017-01-10 11:53:49 +03:00
Gokhan Simsek e4b2c2262e ReplicaSet: Minor change to fix a build error on MAC and Linux. 2017-01-06 23:16:51 +03:00
Gokhan Simsek 9a31b6259d Improving 'remove document' related operations triggered both from explorer and from result widget: Implementing ONE, MULTI and ALL logic, single error window and better logging. Improving Insert Document operation. 2017-01-05 16:38:08 +03:00
Gokhan Simsek 995abe9401 ReplicaSet: Improving re-run of an existing tab for set primary changes. In some cases, need to find the error manually and return error. 2017-01-05 10:10:49 +03:00
Gokhan Simsek a13b39907b ReplicaSet: Improving handler for 'Remove All Documents' and 'Remove Document' for single server and replica sets. Improved logging for fail and success cases. 2017-01-05 10:08:06 +03:00
Gökhan Simsek c64fec22ee Update BuildingRobomongo.md 2017-01-04 10:40:59 +03:00
Gokhan Simsek dd1d835dfe ReplicaSet refactoring on various modules. No behavioral changes. 2017-01-03 12:25:25 +03:00
Gokhan Simsek 5a78becc6f ReplicaSet refactoring: Restructuring mainly MongoWorker and some other modules. No behavioral changes. 2017-01-02 21:33:46 +03:00
Gokhan Simsek f598e62d1c ReplicaSet refactoring: Restructuring MongoServer for better readability. Adding new modules 'string_operations' and 'common' as utility libraries. 2017-01-02 13:16:06 +03:00
Gokhan Simsek b8ae4d0fc5 ReplicaSet: Implementing cached set name feature for 2 times faster first connections. 2017-01-02 10:35:10 +03:00
Gokhan Simsek 5403c16f48 ReplicaSet: Fix for an issue related to saving empty replica set member list. Fix for the issue where connections list was still showing 'Replica Set' after changing connection type from replica set to single server. 2016-12-30 11:13:13 +03:00
Gokhan Simsek 3d162032f4 ReplicaSet: Make replica set member addition smarter than expected by auto incrementing the port and using the last entered hostname by user. 2016-12-30 10:47:09 +03:00
Gokhan Simsek c591a7a91e ReplicaSet: Adding 'keep alive' functionality for replica set connections. 2016-12-29 11:44:34 +03:00
Gokhan Simsek fd2c476ae5 Disabling new create connection dialog, reverting back to original. 2016-12-28 11:48:43 +03:00
Gokhan Simsek 42bf58a0b5 ReplicaSet: Improvement for faster connection refresh by disabling unnecessary replica set refresh. Disabling settings save in MongoServer. Decreasing replica set refresh time to 2 seconds. 2016-12-23 11:19:00 +03:00
Gokhan Simsek 4d09e1b5a5 Fix for the problem where connections list comes empty if config file or it's directory do not exist (connections were imported only after second program opening) 2016-12-21 15:09:16 +03:00
Gokhan Simsek d825b065f2 ReplicaSet: Fix for the problem where primary unreachable case for one replica set connection was causing other unrelated replica set connections to go offline. 2016-12-21 14:25:51 +03:00
Gokhan Simsek 13428b5ed6 ReplicaSet: Removing unused context menu item "Refresh" for replica set member items on explorer widget. 2016-12-21 14:23:59 +03:00
Gokhan Simsek 521d1789bc Improved logging for connection establish & refresh server fail and success cases. 2016-12-21 10:20:46 +03:00
Gokhan Simsek f64257ae86 Improving handler for 'Create Database' for single server and replica sets.
Moving 'refresh databases' from event request to event response.
Improved logging for fail and success cases.
2016-12-19 20:53:38 +03:00
Gokhan Simsek d228ca1c4b Minor refactoring due to failing MAC build. 2016-12-19 19:33:05 +03:00
Gokhan Simsek 690f21e9ae Improving handler for 'Drop Database' for single server and replica sets. For replica set connections, it triggers replica set refresh on failure.
Moving 'refresh databases' from event request to event response. Improved logging for fail and success cases.
2016-12-19 18:23:14 +03:00
Gokhan Simsek 743710a9f0 Improving handler for 'Refresh Users' for single server and replica sets. For replica set connections, it triggers replica set refresh on failure.
Improved logging for fail and success cases.
2016-12-19 11:46:09 +03:00
Gokhan Simsek ba29971e20 Improving handler for 'Refresh Functions' for single server and replica sets. For replica set connections, it triggers replica set refresh on failure.
Improved logging for fail and success cases.
2016-12-19 11:37:17 +03:00
Gokhan Simsek f49e492a92 Improving handler for 'Refresh Collections' for single server and replica sets. For replica set connections, it triggers replica set refresh on failure.
Improved logging for fail and success cases.
2016-12-19 11:27:05 +03:00
Gokhan Simsek 1c62abaec0 Adding response handler for 'Drop User' for single server and replica sets. For replica set connections, it triggers replica set refresh on failure.
Moving 'refresh users' from event request to event response. Improved logging for fail and success cases.
2016-12-19 11:01:32 +03:00
Gokhan Simsek 1940a03a61 Improving handler for 'Create User' and 'Edit User' operations for single server and replica sets. For replica set connections, it triggers replica set refresh on failure.
Moving 'refresh users' from event request to event response. Improved logging for fail and success cases.
2016-12-19 09:59:18 +03:00
Gokhan Simsek 9bee2f443f C++11 transition, automatic clean up for possible memory leak. 2016-12-16 10:10:24 +03:00
Gokhan Simsek 1cf331f83c Fix for SSH connection issue. 2016-12-16 09:59:27 +03:00
Gokhan Simsek 685b835886 Adding response handler for 'Drop Function' for single server and replica sets. For replica set connections, it triggers replica set refresh on failure.
Moving 'refresh functions' from event request to event response. Improved logging for fail and success cases.
2016-12-15 17:48:32 +03:00
Gokhan Simsek ab2494a6ba Adding response handler for 'Create Function' and 'Update Function' operations for single server and replica sets. For replica set connections, it triggers replica set refresh on failure.
Moving 'refresh functions' from event request to event response. Improved logging for fail and success cases.
2016-12-15 17:21:11 +03:00
Gokhan Simsek 3ae0d61bd5 Improving 'Create Collection' for single server and replica sets. For replica set connections, it triggers replica set refresh on failure.
Handling create attempt of already-existing collection names.
Moving 'refresh collections' from event request to event response.
Improved logging for fail and success cases.
2016-12-14 11:53:53 +03:00
Gokhan Simsek f4e11b623f Improving response handler for 'DropCollection' which for replica set connections triggers replica set refresh on failure. Adding code to handle drop attempt of non-existing collections.
Moving 'refresh collections' from event request to event response.
Improved logging for fail and success cases.
2016-12-11 19:54:40 +03:00
Gokhan Simsek 938479e43f DuplicateCollection: Adding code to handle and show error for the case where duplicating a collection with a collection name which already exists finishes silently. 2016-12-11 18:16:26 +03:00
Gokhan Simsek 31ec650659 Implementing response handler for 'Duplicate Collection' which works for single server connections expectedly and for replica set connections triggers replica set refresh on failure.
Moving 'refresh collections' from event request to event response.
Improved logging for fail and success cases.
2016-12-11 18:09:15 +03:00
Gokhan Simsek 87c13ff74a Implementing global response handler for 'Rename Collection' which works for single server connections expectedly and for replica set connections triggers replica set refresh on failure.
Moving 'refresh collections' from event request to event response.
Improved logging for fail and success cases.
2016-12-11 15:50:48 +03:00
Gokhan Simsek 2c820daae4 #1256: Fix for in-progress widget not centered for "Update Documents/Remove Documents" context menu actions 2016-12-09 12:39:25 +03:00
Gokhan Simsek 3b0cdc0caa #1236: Fix for the two global problems where insert or update documents in one tab was causing auto reloading of other tabs and also multiple error windows to popup for each tab opened from same root connection.
ReplicaSets: Adding better handling for insert document failures for both originated from explorer collection context menu and from output document results context menu.
2016-12-08 20:15:23 +03:00
Gokhan Simsek 85570dd61d Adding adjusted log level prefix strings into logger. 2016-12-06 14:59:27 +03:00
Gokhan Simsek 400d3670be ReplicaSets: Adding refresh replica set on error feature for insert document operation. 2016-12-06 14:55:52 +03:00
Gokhan Simsek 45c93a7f3e ReplicaSets: Fixing an error and improving connection between tabs and explorerServerItem's mongo server in order to receive ReplicaSetRefreshed events correctly. 2016-12-06 10:26:20 +03:00
Gokhan Simsek 6ca3f492fe Adding code to handle global problem where re-run of existing shell tab with reachable server fails due to null mongo scope which is result when new shell opened before when server is unreachable. 2016-12-04 21:43:46 +03:00
Gokhan Simsek dee3768d34 ReplicaSets: Adding automatic replica set refresh for failed script queries when set's primary unreachable cases. 2016-12-04 16:57:33 +03:00
Gökhan Simsek d2570f9c44 Update README.md 2016-12-03 12:49:57 +03:00
Gokhan Simsek a630fa9f98 ReplicaSets: Reverting ExplorerServerTreeItem expand() for single server back to its original version. Fixing small error related to [SSL] [SSH] texts. 2016-11-30 00:20:41 +03:00
Gokhan Simsek 875d4248f5 ReplicaSets: Better error messages for failed connections and failed refresh events for SSL & replica sets and single server cases. Fixing a problem related to replica set refresh folder event. 2016-11-29 21:34:49 +03:00
Gokhan Simsek 9523ff2472 ReplicaSets: Adding refresh status of set on expand feature for explorer feature replica set folder 2016-11-28 09:39:35 +03:00
Gokhan Simsek cf92bda8db ReplicaSets: Fixing a potential problem by setting cached set name to empty when connection failed to all nodes. 2016-11-27 01:55:55 +03:00
Gokhan Simsek c73b5ed7dc ReplicaSets: Changes to achieve 3 times faster first connection to replica set. 2016-11-27 01:24:12 +03:00
Gokhan Simsek ceb32f63ad ReplicaSets: Adding new feature to be able to check replica set status even when primary is unreachable and there is at least one reachable secondary. Disabling some context menu items for ServerTreeItem and ReplicaSetFolderItem for the no primary case and all nodes unreachable case. 2016-11-25 15:07:15 +03:00
Gokhan Simsek 4065193dd8 ReplicaSets: Implementing slightly different design for "create collection" for replica set connections. Adding an using new event ReplicaSetFolderLoading. 2016-11-23 13:53:37 +03:00
Gokhan Simsek b4d7ae2d3e Renaming MongoServer::_client to _worker. 2016-11-21 20:30:13 +03:00
Gokhan Simsek 140dce8dd2 ReplicaSets: Fixing issue related to create database and drop database actions. 2016-11-21 16:34:26 +03:00
Gokhan Simsek e80fd77299 ReplicaSets: Major changes, re-implementing connection to replica set, refresh server for replica set and refresh for replica set folder. 2016-11-21 15:56:29 +03:00
Gokhan Simsek 7e552a4db0 ReplicaSets: Updating replica set member icons with transparent background. 2016-11-18 23:14:24 +03:00
Gokhan Simsek 7d28eec8d0 ReplicaSets: Updating replica set icon with transparent background. Adding replica set offline icon. 2016-11-18 23:09:10 +03:00
Gokhan Simsek c29f8bf2f4 Adding save/restore window size feature for Edit, View and Insert Document dialogs 2016-11-17 19:27:56 +03:00
Gokhan Simsek f76433729c Correcting typo for namespace HighDpiConstants 2016-11-16 23:55:15 +03:00
Gokhan Simsek db630fc549 Updating Robomongo version to 1.0.0 2016-11-15 22:14:52 +03:00
Gokhan Simsek 4eb13e84cc Adding first working import connections from 0.9 to 1.0 and from 0.8.5 to 1.0. Updating default config path to /.config/robomongo/1.0/ 2016-11-14 22:16:23 +03:00
Gokhan Simsek 16b8c8efea ConnectionString: Adding generate button, include passwords check box and related functionalities. Adding tooltip popup for copy to clipboard button. 2016-11-13 02:30:51 +03:00
Gokhan Simsek 9b2c72e4a2 ReplicaSets: Adding first working version of export URI connection string feature into ConnectionSettings advanced tab. Refactoring ConnectionSettings info() to hostAndPort(). 2016-11-11 21:53:31 +03:00
Gokhan Simsek b3b9c52144 Temporarily disabling export/import feature 2016-11-10 13:05:01 +03:00
Gokhan Simsek 4bc319e0bb Minor refactorings 2016-11-10 12:45:06 +03:00
Gökhan Simsek 50071a2980 Moving gitter badge next to "About Robomongo" text 2016-11-10 11:15:48 +03:00
Gökhan Simsek 2036ce4ba1 Merge pull request #1207 from revolter/patch-1
Move Gitter badge to the top
2016-11-10 10:14:28 +02:00
Gokhan Simsek f2624fea03 ReplicaSets: Better handling of server item's refresh and replica set folder item's refresh for set's unreachable cases. Adding code to show more correct error string for replica set connections. Updating script engine's dbname dynamically. 2016-11-09 14:56:45 +03:00
Gokhan Simsek 950bc3daaa ReplicaSets: Handling of cases where there is no reachable primary with reachable secondaries or no reachable nodes at all. 2016-11-07 02:18:13 +03:00
Gokhan Simsek 39b6779a8c ReplicaSets: Better and faster handling of set status change for "Replica Set" folder item. Adding new RefreshReplicaSet* event group. Adding new function MongoSever::tryRefreshReplicaSet(). 2016-11-06 14:51:30 +03:00
Gokhan Simsek 743486d88b ReplicaSets: Adding functionality to dynamically update script engine with correct primary node to be able to re-run an existing open tab even when primary changed. Adding ReplicaSet struct as an info class. 2016-11-04 19:21:19 +03:00
Gokhan Simsek 50b0e90495 ReplicaSets: Moving replica set primary update for script engine inside replica set if condition. 2016-11-02 18:38:31 +03:00
Gokhan Simsek a8b049686a Using ConnectionSettings pointer itself instead of deep copy in ConnectionsDialog and some const refactoring. 2016-11-02 18:36:57 +03:00
Gokhan Simsek 23f133dbbe ReplicaSets: Adding functionality to auto-find and auto-configure replica set (and settings) for the case at least one reachable set member is entered by user. 2016-10-31 13:24:02 +03:00
Gokhan Simsek b033375491 ReplicaSets: In order to be able to save some settings changes (i,e, replica set name) after an established connection, passing the original connection settings object to MongoServer instead of the clone. 2016-10-30 23:38:41 +02:00
Gokhan Simsek ca4060473d ReplicaSets: Updating script engine to work with replica sets. Adding remove set item button functionality. 2016-10-30 23:26:44 +02:00
Gokhan Simsek 472b6a8c46 ReplicaSets: Adding replica set info into ConnectionDiagnosticDialog and main window log widget. 2016-10-29 18:17:17 +03:00
Gokhan Simsek 0947bac63c ReplicaSets: Adding setName into ReplicaSetSettings. If set name comes from settings (URI), using it instead of trying to get set name with connecting to nodes. Adding "Topology" column into ConnectionsDialog. 2016-10-29 01:47:03 +03:00
Gokhan Simsek 1a414a2723 ReplicaSets: Adding server icons to represent primary & secondary replica nodes (draft versions) 2016-10-28 18:35:12 +03:00
Gokhan Simsek d7bbf37cbb ReplicaSets: Adding code to extract SSL and credentials information from mongo URI. 2016-10-28 18:21:28 +03:00
Gokhan Simsek 6063ff90eb ReplicaSets: Adding code to obtain replica set name needed by constructor of mongo::DBClientReplicaSet 2016-10-28 16:33:01 +03:00
Gokhan Simsek 20dd2eb564 ReplicaSets: Change to fix build error on MAC 2016-10-27 12:29:59 +03:00
Gokhan Simsek 4a80853be6 ReplicaSets: Adding link from CreateConnectionDialog "Create connection manually" label to ConnectionsDialog addManually() function 2016-10-24 01:28:39 +03:00
Gokhan Simsek b6cd30e22d Adding "Security" header column into ConnectionsDialog 2016-10-24 00:15:12 +03:00
Gokhan Simsek ea59ca59d3 ReplicaSets: Adding new dialog "create connection"; Adding parsing ability from MongoURI to ConnectionSettings; Adding ability to create new connection from mongo connection string URI. 2016-10-23 14:42:24 +03:00
Gokhan Simsek c67d8a942b ReplicaSets: If replica set item widget is down, disabling iteim, it's context menu and double click event; Adding replica set name into replicaSetFolderItem. 2016-10-20 10:33:29 +03:00
Gokhan Simsek 244d94b17d Export_Import_Temp: Removing merge conflicts 2016-10-14 01:02:13 +03:00
Gokhan Simsek b05b348198 Merge branch 'master' into Export_Import_Temp 2016-10-14 00:50:43 +03:00
Gokhan Simsek 38a8595d15 ReplicaSets: Showing grey server icon when a replica member is offline 2016-10-14 00:47:24 +03:00
Gokhan Simsek f2226c92b3 Export_Import: Cancelling some customizations done for the design attempt of export dialog with custom explorer tree items 2016-10-13 22:58:25 +03:00
Gokhan Simsek 466709df23 Export_Import: Cancelling tree widget structure due to unnecessary complexity created; Enabling export button only a collection is selected; Adding "show details" button and feature. 2016-10-13 12:01:17 +03:00
Gokhan Simsek 702459918b ReplicaSet: Adding functionality refresh replica set view via "Refresh" context menu items both on Server and Replica Set Folder explorer tree items. Adding ExplorerReplicaSetFolderItem as a new specialized class. Adding new connection type "ConnectionRefresh" and function MongoServer::tryRefresh function to use updating whole connection view. 2016-10-12 15:50:58 +03:00
Gokhan Simsek 4efbd8b90a Export_Import: Adding support for multiple simultaneous exports in auto mode as well; adding enableDisableWidgets function to disable some widgets during export operation 2016-10-10 02:22:52 +03:00
Gokhan Simsek 655bfcba0a Export_Import: Adding support for multiple simultaneous exports in manual mode; Making export dialog mode-less 2016-10-09 18:27:54 +03:00
Gokhan Simsek 041356ebac Export_Import: Adding progress dialog for long lasting exports for auto mode 2016-10-09 13:52:35 +03:00
Gokhan Simsek 32e9ceb741 Export_Import: Appending mongo export output into auto mode result dialog 2016-10-09 01:00:29 +03:00
Gokhan Simsek 1aeb0e42dc Export_Import: Adding manual export mode 2016-10-08 23:30:33 +03:00
Gokhan Simsek 9c419b1f2e ReplicaSet: Enabling this feature which was disabled code due to release 0.9.0 Final 2016-10-07 19:15:14 +03:00
Gökhan Simsek 0786489924 Update CHANGELOG 2016-10-05 17:28:02 +03:00
Iulian Onofrei 2835aa1685 Move Gitter badge to the top
This is quite a preferred position for most projects and it helps to be this visible. I personally thought Robomongo is not using Gitter at first.
2016-10-05 11:33:19 +03:00
Gokhan Simsek 9cc34df185 Fixing the problem where create collection dialog was too large after switching into non-advanced mode from non-option tabs in advanced mode 2016-10-04 22:19:15 +03:00
Gokhan Simsek d5adcdf988 Fixing not working paralect link in about menu; Hiding validate json button when create collection dialog is in basic mode. 2016-10-04 21:41:08 +03:00
Gokhan Simsek 837c0e1a17 MinimizeToTray: Fixing the problem where incorrect context menu was shown when user minimize and right-click on tray icon. 2016-10-04 21:16:46 +03:00
Gökhan Simsek d458f43b63 Update README.md 2016-10-04 20:58:16 +03:00
Gokhan Simsek 1cfb1b6287 Export_Import: Adding code to support query argument; Moving all changes to _mongoExportArgs into one location inside accept() function. 2016-10-02 22:23:49 +03:00
Gokhan Simsek 6f5124616a Export_Import: Adding support to export in CSV format 2016-10-02 21:40:21 +03:00
Gokhan Simsek 3ba2f8ae93 Export_Import: Working export getting arguments from UI; adding smart filename and path creation when collection selected; adding browse directory functionality. 2016-10-01 18:17:44 +03:00
Gokhan Simsek b6d87141f2 Export_Import: First working mongo export with local executable directory and static arguments 2016-09-30 22:56:31 +03:00
Gokhan Simsek 993e9355bb Export_Import: Not creating "Functions", "User" and "Indexes" folders if items are created inside exp/imp dialog 2016-09-30 22:01:52 +03:00
Gokhan Simsek be655902b4 Updating CHANGELOG for 0.9.0 Final 2016-09-30 19:37:26 +03:00
Gökhan Simsek f9332befb3 Update README.md for 0.9.0 Final. 2016-09-30 19:19:49 +03:00
Gökhan Simsek 66728df978 Update README.md 2016-09-30 18:32:26 +03:00
Gokhan Simsek 60dc85c2fc To fix the problem where "View Documents" window cannot be brought into front from the behind of floating output window 2016-09-29 12:38:36 +03:00
Gökhan Simsek 4b4eb623b9 Update README.md 2016-09-28 19:21:20 +03:00
Gokhan Simsek d2c98bf559 DockableOutputWindow: Adding code to support this feature after toggle orientation; updating tool tip for maximize output result button 2016-09-27 12:57:09 +03:00
Gokhan Simsek 9a31503996 DockableOutputWindow: Fixing problem where dock/undock icon wasn't updated after re-running existing query tab with undocked output window 2016-09-23 13:09:15 +03:00
Gokhan Simsek cb4622f61f Adding protection for all tray icon code which is Windows only 2016-09-22 14:55:21 +03:00
Gokhan Simsek 1ce1091eca Minor change due to MAC build error 2016-09-22 14:23:23 +03:00
Gokhan Simsek 4546eef1e3 Refactoring some functions to avoid access to private members for better encapsulation 2016-09-22 14:05:53 +03:00
Gokhan Simsek 1d8f24ced2 Code Review: Refactoring and non-major changes for commits from RC10 release (33c89ea) to this commit. 2016-09-22 13:32:40 +03:00
Gokhan Simsek 19a61ba517 Code Review: For pull request #1163(minimize to tray) - More minor refactoring and style changes. 2016-09-21 11:56:28 +03:00
Gokhan Simsek 2b377e35f1 Code Review: For pull request #1163(minimize to tray) - Adding fix for the problem when tray icon double clicked event was not caught and so context menu was showing wrong text. 2016-09-20 21:38:06 +03:00
Gokhan Simsek f5011630a3 Code review: For pull request #1127 (copy path & copy name), constness and access level edits, applying Robomongo Coding Style. 2016-09-20 20:26:54 +03:00
Gokhan Simsek 8967a84f12 DockableOutputWindow: Fix for possible Qt bug (MAC only) where dock window is not brought to front when connected tab is selected 2016-09-20 17:08:23 +03:00
Gokhan Simsek 38bed5e4eb Code review : MinimizeToTray pull request minor changes 2016-09-19 01:32:10 +03:00
Gokhan Simsek 5370ecedd8 ReplicaSet: Temporarily disable Replica Set feature - still under development 2016-09-15 13:18:53 +03:00
Gökhan Simsek 0e8cd5cefc Merge pull request #1163 from JoshJ1091/master
Add system tray icon and implement ability to close to system tray (Windows)
2016-09-14 13:37:10 +02:00
Gokhan Simsek e3c3649b0f Robomongo 0.9.0 2016-09-14 13:06:50 +03:00
Gokhan Simsek 19f953fa54 RepSet: Removing QShortCut due to failed build on Ubuntu 2016-09-14 12:10:28 +03:00
Gokhan Simsek ad1b5e35c7 Merge branch 'DockableOutputWindow' 2016-09-13 19:26:43 +03:00
Gokhan Simsek ff2eb0fef6 DockableOutputWindow: Updating clean versions of maximize and minimize icons; Minor change on ReplicaSetSettings due to failed build on MAC 2016-09-13 19:10:21 +03:00
Gokhan Simsek d356c14f87 DockableOutputWindow: Increasing size of dock/undock button icons 2016-09-12 00:57:14 +03:00
Gokhan Simsek 829cbf890b DockableOutputWindow: Adding new icons for maximize/minimize button 2016-09-11 23:40:12 +03:00
Gokhan Simsek 5ae52d4b4f DockableOutputWindow: Fix to solve crash when floating dock window hovered over bottom part of the maximized query widget 2016-09-11 18:50:18 +03:00
Gokhan Simsek 4608c6b7ee DockableOutputWindow: No maximize (via double click or any other source) if there is only one query result 2016-09-11 16:44:00 +03:00
Gokhan Simsek 7c7a818ca0 Merge branch 'DockableOutputWindow' 2016-09-11 15:59:32 +03:00
Gokhan Simsek 08c6d44185 DockableOutputWindow: Removing the additional horizontal line on bottom of tab title that is seen after undock 2016-09-11 15:56:50 +03:00
Gokhan Simsek dc8495b147 DockableOutputWindow: Adding code for frameless query results dock window; adding functionality to dock/undock from OutputItemHeaderWidget; adding dock/undock buttons and icons into OutputItemHeaderWidget; not showing OutputItemHeaderWidget maximize button if there is one query result; showing only one undock button if there are more than one query results. 2016-09-11 15:16:44 +03:00
Gokhan Simsek e2f5765a87 DockableOutputWindow: Adding code to show OutputWidget maximize button only if there are multiple query results 2016-09-10 16:24:46 +03:00
Gokhan Simsek 7ebc059430 Export_Import: Adding function to be able to customize ExplorerDatabaseTreeItem in order to use it inside Export Dialog 2016-09-09 19:52:46 +03:00
Gokhan Simsek 0d3fda185f Export_Import: Adding new dialog/class ExportDialog and it's first visual template; adding import action into main window 2016-09-09 13:41:25 +03:00
Gokhan Simsek 38df06261b New branch Export_Import; adding export tool bar item into main windows; adding icons for export and import 2016-09-08 14:09:11 +03:00
Gokhan Simsek 0086df54f0 DockableOutputWindow: Focus on active shell query tab when related dock output window is selected 2016-09-07 20:05:06 +03:00
Gokhan Simsek ddc0c910c8 DockableOutputWindow: Removing unused signal-slot connection 2016-09-07 14:02:04 +03:00
Gokhan Simsek 31f74baef7 DockableOutputWindow: Adding close button on dock output window with custom close event (dock instead of close) 2016-09-07 13:41:39 +03:00
Gokhan Simsek 8cd4f50304 DockableOutputWindow: Fix to solve crash on "Update Documents" 2016-09-07 11:06:55 +03:00
Gokhan Simsek 48f6abb361 DockableOutputWindow: Fix to solve un-selectable query window text issue when output window is floating. 2016-09-06 19:28:34 +03:00
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
Gokhan Simsek 47039d8685 DockableOutputWindow: Adding code to support full window query editor when output window is docked 2016-09-06 02:51:25 +03:00
Gokhan Simsek bc9aded3b7 DockableOutputWindow: Bring the output dock window front when tabs changed on main window 2016-09-04 21:50:18 +03:00
Gokhan Simsek f919544d1e DockableOutputWindow: Adding docked and undocked(full screen space for queries) modes for query window (ScriptWidget). 2016-09-04 18:59:16 +03:00
Gokhan Simsek ea49d0508a DockableOutputWindow: Adding dock capability for query result window (OutputWidget) 2016-09-03 21:57:08 +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
Josh 578447ecc3 Add system tray icon and implement ability to close to system tray
(Windows)
2016-08-18 16:52:26 +01: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
3209 changed files with 84927 additions and 811335 deletions
+155 -18
View File
@@ -1,30 +1,167 @@
0.9.0-RC6 / February 13, 2016
Latest Version:
Robomongo 1.0 (21 Apr, 2017)
http://blog.robomongo.org/robomongo-1-0
Robomongo 1.x:
Major Features:
- Replica set support.
- Update Notifications Bar
- Welcome Tab
- New operation logging for almost all operations.
Major Fixes/Improvements:
- Improved error handling (no double error windows) for almost all operations.
- Timestamp value is represented incorrectly (https://github.com/Studio3T/robomongo/issues/1159)
- Local Timezone shows wrong date (https://github.com/Studio3T/robomongo/issues/501)
* What's new in patch 1.0.3 (7 June, 2017)
- Attempt to prevent MAC OS Sierra crash related to updates notification bar
* What's new in 1.0 (21 Apr, 2017)
New Features:
- Update Notifications Bar
- Welcome Tab
- Welcome Wizard
Improvements:
- Replica set name configurable on UI
- Configuration file system location change
- "About Robomongo" dialog changes
Fixes:
- Fix to avoid the same set name problem (https://github.com/Studio3T/robomongo/issues/1302)
- Timestamp value is represented incorrectly (https://github.com/Studio3T/robomongo/issues/1159)
- Local Timezone shows wrong date (https://github.com/Studio3T/robomongo/issues/501)
- Time zone shown as "000:00" when UTC +00:00 is used as local timezone(https://github.com/Studio3T/robomongo/issues/1309)
* What's new in 1.0-RC1 (1 Feb, 2017)
New Features:
- Replica set support.
- New 'Attributes' column in connections list window showing connection properties such as 'Replica Set', 'SSL' and 'SSH'.
- New operation logging for almost all operations.
Improvements:
- Improved error handling (no double error windows) for all fail and success conditions for the following operations:
. Create, Drop Database
. Create, Edit User
. Create, Drop, Duplicate, Rename Collection
. Insert Document
. Remove document, Delete document, Remove all documents (triggered both from explorer and from result widget)
. Refresh Users
. Refresh Functions
. Refresh Collections
. Connection establish & refresh server fail and success cases.
- Windows size is saved and restored for Edit, View and Insert Document dialogs.
- Connections are automatically imported from the latest Robomongo version.
- Better error reporting/handling and logging have been added for the operations below:
. Create, Drop Function
. Drop User
- More informative and adjusted log level prefix strings haven added for log window. (Error, Warn, Info etc..).
- After a new connection is created, now the focus goes to the newly created connection (end of list) instead of the
beginning of connections list. And when Robomongo is opened, the focus goes to the end of the connection list instead
of beginning.
- Error messages added and error handling for 'remove document', 'remove all documents' operations triggered explorer
and single or multi 'delete document' from results window have been re-implemented and improved.
- Relevant errors are shown for the following cases now:
. Drop attempt of non-existing collection.
. Duplicating a collection with a collection name which already exists finishes silently.
Fixes:
- Fix for the problem where the connections list comes empty if the Robomongo config file or it's directory do not exist.
- Fix for the problem where 'in-progress bar' widget was not centered on main window for "Update Documents/Remove Documents"
operations. https://github.com/paralect/robomongo/issues/1256
- Fix for two problems where the operations 'insert or update documents' in one tab was causing auto reloading of other
unrelated tabs and also multiple error windows to popup. https://github.com/paralect/robomongo/issues/1236
- Fix for the problem where the operations 'remove document', 'remove all documents' triggered explorer and single or
multi 'delete document' from results window were causing auto reloading of other unrelated tabs.
Robomongo 0.9:
Major Features:
- Support for MongoDB 3.x and WiredTiger engine. Robomongo now embeds MongoDB 3.2 shell.
(Ability to use the latest shell functionality)
- SSH tunnel support for connections
- SSL support
- MongoDB Atlas (Cloud MongoDB) support
- Both MONGODB-CR and SCRAM-SHA-1 authentication mechanisms are supported.
- Allow direct connections to Replica Set members and support IPv6 protocol.
- Loading of huge list of collections is now faster.
- Create Collection dialog is now expandable to support all advanced options.
(Supporting all major mongoDB versions 3.2, 3.0, 2.6 and all storage engine types (WiredTiger
and MMAPV1) is added.)
- Windows High DPI support.
- Full size editor with dockable output window.
Fixed:
- Incorrect representation of floating-point numbers (fixed since RC3). This bug was a reason of a big number of issues:
#910, #748, #697, #699, #760, #815, #870, #892,
#894, #905, #945, #974, #978, #990, #831, #842
- SSH connection failure to servers on Ubuntu and OS X with the following error:
"Resource temporarily unavailable. Error when starting up SSH session: -8" (#1090)
What's new in 0.9.0 Final (30 Sep, 2016)
http://blog.robomongo.org/robomongo-0-9-0-final/
New Features:
- Full size editor with dockable output window.
- New JSON object context menu items: "Copy Name" and "Copy Path" are added.
- New database context menu items: "Current Operations" and "Kill Operation" are added.
- Minimize to Tray Icon for Windows.
Other Changes
- Cloud platforms Compose, mLab and Amazon EC2 were tested and added as supported platforms.
- Maximize/Restore result button behavior and icon have been improved.
- For better code quality and code reviews, Robomongo Code Quality and Robomongo Code Review pages have been created.
https://github.com/paralect/robomongo/wiki/Robomongo-Code-Review
https://github.com/paralect/robomongo/wiki/Robomongo-Code-Quality
Most important changes: support for MongoDB 3.x and WiredTiger engine. Robomongo
now embeds MongoDB 3.2 shell. It means that you can now use the latest
shell functionality. Both MONGODB-CR and SCRAM-SHA-1 authentication mechanisms are
supprted. Also, we now allow direct connections to Replica Set members and
support IPv6 protocol. Loading of huge list of collections is now faster.
What's new in RC10 (Aug 8, 2016)
http://blog.robomongo.org/robomongo-rc10/
New Features:
- SSL connections with advanced options are now supported.
- MongoDB Atlas (Cloud MongoDB) is supported.
- Cross Platform High DPI enhancements.
Enhancements:
- For better security, OpenSSL has been upgraded to OpenSSL 1.0.1p.
- Qt has been updated from Qt 5.5 to latest Qt 5.7. With this update there are many
enhancements including cross platform High DPI support.
Fixed incorrect representation of floating-point numbers (fixed since RC3).
This bug was a reason of a big number of issues:
#910, #748, #697, #699, #760, #815, #870, #892,
#894, #905, #945, #974, #978, #990, #831, #842
What's new in RC7 (March 4, 2016 )
What's new in RC9 (May 30, 2016)
http://blog.robomongo.org/robomongo-rc9/
New Features:
- Create Collection dialog with advanced options.
- New feature has been added to save/restore window sizes. (#1102)
- Connection settings from previous versions of Robomongo are
now automatically imported starting from Robomongo 0.9.0 RC9. (#1086)
- Support for querying documents by UUIDs is added, both in legacy and new encoding. (#432)
Fixed:
- SSH connection failure to servers on Ubuntu and OS X with the following error:
"Resource temporarily unavailable. Error when starting up SSH session: -8" (#1090)
- Correct handling of create/drop of databases and collections. (#1083)
What's new in RC8 (April 14, 2016)
http://blog.robomongo.org/robomongo-rc8/
What's new in RC7 (March 4, 2016)
http://blog.robomongo.org/robomongo-rc7
What's new in RC6 (February 13, 2016 )
What's new in RC6 (February 13, 2016)
http://blog.robomongo.org/ipv6-protocol
What's new in RC5 (February 8, 2016 )
What's new in RC5 (February 8, 2016)
http://blog.robomongo.org/campaign-is-over-robomongo-is-not/
What's new in RC4 (February 1, 2016 )
What's new in RC4 (February 1, 2016)
http://blog.robomongo.org/robomongo-rc4
What's new in RC3 (January 29, 2016 )
What's new in RC3 (January 29, 2016)
http://blog.robomongo.org/robomongo-0-9-0-rc3-released
What's new in RC2 (January 26, 2016)
@@ -86,7 +223,7 @@
* [Andres Kievsky]
- Added new app icon in a variety of png and iDraw formats (#693)
* [Michael Steinacher]
- Retina text display support (#724)
- Retina text display support (#724)
0.8.4 / November 27, 2013
+7 -5
View File
@@ -5,10 +5,10 @@ project(Robomongo)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
# Project version
set(PROJECT_VERSION_MAJOR "0")
set(PROJECT_VERSION_MINOR "9")
set(PROJECT_VERSION_PATCH "0")
set(PROJECT_VERSION_BUILD "-RC7")
set(PROJECT_VERSION_MAJOR "1")
set(PROJECT_VERSION_MINOR "0")
set(PROJECT_VERSION_PATCH "3")
set(PROJECT_VERSION_BUILD "")
set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}${PROJECT_VERSION_BUILD}")
# Modules
@@ -27,14 +27,16 @@ find_package(Qt5Core REQUIRED)
find_package(Qt5Gui REQUIRED)
find_package(Qt5Widgets REQUIRED)
find_package(Qt5PrintSupport REQUIRED)
find_package(Qt5Network REQUIRED)
find_package(Qt5Xml REQUIRED)
find_package(MongoDB REQUIRED)
find_package(OpenSSL REQUIRED)
if (SYSTEM_MACOSX)
find_package(Qt5MacExtras REQUIRED)
endif()
# Projects
add_subdirectory(src/third-party/openssl)
add_subdirectory(src/third-party/libssh2)
add_subdirectory(src/robomongo/ssh)
add_subdirectory(src/third-party/qjson)
+1 -1
View File
@@ -1,6 +1,6 @@
Robomongo, shell-centric cross-platform MongoDB management tool.
Copyright 2013, Paralect.
Copyright 2014-2017 3T Software Labs Ltd.
The Robomongo is released under the terms of the GNU General Public
License, version 3.
+67 -20
View File
@@ -1,4 +1,4 @@
About Robomongo
About Robomongo [![Join the chat at https://gitter.im/paralect/robomongo](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/paralect/robomongo?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
===============
[Robomongo](http://www.robomongo.org) is a shell-centric cross-platform MongoDB management tool. Unlike most other MongoDB admin UI tools, Robomongo embeds the actual `mongo` shell in a tabbed interface with access to a shell command line as well as GUI interaction.
@@ -7,13 +7,71 @@ Starting from version 0.9, Robomongo is compatibile with MongoDB 3.x (including
Robomongo 0.8.x embeds the **MongoDB 2.4.0** shell.
What's new in latest Robomongo 1.0?
====================================
Latest release of Robomongo 1.0 includes:
- Update Notifications Bar
- Welcome Tab
- Welcome Wizard
- Replica set name configurable on UI
- Fixes to avoid same replica set name problem
- Fixes to solve various problems regarding Data and Timestamp data types
Blog: http://blog.robomongo.org/robomongo-1-0/
Download: https://robomongo.org/download
Watch: [Robomongo Youtube channel](https://www.youtube.com/channel/UCM_7WAseRWeeiBikExppstA)
Follow: https://twitter.com/Robomongo
What's Planned for the Next Releases?
====================================
We are currently working towards [Robomongo 2.0 milestone](https://github.com/paralect/robomongo/milestone/21).
Currently in progress:
- MongoDB 3.4 Support
Plans for Future:
- User Roles
- Support execution of multiple simultaneous queries [#1161](https://github.com/paralect/robomongo/issues/1161)
- Selectable mongodb engines [#1249](https://github.com/paralect/robomongo/issues/1249)
- Enhancements for stability, running without crashes ([Stability Milestone](https://github.com/paralect/robomongo/milestone/15))
- More Documentation
- Unit Tests
- Refactoring Debug Log module
Supported Platforms
===============
| MongoDB Versions |
| :-------------------- |
| 3.2 |
| 3.0 |
| 2.6 |
| MongoDB Cloud Platforms|
| :------------ |
| MongoDB Atlas |
| Compose |
| mLab |
| ObjectRocket |
| Amazon EC2 |
| Windows | Mac | Linux |
|:---------------------- | :---------------| :---------------------------|
| Windows 64-bit 10 | Mac OS X 10.11 | Linux Ubuntu 16.04 64-bit* |
Windows 64-bit 8.1 | Mac OS X 10.10 | Linux Ubuntu 14.04 64-bit* |
| Windows 64-bit 7 | | Linux CentOS 7 64-bit* |
| | | Linux CentOS 6 64-bit* |
\* latest stable build
Download
========
You can download tested install packages for OS X, Windows, and Linux from our site: [www.robomongo.org](http://www.robomongo.org).
The latest stable release is currently **Robomongo 0.8.5**.
The latest stable release is currently [**Robomongo 1.0**](http://blog.robomongo.org/robomongo-1-0/).
Support
=======
@@ -24,44 +82,33 @@ You can:
- [Create a new issue in the Github issue queue](https://github.com/paralect/robomongo/issues)
- [Join developer discussion on Gitter](https://gitter.im/paralect/robomongo) [![Join the chat at https://gitter.im/paralect/robomongo](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/paralect/robomongo?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
- [Join developer discussion on Gitter](https://gitter.im/paralect/robomongo)
Build
=====
The wiki contains prerequisites and instructions to [Build Robomongo](https://github.com/paralect/robomongo/wiki/Build-Robomongo).
The wiki contains prerequisites and instructions to [Build Robomongo](https://github.com/paralect/robomongo/wiki).
If you want to compile from source yourself, you should be able to do so cleanly from a [release branch](https://github.com/paralect/robomongo/releases).
What's Planned for the Next Release?
====================================
We are currently working toward a [0.9.0 release milestone](https://github.com/paralect/robomongo/milestones/Robomongo%200.9.0)
with some major features targeted including compatibility with MongoDB 3.0
(updated shell, SCRAM-SHA-1 auth, support for WiredTiger storage engine)
and full support for replica sets.
Contribute!
===========
### Vote for Features
Please view our [Magic Backlog](http://app.robomongo.org/backlog) to see features prioritised by donation. The small amount of donations we currently receive are put toward hosting costs.
### Suggest Features
New feature suggestions or UI improvements are always welcome.
[Create a new feature request on github](https://github.com/paralect/robomongo/issues/new)
This project is powered by open source volunteers, so we have a limited amount of development resource to address all requests. We will certainly make best efforts to progress (particularly for those with strong community upvotes).
### Code Contributions
Code contributions are always welcome! Just try to follow our coding style: [Robomongo Coding Style](https://github.com/paralect/robomongo/wiki/Robomongo-Coding-Style).
Code contributions are always welcome! Just try to follow our pre-commit checks and coding style:
- [Robomongo Code Quality](https://github.com/paralect/robomongo/wiki/Robomongo-Code-Quality)
- [Robomongo Coding Style](https://github.com/paralect/robomongo/wiki/Robomongo-Coding-Style)
If you plan to contribute, please create a Github issue (or comment on the relevant existing issue) so we can help coordinate with upcoming release plans.
For a general workflow, see Github's guide to [Fork a Repo](https://help.github.com/articles/fork-a-repo/).
Pull requests (PRs) should generally be for discrete issues (i.e. one issue per PR please) and be clean to merge against the current master branch. It would also be helpful if you can confirm what testing has been done (specific O/S targets and MongoDB versions if applicable).
A usual naming approach for feature branches is `issue-###`. Include the issue number in your commit message / pull request description to link the PR to the original issue.
@@ -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 2014-2017 [3T Software Labs Ltd](https://studio3t.com/). All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3 as
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
+4 -1
View File
@@ -8,7 +8,10 @@ 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("| Qt5 Network: ${Qt5Network_VERSION} ${Qt5Network_DIR}")
message("| Qt5 Xml: ${Qt5Xml_VERSION} ${Qt5Xml_DIR}")
message("| MongoDB: ${MongoDB_VERSION} ${MongoDB_BUILD_DIR}")
message("| OpenSSL: ${OPENSSL_VERSION} ${OpenSSL_DIR}")
message("|")
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)
+26 -5
View File
@@ -24,7 +24,7 @@ if(SYSTEM_LINUX)
set(qt_conf_dir ${bin_dir})
set(qt_conf_plugins "../lib")
elseif(SYSTEM_MACOSX)
set(bundle_name Robomongo.app)
set(bundle_name "Robomongo.app")
set(contents_path ${bundle_name}/Contents)
set(bin_dir ${contents_path}/MacOS)
@@ -56,6 +56,29 @@ 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"
"${OpenSSL_DIR}/libssl.so.1.0.0"
"${OpenSSL_DIR}/libcrypto.so"
"${OpenSSL_DIR}/libcrypto.so.1.0.0"
DESTINATION ${lib_dir})
endif()
# Install binary
install(
TARGETS robomongo
@@ -72,14 +95,13 @@ install(
DESTINATION ${license_dir})
# Install common dependencies
install_qt_lib(Core Gui Widgets PrintSupport)
install_qt_lib(Core Gui Widgets PrintSupport Network Xml)
install_qt_plugins(QGifPlugin QICOPlugin)
install_icu_libs()
if(SYSTEM_LINUX)
install_qt_lib(XcbQpa DBus)
install_qt_plugins(
QGtk2ThemePlugin
QXcbIntegrationPlugin)
elseif(SYSTEM_MACOSX)
@@ -91,7 +113,7 @@ elseif(SYSTEM_MACOSX)
# Install icon
install(
FILES "${CMAKE_SOURCE_DIR}/install/macosx/Robomongo.icns"
FILES "${CMAKE_SOURCE_DIR}/install/macosx/robomongo.icns"
DESTINATION "${resources_dir}")
elseif(SYSTEM_WINDOWS)
install_qt_plugins(
@@ -105,4 +127,3 @@ elseif(SYSTEM_WINDOWS)
set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION .)
include(InstallRequiredSystemLibraries)
endif()
+4 -4
View File
@@ -15,7 +15,7 @@ execute_process(
string(TIMESTAMP timestamp "%Y-%m-%d")
# Package name (as it should appear in UI)
set(CPACK_PACKAGE_NAME Robomongo)
set(CPACK_PACKAGE_NAME robomongo)
# Version of the package
set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
@@ -34,8 +34,8 @@ set(CPACK_MONOLITHIC_INSTALL ON)
set(CPACK_STRIP_FILES ON)
# Additional information
set(CPACK_PACKAGE_VENDOR Paralect)
set(CPACK_PACKAGE_CONTACT robomongo@paralect.com)
set(CPACK_PACKAGE_VENDOR "3T Software Labs Ltd")
set(CPACK_PACKAGE_CONTACT robomongo@3t.io)
set(CPACK_PACKAGE_DESCRIPTION "Shell-centric cross-platform MongoDB management tool.")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Robomongo is a shell-centric cross-platform MongoDB management tool.")
@@ -62,7 +62,7 @@ elseif(SYSTEM_MACOSX)
elseif(SYSTEM_WINDOWS)
set(files_dir "${CMAKE_SOURCE_DIR}/install/windows")
set(exe_name "Robomongo.exe")
set(exe_name "robomongo.exe")
set(CPACK_GENERATOR NSIS ZIP)
set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_SOURCE_DIR}/DESCRIPTION")
+4 -3
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()
@@ -17,9 +18,9 @@ endif()
set(PROJECT_NAME "Robomongo")
set(PROJECT_NAME_TITLE ${PROJECT_NAME})
set(PROJECT_DOMAIN "www.robomongo.org")
set(PROJECT_COMPANYNAME "Paralect")
set(PROJECT_COPYRIGHT "Copyright (C) 2013-2015 ${PROJECT_COMPANYNAME} All Rights Reserved.")
set(PROJECT_COMPANYNAME_DOMAIN "www.paralect.com")
set(PROJECT_COMPANYNAME "3T Software Labs Ltd")
set(PROJECT_COPYRIGHT "Copyright (C) 2014-2017 ${PROJECT_COMPANYNAME} All Rights Reserved.")
set(PROJECT_COMPANYNAME_DOMAIN "https://studio3t.com/")
set(PROJECT_GITHUB_FORK "www.github.com/paralect/robomongo")
set(PROJECT_GITHUB_ISSUES "www.github.com/paralect/robomongo/issues")
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
+107 -20
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
@@ -49,4 +125,15 @@ Install Robomongo to `build/release/install` folder:
Create package for your OS in `build/release/package` folder:
$ bin/pack
**Important Notes**
- For Ubuntu 16.04 builds, it has been reported that Robomongo /bin/configure step might fail and installing mesa-common-dev package solves it. Details: https://github.com/paralect/robomongo/issues/1268
- For Centos builds, Robomongo /bin/configure step might fail due to error: `failed to find gl/gl.h`. In this case, the following packages must be installed:
```sh
$ sudo yum install mesa-libGL
$ sudo yum install mesa-libGL-devel
```
+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
+32 -5
View File
@@ -12,19 +12,46 @@ Runtime dependencies that are not privided by platforms but required by Robomong
Diagnostic of dependencies
--------------------------
##### Linux
#### Linux
View dependencies of executable or shared library:
a. View dependencies of executable or shared library:
$ ldd robomongo
View RPATH or RUNPATH records of executable or shared library:
$ readelf -d robomongo
b. View dependency tree of executable or shared library:
$ lddtree robomongo
$ ldd -v robomongo
c. View RPATH or RUNPATH records of executable or shared library:
$ objdump -x bin/robomongo | grep "RPATH\|RUNPATH"
$ readelf -d bin/robomongo | grep RPATH
For Robomongo binary it should be `$ORIGIN/../lib`
##### Mac OS X
d. Enable extensive logging for run-time shared library search
$ export LD_DEBUG=files
$ bin/robomongo
Source:
http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html
e. Modify RPATH using `chrpath` and the dynamic linker and RPATH using paths `patchelf`
$ patchelf --set-rpath '$ORIGIN/../lib' robo
More: https://nixos.org/patchelf.html
$ chrpath -r "/somepath/lib/" robomongo
More:
https://linux.die.net/man/1/chrpath
http://www.programering.com/a/MTOwcDNwATU.html // examples
#### Mac OS X
Show dependencies of executable or shared library:

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 93 KiB

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Before

Width:  |  Height:  |  Size: 827 B

After

Width:  |  Height:  |  Size: 827 B

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 93 KiB

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

+2 -2
View File
@@ -26,10 +26,10 @@ IDI_ICON1 ICON "@windows_icon@"
BEGIN
BLOCK "040904e4"
BEGIN
VALUE "CompanyName", "Paralect, Inc\0"
VALUE "CompanyName", "3T Software Labs Ltd\0"
VALUE "FileDescription", "Robomongo, MongoDB management tool\0"
VALUE "FileVersion", VER_FILEVERSION_STR
VALUE "LegalCopyright", "Copyright Paralect, 2013 - 2016\0"
VALUE "LegalCopyright", "Copyright 3T Software Labs Ltd, 2014 - 2017\0"
VALUE "ProductName", "Robomongo\0"
VALUE "ProductVersion", VER_PRODUCTVERSION_STR
VALUE "OriginalFilename", "Robomongo.exe\0"
+1 -1
View File
@@ -1,6 +1,6 @@
Robomongo shortcut key reference
================================
"3T", "Robomongo"
### Windows/Linux users
Ctrl+Enter or F5 or Ctrl+R
+28 -5
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
@@ -49,12 +53,16 @@ set(SOURCES
core/domain/App.cpp
core/mongodb/MongoClient.cpp
core/mongodb/MongoWorker.cpp
core/mongodb/ReplicaSet.cpp
core/settings/SettingsManager.cpp
core/AppRegistry.cpp
utils/string_operations.cpp
utils/common.cpp
# Isolated Scope #3
gui/GuiRegistry.cpp
gui/dialogs/AboutDialog.cpp
gui/dialogs/EulaDialog.cpp
gui/dialogs/ConnectionAdvancedTab.cpp
gui/dialogs/ConnectionAuthTab.cpp
gui/dialogs/ConnectionBasicTab.cpp
@@ -62,6 +70,7 @@ set(SOURCES
gui/dialogs/ConnectionDialog.cpp
gui/dialogs/CopyCollectionDialog.cpp
gui/widgets/workarea/IndicatorLabel.cpp
gui/dialogs/CreateCollectionDialog.cpp
gui/dialogs/CreateDatabaseDialog.cpp
gui/dialogs/CreateUserDialog.cpp
gui/utils/ComboBoxUtils.cpp
@@ -70,6 +79,8 @@ set(SOURCES
# Isolated scope #4
gui/dialogs/PreferencesDialog.cpp
gui/dialogs/ConnectionsDialog.cpp
gui/dialogs/CreateConnectionDialog.cpp
gui/dialogs/ExportDialog.cpp
# Isolated scope #5
gui/editors/PlainJavaScriptEditor.cpp
@@ -81,6 +92,8 @@ set(SOURCES
# Isolated scope #6
gui/widgets/explorer/ExplorerCollectionTreeItem.cpp
gui/widgets/explorer/ExplorerTreeItem.cpp
gui/widgets/explorer/ExplorerReplicaSetFolderItem.cpp
gui/widgets/explorer/ExplorerReplicaSetTreeItem.cpp
gui/widgets/explorer/ExplorerDatabaseTreeItem.cpp
gui/widgets/explorer/ExplorerDatabaseCategoryTreeItem.cpp
gui/widgets/explorer/ExplorerUserTreeItem.cpp
@@ -111,15 +124,20 @@ set(SOURCES
gui/widgets/workarea/QueryWidget.cpp
gui/widgets/workarea/WorkAreaTabBar.cpp
gui/widgets/workarea/WorkAreaTabWidget.cpp
gui/widgets/workarea/WelcomeTab.cpp
# Final scope
gui/widgets/LogWidget.cpp
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
resources/robo.qrc
gui/resources/gui.qrc
)
@@ -130,6 +148,8 @@ add_executable(robomongo MACOSX_BUNDLE WIN32 app/main.cpp ${SOURCES})
target_link_libraries(robomongo
PRIVATE
Qt5::Widgets
Qt5::Network
Qt5::Xml
qjson
qscintilla
mongodb
@@ -151,6 +171,8 @@ target_compile_definitions(robomongo
PROJECT_GITHUB_FORK="${PROJECT_GITHUB_FORK}"
PROJECT_GITHUB_ISSUES="${PROJECT_GITHUB_ISSUES}"
PROJECT_VERSION="${PROJECT_VERSION}"
PROJECT_VERSION_SHORT="${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}"
PROJECT_QT_VERSION="${Qt5Core_VERSION}"
PROJECT_NAME_LOWERCASE="${PROJECT_NAME_LOWERCASE}")
if(SYSTEM_WINDOWS)
@@ -181,17 +203,18 @@ elseif(SYSTEM_MACOSX)
OUTPUT_NAME "Robomongo"
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_SOURCE_DIR}/install/macosx/Info.plist.in"
MACOSX_BUNDLE_BUNDLE_NAME "Robomongo"
MACOSX_BUNDLE_GUI_IDENTIFIER "com.paralect.Robomongo"
MACOSX_BUNDLE_INFO_STRING "Robomongo ${PROJECT_VERSION}. Copyright Paralect, 2013 - 2016"
MACOSX_BUNDLE_GUI_IDENTIFIER "com.3tsoftwarelabs.robomongo"
MACOSX_BUNDLE_INFO_STRING "Robomongo ${PROJECT_VERSION}. Copyright 3T Software Labs Ltd, 2014 - 2017"
MACOSX_BUNDLE_SHORT_VERSION "${PROJECT_VERSION}"
MACOSX_BUNDLE_LONG_VERSION "${PROJECT_VERSION}"
MACOSX_BUNDLE_BUNDLE_VERSION "${PROJECT_VERSION}"
MACOSX_BUNDLE_COPYRIGHT "Copyright Paralect, 2013 - 2016"
MACOSX_BUNDLE_ICON_FILE "Robomongo")
MACOSX_BUNDLE_COPYRIGHT "Copyright 3T Software Labs Ltd, 2014 - 2017"
MACOSX_BUNDLE_ICON_FILE "robomongo")
elseif(SYSTEM_LINUX)
set_target_properties(robomongo PROPERTIES
INSTALL_RPATH "$ORIGIN/../lib")
INSTALL_RPATH "$ORIGIN/../lib"
OUTPUT_NAME "robomongo")
endif()
# Install
+32 -20
View File
@@ -7,19 +7,21 @@
// 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/core/AppRegistry.h"
#include "robomongo/core/settings/SettingsManager.h"
#include "robomongo/gui/MainWindow.h"
#include "robomongo/gui/AppStyle.h"
#include "robomongo/gui/dialogs/EulaDialog.h"
#include "robomongo/ssh/ssh.h"
int main(int argc, char *argv[], char** envp)
{
if (rbm_ssh_init()) {
if (rbm_ssh_init())
return 1;
}
// Please check, do we really need envp for other OSes?
#ifdef Q_OS_WIN
@@ -29,6 +31,12 @@ int main(int argc, char *argv[], char** envp)
// Support for IPv6 is disabled by default. Enable it.
mongo::enableIPv6(true);
// Perform SSL-enabled mongo initialization
mongo::sslGlobalParams.sslMode.store(mongo::SSLParams::SSLMode_allowSSL);
// Cross Platform High DPI support - Qt 5.7
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
// Initialization routine for MongoDB shell
mongo::runGlobalInitializersOrDie(argc, argv, envp);
@@ -39,30 +47,34 @@ 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
// EULA License Agreement
auto const settingsManager = Robomongo::AppRegistry::instance().settingsManager();
if (!settingsManager->acceptedEulaVersions().contains(PROJECT_VERSION)) {
Robomongo::EulaDialog eulaDialog;
if (eulaDialog.exec() == QDialog::Rejected) {
rbm_ssh_cleanup();
return 1;
}
// EULA accepted
settingsManager->addAcceptedEulaVersion(PROJECT_VERSION);
settingsManager->save();
}
// Init GUI style
Robomongo::AppStyleUtils::initStyle();
// Application main window
Robomongo::MainWindow win;
// Resize main window. We are trying to keep it "almost" maximized.
QRect screenGeometry = QApplication::desktop()->availableGeometry();
int horizontalMargin = (int)(screenGeometry.width() * 0.1);
int verticalMargin = (int)(screenGeometry.height() * 0.1);
int width = screenGeometry.width() - horizontalMargin;
int height = screenGeometry.height() - verticalMargin;
win.resize(QSize(width, height));
// Center main window
int x = (screenGeometry.width() - win.width()) / 2;
int y = (screenGeometry.height() - win.height()) / 2;
win.move(x, y);
win.show();
Robomongo::MainWindow mainWindow;
mainWindow.show();
int rc = app.exec();
rbm_ssh_cleanup();
+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);
+16
View File
@@ -30,4 +30,20 @@ namespace Robomongo
class MongoDocument;
typedef boost::shared_ptr<MongoDocument> MongoDocumentPtr;
enum ConnectionType {
// This type of connection is shown in Explorer and also opens SSH tunnel for secondary
// connections (if needed)
ConnectionPrimary = 0,
// Never shown in Explorer and uses SSH tunnel from primary connection (if needed)
ConnectionSecondary = 1,
// The same as Primary, but is specifically for testing connections.
ConnectionTest = 2,
// Never shown in Explorer and can be used to refresh (via reconnecting) current connection view
// (i.e. db version, storage engine, current replica set primary, status of replica set etc...)
ConnectionRefresh = 3
};
}
+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);
}
+20 -4
View File
@@ -1,19 +1,35 @@
#include "robomongo/core/EventError.h"
#include "robomongo/utils/string_operations.h"
namespace Robomongo
{
EventError::EventError() :
_isNull(true) {}
EventError::EventError(const std::string &errorMessage) :
_errorMessage(errorMessage),
EventError::EventError(const std::string &errorMessage, ErrorCode errorCode /*= Unknown*/,
bool showErrorWindow /* = true */) :
_errorMessage(captilizeFirstChar(errorMessage)),
_errorCode(errorCode),
_showErrorWindow(showErrorWindow),
_isNull(false) {}
bool EventError::isNull() const {
EventError::EventError(const std::string &errorMessage, ReplicaSet const& replicaSetInfo,
bool showErrorWindow /* = true */ ) :
_errorMessage(errorMessage),
_errorCode(SetPrimaryUnreachable),
_replicaSetInfo(replicaSetInfo),
_showErrorWindow(showErrorWindow),
_isNull(false) {}
bool EventError::isNull() const
{
return _isNull;
}
const std::string &EventError::errorMessage() const {
const std::string &EventError::errorMessage() const
{
return _errorMessage;
}
}
+23 -4
View File
@@ -1,6 +1,8 @@
#pragma once
#include <string>
#include "robomongo/core/mongodb/ReplicaSet.h"
namespace Robomongo
{
/**
@@ -10,7 +12,11 @@ namespace Robomongo
*/
class EventError
{
public:
enum ErrorCode { Unknown, SetPrimaryUnreachable, ServerHasDifferentMembers };
/**
* @brief Creates "null" (or, in other words, empty) error.
* Subsequent call of isNull() on this object will return true.
@@ -21,7 +27,14 @@ namespace Robomongo
* @brief Creates error object with specified error message.
* Subsequent call of isNull() on this object will return false.
*/
explicit EventError(const std::string &errorMessage);
explicit EventError(const std::string &errorMessage, ErrorCode errorCode = Unknown,
bool showErrorWindow = true);
/**
* @brief Creates error object only for replica set connection failures
*/
explicit EventError(const std::string &errorMessage, ReplicaSet const& replicaSetInfo,
bool showErrorWindow = true);
/**
* @brief Tests whether error is "null" (or, in other words, empty).
@@ -35,13 +48,19 @@ namespace Robomongo
* @brief Returns error message that describes this error.
*/
const std::string &errorMessage() const;
ErrorCode errorCode() const { return _errorCode; }
ReplicaSet replicaSetInfo() const { return _replicaSetInfo; }
bool showErrorWindow() const { return _showErrorWindow; }
private:
/**
* @brief Error message
*/
const std::string _errorMessage;
bool _isNull;
std::string const _errorMessage;
ErrorCode const _errorCode = Unknown;
ReplicaSet const _replicaSetInfo;
bool const _showErrorWindow = true;
bool const _isNull;
};
}
+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);
+153 -41
View File
@@ -1,11 +1,14 @@
#include "robomongo/core/domain/App.h"
#include <QHash>
#include <QInputDialog>
#include "robomongo/core/domain/MongoServer.h"
#include "robomongo/core/domain/MongoShell.h"
#include "robomongo/core/domain/MongoCollection.h"
#include "robomongo/core/settings/ConnectionSettings.h"
#include "robomongo/core/settings/ReplicaSetSettings.h"
#include "robomongo/core/settings/SshSettings.h"
#include "robomongo/core/settings/SslSettings.h"
#include "robomongo/core/mongodb/SshTunnelWorker.h"
#include "robomongo/core/EventBus.h"
#include "robomongo/core/utils/QtUtils.h"
@@ -33,7 +36,7 @@ namespace Robomongo
}
App::App(EventBus *const bus) : QObject(),
_bus(bus) {
_bus(bus), _lastServerHandle(0) {
_bus->subscribe(this, EstablishSshConnectionResponse::Type);
_bus->subscribe(this, ListenSshConnectionResponse::Type);
_bus->subscribe(this, LogEvent::Type);
@@ -45,29 +48,84 @@ namespace Robomongo
std::for_each(_servers.begin(), _servers.end(), stdutils::default_delete<MongoServer*>());
}
bool App::openServer(ConnectionSettings *connection, ConnectionType type) {
SshSettings *ssh = connection->sshSettings();
if (ssh->enabled() && ssh->askPassword() &&
(type == ConnectionPrimary || type == ConnectionTest)) {
bool ok = false;
bool isByKey = ssh->authMethod() == "publickey";
std::string passText = isByKey ? "passphrase" : "password";
std::stringstream s;
s << "In order to continue, please provide the " << passText;
if (isByKey)
s << " for the key file";
s << "." << std::endl << std::endl;
if (ssh->authMethod() == "publickey")
s << "Private Key: " << ssh->privateKeyFile() << std::endl;
s << "Server: " << ssh->host() << std::endl;
s << "User: " << ssh->userName() << std::endl;
s << std::endl << "Enter your " << passText << " that will never be stored:";
QString userInput = QInputDialog::getText(NULL, tr("SSH Authentication"),
QtUtils::toQString(s.str()),
QLineEdit::Password, "", &ok);
if (!ok)
return false;
ssh->setAskedPassword(QtUtils::toStdString(userInput));
}
SslSettings *sslSettings = connection->sslSettings();
if (sslSettings->sslEnabled() && sslSettings->usePemFile() && sslSettings->askPassphrase()
&& (type == ConnectionPrimary || type == ConnectionTest))
{
if (!askSslPassphrasePromptDialog(connection))
{
return false;
}
}
openServerInternal(connection, type);
return true;
}
/**
* Creates and opens new server connection.
* @param connection: ConnectionSettings, that will be owned by MongoServer.
* @param visible: should this server be visible in UI (explorer) or not.
*/
MongoServer *App::openServer(ConnectionSettings *connection, bool visible) {
if (visible)
MongoServer *App::openServerInternal(ConnectionSettings* connSettings, ConnectionType type) {
++_lastServerHandle;
if (type == ConnectionPrimary)
_bus->publish(new ConnectingEvent(this));
// When connection is not "visible" or do not have
// When connection is SECONDARY or do not have
// ssh settings enabled, continue without SSH Tunnel
if (!visible || !connection->sshSettings()->enabled()) {
return continueOpenServer(connection, visible);
if (type == ConnectionSecondary || !connSettings->sshSettings()->enabled()) {
return continueOpenServer(_lastServerHandle, connSettings, type);
}
// Open SSH channel and only after that open connection
LOG_MSG(QString("Creating SSH tunnel to %1:%2...")
.arg(QtUtils::toQString(connection->sshSettings()->host()))
.arg(connection->sshSettings()->port()), mongo::logger::LogSeverity::Info());
.arg(QtUtils::toQString(connSettings->sshSettings()->host()))
.arg(connSettings->sshSettings()->port()), mongo::logger::LogSeverity::Info());
ConnectionSettings* settingsCopy = connection->clone();
ConnectionSettings* settingsCopy = connSettings->clone();
SshTunnelWorker* sshWorker = new SshTunnelWorker(settingsCopy);
_bus->send(sshWorker, new EstablishSshConnectionRequest(this, sshWorker, settingsCopy, visible));
_bus->send(sshWorker, new EstablishSshConnectionRequest(this, _lastServerHandle, sshWorker, settingsCopy, type));
return NULL;
}
@@ -77,46 +135,54 @@ namespace Robomongo
*/
void App::closeServer(MongoServer *server)
{
_servers.erase(std::remove_if(_servers.begin(), _servers.end(), stdutils::RemoveIfFound<MongoServer*>(server)), _servers.end());
_servers.erase(std::remove_if(_servers.begin(), _servers.end(), stdutils::RemoveIfFound<MongoServer*>(server)),
_servers.end());
}
MongoShell *App::openShell(MongoCollection *collection,const QString &filePathToSave)
void App::openShell(MongoCollection *collection, const QString &filePathToSave)
{
ConnectionSettings *connection = collection->database()->server()->connectionRecord();
connection->setDefaultDatabase(collection->database()->name());
QString script = detail::buildCollectionQuery(collection->name(), "find({})");
return openShell(connection, ScriptInfo(script, true, CursorPosition(0, -2), QtUtils::toQString(collection->database()->name()),filePathToSave));
openShell(collection->database()->server(), connection, ScriptInfo(script, true, CursorPosition(0, -2),
QtUtils::toQString(collection->database()->name()), filePathToSave));
}
MongoShell *App::openShell(MongoServer *server,const QString &script, const std::string &dbName,
void App::openShell(MongoServer *server, const QString &script, const std::string &dbName,
bool execute, const QString &shellName,
const CursorPosition &cursorPosition,const QString &filePathToSave)
const CursorPosition &cursorPosition, const QString &filePathToSave)
{
ConnectionSettings *connection = server->connectionRecord();
if (!dbName.empty())
connection->setDefaultDatabase(dbName);
return openShell(connection, ScriptInfo(script, execute, cursorPosition, shellName,filePathToSave));
openShell(server, connection, ScriptInfo(script, execute, cursorPosition, shellName, filePathToSave));
}
MongoShell *App::openShell(MongoDatabase *database, const QString &script,
void App::openShell(MongoDatabase *database, const QString &script,
bool execute, const QString &shellName,
const CursorPosition &cursorPosition,const QString &filePathToSave)
const CursorPosition &cursorPosition, const QString &filePathToSave)
{
ConnectionSettings *connection = database->server()->connectionRecord();
connection->setDefaultDatabase(database->name());
return openShell(connection, ScriptInfo(script, execute, cursorPosition, shellName,filePathToSave));
openShell(database->server(), connection, ScriptInfo(script, execute, cursorPosition,
shellName, filePathToSave));
}
MongoShell *App::openShell(ConnectionSettings *connection, const ScriptInfo &scriptInfo)
void App::openShell(MongoServer* server, ConnectionSettings* connection, const ScriptInfo &scriptInfo)
{
MongoServer *server = openServer(connection, false);
MongoShell *shell = new MongoShell(server,scriptInfo);
MongoServer *serverClone = openServerInternal(connection, ConnectionSecondary);
if (!serverClone || !server)
return;
MongoShell *shell = new MongoShell(serverClone, scriptInfo);
// Connection between explorer's server and tab's MongoShells
_bus->subscribe(server, ReplicaSetRefreshed::Type, shell);
_shells.push_back(shell);
_bus->publish(new OpeningShellEvent(this, shell));
shell->execute();
return shell;
return;
}
/**
@@ -126,7 +192,7 @@ namespace Robomongo
void App::closeShell(MongoShell *shell)
{
// Do nothing, if this shell not owned by this App.
MongoShellsContainerType::iterator it = std::find_if(_shells.begin(),_shells.end(),std::bind1st(std::equal_to<MongoShell *>(),shell));
MongoShellsContainerType::iterator it = std::find_if(_shells.begin(), _shells.end(), std::bind1st(std::equal_to<MongoShell *>(), shell));
if (it == _shells.end())
return;
@@ -135,57 +201,103 @@ namespace Robomongo
delete shell;
}
MongoServer* App::continueOpenServer(ConnectionSettings *connection, bool visible, int localport) {
ConnectionSettings* settings = connection->clone();
MongoServer *App::continueOpenServer(int serverHandle, ConnectionSettings* connSettings, ConnectionType type,
int localport)
{
ConnectionSettings* connSettingsClone = connSettings->clone();
// Modify connection settings when SSH tunnel is used
if (visible && settings->sshSettings()->enabled()) {
settings->setServerHost("127.0.0.1");
settings->setServerPort(localport);
if ((type == ConnectionPrimary || type == ConnectionTest)
&& connSettingsClone->sshSettings()->enabled()) {
connSettingsClone->setServerHost("127.0.0.1");
connSettingsClone->setServerPort(localport);
}
MongoServer *server = new MongoServer(settings, visible);
MongoServer *server = new MongoServer(serverHandle, connSettingsClone, type);
_servers.push_back(server);
server->runWorkerThread();
LOG_MSG(QString("Connecting to %1...").arg(QtUtils::toQString(server->connectionRecord()->getFullAddress())), mongo::logger::LogSeverity::Info());
auto replicaSetStr = QString::fromStdString(connSettings->connectionName()) + " [Replica Set]";
replicaSetStr = (connSettings->replicaSetSettings()->members().size() > 0)
? replicaSetStr + QString::fromStdString(connSettings->replicaSetSettings()->members()[0])
: replicaSetStr + "";
QString serverAddress = connSettings->isReplicaSet()
? replicaSetStr
: QString::fromStdString(connSettings->getFullAddress());
LOG_MSG(QString("Connecting to %1...").arg(serverAddress), mongo::logger::LogSeverity::Info());
server->tryConnect();
return server;
}
void App::handle(EstablishSshConnectionResponse *event) {
if (event->isError()) {
_bus->publish(new ConnectionFailedEvent(this, event->error().errorMessage()));
_bus->publish(new ConnectionFailedEvent(
this, event->serverHandle, event->connectionType, event->error().errorMessage(),
ConnectionFailedEvent::SshConnection));
return;
}
LOG_MSG(QString("SSH tunnel created successfully"), mongo::logger::LogSeverity::Info());
_bus->send(event->worker, new ListenSshConnectionRequest(this));
continueOpenServer(event->settings, event->visible, event->localport);
// record event->worker and delete when needed
continueOpenServer(event->serverHandle, event->settings, event->connectionType, event->localport);
_bus->send(event->worker, new ListenSshConnectionRequest(this, event->serverHandle, event->connectionType));
}
void App::handle(LogEvent *event) {
if (event->level == LogEvent::ERROR) {
if (event->level == LogEvent::RBM_ERROR) {
LOG_MSG(event->message, mongo::logger::LogSeverity::Error());
} else if (event->level == LogEvent::WARN) {
} else if (event->level == LogEvent::RBM_WARN) {
LOG_MSG(event->message, mongo::logger::LogSeverity::Warning());
} else if (event->level == LogEvent::INFO) {
} else if (event->level == LogEvent::RBM_INFO) {
LOG_MSG(event->message, mongo::logger::LogSeverity::Info());
} else if (event->level == LogEvent::DEBUG) {
} else if (event->level == LogEvent::RBM_DEBUG) {
LOG_MSG(event->message, mongo::logger::LogSeverity::Log());
}
}
void App::handle(ListenSshConnectionResponse *event) {
if (event->isError()) {
_bus->publish(new ConnectionFailedEvent(this, event->error().errorMessage()));
_bus->publish(new ConnectionFailedEvent(this, event->serverHandle, event->connectionType, event->error().errorMessage(),
ConnectionFailedEvent::SshChannel));
return;
}
LOG_MSG(QString("SSH tunnel closed."), mongo::logger::LogSeverity::Error());
}
void App::fireConnectionFailedEvent(int serverHandle, ConnectionType type, std::string errormsg,
ConnectionFailedEvent::Reason reason) {
_bus->publish(new ConnectionFailedEvent(this, serverHandle, type, errormsg, reason));
}
bool App::askSslPassphrasePromptDialog(ConnectionSettings *connSettings) const
{
auto sslSettings = connSettings->sslSettings();
bool ok = false;
std::stringstream s;
s << "In order to continue, please provide the passphrase";
s << "." << std::endl << std::endl;
s << "Server: " << connSettings->serverHost() << ":" << connSettings->serverPort() << std::endl;
s << "PEM file: " << sslSettings->pemKeyFile() << std::endl;
s << std::endl << "Enter your PEM key passphrase (will never be stored):";
QString userInput = QInputDialog::getText(NULL, tr("SSL Authentication"),
QtUtils::toQString(s.str()),
QLineEdit::Password, "", &ok);
if (!ok)
{
return false;
}
sslSettings->setPemPassPhrase(QtUtils::toStdString(userInput));
return ok;
}
}
+28 -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,22 @@ namespace Robomongo
/**
* @brief Open new shell based on specified collection
*/
MongoShell *openShell(MongoCollection *collection,const QString &filePathToSave=QString());
void openShell(MongoCollection *collection, const QString &filePathToSave = QString());
MongoShell *openShell(MongoServer *server, const QString &script, const std::string &dbName = std::string(),
void openShell(MongoServer *server, const QString &script, const std::string &dbName = std::string(),
bool execute = true, const QString &shellName = QString(),
const CursorPosition &cursorPosition = CursorPosition(), const QString &file = QString());
MongoShell *openShell(MongoDatabase *database, const QString &script,
void openShell(MongoDatabase *database, const QString &script,
bool execute = true, const QString &shellName = QString(),
const CursorPosition &cursorPosition = CursorPosition(), const QString &filePathToSave = QString());
MongoShell *openShell(ConnectionSettings *connection, const ScriptInfo &scriptInfo);
MongoServersContainerType getServers() const {return _servers; };
/**
* @brief Open new shell using explorer's MongoServer (ExplorerServerTreeItem's _server)
*/
void openShell(MongoServer* server, ConnectionSettings* connSettings, const ScriptInfo &scriptInfo);
MongoServersContainerType getServers() const { return _servers; };
/**
* @brief Closes MongoShell and frees all resources, owned by specified MongoShell.
@@ -78,13 +83,25 @@ namespace Robomongo
*/
void closeShell(MongoShell *shell);
void fireConnectionFailedEvent(int serverHandle, ConnectionType type, std::string errormsg, ConnectionFailedEvent::Reason reason);
int getLastServerHandle() const { return _lastServerHandle; };
public Q_SLOTS:
void handle(EstablishSshConnectionResponse *event);
void handle(ListenSshConnectionResponse *event);
void handle(LogEvent *event);
private:
MongoServer* continueOpenServer(ConnectionSettings *connection, bool visible, int localport = 0);
MongoServer *openServerInternal(ConnectionSettings* connSettings, ConnectionType type);
MongoServer *continueOpenServer(int serverHandle, ConnectionSettings* connSettings, ConnectionType type, int localport = 0);
/**
* @brief Create prompt dialog to enter SSL PEM key passphrase and save passphrase into SSL settings
* @param connection Pointer to active connection settings
* @return true on success, false otherwise
*/
bool askSslPassphrasePromptDialog(ConnectionSettings *connSettings) const;
/**
* MongoServers, owned by this App.
@@ -97,5 +114,9 @@ namespace Robomongo
MongoShellsContainerType _shells;
EventBus *const _bus;
// Increase monotonically when new MongoServer is created
// Never decreases.
int _lastServerHandle;
};
}
+161 -29
View File
@@ -5,6 +5,8 @@
#include "robomongo/core/mongodb/MongoWorker.h"
#include "robomongo/core/AppRegistry.h"
#include "robomongo/core/EventBus.h"
#include "robomongo/core/utils/Logger.h"
#include "robomongo/utils/common.h"
namespace Robomongo
{
@@ -15,6 +17,13 @@ namespace Robomongo
R_REGISTER_EVENT(MongoDatabaseFunctionsLoadingEvent)
R_REGISTER_EVENT(MongoDatabaseCollectionsLoadingEvent)
const std::string MongoDatabase::StorageEngineType::WIRED_TIGER = "wiredTiger";
const std::string MongoDatabase::StorageEngineType::MMAPV1 = "mmapv1";
const float MongoDatabase::DBVersion::MONGODB_2_6 = 2.6f;
const float MongoDatabase::DBVersion::MONGODB_3_0 = 3.0f;
const float MongoDatabase::DBVersion::MONGODB_3_2 = 3.2f;
MongoDatabase::MongoDatabase(MongoServer *server, const std::string &name) :
QObject(),
_system(name == "admin" || name == "local"),
@@ -30,113 +39,148 @@ namespace Robomongo
void MongoDatabase::loadCollections()
{
_bus->publish(new MongoDatabaseCollectionsLoadingEvent(this));
_bus->send(_server->client(), new LoadCollectionNamesRequest(this, _name));
_bus->send(_server->worker(), new LoadCollectionNamesRequest(this, _name));
}
void MongoDatabase::loadUsers()
{
_bus->publish(new MongoDatabaseUsersLoadingEvent(this));
_bus->send(_server->client(), new LoadUsersRequest(this, _name));
_bus->send(_server->worker(), new LoadUsersRequest(this, _name));
}
void MongoDatabase::loadFunctions()
{
_bus->publish(new MongoDatabaseFunctionsLoadingEvent(this));
_bus->send(_server->client(), new LoadFunctionsRequest(this, _name));
_bus->send(_server->worker(), new LoadFunctionsRequest(this, _name));
}
void MongoDatabase::createCollection(const std::string &collection)
void MongoDatabase::createCollection(const std::string &collection, long long size, bool capped, int maxDocNum, const mongo::BSONObj& extraOptions)
{
_bus->send(_server->client(), new CreateCollectionRequest(this, MongoNamespace(_name, collection)));
_bus->send(_server->worker(),
new CreateCollectionRequest(this, MongoNamespace(_name, collection), extraOptions, size, capped, maxDocNum));
}
void MongoDatabase::dropCollection(const std::string &collection)
{
_bus->send(_server->client(), new DropCollectionRequest(this, MongoNamespace(_name, collection)));
_bus->send(_server->worker(), new DropCollectionRequest(this, MongoNamespace(_name, collection)));
}
void MongoDatabase::renameCollection(const std::string &collection, const std::string &newCollection)
{
_bus->send(_server->client(), new RenameCollectionRequest(this, MongoNamespace(_name, collection), newCollection));
_bus->send(_server->worker(), new RenameCollectionRequest(this, MongoNamespace(_name, collection),
newCollection));
}
void MongoDatabase::duplicateCollection(const std::string &collection, const std::string &newCollection)
{
_bus->send(_server->client(), new DuplicateCollectionRequest(this, MongoNamespace(_name, collection), newCollection));
_bus->send(_server->worker(), new DuplicateCollectionRequest(this, MongoNamespace(_name, collection), newCollection));
}
void MongoDatabase::copyCollection(MongoServer *server, const std::string &sourceDatabase, const std::string &collection)
{
_bus->send(_server->client(), new CopyCollectionToDiffServerRequest(this, server->client(), sourceDatabase, collection, _name));
_bus->send(_server->worker(), new CopyCollectionToDiffServerRequest(this, server->worker(), sourceDatabase, collection, _name));
}
void MongoDatabase::createUser(const MongoUser &user, bool overwrite)
{
_bus->send(_server->client(), new CreateUserRequest(this, _name, user, overwrite));
_bus->send(_server->worker(), new CreateUserRequest(this, _name, user, overwrite));
}
void MongoDatabase::dropUser(const mongo::OID &id)
void MongoDatabase::dropUser(const mongo::OID &id, std::string const& userName)
{
_bus->send(_server->client(), new DropUserRequest(this, _name, id));
_bus->send(_server->worker(), new DropUserRequest(this, _name, id, userName));
}
void MongoDatabase::createFunction(const MongoFunction &fun)
{
_bus->send(_server->client(), new CreateFunctionRequest(this, _name, fun));
_bus->send(_server->worker(), new CreateFunctionRequest(this, _name, fun));
}
void MongoDatabase::updateFunction(const std::string &name, const MongoFunction &fun)
{
_bus->send(_server->client(), new CreateFunctionRequest(this, _name, fun, name));
_bus->send(_server->worker(), new CreateFunctionRequest(this, _name, fun, name));
}
void MongoDatabase::dropFunction(const std::string &name)
{
_bus->send(_server->client(), new DropFunctionRequest(this, _name, name));
_bus->send(_server->worker(), new DropFunctionRequest(this, _name, name));
}
void MongoDatabase::handle(LoadCollectionNamesResponse *loaded)
void MongoDatabase::handle(LoadCollectionNamesResponse *event)
{
if (loaded->isError()) {
_bus->publish(new MongoDatabaseCollectionListLoadedEvent(this, loaded->error()));
if (event->isError()) {
if (_server->connectionRecord()->isReplicaSet()) // replica set
handleIfReplicaSetUnreachable(event);
else // single server
_bus->publish(new MongoDatabaseCollectionListLoadedEvent(this, event->error()));
genericEventErrorHandler(event, "Failed to refresh 'Collections'.", _bus, this);
return;
}
clearCollections();
const std::vector<MongoCollectionInfo> &colectionsInfos = loaded->collectionInfos();
for (std::vector<MongoCollectionInfo>::const_iterator it = colectionsInfos.begin(); it != colectionsInfos.end(); ++it) {
const MongoCollectionInfo &info = *it;
MongoCollection *collection = new MongoCollection(this, info);
addCollection(collection);
}
for (auto const& collectionInfo : event->collectionInfos())
addCollection(new MongoCollection(this, collectionInfo));
_bus->publish(new MongoDatabaseCollectionListLoadedEvent(this, _collections));
LOG_MSG("'Collections' refreshed.", mongo::logger::LogSeverity::Info());
}
void MongoDatabase::handle(CreateFunctionResponse *event)
{
if (event->isError()) {
handleIfReplicaSetUnreachable(event);
genericEventErrorHandler(event, "Failed to create function \'" + event->functionName + "\'.", _bus, this);
}
else {
loadFunctions();
LOG_MSG("Function \'" + event->functionName + "\' created.", mongo::logger::LogSeverity::Info());
}
}
void MongoDatabase::handle(CreateUserResponse *event)
{
if (event->isError())
return;
if (event->isError()) {
handleIfReplicaSetUnreachable(event);
genericEventErrorHandler(event, "Failed to create user \'" + event->userName + "\'.", _bus, this);
}
else {
loadUsers();
LOG_MSG("User \'" + event->userName + "\' created.", mongo::logger::LogSeverity::Info());
}
}
void MongoDatabase::handle(LoadUsersResponse *event)
{
if (event->isError()) {
_bus->publish(new MongoDatabaseUsersLoadedEvent(this, event->error()));
if (_server->connectionRecord()->isReplicaSet()) // replica set
handleIfReplicaSetUnreachable(event);
else // single server
_bus->publish(new MongoDatabaseUsersLoadedEvent(this, event->error()));
genericEventErrorHandler(event, "Failed to refresh 'Users'.", _bus, this);
return;
}
_bus->publish(new MongoDatabaseUsersLoadedEvent(this, this, event->users()));
LOG_MSG("'Users' refreshed.", mongo::logger::LogSeverity::Info());
}
void MongoDatabase::handle(LoadFunctionsResponse *event)
{
if (event->isError()) {
_bus->publish(new MongoDatabaseFunctionsLoadedEvent(this, event->error()));
if (_server->connectionRecord()->isReplicaSet()) // replica set
handleIfReplicaSetUnreachable(event);
else // single server
_bus->publish(new MongoDatabaseFunctionsLoadedEvent(this, event->error()));
genericEventErrorHandler(event, "Failed to refresh 'Functions'.", _bus, this);
return;
}
_bus->publish(new MongoDatabaseFunctionsLoadedEvent(this, this, event->functions()));
LOG_MSG("'Functions' refreshed.", mongo::logger::LogSeverity::Info());
}
void MongoDatabase::clearCollections()
@@ -149,4 +193,92 @@ namespace Robomongo
{
_collections.push_back(collection);
}
void MongoDatabase::handle(CreateCollectionResponse *event)
{
if (event->isError()) {
handleIfReplicaSetUnreachable(event);
genericEventErrorHandler(event, "Failed to create collection \'" + event->collection + "\'.", _bus, this);
}
else {
loadCollections();
LOG_MSG("Collection \'" + event->collection + "\' created.", mongo::logger::LogSeverity::Info());
}
}
void MongoDatabase::handle(DropCollectionResponse *event)
{
if (event->isError()) {
handleIfReplicaSetUnreachable(event);
genericEventErrorHandler(event, "Failed to drop collection \'" + event->collection + "\'.", _bus, this);
}
else {
loadCollections();
LOG_MSG("Collection \'" + event->collection + "\' dropped", mongo::logger::LogSeverity::Info());
}
}
void MongoDatabase::handle(DropFunctionResponse *event)
{
if (event->isError()) {
handleIfReplicaSetUnreachable(event);
genericEventErrorHandler(event, "Failed to remove function \'" + event->functionName + "\'.", _bus, this);
}
else {
loadFunctions();
LOG_MSG("Function \'" + event->functionName + "\' removed", mongo::logger::LogSeverity::Info());
}
}
void MongoDatabase::handle(DropUserResponse *event)
{
if (event->isError()) {
handleIfReplicaSetUnreachable(event);
genericEventErrorHandler(event, "Failed to drop user \'" + event->username + "\'.", _bus, this);
}
else {
loadUsers();
LOG_MSG("User \'" + event->username + "\' dropped", mongo::logger::LogSeverity::Info());
}
}
void MongoDatabase::handle(RenameCollectionResponse *event)
{
if (event->isError()) {
handleIfReplicaSetUnreachable(event);
genericEventErrorHandler(event, "Failed to rename collection.", _bus, this);
}
else {
loadCollections();
LOG_MSG("Collection \'" + event->oldCollection + "\' renamed to \'" +
event->newCollection +"\'." , mongo::logger::LogSeverity::Info());
}
}
void MongoDatabase::handle(DuplicateCollectionResponse *event)
{
if (event->isError()) {
handleIfReplicaSetUnreachable(event);
genericEventErrorHandler(event, "Failed to duplicate collection \'" + event->sourceCollection + "\'.",
_bus, this);
}
else {
loadCollections();
LOG_MSG("Collection \'" + event->sourceCollection + "\' duplicated as \'" +
event->duplicateCollection + "\'.", mongo::logger::LogSeverity::Info());
}
}
void MongoDatabase::handleIfReplicaSetUnreachable(Event *event)
{
if (!_server->connectionRecord()->isReplicaSet())
return;
if (EventError::SetPrimaryUnreachable == event->error().errorCode()) {
auto refreshEvent = ReplicaSetRefreshed(this, event->error(), event->error().replicaSetInfo());
_server->handle(&refreshEvent);
}
}
}
+29 -2
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,14 +56,14 @@ namespace Robomongo
void loadFunctions();
void createCollection(const std::string &collection);
void createCollection(const std::string &collection, long long size, bool capped, int maxDocNum, const mongo::BSONObj& extraOptions);
void dropCollection(const std::string &collection);
void renameCollection(const std::string &collection, const std::string &newCollection);
void duplicateCollection(const std::string &collection, const std::string &newCollection);
void copyCollection(MongoServer *server, const std::string &sourceDatabase, const std::string &collection);
void createUser(const MongoUser &user, bool overwrite);
void dropUser(const mongo::OID &id);
void dropUser(const mongo::OID &id, std::string const& userName);
void createFunction(const MongoFunction &fun);
void updateFunction(const std::string &name, const MongoFunction &fun);
@@ -64,11 +83,19 @@ namespace Robomongo
void handle(LoadCollectionNamesResponse *event);
void handle(LoadUsersResponse *event);
void handle(LoadFunctionsResponse *event);
void handle(CreateFunctionResponse *event);
void handle(CreateUserResponse *event);
void handle(CreateCollectionResponse *event);
void handle(DropCollectionResponse *event);
void handle(DropFunctionResponse *event);
void handle(DropUserResponse *event);
void handle(RenameCollectionResponse *event);
void handle(DuplicateCollectionResponse *event);
private:
void clearCollections();
void addCollection(MongoCollection *collection);
void handleIfReplicaSetUnreachable(Event *event);
private:
MongoServer *_server;
+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
+355 -63
View File
@@ -4,55 +4,82 @@
#include "robomongo/core/settings/ConnectionSettings.h"
#include "robomongo/core/settings/SshSettings.h"
#include "robomongo/core/settings/SettingsManager.h"
#include "robomongo/core/settings/ReplicaSetSettings.h"
#include "robomongo/core/mongodb/MongoWorker.h"
#include "robomongo/core/mongodb/SshTunnelWorker.h"
#include "robomongo/core/AppRegistry.h"
#include "robomongo/core/domain/App.h"
#include "robomongo/core/EventBus.h"
#include "robomongo/core/events/MongoEventsInfo.h"
#include "robomongo/core/utils/Logger.h"
#include "robomongo/core/utils/QtUtils.h"
#include "robomongo/utils/common.h"
#include "robomongo/utils/string_operations.h"
namespace Robomongo {
R_REGISTER_EVENT(MongoServerLoadingDatabasesEvent)
MongoServer::MongoServer(ConnectionSettings *settings, bool visible) : QObject(),
MongoServer::MongoServer(int handle, ConnectionSettings *settings, ConnectionType connectionType) : QObject(),
_version(0.0f),
_visible(visible),
_client(NULL),
_connectionType(connectionType),
_worker(nullptr),
_isConnected(false),
_settings(settings),
_bus(AppRegistry::instance().bus()) { }
_connSettings(settings),
_handle(handle),
_bus(AppRegistry::instance().bus()),
_app(AppRegistry::instance().app()),
_replicaSetInfo(nullptr)
{}
bool MongoServer::isConnected() const {
return _isConnected;
}
ConnectionSettings *MongoServer::connectionRecord() const {
return _settings;
return _connSettings.get();
}
MongoServer::~MongoServer() {
clearDatabases();
if (_client != NULL) {
_client->stopAndDelete();
if (_worker) {
_worker->stopAndDelete();
}
// MongoWorker "_client" does not deleted here, because it is now owned by
// MongoWorker "_worker" is not deleted here, because it is now owned by
// another thread (call to moveToThread() made in MongoWorker constructor).
// It will be deleted by this thread by means of "deleteLater()", which
// is also specified in MongoWorker constructor.
delete _settings;
}
/**
* @brief Try to connect to MongoDB server.
* @throws MongoException, if fails
*/
void MongoServer::tryConnect() {
_bus->send(_client, new EstablishConnectionRequest(this));
void MongoServer::tryConnect()
{
_bus->send(_worker, new EstablishConnectionRequest(this, _connectionType, _connSettings->uuid().toStdString()));
}
QStringList MongoServer::getDatabasesNames() const {
void MongoServer::tryRefresh()
{
_bus->send(_worker, new EstablishConnectionRequest(this, ConnectionRefresh, _connSettings->uuid().toStdString()));
}
void MongoServer::tryRefreshReplicaSetConnection()
{
_bus->send(_worker, new EstablishConnectionRequest(this, ConnectionRefresh, _connSettings->uuid().toStdString()));
}
void MongoServer::tryRefreshReplicaSetFolder(bool expanded, bool showLoading /*= true*/)
{
if (!_connSettings->isReplicaSet())
return;
if (showLoading)
_bus->publish(new ReplicaSetFolderLoading(this));
_bus->send(_worker, new RefreshReplicaSetFolderRequest(this, expanded));
}
QStringList MongoServer::getDatabasesNames() const
{
QStringList result;
for (QList<MongoDatabase *>::const_iterator it = _databases.begin(); it != _databases.end(); ++it) {
MongoDatabase *datab = *it;
@@ -61,11 +88,17 @@ namespace Robomongo {
return result;
}
void MongoServer::createDatabase(const std::string &dbName) {
_bus->send(_client,new CreateDatabaseRequest(this, dbName));
void MongoServer::addDatabase(MongoDatabase *database) {
_databases.append(database);
}
MongoDatabase *MongoServer::findDatabaseByName(const std::string &dbName) const {
void MongoServer::createDatabase(const std::string &dbName)
{
_bus->send(_worker, new CreateDatabaseRequest(this, dbName));
}
MongoDatabase *MongoServer::findDatabaseByName(const std::string &dbName) const
{
for (QList<MongoDatabase *>::const_iterator it = _databases.begin(); it != _databases.end(); ++it) {
MongoDatabase *datab = *it;
if (datab->name() == dbName) {
@@ -76,7 +109,7 @@ namespace Robomongo {
}
void MongoServer::dropDatabase(const std::string &dbName) {
_bus->send(_client,new DropDatabaseRequest(this, dbName));
_bus->send(_worker, new DropDatabaseRequest(this, dbName));
}
void MongoServer::insertDocuments(const std::vector<mongo::BSONObj> &objCont,
@@ -87,94 +120,353 @@ namespace Robomongo {
}
void MongoServer::insertDocument(const mongo::BSONObj &obj, const MongoNamespace &ns) {
_bus->send(_client,new InsertDocumentRequest(this, obj, ns));
_bus->send(_worker, new InsertDocumentRequest(this, obj, ns));
}
void MongoServer::saveDocuments(const std::vector<mongo::BSONObj> &objCont, const MongoNamespace &ns) {
for (std::vector<mongo::BSONObj>::const_iterator it = objCont.begin(); it != objCont.end(); it++) {
saveDocument(*it,ns);
saveDocument(*it, ns);
}
}
void MongoServer::saveDocument(const mongo::BSONObj &obj, const MongoNamespace &ns) {
_bus->send(_client, new InsertDocumentRequest(this, obj, ns, true));
_bus->send(_worker, new InsertDocumentRequest(this, obj, ns, true));
}
void MongoServer::removeDocuments(mongo::Query query, const MongoNamespace &ns, bool justOne) {
_bus->send(_client, new RemoveDocumentRequest(this, query, ns, justOne));
void MongoServer::removeDocuments(mongo::Query query, const MongoNamespace &ns,
RemoveDocumentCount removeCount, int index)
{
_bus->send(_worker, new RemoveDocumentRequest(this, query, ns, removeCount, index));
}
void MongoServer::loadDatabases() {
void MongoServer::loadDatabases()
{
_bus->publish(new MongoServerLoadingDatabasesEvent(this));
_bus->send(_client, new LoadDatabaseNamesRequest(this));
if (_connSettings->isReplicaSet()) {
tryRefreshReplicaSetConnection();
}
else { // single server
_bus->send(_worker, new LoadDatabaseNamesRequest(this));
}
}
void MongoServer::clearDatabases() {
void MongoServer::clearDatabases()
{
qDeleteAll(_databases);
_databases.clear();
}
void MongoServer::addDatabase(MongoDatabase *database) {
_databases.append(database);
}
void MongoServer::handle(EstablishConnectionResponse *event)
{
_connectionType = event->connectionType;
void MongoServer::handle(EstablishConnectionResponse *event) {
// In any case, replica set info must be updated, there might be reachable secondary(ies).
// Also cached set name must be updated or cleared for failed connections.
updateReplicaSetSettings(event);
// --- Connection Failed
if (event->isError()) {
_isConnected = false;
std::stringstream ss;
ss << "Cannot connect to the MongoDB at " << connectionRecord()->getFullAddress()
<< ".\n\nError:\n" << event->error().errorMessage();
_bus->publish(new ConnectionFailedEvent(this, ss.str()));
handleConnectionFailure(event);
return;
}
const ConnectionInfo &info = event->info();
// --- Connections Successful
// Save various information after successful connection
const ConnectionInfo &info = event->info;
_version = info._version;
_storageEngineType = info._storageEngineType;
_isConnected = true;
// Do nothing if this is not "visible" connection
if (!_visible) {
return;
// ConnectionRefresh is used just to update connection view (_version, _storageEngineType, _repPrimary etc..)
// So we return here after updating(refreshing) information related to connection view.
if (ConnectionRefresh == event->connectionType) {
if (_connSettings->isReplicaSet()) {
// If it is replica set connection, do not return yet.
LOG_MSG("Replica set refreshed. [" + _connSettings->connectionName() + ']',
mongo::logger::LogSeverity::Info());
}
else { // single server
LOG_MSG("Server refreshed.", mongo::logger::LogSeverity::Info());
return;
}
}
_bus->publish(new ConnectionEstablishedEvent(this));
// Only for single servers
if (!_connSettings->isReplicaSet()) {
_bus->publish(new ConnectionEstablishedEvent(this, _connectionType, info));
// Do nothing if this is not a "primary" connection
if (ConnectionPrimary != _connectionType)
return;
}
if (ConnectionPrimary == _connectionType)
LOG_MSG("Establish connection successful. Connection: " + _connSettings->connectionName(),
mongo::logger::LogSeverity::Info());
clearDatabases();
for(std::vector<std::string>::const_iterator it = info._databases.begin(); it != info._databases.end(); ++it) {
const std::string &name = *it;
MongoDatabase *db = new MongoDatabase(this, name);
addDatabase(db);
for (auto const& dbname : info._databases) {
MongoDatabase *db = new MongoDatabase(this, dbname);
addDatabase(db); // todo: serverClones for replica sets should not do this
}
if (_connSettings->isReplicaSet()) {
_bus->publish(new ConnectionEstablishedEvent(this, event->connectionType, info));
// In order to do first connection much faster, time consuming refresh
// "repSetMonitor->startOrContinueRefresh(). refreshAll()" is being requested after
// successful connection.
if (ConnectionPrimary == event->connectionType)
_bus->send(_worker, new RefreshReplicaSetFolderRequest(this, false));
}
}
void MongoServer::handle(LoadDatabaseNamesResponse *event) {
void MongoServer::handle(RefreshReplicaSetFolderResponse *event)
{
handleReplicaSetRefreshEvents(event->isError(), event->error(), event->replicaSet, event->expanded);
}
void MongoServer::handle(LoadDatabaseNamesResponse *event)
{
if (event->isError()) {
_bus->publish(new DatabaseListLoadedEvent(this, event->error()));
return;
}
clearDatabases();
for(std::vector<std::string>::iterator it = event->databaseNames.begin(); it != event->databaseNames.end(); ++it) {
const std::string &name = *it;
MongoDatabase *db = new MongoDatabase(this, name);
for (auto const& dbname : event->databaseNames) {
MongoDatabase *db = new MongoDatabase(this, dbname);
addDatabase(db);
}
_bus->publish(new DatabaseListLoadedEvent(this, _databases));
LOG_MSG("Database list refreshed. Connection: " + _connSettings->connectionName(),
mongo::logger::LogSeverity::Info());
}
void MongoServer::handle(InsertDocumentResponse *event) {
_bus->publish(new InsertDocumentResponse(event->sender(), event->error()));
void MongoServer::handle(InsertDocumentResponse *event)
{
if (event->isError()) {
if (_connSettings->isReplicaSet()) {
if (ConnectionPrimary == _connectionType) { // Insert document from explorer context menu
if (EventError::SetPrimaryUnreachable == event->error().errorCode()) {
auto refreshEvent = ReplicaSetRefreshed(this, event->error(), event->error().replicaSetInfo());
handle(&refreshEvent);
}
}
else { // Insert document from tab results window (Notifier, OutputWindow widget)
_bus->publish(new InsertDocumentResponse(this, event->error()));
}
}
genericEventErrorHandler(event, "Failed to insert document.", _bus, this);
}
else {
_bus->publish(new InsertDocumentResponse(this, event->error()));
LOG_MSG("Document inserted.", mongo::logger::LogSeverity::Info());
}
}
void MongoServer::handle(RemoveDocumentResponse *event) {
_bus->publish(new RemoveDocumentResponse(event->sender(), event->error()));
void MongoServer::handle(RemoveDocumentResponse *event)
{
if (event->removeCount == RemoveDocumentCount::MULTI && event->index > 0)
return;
std::string subStr;
switch (event->removeCount) {
case RemoveDocumentCount::ONE: subStr = "document."; break;
case RemoveDocumentCount::MULTI: subStr = "documents."; break;
case RemoveDocumentCount::ALL: subStr = "all documents."; break;
default: subStr = "(logic error)."; break;
}
if (event->isError()) {
if (_connSettings->isReplicaSet() &&
EventError::SetPrimaryUnreachable == event->error().errorCode()) {
auto refreshEvent = ReplicaSetRefreshed(this, event->error(), event->error().replicaSetInfo());
handle(&refreshEvent);
}
genericEventErrorHandler(event, "Failed to remove " + subStr, _bus, this);
}
else { // success
_bus->publish(new RemoveDocumentResponse(this, event->error(), event->removeCount, event->index));
LOG_MSG("Removed " + subStr, mongo::logger::LogSeverity::Info());
}
}
void MongoServer::runWorkerThread() {
_client = new MongoWorker(_settings->clone(),
AppRegistry::instance().settingsManager()->loadMongoRcJs(),
AppRegistry::instance().settingsManager()->batchSize());
void MongoServer::runWorkerThread()
{
_worker = new MongoWorker(_connSettings->clone(),
AppRegistry::instance().settingsManager()->loadMongoRcJs(),
AppRegistry::instance().settingsManager()->batchSize(),
AppRegistry::instance().settingsManager()->mongoTimeoutSec(),
AppRegistry::instance().settingsManager()->shellTimeoutSec());
}
}
void MongoServer::handle(CreateDatabaseResponse *event)
{
if (event->isError()) {
if (_connSettings->isReplicaSet() &&
EventError::SetPrimaryUnreachable == event->error().errorCode()) {
auto refreshEvent = ReplicaSetRefreshed(this, event->error(), event->error().replicaSetInfo());
handle(&refreshEvent);
}
genericEventErrorHandler(event, "Failed to create database \'" + event->database + "\'.", _bus, this);
}
else {
loadDatabases();
LOG_MSG("Database \'" + event->database + "\' created.", mongo::logger::LogSeverity::Info());
}
}
void MongoServer::handle(DropDatabaseResponse *event)
{
if (event->isError()) {
if (_connSettings->isReplicaSet() &&
EventError::SetPrimaryUnreachable == event->error().errorCode()) {
auto refreshEvent = ReplicaSetRefreshed(this, event->error(), event->error().replicaSetInfo());
handle(&refreshEvent);
}
genericEventErrorHandler(event, "Failed to drop database \'" + event->database + "\'.", _bus, this);
}
else {
loadDatabases();
LOG_MSG("Database \'" + event->database + "\' dropped.", mongo::logger::LogSeverity::Info());
}
}
void MongoServer::handle(ReplicaSetRefreshed *event)
{
handleReplicaSetRefreshEvents(event->isError(), event->error(), event->replicaSet, false);
}
void MongoServer::handleReplicaSetRefreshEvents(bool isError, EventError eventError,
ReplicaSet const& replicaSet, bool expanded)
{
if (isError) { // Primary is unreachable
_replicaSetInfo.reset(new ReplicaSet(replicaSet));
LOG_MSG("Replica set folder refreshed with error. " + eventError.errorMessage() +
". Connection: " + _connSettings->connectionName(), mongo::logger::LogSeverity::Error());
_bus->publish(new ReplicaSetFolderRefreshed(this, eventError, true));
return;
}
// Primary is reachable
// Update replica set settings and mongo server _replicaSetInfo
_replicaSetInfo.reset(new ReplicaSet(replicaSet));
_connSettings->setServerHost(_replicaSetInfo->primary.host());
_connSettings->setServerPort(_replicaSetInfo->primary.port());
_connSettings->replicaSetSettings()->setCachedSetName(
_connSettings->replicaSetSettings()->setNameUserEntered().empty() ? _replicaSetInfo->setName : "");
LOG_MSG("Replica set folder refreshed. Connection: " + _connSettings->connectionName(),
mongo::logger::LogSeverity::Info());
_bus->publish(new ReplicaSetFolderRefreshed(this, expanded));
}
void MongoServer::updateReplicaSetSettings(EstablishConnectionResponse* event)
{
if (!_connSettings->isReplicaSet())
return;
_replicaSetInfo.reset(new ReplicaSet(event->replicaSet));
_connSettings->setServerHost(_replicaSetInfo->primary.host());
_connSettings->setServerPort(_replicaSetInfo->primary.port());
_connSettings->replicaSetSettings()->setCachedSetName(
_connSettings->replicaSetSettings()->setNameUserEntered().empty() ? _replicaSetInfo->setName : "");
if (_connSettings->replicaSetSettings()->setNameUserEntered().empty()) {
// Cache replica set name for 2 times faster first connection
if (ConnectionPrimary == _connectionType) {
ConnectionSettings* originalConnSettings = AppRegistry::instance().settingsManager()
->getConnectionSettingsByUuid(event->info._uuid);
if (originalConnSettings) {
auto setName = event->isError() ? "" : _replicaSetInfo->setName;
originalConnSettings->replicaSetSettings()->setCachedSetName(setName);
AppRegistry::instance().settingsManager()->save();
LOG_MSG("Replica set name cached as \"" + setName + "\".", mongo::logger::LogSeverity::Info());
}
else
LOG_MSG("Failed to cache the replica set name.", mongo::logger::LogSeverity::Warning());
}
}
else { // User entered set name is not empty, clear cached set name just in case
ConnectionSettings* originalConnSettings = AppRegistry::instance().settingsManager()
->getConnectionSettingsByUuid(event->info._uuid);
if (originalConnSettings) {
originalConnSettings->replicaSetSettings()->setCachedSetName("");
AppRegistry::instance().settingsManager()->save();
LOG_MSG("Replica set's cached set name cleared. Using user entered set name.",
mongo::logger::LogSeverity::Info());
}
}
}
void MongoServer::handleConnectionFailure(EstablishConnectionResponse* event)
{
_isConnected = false;
std::stringstream ss("Unknown error");
auto eventErrorReason = event->errorReason;
if (_connSettings->isReplicaSet())
{
ss.clear();
std::string server = "";
if (_connSettings->replicaSetSettings()->members().size() > 0)
server = "[" + _connSettings->replicaSetSettings()->members().front() + "]";
if (event->error().errorCode() == EventError::ErrorCode::ServerHasDifferentMembers) {
ss << "Cannot connect to replica set \"" << _connSettings->connectionName() << "\"" << server
<< ". \n\nA primary with different host name [" << event->replicaSet.primary <<
"] found in server side. "
"Please double check if same host names and ports are used as in server's replica set"
" configuration. \nIf same set name is used for different replica sets, this configuration"
" is supported only on different instances of Robomongo. "
" Please open a new Robomongo instance for each replica set which has the same set name."
"\n\nReason:\n" << event->error().errorMessage();
}
else {
ss << "Cannot connect to replica set \"" << _connSettings->connectionName() << "\"" << server
<< ". \nSet's primary is unreachable.\n\nReason:\n" << event->error().errorMessage();
}
_bus->publish(new ConnectionFailedEvent(this, _handle, event->connectionType, ss.str(),
ConnectionFailedEvent::MongoConnection));
_app->fireConnectionFailedEvent(_handle, event->connectionType, ss.str(),
ConnectionFailedEvent::MongoConnection);
LOG_MSG("Establish connection failed. " + event->error().errorMessage() + ". Connection: "
+ _connSettings->connectionName(), mongo::logger::LogSeverity::Error());
}
else // single server
{
if (EstablishConnectionResponse::ErrorReason::MongoSslConnection == eventErrorReason)
{
auto reason = ConnectionFailedEvent::SslConnection;
ss.clear();
ss << "Cannot connect to the MongoDB at " << connectionRecord()->getFullAddress()
<< ".\n\nError:\n" << "SSL connection failure: " << event->error().errorMessage();
_app->fireConnectionFailedEvent(_handle, _connectionType, ss.str(), reason);
}
else
{
auto reason = (EstablishConnectionResponse::ErrorReason::MongoAuth == eventErrorReason) ?
ConnectionFailedEvent::MongoAuth : ConnectionFailedEvent::MongoConnection;
ss.clear();
ss << "Cannot connect to the MongoDB at " << connectionRecord()->getFullAddress()
<< ".\n\nError:\n" << event->error().errorMessage();
_app->fireConnectionFailedEvent(_handle, _connectionType, ss.str(), reason);
}
// When connection cannot be established, we should cleanup this instance of MongoServer if it wasn't
// shown in UI (i.e. it is not a Secondary connection that is used for shells tab)
if (_connectionType == ConnectionPrimary || _connectionType == ConnectionTest)
{
LOG_MSG("Establish connection failed. " + event->error().errorMessage() +
". Connection: " + _connSettings->connectionName(),
mongo::logger::LogSeverity::Error());
_app->closeServer(this);
}
}
}
} // namespace Robomongo
+64 -15
View File
@@ -8,10 +8,16 @@ namespace Robomongo
{
class MongoWorker;
class MongoDatabase;
class EstablishConnectionResponse;
class EventBus;
class App;
// Messages
struct EstablishConnectionResponse;
struct RefreshReplicaSetFolderResponse;
class LoadDatabaseNamesResponse;
class InsertDocumentResponse;
class EventBus;
struct CreateDatabaseResponse;
struct DropDatabaseResponse;
/**
* @brief MongoServer represents active connection to MongoDB server.
@@ -29,30 +35,60 @@ namespace Robomongo
* @param visible
* @param defaultDatabase
*/
MongoServer(ConnectionSettings *connectionRecord, bool visible);
MongoServer(int handle, ConnectionSettings *connectionRecord, ConnectionType connectionType);
~MongoServer();
void runWorkerThread();
/**
* @brief Try to connect to MongoDB server.
* @brief Try to connect to MongoDB single server or replica set.
* @throws MongoException, if fails
*/
void tryConnect();
bool isConnected()const;
/**
* @brief Try to re-connect to MongoDB server in order to refresh connection view.
* Never shown in Explorer and can be used to refresh (via reconnecting) current connection view.
* (i.e. db version, storage engine etc...)
* @throws MongoException, if fails
*/
void tryRefresh();
/**
* @brief Try to re-connect to MongoDB replica set in order to refresh connection view.
* It is used to refresh (via reconnecting) current connection view.
* (i.e. db version, storage engine, current replica set primary, status of replica set etc...)
* It will update Explorer widgets depending on replica set status (online if primary reachable and
* offline otherwise)
*/
void tryRefreshReplicaSetConnection();
/**
* @brief Try to re-connect to MongoDB replica set in order to refresh connection view.
* It is used to refresh (via reconnecting) current connection view.
* (i.e. db version, storage engine, current replica set primary, status of replica set etc...)
* It will update only 'Replica Set' folder widgets depending on replica set status
* (online if primary reachable and offline otherwise)
*/
void tryRefreshReplicaSetFolder(bool expanded, bool showLoading = true);
bool isConnected() const;
void addDatabase(MongoDatabase *database);
void createDatabase(const std::string &dbName);
void dropDatabase(const std::string &dbName);
QStringList getDatabasesNames() const;
QList<MongoDatabase*> const& databases() const { return _databases; };
MongoDatabase *findDatabaseByName(const std::string &dbName) const;
void insertDocuments(const std::vector<mongo::BSONObj> &objCont, const MongoNamespace &ns);
void insertDocument(const mongo::BSONObj &obj, const MongoNamespace &ns);
void saveDocuments(const std::vector<mongo::BSONObj> &objCont, const MongoNamespace &ns);
void saveDocument(const mongo::BSONObj &obj, const MongoNamespace &ns);
void removeDocuments(mongo::Query query, const MongoNamespace &ns, bool justOne = true);
void removeDocuments(mongo::Query query, const MongoNamespace &ns, RemoveDocumentCount removeCount,
int index = 0);
float version() const{ return _version; }
const std::string& getStorageEngineType() const { return _storageEngineType; }
/**
* @brief Returns associated connection record
@@ -63,28 +99,41 @@ namespace Robomongo
* @brief Loads databases of this server asynchronously.
*/
void loadDatabases();
bool visible() const { return _visible; }
MongoWorker *const client() const { return _client; }
MongoWorker *const worker() const { return _worker; }
ReplicaSet* replicaSetInfo() const { return _replicaSetInfo.get(); }
void handle(ReplicaSetRefreshed *event);
protected Q_SLOTS:
void handle(EstablishConnectionResponse *event);
void handle(RefreshReplicaSetFolderResponse *event);
void handle(LoadDatabaseNamesResponse *event);
void handle(InsertDocumentResponse *event);
void handle(RemoveDocumentResponse *event);
void handle(CreateDatabaseResponse *event);
void handle(DropDatabaseResponse *event);
private:
private:
void clearDatabases();
void addDatabase(MongoDatabase *database);
void handleReplicaSetRefreshEvents(bool isError, EventError eventError, ReplicaSet const& replicaSet,
bool expanded);
void updateReplicaSetSettings(EstablishConnectionResponse* event);
void handleConnectionFailure(EstablishConnectionResponse* event);
MongoWorker* _client;
ConnectionSettings* _settings;
EventBus* _bus;
MongoWorker *_worker;
std::unique_ptr<ConnectionSettings> _connSettings;
EventBus *_bus;
App *_app;
float _version;
bool _visible;
std::string _storageEngineType;
ConnectionType _connectionType;
bool _isConnected;
int _handle;
QList<MongoDatabase *> _databases;
std::unique_ptr<ReplicaSet> _replicaSetInfo;
};
class MongoServerLoadingDatabasesEvent : public Event
+19 -10
View File
@@ -11,7 +11,7 @@
#include "robomongo/core/settings/SettingsManager.h"
namespace Robomongo
{
{
MongoShell::MongoShell(MongoServer *server, const ScriptInfo &scriptInfo) :
QObject(),
@@ -24,7 +24,7 @@ namespace Robomongo
{
AppRegistry::instance().bus()->publish(new ScriptExecutingEvent(this));
_scriptInfo.setScript(QtUtils::toQString(script));
AppRegistry::instance().bus()->send(_server->client(), new ExecuteScriptRequest(this, query(), dbName));
AppRegistry::instance().bus()->send(_server->worker(), new ExecuteScriptRequest(this, query(), dbName));
LOG_MSG(_scriptInfo.script(), mongo::logger::LogSeverity::Info());
}
@@ -37,19 +37,19 @@ namespace Robomongo
{
if (_scriptInfo.execute()) {
AppRegistry::instance().bus()->publish(new ScriptExecutingEvent(this));
AppRegistry::instance().bus()->send(_server->client(), new ExecuteScriptRequest(this, query(), dbName));
if(!_scriptInfo.script().isEmpty())
AppRegistry::instance().bus()->send(_server->worker(), new ExecuteScriptRequest(this, query(), dbName));
if (!_scriptInfo.script().isEmpty())
LOG_MSG(_scriptInfo.script(), mongo::logger::LogSeverity::Info());
} else {
AppRegistry::instance().bus()->publish(new ScriptExecutingEvent(this));
_scriptInfo.setScript("");
AppRegistry::instance().bus()->send(_server->client(), new ExecuteScriptRequest(this,query() , dbName));
AppRegistry::instance().bus()->send(_server->worker(), new ExecuteScriptRequest(this, query() , dbName));
}
}
void MongoShell::query(int resultIndex, const MongoQueryInfo &info)
{
AppRegistry::instance().bus()->send(_server->client(), new ExecuteQueryRequest(this, resultIndex, info));
AppRegistry::instance().bus()->send(_server->worker(), new ExecuteQueryRequest(this, resultIndex, info));
}
void MongoShell::autocomplete(const std::string &prefix)
@@ -57,12 +57,12 @@ namespace Robomongo
AutocompletionMode autocompletionMode = AppRegistry::instance().settingsManager()->autocompletionMode();
if (autocompletionMode == AutocompleteNone)
return;
AppRegistry::instance().bus()->send(_server->client(), new AutocompleteRequest(this, prefix, autocompletionMode));
AppRegistry::instance().bus()->send(_server->worker(), new AutocompleteRequest(this, prefix, autocompletionMode));
}
void MongoShell::stop()
{
// _server->client()->interrupt();
// _server->worker()->interrupt();
// mongo::Scope::setInterruptFlag(true);
}
@@ -94,8 +94,17 @@ namespace Robomongo
void MongoShell::handle(ExecuteScriptResponse *event)
{
if (event->isError()) {
AppRegistry::instance().bus()->publish(new ScriptExecutedEvent(this, event->error()));
return;
if (_server->connectionRecord()->isReplicaSet()) {
AppRegistry::instance().bus()->publish(
new ReplicaSetRefreshed(this, event->error(), event->error().replicaSetInfo()));
AppRegistry::instance().bus()->publish(new ScriptExecutedEvent(this, event->error()));
return;
}
else { // single server
AppRegistry::instance().bus()->publish(new ScriptExecutedEvent(this, event->error()));
return;
}
}
AppRegistry::instance().bus()->publish(new ScriptExecutedEvent(this, event->result, event->empty));
+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); }
+10 -1
View File
@@ -29,15 +29,22 @@ namespace Robomongo
{
public:
MongoShellExecResult() { }
MongoShellExecResult(const std::vector<MongoShellResult> &results,
const std::string &currentServer, bool isCurrentServerValid,
const std::string &currentDatabase, bool isCurrentDatabaseValid);
MongoShellExecResult(bool error, std::string const& errorMsg = "") :
_error(error), _errorMessage(errorMsg) { }
std::vector<MongoShellResult> results() const { return _results; }
std::string currentServer() const { return _currentServer; }
std::string currentDatabase() const { return _currentDatabase; }
void setCurrentServer(std::string const& server) { _currentServer = server; }
std::string currentDatabase() const { return _currentDatabase; }
bool isCurrentServerValid() const { return _isCurrentServerValid; }
bool isCurrentDatabaseValid() const { return _isCurrentDatabaseValid; }
std::string errorMessage() const { return _errorMessage; }
bool error() const { return _error; }
private:
std::vector<MongoShellResult> _results;
@@ -45,5 +52,7 @@ namespace Robomongo
std::string _currentDatabase;
bool _isCurrentServerValid;
bool _isCurrentDatabaseValid;
std::string _errorMessage;
bool _error = false;
};
}
+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;
}
+136 -56
View File
@@ -25,10 +25,10 @@ namespace Robomongo
{
namespace detail
{
bool isSimpleType(Robomongo::BsonTreeItem *item)
bool isSimpleType(Robomongo::BsonTreeItem const *item)
{
return Robomongo::BsonUtils::isSimpleType(item->type())
|| Robomongo::BsonUtils::isUuidType(item->type(), item->binType());
return BsonUtils::isSimpleType(item->type()) ||
BsonUtils::isUuidType(item->type(), item->binType());
}
bool isObjectIdType(Robomongo::BsonTreeItem *item)
@@ -38,12 +38,22 @@ namespace Robomongo
bool isMultiSelection(const QModelIndexList &indexes)
{
return indexes.count()>1;
return indexes.count() > 1;
}
bool isDocumentType(BsonTreeItem *item)
bool isDocumentType(BsonTreeItem const *item)
{
return Robomongo::BsonUtils::isDocument(item->type());
return BsonUtils::isDocument(item->type());
}
bool isArrayChild(BsonTreeItem const *item)
{
return BsonUtils::isArray(dynamic_cast<BsonTreeItem*>(item->parent())->type());
}
bool isDocumentRoot(BsonTreeItem const *item)
{
return ( item == item->superParent() );
}
/**
@@ -55,20 +65,20 @@ namespace Robomongo
QModelIndexList uniqueRows(QModelIndexList indexes, bool returnSuperParents)
{
QModelIndexList result;
for (QModelIndexList::const_iterator it = indexes.begin(); it!=indexes.end(); ++it)
for (QModelIndexList::const_iterator it = indexes.begin(); it != indexes.end(); ++it)
{
QModelIndex isUnique = *it;
Robomongo::BsonTreeItem *item = Robomongo::QtUtils::item<Robomongo::BsonTreeItem*>(isUnique);
if(item){
for (QModelIndexList::const_iterator jt = result.begin(); jt!=result.end(); ++jt)
if (item) {
for (QModelIndexList::const_iterator jt = result.begin(); jt != result.end(); ++jt)
{
Robomongo::BsonTreeItem *jItem = Robomongo::QtUtils::item<Robomongo::BsonTreeItem*>(*jt);
if (jItem && jItem->superParent() == item->superParent()){
if (jItem && jItem->superParent() == item->superParent()) {
isUnique = QModelIndex();
break;
}
}
if (isUnique.isValid()){
if (isUnique.isValid()) {
if (returnSuperParents) {
// Move index onto "super parent" element before pushing it into result set
QModelIndex parent = isUnique.parent();
@@ -92,8 +102,8 @@ namespace Robomongo
_queryInfo(queryInfo)
{
QWidget *wid = dynamic_cast<QWidget*>(_observer);
AppRegistry::instance().bus()->subscribe(this, InsertDocumentResponse::Type);
AppRegistry::instance().bus()->subscribe(this, RemoveDocumentResponse::Type);
AppRegistry::instance().bus()->subscribe(this, InsertDocumentResponse::Type, _shell->server());
AppRegistry::instance().bus()->subscribe(this, RemoveDocumentResponse::Type, _shell->server());
_deleteDocumentAction = new QAction("Delete Document...", wid);
VERIFY(connect(_deleteDocumentAction, SIGNAL(triggered()), SLOT(onDeleteDocument())));
@@ -113,6 +123,12 @@ namespace Robomongo
_copyValueAction = new QAction("Copy Value", wid);
VERIFY(connect(_copyValueAction, SIGNAL(triggered()), SLOT(onCopyDocument())));
_copyValueNameAction = new QAction("Copy Name", wid);
VERIFY(connect(_copyValueNameAction, SIGNAL(triggered()), SLOT(onCopyNameDocument())));
_copyValuePathAction = new QAction("Copy Path", wid);
VERIFY(connect(_copyValuePathAction, SIGNAL(triggered()), SLOT(onCopyPathDocument())));
_copyTimestampAction = new QAction("Copy Timestamp from ObjectId", wid);
VERIFY(connect(_copyTimestampAction, SIGNAL(triggered()), SLOT(onCopyTimestamp())));
@@ -128,20 +144,29 @@ namespace Robomongo
bool isSimple = false;
bool isDocument = false;
bool isObjectId = false;
bool isNotArrayChild = false;
bool isRoot = false;
if (item) {
isSimple = detail::isSimpleType(item);
isDocument = detail::isDocumentType(item);
isObjectId = detail::isObjectIdType(item);
isNotArrayChild = !detail::isArrayChild(item);
isRoot = detail::isDocumentRoot(item);
}
if (onItem && isEditable) menu->addAction(_editDocumentAction);
if (onItem) menu->addAction(_viewDocumentAction);
if (isEditable) menu->addAction(_insertDocumentAction);
if (onItem && (isSimple || isDocument)) menu->addSeparator();
if (onItem && isSimple) menu->addAction(_copyValueAction);
if (onItem && (isSimple || isDocument) && isNotArrayChild && !isRoot)
menu->addAction(_copyValueNameAction);
if (onItem && (isSimple || isDocument) && !isRoot)
menu->addAction(_copyValuePathAction);
if (onItem && isObjectId) menu->addAction(_copyTimestampAction);
if (onItem && isDocument) menu->addAction(_copyJsonAction);
if (onItem && isEditable) menu->addSeparator();
@@ -156,11 +181,12 @@ namespace Robomongo
if (isEditable) menu->addAction(_deleteDocumentsAction);
}
void Notifier::deleteDocuments(std::vector<BsonTreeItem*> items, bool force)
void Notifier::deleteDocuments(std::vector<BsonTreeItem*> const& items, bool force)
{
bool isNeededRefresh = false;
for (std::vector<BsonTreeItem*>::const_iterator it = items.begin(); it != items.end(); ++it) {
BsonTreeItem * documentItem = *it;
int index = 0;
for (auto const * const documentItem : items) {
if (!documentItem)
break;
@@ -168,7 +194,8 @@ namespace Robomongo
mongo::BSONElement id = obj.getField("_id");
if (id.eoo()) {
QMessageBox::warning(dynamic_cast<QWidget*>(_observer), "Cannot delete", "Selected document doesn't have _id field. \n"
QMessageBox::warning(dynamic_cast<QWidget*>(_observer), "Cannot delete",
"Selected document doesn't have _id field. \n"
"Maybe this is a system document that should be managed in a special way?");
break;
}
@@ -187,45 +214,98 @@ namespace Robomongo
break;
}
isNeededRefresh=true;
_shell->server()->removeDocuments(query, _queryInfo._info._ns);
}
isNeededRefresh = true;
if (isNeededRefresh)
_shell->query(0, _queryInfo);
RemoveDocumentCount removeCount = items.size() == 1 ? RemoveDocumentCount::ONE :
RemoveDocumentCount::MULTI;
_shell->server()->removeDocuments(query, _queryInfo._info._ns, removeCount, index);
++index;
}
}
void Notifier::handle(InsertDocumentResponse *event)
{
if (event->isError()) {
QMessageBox::warning(NULL, "Database Error", QString::fromStdString(event->error().errorMessage()));
if (event->isError()) { // Error
if (_shell->server()->connectionRecord()->isReplicaSet()) {
// Insert document from tab results window (Notifier, OutputWindow widget)
if (EventError::SetPrimaryUnreachable == event->error().errorCode()) {
AppRegistry::instance().bus()->publish(
new ReplicaSetRefreshed(_shell, event->error(), event->error().replicaSetInfo()));
}
}
else // single server
QMessageBox::warning(NULL, "Database Error", QString::fromStdString(event->error().errorMessage()));
return;
}
// Success
_shell->query(0, _queryInfo);
}
void Notifier::handle(RemoveDocumentResponse *event)
{
if (event->isError()) {
// Commented because when you'll delete multiple documents,
// we'll receive this event as many times as many selected
// documents you have. This is incorrect and remove of
// multiple documents should be a single command, instead
// of many. Error message should be printed in the logs.
if (event->isError()) {
if (!(event->removeCount == RemoveDocumentCount::MULTI && event->index > 0))
QMessageBox::warning(NULL, "Database Error", QString::fromStdString(event->error().errorMessage()));
}
else // Success
_shell->query(0, _queryInfo);
}
// QMessageBox::warning(NULL, "Database Error", QString::fromStdString(event->error().errorMessage()));
void Notifier::onCopyNameDocument()
{
QModelIndex const& selectedInd = _observer->selectedIndex();
if (!selectedInd.isValid())
return;
BsonTreeItem const *documentItem = QtUtils::item<BsonTreeItem*>(selectedInd);
if (!documentItem)
return;
if (!(detail::isSimpleType(documentItem) || detail::isDocumentType(documentItem) ||
!detail::isArrayChild(documentItem) || !detail::isDocumentRoot(documentItem)))
return;
QClipboard *clipboard = QApplication::clipboard();
clipboard->setText(QString::fromStdString(documentItem->fieldName()));
}
void Notifier::onCopyPathDocument()
{
QModelIndex const& selectedInd = _observer->selectedIndex();
if (!selectedInd.isValid())
return;
BsonTreeItem const *documentItem = QtUtils::item<BsonTreeItem*>(selectedInd);
if (!documentItem)
return;
if (!(detail::isSimpleType(documentItem) || detail::isDocumentType(documentItem) ||
!detail::isDocumentRoot(documentItem)))
return;
QStringList namesList;
BsonTreeItem const *documentItemHelper = documentItem;
while (!detail::isDocumentRoot(documentItemHelper)) {
if (!detail::isArrayChild(documentItemHelper)) {
namesList.push_front(QString::fromStdString(documentItemHelper->fieldName()));
}
documentItemHelper = dynamic_cast<BsonTreeItem*>(documentItemHelper->parent());
}
QClipboard *clipboard = QApplication::clipboard();
clipboard->setText(namesList.join("."));
}
void Notifier::handleDeleteCommand()
{
if (_observer->selectedIndexes().count() > 1) {
if (_observer->selectedIndexes().count() > 1)
onDeleteDocuments();
} else {
onDeleteDocument();
}
else
onDeleteDocument();
}
void Notifier::onDeleteDocuments()
@@ -237,14 +317,16 @@ namespace Robomongo
if (!detail::isMultiSelection(selectedIndexes))
return;
int answer = QMessageBox::question(dynamic_cast<QWidget*>(_observer), "Delete", QString("Do you want to delete %1 selected documents?").arg(selectedIndexes.count()));
if (answer == QMessageBox::Yes) {
int const answer = QMessageBox::question(dynamic_cast<QWidget*>(_observer), "Delete",
QString("Do you want to delete %1 selected documents?").
arg(selectedIndexes.count()));
if (QMessageBox::Yes == answer) {
std::vector<BsonTreeItem*> items;
for (QModelIndexList::const_iterator it = selectedIndexes.begin(); it!= selectedIndexes.end(); ++it) {
BsonTreeItem *item = QtUtils::item<BsonTreeItem*>(*it);
items.push_back(item);
}
deleteDocuments(items,true);
for (auto index : selectedIndexes)
items.push_back(QtUtils::item<BsonTreeItem*>(index));
deleteDocuments(items, true);
}
}
@@ -260,7 +342,7 @@ namespace Robomongo
BsonTreeItem *documentItem = QtUtils::item<BsonTreeItem*>(selectedIndex);
std::vector<BsonTreeItem*> vec;
vec.push_back(documentItem);
return deleteDocuments(vec,false);
return deleteDocuments(vec, false);
}
void Notifier::onEditDocument()
@@ -317,7 +399,7 @@ namespace Robomongo
json, true, dynamic_cast<QWidget*>(_observer));
editor->setWindowTitle("View Document");
editor->show();
editor->exec();
}
void Notifier::onInsertDocument()
@@ -339,8 +421,6 @@ namespace Robomongo
for (DocumentTextEditor::ReturnType::const_iterator it = obj.begin(); it != obj.end(); ++it) {
_shell->server()->insertDocument(*it, _queryInfo._info._ns);
}
_shell->query(0, _queryInfo);
}
void Notifier::onCopyDocument()
@@ -376,19 +456,19 @@ namespace Robomongo
QClipboard *clipboard = QApplication::clipboard();
// new Date(parseInt(this.valueOf().slice(0,8), 16)*1000);
QString hexTimestamp = documentItem->value().mid(10,8);
QString hexTimestamp = documentItem->value().mid(10, 8);
bool ok;
long long milliTimestamp = (long long)hexTimestamp.toLongLong(&ok,16)*1000;
long long milliTimestamp = (long long)hexTimestamp.toLongLong(&ok, 16)*1000;
bool isSupportedDate = (miutil::minDate < milliTimestamp) && (milliTimestamp < miutil::maxDate);
boost::posix_time::ptime epoch(boost::gregorian::date(1970,1,1));
boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1));
boost::posix_time::time_duration diff = boost::posix_time::millisec(milliTimestamp);
boost::posix_time::ptime time = epoch + diff;
if(isSupportedDate)
if (isSupportedDate)
{
std::string date = miutil::isotimeString(time,false,false);
std::string date = miutil::isotimeString(time, false, false);
clipboard->setText("ISODate(\""+QString::fromStdString(date)+"\")");
}
else {
@@ -411,13 +491,13 @@ namespace Robomongo
QClipboard *clipboard = QApplication::clipboard();
mongo::BSONObj obj = documentItem->root();
if (documentItem != documentItem->superParent()){
obj = obj[QtUtils::toStdString(documentItem->key())].Obj();
if (documentItem != documentItem->superParent()) {
obj = obj[documentItem->fieldName()].Obj();
}
bool isArray = BsonUtils::isArray(documentItem->type());
std::string str = BsonUtils::jsonString(obj, mongo::TenGen, 1,
AppRegistry::instance().settingsManager()->uuidEncoding(),
AppRegistry::instance().settingsManager()->timeZone(),isArray);
AppRegistry::instance().settingsManager()->timeZone(), isArray);
const QString &json = QtUtils::toQString(str);
clipboard->setText(json);
+10 -4
View File
@@ -14,14 +14,14 @@ namespace Robomongo
class MongoShell;
class BsonTreeItem;
class InsertDocumentResponse;
class RemoveDocumentResponse;
struct RemoveDocumentResponse;
namespace detail
{
bool isSimpleType(BsonTreeItem *item);
bool isSimpleType(BsonTreeItem const *item);
bool isObjectIdType(BsonTreeItem *item);
bool isMultiSelection(const QModelIndexList &indexes);
bool isDocumentType(BsonTreeItem *item);
bool isDocumentType(BsonTreeItem const *item);
QModelIndexList uniqueRows(QModelIndexList indexes, bool returnSuperParents = false);
}
@@ -45,7 +45,7 @@ namespace Robomongo
void initMenu(QMenu *const menu, BsonTreeItem *const item);
void initMultiSelectionMenu(QMenu *const menu);
void deleteDocuments(std::vector<BsonTreeItem*> items, bool force);
void deleteDocuments(std::vector<BsonTreeItem*> const& items, bool force);
void handleDeleteCommand();
public Q_SLOTS:
@@ -60,6 +60,10 @@ namespace Robomongo
void handle(InsertDocumentResponse *event);
void handle(RemoveDocumentResponse *event);
private Q_SLOTS:
void onCopyNameDocument();
void onCopyPathDocument();
private:
QAction *_deleteDocumentAction;
QAction *_deleteDocumentsAction;
@@ -67,6 +71,8 @@ namespace Robomongo
QAction *_viewDocumentAction;
QAction *_insertDocumentAction;
QAction *_copyValueAction;
QAction *_copyValueNameAction;
QAction *_copyValuePathAction;
QAction *_copyTimestampAction;
QAction *_copyJsonAction;
const MongoQueryInfo _queryInfo;
+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;
}
+74 -31
View File
@@ -55,34 +55,30 @@ namespace mongo {
namespace Robomongo
{
ScriptEngine::ScriptEngine(ConnectionSettings *connection) :
ScriptEngine::ScriptEngine(ConnectionSettings *connection, int timeoutSec) :
_connection(connection),
_scope(NULL),
_scope(nullptr),
_engine(NULL),
_timeoutSec(timeoutSec),
_initialized(false),
_mutex(QMutex::Recursive) { }
ScriptEngine::~ScriptEngine()
{
QMutexLocker lock(&_mutex);
delete _scope;
_scope = NULL;
// delete _engine;
// _engine = NULL;
}
void ScriptEngine::init(bool isLoadMongoRcJs)
void ScriptEngine::init(bool isLoadMongoRcJs, const std::string& serverAddr, const std::string& dbName)
{
QMutexLocker lock(&_mutex);
std::string connectDatabase = "test";
std::string connectDatabase = dbName.empty() ? "test" : dbName;
if (_connection->hasEnabledPrimaryCredential())
connectDatabase = _connection->primaryCredential()->databaseName();
std::stringstream ss;
ss << "db = connect('" << _connection->info().toString() << "/" << connectDatabase;
auto hostAndPort = serverAddr.empty() ? _connection->hostAndPort().toString() : serverAddr;
ss << "db = connect('" << hostAndPort << "/" << connectDatabase;
// v0.9
// ss << "db = connect('" << _connection->serverHost() << ":" << _connection->serverPort() << _connection->sslInfo() << _connection->sshInfo() << "/" << connectDatabase;
@@ -106,49 +102,63 @@ namespace Robomongo
mongo::globalScriptEngine->setScopeInitCallback(mongo::shell_utils::initScope);
mongo::globalScriptEngine->enableJIT(true);
mongo::Scope *scope = mongo::globalScriptEngine->newScope();
_scope = scope;
_scope.reset(mongo::globalScriptEngine->newScope());
_engine = mongo::globalScriptEngine;
// Load '.mongorc.js' from user's home directory
if (isLoadMongoRcJs) {
QString mongorcPath = QString("%1/.mongorc.js").arg(QDir::homePath());
if (QFile::exists(mongorcPath)) {
scope->execFile(QtUtils::toStdString(mongorcPath), false, false);
_scope->execFile(QtUtils::toStdString(mongorcPath), false, false);
}
}
// Load '.robomongorc.js'
QString robomongorcPath = QString("%1/.robomongorc.js").arg(QDir::homePath());
if (QFile::exists(robomongorcPath)) {
scope->execFile(QtUtils::toStdString(robomongorcPath), false, false);
_scope->execFile(QtUtils::toStdString(robomongorcPath), false, false);
}
_failedScope = false;
}
// Esprima ECMAScript parser: http://esprima.org/
QFile file(":/robomongo/scripts/esprima.js");
if (!file.open(QIODevice::ReadOnly))
throw std::runtime_error("Unable to read esprima.js ");
std::string esprima = loadFile(":/robomongo/scripts/esprima.js", true);
_scope->exec(esprima, "(esprima)", false, true, true);
QTextStream in(&file);
QString esprima = in.readAll();
// Inject Esprima into Javascript scope
_scope->exec(QtUtils::toStdString(esprima), "(esprima)", true, true, true);
// UUID helpers
std::string uuidhelpers = loadFile(":/robomongo/scripts/uuidhelpers.js", true);
_scope->exec(uuidhelpers, "(uuidhelpers)", false, true, true);
// Enable verbose shell reporting
_scope->exec("_verboseShell = true;", "(verboseShell)", false, false, false);
// Save original autocomplete function so it can be restored if overwritten by user preference
_scope->exec("DB.autocompleteOriginal = DB.autocomplete;", "(saveOriginalAutocomplete)", false, false, false);
// Cache result of original "DB.autocomplete"
// Cache invalidated by the invalidateDbCollectionsCache() method.
std::string cacheAutocompletion =
"__robomongoAutocompletionCache = null;"
"DB.autocompleteCached = function(obj) { "
" if (__robomongoAutocompletionCache == null) {"
" __robomongoAutocompletionCache = DB.autocompleteOriginal(obj);"
" }"
" return __robomongoAutocompletionCache;"
"}";
_scope->exec(cacheAutocompletion, "", false, false, false);
_initialized = true;
}
MongoShellExecResult ScriptEngine::exec(const std::string &originalScript, const std::string &dbName)
{
QMutexLocker lock(&_mutex);
if(!_scope)
return MongoShellExecResult();
if (!_scope) {
_failedScope = true;
return MongoShellExecResult(true, "Connection error. Uninitialized mongo scope.");
}
/*
* Replace all commands ('show dbs', 'use db' etc.) with call
@@ -177,7 +187,7 @@ namespace Robomongo
use(dbName);
for(std::vector<std::string>::const_iterator it = statements.begin(); it != statements.end(); ++it)
for (std::vector<std::string>::const_iterator it = statements.begin(); it != statements.end(); ++it)
{
std::string statement = *it;
// clear global objects
@@ -188,10 +198,15 @@ namespace Robomongo
if (true /* ! wascmd */) {
try {
bool failed = false;
QElapsedTimer timer;
timer.start();
if ( _scope->exec( statement , "(shell)" , false , true , false, 0 ) ) {
_scope->exec( "__robomongoLastRes = __lastres__; shellPrintHelper( __lastres__ );" , "(shell2)" , true , true , false, 0);
if ( _scope->exec( statement , "(shell)" , false , true , false, _timeoutSec * 1000) ) {
_scope->exec( "__robomongoLastRes = __lastres__; shellPrintHelper( __lastres__ );",
"(shell2)" , true , true , false, _timeoutSec * 1000);
}
else { // failed to run script
failed = true;
}
qint64 elapsed = timer.elapsed();
@@ -199,6 +214,10 @@ namespace Robomongo
std::string logs = __logs.str();
std::string answer = logs.c_str();
std::string type = __type.c_str();
if (failed)
return MongoShellExecResult(true, answer);
std::vector<MongoDocumentPtr> docs = MongoDocument::fromBsonObj(__objects);
if (!answer.empty() || docs.size() > 0)
@@ -251,8 +270,11 @@ namespace Robomongo
void ScriptEngine::ping()
{
if (!_scope)
return;
QMutexLocker lock(&_mutex);
_scope->exec("if (db) { db.runCommand({ping:1}); }", "(ping)", false, false, false);
_scope->exec("if (db) { db.runCommand({ping:1}); }", "(ping)", false, false, false, 3000);
}
QStringList ScriptEngine::complete(const std::string &prefix, const AutocompletionMode mode)
@@ -262,7 +284,7 @@ namespace Robomongo
try {
if (mode == AutocompleteAll)
_scope->exec("DB.autocomplete = DB.autocompleteOriginal;", "", false, false, false);
_scope->exec("DB.autocomplete = DB.autocompleteCached;", "", false, false, false);
else if (mode == AutocompleteNoCollectionNames)
_scope->exec("DB.autocomplete = function(obj){return [];}", "", false, false, false);
@@ -413,4 +435,25 @@ namespace Robomongo
return true;
}
void ScriptEngine::invalidateDbCollectionsCache() {
if (!_initialized)
return;
_scope->exec("__robomongoAutocompletionCache = null;", "", false, false, false);
}
std::string ScriptEngine::loadFile(const QString &path, bool throwOnError) {
QFile file(path);
if (!file.open(QIODevice::ReadOnly)) {
if (throwOnError)
throw std::runtime_error("Unable to load file");
return "";
}
QTextStream in(&file);
QString content = in.readAll();
return QtUtils::toStdString(content);
}
}
+11 -5
View File
@@ -15,10 +15,10 @@ namespace Robomongo
{
public:
ScriptEngine(ConnectionSettings *connection);
ScriptEngine(ConnectionSettings *connection, int timeoutSec);
~ScriptEngine();
void init(bool isLoadMongoJs);
void init(bool isLoadMongoJs, const std::string& serverAddr = "", const std::string& dbName = "");
MongoShellExecResult exec(const std::string &script, const std::string &dbName = std::string());
void interrupt();
@@ -27,20 +27,26 @@ namespace Robomongo
void ping();
QStringList complete(const std::string &prefix, const AutocompletionMode mode);
void invalidateDbCollectionsCache();
bool failedScope() const { return _failedScope; }
private:
ConnectionSettings *_connection;
MongoShellResult prepareResult(const std::string &type, const std::string &output, const std::vector<MongoDocumentPtr> &objects, qint64 elapsedms);
MongoShellExecResult prepareExecResult(const std::vector<MongoShellResult> &results);
std::string loadFile(const QString &path, bool throwOnError);
std::string getString(const char *fieldName);
bool statementize(const std::string &script, std::vector<std::string> &outList, std::string &outError);
std::vector<std::string> statementize2(const std::string &script);
// void parseTree(JSParseNode * root, int indent, const std::string &script, std::vector<std::string> &list, bool topList);
int _timeoutSec;
mongo::ScriptEngine *_engine;
mongo::Scope *_scope;
std::unique_ptr<mongo::Scope> _scope;
bool _failedScope = false;
QMutex _mutex;
bool _initialized;
};
}
@@ -5,6 +5,11 @@ namespace Robomongo
{
R_REGISTER_EVENT(EstablishConnectionRequest)
R_REGISTER_EVENT(EstablishConnectionResponse)
R_REGISTER_EVENT(ReplicaSetRefreshed)
R_REGISTER_EVENT(RefreshReplicaSetFolderRequest)
R_REGISTER_EVENT(RefreshReplicaSetFolderResponse)
R_REGISTER_EVENT(ReplicaSetFolderLoading)
R_REGISTER_EVENT(ReplicaSetFolderRefreshed)
R_REGISTER_EVENT(LoadDatabaseNamesRequest)
R_REGISTER_EVENT(LoadDatabaseNamesResponse)
R_REGISTER_EVENT(LoadCollectionNamesRequest)
@@ -66,4 +71,5 @@ namespace Robomongo
R_REGISTER_EVENT(ListenSshConnectionResponse)
R_REGISTER_EVENT(LogEvent)
R_REGISTER_EVENT(StopScriptRequest)
R_REGISTER_EVENT(OperationFailedEvent)
}
+318 -149
View File
@@ -12,6 +12,7 @@
#include "robomongo/core/events/MongoEventsInfo.h"
#include "robomongo/core/Event.h"
#include "robomongo/core/Enums.h"
#include "robomongo/core/mongodb/ReplicaSet.h"
namespace Robomongo
{
@@ -26,32 +27,119 @@ namespace Robomongo
* @brief EstablishConnection
*/
class EstablishConnectionRequest : public Event
struct EstablishConnectionRequest : public Event
{
R_EVENT
EstablishConnectionRequest(QObject *sender) :
EstablishConnectionRequest(QObject *sender, ConnectionType connectionType, std::string const& uuid) :
Event(sender),
connectionType(connectionType),
uuid(uuid) {}
ConnectionType const connectionType;
std::string const uuid;
};
struct EstablishConnectionResponse : public Event
{
R_EVENT
enum ErrorReason {
NoError = 0,
MongoConnection = 1,
MongoAuth = 2,
MongoSslConnection = 3
};
EstablishConnectionResponse(QObject *sender, const ConnectionInfo &info, ConnectionType connectionType,
const ReplicaSet& replicaSet) :
Event(sender),
info(info),
connectionType(connectionType),
replicaSet(replicaSet)
{}
EstablishConnectionResponse(QObject *sender, const EventError &error, ConnectionType connectionType,
ConnectionInfo const& info, const ReplicaSet& replicaSet, ErrorReason errorReason) :
Event(sender, error),
info(info),
connectionType(connectionType),
errorReason(errorReason),
replicaSet(replicaSet)
{}
ConnectionInfo const info;
ConnectionType const connectionType;
ErrorReason const errorReason = NoError;
ReplicaSet const replicaSet;
};
struct ReplicaSetRefreshed : public Event
{
R_EVENT
ReplicaSetRefreshed(QObject *sender) :
Event(sender) {}
ReplicaSetRefreshed(QObject *sender, const EventError &error, ReplicaSet const& replicaSet) :
Event(sender, error), replicaSet(replicaSet) {}
ReplicaSet const replicaSet;
};
struct RefreshReplicaSetFolderRequest : public Event
{
R_EVENT
RefreshReplicaSetFolderRequest(QObject *sender, bool expanded) :
Event(sender), expanded(expanded) {}
bool const expanded = false;
};
struct RefreshReplicaSetFolderResponse : public Event
{
R_EVENT
// Primary is reachable
RefreshReplicaSetFolderResponse(QObject *sender, ReplicaSet const& replicaSet, bool expanded) :
Event(sender), replicaSet(replicaSet), expanded(expanded) {}
// Primary is unreachable, secondary(ies) might be reachable
RefreshReplicaSetFolderResponse(QObject *sender, ReplicaSet const& replicaSet, bool expanded,
const EventError &error) :
Event(sender, error), replicaSet(replicaSet), expanded(expanded) {}
ReplicaSet const replicaSet;
bool const expanded = false;
};
struct ReplicaSetFolderLoading : public Event
{
R_EVENT
ReplicaSetFolderLoading(QObject *sender) :
Event(sender) {}
};
class EstablishConnectionResponse : public Event
struct ReplicaSetFolderRefreshed : public Event
{
R_EVENT
EstablishConnectionResponse(QObject *sender, const ConnectionInfo &info) :
Event(sender),
_info(info) {}
ReplicaSetFolderRefreshed(QObject *sender, bool expanded) :
Event(sender), expanded(expanded) {}
EstablishConnectionResponse(QObject *sender, const EventError &error) :
Event(sender, error),_info() {}
ReplicaSetFolderRefreshed(QObject *sender, const EventError &error, bool expanded) :
Event(sender, error), expanded(expanded) {}
const ConnectionInfo &info() const{
return _info;
}
const ConnectionInfo _info;
ReplicaSetFolderRefreshed(QObject *sender, const EventError &error, ReplicaSet const& replicaSet,
bool expanded) :
Event(sender, error), replicaSet(replicaSet), expanded(expanded) {}
ReplicaSet const replicaSet;
bool const expanded = false;
};
/**
* @brief LoadDatabaseNames
*/
@@ -109,7 +197,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 +213,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 +236,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 +260,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 +270,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 +287,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 +328,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 +370,7 @@ namespace Robomongo
_databaseName(databaseName),
_functions(functions) { }
LoadFunctionsResponse(QObject *sender,const EventError &error) :
LoadFunctionsResponse(QObject *sender, const EventError &error) :
Event(sender, error) {}
std::string databaseName() const { return _databaseName; }
@@ -326,7 +414,7 @@ namespace Robomongo
InsertDocumentResponse(QObject *sender) :
Event(sender) {}
InsertDocumentResponse(QObject *sender, const EventError &error) :
InsertDocumentResponse(QObject *sender, EventError const& error) :
Event(sender, error) {}
};
@@ -334,37 +422,46 @@ namespace Robomongo
* @brief Remove Document
*/
enum class RemoveDocumentCount { ONE, MULTI, ALL };
class RemoveDocumentRequest : public Event
{
R_EVENT
public:
RemoveDocumentRequest(QObject *sender, mongo::Query query, const MongoNamespace &ns, bool justOne = true) :
RemoveDocumentRequest(QObject *sender, mongo::Query query, const MongoNamespace &ns,
RemoveDocumentCount removeCount, int index) :
Event(sender),
_query(query),
_ns(ns),
_justOne(justOne) {}
_removeCount(removeCount),
_index(index) {}
mongo::Query query() const { return _query; }
MongoNamespace ns() const { return _ns; }
bool justOne() const { return _justOne; }
RemoveDocumentCount removeCount() const { return _removeCount; }
int index() const { return _index; }
private:
mongo::Query _query;
const MongoNamespace _ns;
bool _justOne;
mongo::Query const _query;
MongoNamespace const _ns;
RemoveDocumentCount const _removeCount;
// if this is a multi remove, this is the index of current document deleted. 0 for first document.
int const _index;
};
class RemoveDocumentResponse : public Event
struct RemoveDocumentResponse : public Event
{
R_EVENT
public:
RemoveDocumentResponse(QObject *sender) :
Event(sender) {}
RemoveDocumentResponse(QObject *sender, RemoveDocumentCount removeCount, int index) :
Event(sender), removeCount(removeCount), index(index) {}
RemoveDocumentResponse(QObject *sender, const EventError &error) :
Event(sender, error) {}
RemoveDocumentResponse(QObject *sender, const EventError &error, RemoveDocumentCount removeCount, int index) :
Event(sender, error), removeCount(removeCount), index(index) {}
RemoveDocumentCount const removeCount;
int const index;
};
/**
@@ -383,52 +480,49 @@ namespace Robomongo
std::string database() const { return _database; }
private:
std::string _database;
std::string const _database;
};
class CreateDatabaseResponse : public Event
struct CreateDatabaseResponse : public Event
{
R_EVENT
public:
CreateDatabaseResponse(QObject *sender) :
Event(sender) {}
CreateDatabaseResponse(QObject *sender, const std::string &database) :
Event(sender), database(database) {}
CreateDatabaseResponse(QObject *sender, const EventError &error) :
Event(sender, error) {}
CreateDatabaseResponse(QObject *sender, const std::string &database, const EventError &error) :
Event(sender, error), database(database) {}
std::string const database;
};
/**
* @brief Drop Database
*/
class DropDatabaseRequest : public Event
struct DropDatabaseRequest : public Event
{
R_EVENT
public:
DropDatabaseRequest(QObject *sender, const std::string &database) :
Event(sender),
_database(database) {}
Event(sender), database(database) {}
std::string database() const { return _database; }
private:
std::string _database;
std::string const database;
};
class DropDatabaseResponse : public Event
struct DropDatabaseResponse : public Event
{
R_EVENT
public:
DropDatabaseResponse(QObject *sender) :
Event(sender) {}
DropDatabaseResponse(QObject *sender, const std::string &database) :
Event(sender), database(database) {}
DropDatabaseResponse(QObject *sender, const EventError &error) :
Event(sender, error) {}
DropDatabaseResponse(QObject *sender, const std::string &database, const EventError &error) :
Event(sender, error), database(database) {}
std::string const database;
};
/**
@@ -440,26 +534,40 @@ namespace Robomongo
R_EVENT
public:
CreateCollectionRequest(QObject *sender, const MongoNamespace &ns) :
Event(sender),
_ns(ns) {}
CreateCollectionRequest(QObject *sender, const MongoNamespace &ns,
const mongo::BSONObj& extraOptions,
long long size = 0,
bool capped = false,
int maxDocNum = 0
) :
Event(sender), _ns(ns), _extraOptions(extraOptions),
_size(size), _capped(capped), _maxDocNum(maxDocNum) {}
MongoNamespace ns() const { return _ns; }
long long getSize() const { return _size; }
bool getCapped() const { return _capped; }
int getMaxDocNum() const { return _maxDocNum; }
const mongo::BSONObj getExtraOptions() const { return _extraOptions; }
private:
const MongoNamespace _ns;
MongoNamespace const _ns;
long long const _size;
bool const _capped;
int const _maxDocNum;
mongo::BSONObj const _extraOptions;
};
class CreateCollectionResponse : public Event
struct CreateCollectionResponse : public Event
{
R_EVENT
public:
CreateCollectionResponse(QObject *sender) :
Event(sender) {}
CreateCollectionResponse(QObject *sender, std::string const& collection) :
Event(sender), collection(collection) {}
CreateCollectionResponse(QObject *sender, const EventError &error) :
Event(sender, error) {}
CreateCollectionResponse(QObject *sender, std::string const& collection, const EventError &error) :
Event(sender, error), collection(collection) {}
std::string const collection;
};
@@ -483,16 +591,18 @@ namespace Robomongo
MongoNamespace _ns;
};
class DropCollectionResponse : public Event
struct DropCollectionResponse : public Event
{
R_EVENT
public:
DropCollectionResponse(QObject *sender) :
Event(sender) {}
DropCollectionResponse(QObject *sender, std::string const& collection) :
Event(sender), collection(collection) {}
DropCollectionResponse(QObject *sender, const EventError &error) :
Event(sender, error) {}
DropCollectionResponse(QObject *sender, std::string const& collection, const EventError &error) :
Event(sender, error), collection(collection) {}
std::string const collection;
};
/**
@@ -513,20 +623,23 @@ namespace Robomongo
std::string newCollection() const { return _newCollection; }
private:
MongoNamespace _ns;
std::string _newCollection;
MongoNamespace const _ns;
std::string const _newCollection;
};
class RenameCollectionResponse : public Event
struct RenameCollectionResponse : public Event
{
R_EVENT
public:
RenameCollectionResponse(QObject *sender) :
Event(sender) {}
RenameCollectionResponse(QObject *sender, std::string const& oldCollection,
std::string const& newCollection) :
Event(sender), oldCollection(oldCollection), newCollection(newCollection) {}
RenameCollectionResponse(QObject *sender, const EventError &error) :
Event(sender, error) {}
std::string const oldCollection;
std::string const newCollection;
};
/**
@@ -547,20 +660,24 @@ namespace Robomongo
std::string newCollection() const { return _newCollection; }
private:
MongoNamespace _ns;
std::string _newCollection;
MongoNamespace const _ns;
std::string const _newCollection;
};
class DuplicateCollectionResponse : public Event
struct DuplicateCollectionResponse : public Event
{
R_EVENT
public:
DuplicateCollectionResponse(QObject *sender) :
Event(sender) {}
DuplicateCollectionResponse(QObject *sender, std::string const& sourceCollection,
std::string const& duplicateCollection) :
Event(sender), sourceCollection(sourceCollection), duplicateCollection(duplicateCollection) {}
DuplicateCollectionResponse(QObject *sender, const EventError &error) :
Event(sender, error) {}
DuplicateCollectionResponse(QObject *sender, std::string const& sourceCollection, const EventError &error) :
Event(sender, error), sourceCollection(sourceCollection) {}
std::string const sourceCollection;
std::string const duplicateCollection;
};
/**
@@ -580,7 +697,7 @@ namespace Robomongo
_to(databaseTo, collection) {}
MongoWorker *worker() const { return _worker; }
MongoNamespace from() const { return _from;}
MongoNamespace from() const { return _from; }
MongoNamespace to() const { return _to; }
private:
MongoWorker *_worker;
@@ -625,16 +742,17 @@ namespace Robomongo
bool _overwrite;
};
class CreateUserResponse : public Event
struct CreateUserResponse : public Event
{
R_EVENT
public:
CreateUserResponse(QObject *sender) :
Event(sender) {}
CreateUserResponse(QObject *sender, std::string const& userName) :
Event(sender), userName(userName) {}
CreateUserResponse(QObject *sender, const EventError &error) :
Event(sender, error) {}
CreateUserResponse(QObject *sender, std::string const& userName, const EventError &error) :
Event(sender, error), userName(userName) {}
std::string const userName;
};
@@ -647,29 +765,31 @@ namespace Robomongo
R_EVENT
public:
DropUserRequest(QObject *sender, const std::string &database, const mongo::OID &id) :
Event(sender),
_database(database),
_id(id) {}
DropUserRequest(QObject *sender, const std::string &database, const mongo::OID &id,
std::string const& username) :
Event(sender), _database(database), _id(id), _username(username) {}
std::string database() const { return _database; }
mongo::OID id() const { return _id; }
std::string username() const { return _username; }
private:
std::string _database;
mongo::OID _id;
std::string const _username;
};
class DropUserResponse : public Event
struct DropUserResponse : public Event
{
R_EVENT
public:
DropUserResponse(QObject *sender) :
Event(sender) {}
DropUserResponse(QObject *sender, std::string const& username) :
Event(sender), username(username) {}
DropUserResponse(QObject *sender, const EventError &error) :
Event(sender, error) {}
DropUserResponse(QObject *sender, std::string const& username, const EventError &error) :
Event(sender, error), username(username) {}
std::string const username;
};
@@ -702,19 +822,21 @@ namespace Robomongo
bool _overwrite;
};
class CreateFunctionResponse : public Event
struct CreateFunctionResponse : public Event
{
R_EVENT
enum Operation { NewFunction, NameUpdate, CodeUpdate }; // todo
public:
CreateFunctionResponse(QObject *sender) :
Event(sender) {}
CreateFunctionResponse(QObject *sender, std::string const& functionName) :
Event(sender), functionName(functionName) {}
CreateFunctionResponse(QObject *sender, const EventError &error) :
Event(sender, error) {}
CreateFunctionResponse(QObject *sender, std::string const& functionName, const EventError &error) :
Event(sender, error), functionName(functionName) {}
std::string const functionName;
};
/**
* @brief Drop Function
*/
@@ -724,29 +846,30 @@ namespace Robomongo
R_EVENT
public:
DropFunctionRequest(QObject *sender, const std::string &database, const std::string &name) :
DropFunctionRequest(QObject *sender, const std::string &database, const std::string &funcName) :
Event(sender),
_database(database),
_name(name) {}
_functionName(funcName) {}
std::string database() const { return _database; }
std::string name() const { return _name; }
std::string functionName() const { return _functionName; }
private:
std::string _database;
std::string _name;
std::string _functionName;
};
class DropFunctionResponse : public Event
struct DropFunctionResponse : public Event
{
R_EVENT
public:
DropFunctionResponse(QObject *sender) :
Event(sender) {}
DropFunctionResponse(QObject *sender, const std::string &functionName) :
Event(sender), functionName(functionName) {}
DropFunctionResponse(QObject *sender, const EventError &error) :
Event(sender, error) {}
DropFunctionResponse(QObject *sender, const std::string &functionName, const EventError &error) :
Event(sender, error), functionName(functionName) {}
std::string const functionName;
};
/**
@@ -806,7 +929,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,27 +1002,43 @@ 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
class ConnectionEstablishedEvent : public Event
struct ConnectionEstablishedEvent : public Event
{
R_EVENT
ConnectionEstablishedEvent(MongoServer *server) :
ConnectionEstablishedEvent(MongoServer *server, ConnectionType type, ConnectionInfo connInfo) :
Event((QObject *)server),
server(server) { }
server(server),
connectionType(type),
connInfo(connInfo) { }
MongoServer *server;
ConnectionType const connectionType;
ConnectionInfo const connInfo;
};
class DatabaseListLoadedEvent : public Event
@@ -973,6 +1112,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 +1149,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 +1196,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 +1231,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) :
+10 -4
View File
@@ -17,13 +17,19 @@ namespace Robomongo
_languageOverride(languageOverride),
_textWeights(textWeights) {}
ConnectionInfo::ConnectionInfo() :
ConnectionInfo::ConnectionInfo(std::string const& uuid) :
_address(),
_databases(),
_version(0.0f) {}
_version(0.0f),
_uuid(uuid)
{}
ConnectionInfo::ConnectionInfo(const std::string &address, const std::vector<std::string> &databases, float version) :
ConnectionInfo::ConnectionInfo(const std::string &address, const std::vector<std::string> &databases,
float version, const std::string& storageEngine, std::string const& uuid) :
_address(address),
_databases(databases),
_version(version) {}
_version(version),
_storageEngineType(storageEngine),
_uuid(uuid)
{}
}
+5 -2
View File
@@ -34,10 +34,13 @@ namespace Robomongo
struct ConnectionInfo
{
ConnectionInfo();
ConnectionInfo(const std::string &address, const std::vector<std::string> &databases, float version);
ConnectionInfo(std::string const& uuid);
ConnectionInfo(const std::string &address, const std::vector<std::string> &databases, float version,
const std::string& storageEngine, std::string const& uuid);
const std::string _address;
const std::vector<std::string> _databases;
const float _version;
const std::string _storageEngineType;
std::string const _uuid;
};
}
+136 -32
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);
}
@@ -99,9 +108,10 @@ namespace Robomongo
MongoNamespace ns(dbName, "system.users");
mongo::BSONObj obj = user.toBson();
if (!overwrite) {
if (!overwrite) { // create new user
_dbclient->insert(ns.toString(), obj);
} else {
}
else { // update existing user
mongo::BSONElement id = obj.getField("_id");
mongo::BSONObjBuilder builder;
builder.append(id);
@@ -110,6 +120,10 @@ namespace Robomongo
_dbclient->update(ns.toString(), query, obj, true, false);
}
std::string errorStr = _dbclient->getLastError();
if (!errorStr.empty())
throw mongo::DBException(errorStr, 0);
}
void MongoClient::dropUser(const std::string &dbName, const mongo::OID &id)
@@ -122,6 +136,10 @@ namespace Robomongo
mongo::Query query(bsonQuery);
_dbclient->remove(ns.toString(), query, true);
std::string errorStr = _dbclient->getLastError();
if (!errorStr.empty())
throw mongo::DBException(errorStr, 0);
}
std::vector<MongoFunction> MongoClient::getFunctions(const std::string &dbName)
@@ -157,13 +175,13 @@ namespace Robomongo
for (std::list<mongo::BSONObj>::iterator it = indexes.begin(); it != indexes.end(); ++it) {
mongo::BSONObj bsonObj = *it;
result.push_back(makeEnsureIndexInfoFromBsonObj(collection,bsonObj));
result.push_back(makeEnsureIndexInfoFromBsonObj(collection, bsonObj));
}
return result;
}
void MongoClient::ensureIndex(const EnsureIndexInfo &oldInfo,const EnsureIndexInfo &newInfo) const
void MongoClient::ensureIndex(const EnsureIndexInfo &oldInfo, const EnsureIndexInfo &newInfo) const
{
std::string ns = newInfo._collection.ns().toString();
@@ -172,8 +190,8 @@ namespace Robomongo
mongo::BSONObj keys = mongo::Robomongo::fromjson(newInfo._request);
mongo::BSONObjBuilder toSave;
bool cache=true;
int version =-1;
bool cache = true;
int version = -1;
toSave.append( "ns" , ns );
toSave.append( "key" , keys );
@@ -284,36 +302,46 @@ namespace Robomongo
_dbclient->dropIndex(collection.ns().toString(), indexName);
}
void MongoClient::createFunction(const std::string &dbName, const MongoFunction &fun, const std::string &existingFunctionName /* = QString() */)
void MongoClient::createFunction(const std::string &dbName, const MongoFunction &fun,
const std::string &existingFunctionName /* = QString() */)
{
MongoNamespace ns(dbName, "system.js");
mongo::BSONObj obj = fun.toBson();
if (existingFunctionName.empty()) { // this is insert
if (existingFunctionName.empty()) { // create new function
_dbclient->insert(ns.toString(), obj);
std::string errorStr = _dbclient->getLastError();
if (!errorStr.empty())
throw mongo::DBException(errorStr, 0);
} else { // this is update
std::string name = fun.name();
if (existingFunctionName == name) {
if (existingFunctionName == name) { // update existing function code
mongo::BSONObjBuilder builder;
builder.append("_id", name);
mongo::BSONObj bsonQuery = builder.obj();
mongo::Query query(bsonQuery);
_dbclient->update(ns.toString(), query, obj, true, false);
} else {
std::string errorStr = _dbclient->getLastError();
if (!errorStr.empty())
throw mongo::DBException(errorStr, 0);
} else { // update function name (remove & insert)
_dbclient->insert(ns.toString(), obj);
std::string res = _dbclient->getLastError();
std::string errorStr = _dbclient->getLastError();
// if no errors
if (res.empty()) {
if (errorStr.empty()) {
mongo::BSONObjBuilder builder;
builder.append("_id", existingFunctionName);
mongo::BSONObj bsonQuery = builder.obj();
mongo::Query query(bsonQuery);
_dbclient->remove(ns.toString(), query, true);
}
else {
throw mongo::DBException(errorStr, 0);
}
}
}
}
@@ -328,6 +356,9 @@ namespace Robomongo
mongo::Query query(bsonQuery);
_dbclient->remove(ns.toString(), query, true);
std::string errorStr = _dbclient->getLastError();
if (!errorStr.empty())
throw mongo::DBException(errorStr, 0);
}
void MongoClient::createDatabase(const std::string &dbName)
@@ -342,7 +373,7 @@ namespace Robomongo
// If <dbName>.temp already exists, stop.
if (_dbclient->exists(ns.toString()))
return;
throw mongo::DBException(dbName + ".temp already exists.", 0);
// Building { _id : "temp" } document
mongo::BSONObjBuilder builder;
@@ -351,6 +382,9 @@ namespace Robomongo
// Insert this document
_dbclient->insert(ns.toString(), obj);
std::string errorStr = _dbclient->getLastError();
if (!errorStr.empty())
throw mongo::DBException(errorStr, 0);
// Drop temp collection
_dbclient->dropCollection(ns.toString());
@@ -358,12 +392,50 @@ namespace Robomongo
void MongoClient::dropDatabase(const std::string &dbName)
{
_dbclient->dropDatabase(dbName);
mongo::BSONObj result;
if (!_dbclient->dropDatabase(dbName, &result)) {
std::string errStr = result.getStringField("errmsg");
if (errStr.empty())
errStr = "Failed to get error message.";
throw mongo::DBException(errStr, 0);
}
}
void MongoClient::createCollection(const MongoNamespace &ns)
void MongoClient::createCollection(const std::string& ns, long long size, bool capped, int max,
const mongo::BSONObj& extraOptions, mongo::BSONObj* info)
{
_dbclient->createCollection(ns.toString());
verify(!capped || size);
mongo::BSONObj o;
if (info == 0)
info = &o;
mongo::BSONObjBuilder b;
std::string db = mongo::nsToDatabase(ns);
b.append("create", ns.c_str() + db.length() + 1);
if (size) {
b.append("size", size);
}
if (capped) {
b.append("capped", true);
}
if (max) {
b.append("max", max);
}
b.appendElements(extraOptions);
if (!_dbclient->exists(ns)) {
mongo::BSONObj result;
if (!_dbclient->runCommand(db.c_str(), b.done(), result)) {
std::string errStr = result.getStringField("errmsg");
if (errStr.empty())
errStr = "Failed to get error message.";
throw mongo::DBException(errStr, 0);
}
}
else {
throw mongo::DBException("Collection with same name already exists.", 0);
}
}
void MongoClient::renameCollection(const MongoNamespace &ns, const std::string &newCollectionName)
@@ -377,18 +449,38 @@ namespace Robomongo
command.append("to", to.toString());
mongo::BSONObj result;
_dbclient->runCommand("admin", command.obj(), result); // this command should be run against "admin" db
if (!_dbclient->runCommand("admin", command.obj(), result)) { // this command should be run against "admin" db
std::string errStr = result.getStringField("errmsg");
if (errStr.empty())
errStr = "Failed to get error message.";
throw mongo::DBException(errStr, 0);
}
}
void MongoClient::duplicateCollection(const MongoNamespace &ns, const std::string &newCollectionName)
{
MongoNamespace from(ns);
MongoNamespace to(ns.databaseName(), newCollectionName);
MongoNamespace sourceCollection(ns);
MongoNamespace newCollection(ns.databaseName(), newCollectionName);
if (!_dbclient->exists(to.toString()))
_dbclient->createCollection(to.toString());
if (!_dbclient->exists(newCollection.toString())) {
mongo::BSONObj result;
// todo: Issue #1258 : Duplicate Collection should support advanced collection options.
// _dbclient->createCollection() should be called with properties of source collection
// not with default parameters as below.
if (!_dbclient->createCollection(newCollection.toString(), 0, false, 0, &result)) {
std::string errStr = result.getStringField("errmsg");
if (errStr.empty())
errStr = "Failed to get error message.";
std::unique_ptr<mongo::DBClientCursor> cursor(_dbclient->query(from.toString(), mongo::Query()));
throw mongo::DBException(errStr, 0);
}
}
else {
throw mongo::DBException("Collection with same name already exists.", 0);
}
std::unique_ptr<mongo::DBClientCursor> cursor(_dbclient->query(sourceCollection.toString(), mongo::Query()));
// Cursor may be NULL, it means we have connectivity problem
if (!cursor)
@@ -396,11 +488,12 @@ namespace Robomongo
while (cursor->more()) {
mongo::BSONObj bsonObj = cursor->next();
_dbclient->insert(to.toString(), bsonObj);
_dbclient->insert(newCollection.toString(), bsonObj);
}
}
void MongoClient::copyCollectionToDiffServer(mongo::DBClientBase *const fromServ,const MongoNamespace &from, const MongoNamespace &to)
void MongoClient::copyCollectionToDiffServer(mongo::DBClientBase *const fromServ, const MongoNamespace &from,
const MongoNamespace &to)
{
if (!_dbclient->exists(to.toString()))
_dbclient->createCollection(to.toString());
@@ -419,7 +512,19 @@ namespace Robomongo
void MongoClient::dropCollection(const MongoNamespace &ns)
{
_dbclient->dropCollection(ns.toString());
if (_dbclient->exists(ns.toString())) {
mongo::BSONObj result;
if (!_dbclient->dropCollection(ns.toString(), &result)) {
std::string errStr = result.getStringField("errmsg");
if (errStr.empty())
errStr = "Failed to get error message.";
throw mongo::DBException(errStr, 0);
}
}
else {
throw mongo::DBException("Collection does not exist.", 0);
}
}
void MongoClient::insertDocument(const mongo::BSONObj &obj, const MongoNamespace &ns)
@@ -497,11 +602,10 @@ namespace Robomongo
std::vector<MongoCollectionInfo> MongoClient::runCollStatsCommand(const std::vector<std::string> &namespaces)
{
std::vector<MongoCollectionInfo> infos;
for (std::vector<std::string>::const_iterator it = namespaces.begin(); it!=namespaces.end(); ++it) {
MongoCollectionInfo info = runCollStatsCommand(*it);
if (info.ns().isValid()){
for (auto const& ns : namespaces) {
MongoCollectionInfo info = runCollStatsCommand(ns);
if (info.ns().isValid())
infos.push_back(info);
}
}
return infos;
}
+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);
File diff suppressed because it is too large Load Diff
+68 -10
View File
@@ -4,6 +4,8 @@
#include <QMutex>
#include <unordered_set>
#include <mongo/client/dbclient_rs.h>
#include "robomongo/core/events/MongoEvents.h"
QT_BEGIN_NAMESPACE
@@ -22,15 +24,23 @@ namespace Robomongo
Q_OBJECT
public:
typedef std::vector<std::string> DatabasesContainerType;
explicit MongoWorker(ConnectionSettings *connection, bool isLoadMongoRcJs, int batchSize, QObject *parent = NULL);
~MongoWorker();
enum { pingTimeMs = 60 * 1000 };
typedef std::vector<std::string> DatabasesContainerType;
using DBClientReplicaSet = std::unique_ptr<mongo::DBClientReplicaSet>;
using DBClientConnection = std::unique_ptr<mongo::DBClientConnection>;
explicit MongoWorker(ConnectionSettings *connection, bool isLoadMongoRcJs, int batchSize,
int mongoTimeoutSec, int shellTimeoutSec, QObject *parent = NULL);
~MongoWorker();
void interrupt();
void stopAndDelete();
protected Q_SLOTS: // handlers:
protected Q_SLOTS:
void init();
/**
* @brief Every minute we are issuing { ping : 1 } command to every used connection
* in order to avoid dropped connections.
@@ -40,7 +50,13 @@ namespace Robomongo
/**
* @brief Initiate connection to MongoDB
*/
void handle(EstablishConnectionRequest *event);
bool handle(EstablishConnectionRequest *event);
/**
* @brief Try to re-connect to MongoDB replica set in order to refresh connection view.
* For more details, see MongoServer::tryRefreshReplicaSetFolder()
*/
void handle(RefreshReplicaSetFolderRequest *event);
/**
* @brief Load list of all database names
@@ -127,33 +143,75 @@ namespace Robomongo
* @brief Send event to this MongoWorker
*/
void send(Event *event);
DatabasesContainerType getDatabaseNamesSafe();
std::string getAuthBase() const;
mongo::DBClientBase *_dbclient;
mongo::DBClientBase *getConnection();
mongo::DBClientBase *getConnection(bool mayReturnNull = false);
MongoClient *getClient();
/**
*@brief Reset and update global mongo SSL settings (mongo::sslGlobalParams)
*/
void configureSSL();
/**
*@brief Update global mongo SSL settings (mongo::sslGlobalParams) according to active connection
* request's SSL settings.
*/
void updateGlobalSSLparams() const;
/**
*@brief Reset global mongo SSL settings (mongo::sslGlobalParams) into default zero state
*/
void resetGlobalSSLparams() const;
/**
*@brief Update Replica Set related parameters/settings.
* Warning: Using parameter 'refresh' as false might return incorrect cached values for status
* of secondary members.
* @param refresh : If true it will be deep scan of replica set status which
* might take quite some time. If false, it will be quick scan
* which might return incorrect cached values for status of secondary members.
* Use false only to achieve quick first connection, in all other cases true
* should be used.
*/
ReplicaSet getReplicaSetInfo(bool refresh = true) const;
std::string connectAndGetReplicaSetName() const;
/**
* @brief Send reply event to object 'obj'
*/
void reply(QObject *receiver, Event *event);
/**
* @brief Send hear beat messages to database in order to keep connection alive
*/
void pingDatabase(mongo::DBClientBase *dbclient) const;
QThread *_thread;
QMutex _firstConnectionMutex;
ScriptEngine *_scriptEngine;
std::unique_ptr<ScriptEngine> _scriptEngine;
bool _isAdmin;
const bool _isLoadMongoRcJs;
const int _batchSize;
int _timerId;
int _dbAutocompleteCacheTimerId;
int _mongoTimeoutSec;
int _shellTimeoutSec;
QAtomicInteger<int> _isQuiting;
ConnectionSettings *_connection;
std::unique_ptr<mongo::DBClientConnection> _dbclient;
std::unique_ptr<mongo::DBClientReplicaSet> _dbclientRepSet;
ConnectionSettings *_connSettings;
// Collection of created databases.
// Starting from 3.0, MongoDB drops empty databases.
// It means, we do not find a way to create "empty" database.
// It means, we did not find a way to create "empty" database.
// We save all created databases in this collection and merge with
// list of real databases returned from MongoDB server.
std::unordered_set<std::string> _createdDbs;
+12
View File
@@ -0,0 +1,12 @@
#include "robomongo/core/mongodb/ReplicaSet.h"
namespace Robomongo
{
ReplicaSet::ReplicaSet(std::string const& setName, const mongo::HostAndPort primary,
std::vector<std::pair<std::string, bool>> const& membersAndHealths,
std::string const& errorStr)
: setName(setName), primary(primary), membersAndHealths(membersAndHealths),
errorStr(errorStr)
{}
}
+22
View File
@@ -0,0 +1,22 @@
#pragma once
#include <mongo/util/net/hostandport.h>
namespace Robomongo
{
/**
* @brief Struct to represent live information of a replica set.
*/
struct ReplicaSet
{
ReplicaSet(std::string const& setName, const mongo::HostAndPort primary,
std::vector<std::pair<std::string, bool>> const& membersAndHealths, std::string const& errorStr = "");
ReplicaSet() {};
std::string const setName;
mongo::HostAndPort const primary;
std::vector<std::pair<std::string, bool>> const membersAndHealths; // pair: {HostNameAndPort, Health}
std::string const errorStr;
};
}
+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;
};
}
@@ -2,37 +2,84 @@
#include <stdio.h>
#include <QUuid>
#include "robomongo/core/settings/CredentialSettings.h"
#include "robomongo/core/settings/ReplicaSetSettings.h"
#include "robomongo/core/settings/SshSettings.h"
#include "robomongo/core/settings/SslSettings.h"
#include "robomongo/core/utils/QtUtils.h"
#include <boost/algorithm/string.hpp>
namespace
{
const unsigned port = 27017;
const char *defaultServerHost = "localhost";
const char *defaultNameConnection = "New Connection";
const int maxLength = 300;
}
namespace Robomongo
{
/**
* Creates ConnectionSettings with default values
*/
ConnectionSettings::ConnectionSettings() : QObject(),
ConnectionSettings::ConnectionSettings(bool isClone) : QObject(),
_connectionName(defaultNameConnection),
_host(defaultServerHost),
_port(port),
_sshSettings(new SshSettings()) { }
_imported(false),
_sshSettings(new SshSettings()),
_sslSettings(new SslSettings()),
_isReplicaSet(false),
_replicaSetSettings(new ReplicaSetSettings()),
_clone(isClone)
{ }
ConnectionSettings::ConnectionSettings(const mongo::MongoURI& uri, bool isClone)
: _connectionName(defaultNameConnection),
_host(defaultServerHost),
_port(port),
_imported(false),
_sshSettings(new SshSettings()),
_sslSettings(new SslSettings()),
_isReplicaSet((uri.type() == mongo::ConnectionString::ConnectionType::SET)),
_replicaSetSettings(new ReplicaSetSettings(uri)),
_clone(isClone)
{
if (!uri.getServers().empty()) {
_host = uri.getServers().front().host();
_port = uri.getServers().front().port();
}
auto str = std::string(uri.getOptions().getStringField("ssl"));
auto sslEnabled = ("true" == str);
if (sslEnabled) {
_sslSettings->enableSSL(true);
_sslSettings->setAllowInvalidCertificates(true);
}
auto credential = new CredentialSettings();
credential->setUserName(uri.getUser());
credential->setUserPassword(uri.getPassword());
credential->setDatabaseName(uri.getDatabase());
if (!credential->userName().empty() && !credential->userPassword().empty()) { // todo:
credential->setEnabled(true);
}
addCredential(credential);
}
void ConnectionSettings::fromVariant(const QVariantMap &map) {
setConnectionName(QtUtils::toStdString(map.value("connectionName").toString()));
setServerHost(QtUtils::toStdString(map.value("serverHost").toString()));
setServerHost(QtUtils::toStdString(map.value("serverHost").toString().left(maxLength)));
setServerPort(map.value("serverPort").toInt());
setDefaultDatabase(QtUtils::toStdString(map.value("defaultDatabase").toString()));
setReplicaSet(map.value("isReplicaSet").toBool());
QVariantList list = map.value("credentials").toList();
for(QVariantList::const_iterator it = list.begin(); it != list.end(); ++it) {
for (QVariantList::const_iterator it = list.begin(); it != list.end(); ++it) {
QVariant var = *it;
CredentialSettings *credential = new CredentialSettings(var.toMap());
addCredential(credential);
@@ -42,6 +89,21 @@ 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());
}
// If UUID has never been created or is empty, create a new one. Otherwise load the existing.
if (!map.contains("uuid") || map.value("uuid").toString().isEmpty())
_uuid = QUuid::createUuid().toString();
else
_uuid = map.value("uuid").toString();
//#ifdef MONGO_SSL
// ,SSLInfo(map.value("sslEnabled").toBool(),QtUtils::toStdString(map.value("sslPemKeyFile").toString()))
//#endif
@@ -53,7 +115,6 @@ namespace Robomongo
ConnectionSettings::~ConnectionSettings()
{
clearCredentials();
delete _sshSettings;
}
/**
@@ -61,9 +122,10 @@ namespace Robomongo
*/
ConnectionSettings *ConnectionSettings::clone() const
{
ConnectionSettings *record = new ConnectionSettings();
record->apply(this);
return record;
auto settings = new ConnectionSettings(true);
settings->setUuid(_uuid);
settings->apply(this);
return settings;
}
/**
@@ -75,6 +137,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 +146,9 @@ namespace Robomongo
addCredential((*it)->clone());
}
// Clone SSH settings
_sshSettings = source->sshSettings()->clone();
_sshSettings.reset(source->sshSettings()->clone());
_sslSettings.reset(source->sslSettings()->clone());
_replicaSetSettings.reset(source->_replicaSetSettings->clone());
//#ifdef MONGO_SSL
// setSslInfo(source->sslInfo());
@@ -100,27 +165,34 @@ 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());
map.insert("uuid", _uuid);
return map;
}
CredentialSettings *ConnectionSettings::findCredential(const std::string &databaseName) const
{
CredentialSettings *result = NULL;
for(QList<CredentialSettings *>::const_iterator it = _credentials.begin(); it != _credentials.end(); ++it) {
CredentialSettings *result = nullptr;
for (QList<CredentialSettings *>::const_iterator it = _credentials.begin(); it != _credentials.end(); ++it) {
CredentialSettings *cred = *it;
if (cred->databaseName() == databaseName) {
result = cred;
@@ -143,7 +215,7 @@ namespace Robomongo
* @brief Checks whether this connection has primary credential
* which is also enabled.
*/
bool ConnectionSettings::hasEnabledPrimaryCredential()
bool ConnectionSettings::hasEnabledPrimaryCredential() const
{
if (_credentials.count() == 0)
return false;
@@ -152,12 +224,13 @@ namespace Robomongo
}
/**
* @brief Returns primary credential, or NULL if no credentials exists.
* @brief Returns primary credential
*/
CredentialSettings *ConnectionSettings::primaryCredential()
CredentialSettings *ConnectionSettings::primaryCredential() const
{
if (_credentials.count() == 0)
return NULL;
if (_credentials.count() == 0) {
_credentials.append(new CredentialSettings());
}
return _credentials.at(0);
}
@@ -173,9 +246,26 @@ namespace Robomongo
std::string ConnectionSettings::getFullAddress() const
{
return info().toString();
return hostAndPort().toString();
}
mongo::HostAndPort ConnectionSettings::hostAndPort() const
{
// If it doesn't look like IPv6 address,
// treat it like IPv4 or literal hostname
if (_host.find(':') == std::string::npos) {
return mongo::HostAndPort(_host, _port);
}
// The following code assumes, that it is IPv6 address
// If address contains square brackets ("["), remove them:
std::string hostCopy = _host;
if (_host.find('[') != std::string::npos) {
boost::erase_all(hostCopy, "[");
boost::erase_all(hostCopy, "]");
}
return mongo::HostAndPort(hostCopy, _port);
}
}
@@ -3,13 +3,18 @@
#include <QString>
#include <QVariant>
#include <QVariantMap>
#include <mongo/client/dbclientinterface.h>
#include <boost/algorithm/string.hpp>
#include <mongo/client/mongo_uri.h>
#include <mongo/util/net/hostandport.h>
namespace Robomongo
{
class CredentialSettings;
class SshSettings;
class SslSettings;
class ReplicaSetSettings;
/**
* @brief Represents connection record
*/
@@ -21,9 +26,14 @@ namespace Robomongo
/**
* @brief Creates ConnectionSettings with default values
*/
ConnectionSettings();
ConnectionSettings(bool isClone);
/**
* @brief Creates ConnectionSettings from mongo connection string URI
*/
ConnectionSettings(const mongo::MongoURI& uri, bool isClone);
explicit ConnectionSettings(QVariantMap map);
explicit ConnectionSettings(QVariantMap map, bool isClone);
/**
* @brief Cleanup used resources
@@ -61,7 +71,7 @@ namespace Robomongo
/**
* @brief Port of server
*/
unsigned short serverPort() const { return _port; }
int serverPort() const { return _port; }
void setServerPort(const int port) { _port = port; }
/**
@@ -70,6 +80,12 @@ namespace Robomongo
std::string defaultDatabase() const { return _defaultDatabase; }
void setDefaultDatabase(const std::string &defaultDatabase) { _defaultDatabase = defaultDatabase; }
/**
* Was this connection imported from somewhere?
*/
bool imported() const { return _imported; }
void setImported(bool imported) { _imported = imported; }
/**
* @brief Adds credential to this connection
*/
@@ -84,12 +100,12 @@ namespace Robomongo
* @brief Checks whether this connection has primary credential
* which is also enabled.
*/
bool hasEnabledPrimaryCredential();
bool hasEnabledPrimaryCredential() const;
/**
* @brief Returns primary credential, or NULL if no credentials exists.
* @brief Returns primary credential
*/
CredentialSettings *primaryCredential();
CredentialSettings *primaryCredential() const;
/**
* @brief Returns number of credentials
@@ -124,34 +140,46 @@ namespace Robomongo
return _connectionName;
}
mongo::HostAndPort info() const {
// If it doesn't look like IPv6 address,
// treat it like IPv4 or literal hostname
if (_host.find(':') == std::string::npos) {
return mongo::HostAndPort(_host, _port);
}
mongo::HostAndPort hostAndPort() const;
SshSettings *sshSettings() const { return _sshSettings.get(); }
SslSettings *sslSettings() const { return _sslSettings.get(); }
ReplicaSetSettings *replicaSetSettings() const { return _replicaSetSettings.get(); }
// The following code assumes, that it is IPv6 address
// If address contains square brackets ("["), remove them:
std::string hostCopy = _host;
if (_host.find('[') != std::string::npos) {
boost::erase_all(hostCopy, "[");
boost::erase_all(hostCopy, "]");
}
return mongo::HostAndPort(hostCopy, _port);
}
SshSettings *sshSettings() const { return _sshSettings; }
bool isReplicaSet() const { return _isReplicaSet; }
void setReplicaSet(bool flag) { _isReplicaSet = flag; }
QString uuid() const { return _uuid; }
void setUuid(QString const& uuid) { _uuid = uuid; }
private:
CredentialSettings *findCredential(const std::string &databaseName) const;
std::string _connectionName;
std::string _host;
int _port;
std::string _defaultDatabase;
QList<CredentialSettings *> _credentials;
SshSettings *_sshSettings;
mutable QList<CredentialSettings *> _credentials;
std::unique_ptr<SshSettings> _sshSettings;
std::unique_ptr<SslSettings> _sslSettings;
bool _isReplicaSet;
std::unique_ptr<ReplicaSetSettings> _replicaSetSettings;
// Was this connection imported from somewhere?
bool _imported;
// Flag to check if this is a clone(copy) or original ConnectionSettings
// Note: If this is not a clone connection settings, this object is original connection
// ConnectionSettings object which is loaded/saved into Robomongo config. file.
bool _clone = false;
// If this is a clone connection settings, unique ID will be used to identify from which
// original connection settings this object is cloned.
// -1 for invalid(uninitialized) unique ID which should not be seen in theory
int _uniqueId = -1;
// UUID string taken from QUuid.
// It is used to identify the unique ID of a connection settings object
QString _uuid;
};
}
@@ -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,85 @@
#include "robomongo/core/settings/ReplicaSetSettings.h"
#include "robomongo/core/utils/QtUtils.h"
namespace Robomongo
{
ReplicaSetSettings::ReplicaSetSettings()
: _cachedSetName(""), _members(), _readPreference(ReadPreference::PRIMARY)
{}
ReplicaSetSettings::ReplicaSetSettings(const mongo::MongoURI& uri)
: ReplicaSetSettings()
{
_cachedSetName = uri.getSetName();
for (auto const& server : uri.getServers()) {
_members.push_back(server.host() + ":" + std::to_string(server.port()));
}
}
ReplicaSetSettings *ReplicaSetSettings::clone() const
{
ReplicaSetSettings *cloned = new ReplicaSetSettings(*this);
return cloned;
}
QVariant ReplicaSetSettings::toVariant() const
{
QVariantMap map;
map.insert("setNameUserEntered", QString::fromStdString(_setNameUserEntered));
map.insert("cachedSetName", QString::fromStdString(_cachedSetName));
int idx = 0;
for (std::string const& str : _members) {
map.insert(QString::number(idx), QtUtils::toQString(str));
++idx;
}
map.insert("readPreference", static_cast<int>(readPreference()));
return map;
}
void ReplicaSetSettings::fromVariant(const QVariantMap &map)
{
setSetNameUserEntered(map.value("setNameUserEntered").toString().toStdString());
setCachedSetName(map.value("cachedSetName").toString().toStdString());
// Extract and set replica members
std::vector<std::string> vec;
auto itr = map.begin();
int idx = 0;
do
{
itr = map.find(QString::number(idx));
if (map.end() == itr) break;
vec.push_back(itr->toString().toStdString());
++idx;
} while (map.end() != itr);
setMembers(vec);
// Extract and set read reference
setReadPreference(static_cast<ReadPreference>(map.value("readPreference").toInt()));
}
void ReplicaSetSettings::setMembers(const std::vector<std::string>& members)
{
_members.clear();
for (auto const& member : members)
_members.push_back(member);
}
void ReplicaSetSettings::setMembers(const std::vector<std::pair<std::string, bool>>& membersAndHealts)
{
_members.clear();
for (auto const& memberAndHealth : membersAndHealts)
_members.push_back(memberAndHealth.first);
}
std::vector<mongo::HostAndPort> ReplicaSetSettings::membersToHostAndPort() const
{
std::vector<mongo::HostAndPort> membersHostAndPort;
for (auto const& member : _members)
membersHostAndPort.push_back(mongo::HostAndPort(member));
return membersHostAndPort;
}
}
@@ -0,0 +1,66 @@
#pragma once
#include <QString>
#include <QVariant>
#include <QVariantMap>
#include <mongo/util/net/hostandport.h>
#include <mongo/client/mongo_uri.h>
namespace Robomongo
{
class ReplicaSetSettings
{
public:
enum class ReadPreference
{
PRIMARY = 0,
PRIMARY_PREFERRED = 1
// todo: Add all supported read preferences
};
ReplicaSetSettings();
ReplicaSetSettings(const mongo::MongoURI& uri);
/**
* Clones Replica Set settings.
*/
ReplicaSetSettings* clone() const;
/**
* Converts to QVariantMap
*/
QVariant toVariant() const;
void fromVariant(const QVariantMap &map);
// Getters
std::string const& setNameUserEntered() { return _setNameUserEntered; }
std::string const& cachedSetName() { return _cachedSetName; }
std::vector<std::string> const& members() const { return _members; }
std::vector<mongo::HostAndPort> membersToHostAndPort() const;
std::set<mongo::HostAndPort> membersToHostAndPortAsSet() const
{
auto const& members = membersToHostAndPort();
return std::set<mongo::HostAndPort>(members.begin(), members.end());
}
ReadPreference readPreference() const { return _readPreference; }
// Setters
void setSetNameUserEntered(const std::string& setName) { _setNameUserEntered = setName; }
void setCachedSetName(const std::string& setName) { _cachedSetName = setName; }
void setMembers(const std::vector<std::string>& members);
void setMembers(const std::vector<std::pair<std::string,bool>>& membersAndHealts);
void deleteAllMembers() { _members.clear(); }
void setReadPreference(ReadPreference readPreference) { _readPreference = readPreference; }
private:
std::string _setNameUserEntered;
std::string _cachedSetName;
std::vector<std::string> _members;
ReadPreference _readPreference = ReadPreference::PRIMARY;
};
}
+460 -60
View File
@@ -3,36 +3,96 @@
#include <QDir>
#include <QFile>
#include <QVariantList>
#include <QUuid>
#include <QJsonArray>
#include <QXmlStreamReader>
// Todo:
// Warning:
// Currently, this include statement should be maintained manually.
// The Qt version must be updated if Qt is upgraded. Otherwise it might cause build error.
// The correct path will be: PROJECT_QT_VERSION/QtGui/private/qzipreader_p.h
// Details:
// Ideally, private class inside qzipreader_p.h should not be used since it is not
// a publicly/officially supported API by Qt. It is used here since we are doing a
// a very lightweight zip operation which is unzipping a zip archive with one, small file.
// The alternative is including QuaZip and Zlib third party libraries into CMAKE project and
// building, and using them which might be time consuming.
#include <5.7.0/QtGui/private/qzipreader_p.h>
#include <parser.h>
#include <serializer.h>
#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"
#include "robomongo/utils/common.h"
namespace
namespace Robomongo
{
/**
* @brief Version of schema
*/
const QString SchemaVersion = "2.0";
// 3T config files
auto const Studio3T_PropertiesDat = QString("%1/.3T/studio-3t/properties.dat").arg(QDir::homePath());
auto const DataMongodb_PropertiesDat = QString("%1/.3T/data-man-mongodb/properties.dat").arg(QDir::homePath());
auto const MongoChefPro_PropertiesDat = QString("%1/.3T/mongochef-pro/properties.dat").arg(QDir::homePath());
auto const MongoChefEnt_PropertiesDat = QString("%1/.3T/mongochef-enterprise/properties.dat").arg(QDir::homePath());
/**
* @brief Config file absolute path
* (usually: /home/user/.config/robomongo/robomongo.json)
*/
const QString _configPath = QString("%1/.config/robomongo/0.9/robomongo.json").arg(QDir::homePath());
const std::vector<std::pair<QString, QString>> S_3T_ZipFile_And_ConfigFile_List
{
{ Studio3T_PropertiesDat, "Studio3T.properties" },
{ DataMongodb_PropertiesDat, "3T.data-man-mongodb.properties" },
{ MongoChefPro_PropertiesDat, "3T.mongochef-pro.properties" },
{ MongoChefEnt_PropertiesDat, "3T.mongochef-enterprise.properties" }
};
// Extract zipFile and find the value of "anonymousID" field in propFile
QString extractAnonymousID(QString const& zipFile, QString const& propfile);
/**
* @brief Version of schema
*/
const QString SchemaVersion = "2.0";
/**
* @brief Config file containing directory path
* (usually: /home/user/.config/robomongo)
*/
const QString _configDir = QString("%1/.config/robomongo/0.9").arg(QDir::homePath());
}
namespace Robomongo
{
/**
* @brief Robomongo config. files
*/
const auto CONFIG_FILE_0_8_5 = QString("%1/.config/robomongo/robomongo.json").arg(QDir::homePath());
const auto CONFIG_FILE_0_9 = QString("%1/.config/robomongo/0.9/robomongo.json").arg(QDir::homePath());
const auto CONFIG_FILE_1_0_RC1 = QString("%1/.config/robomongo/1.0/robomongo.json").arg(QDir::homePath());
// Change of path due to re-branding to 3T
const auto CONFIG_FILE_1_0_0 = QString("%1/.3T/robomongo/1.0.0/robomongo.json").arg(QDir::homePath());
struct ConfigFileAndImportFunction
{
ConfigFileAndImportFunction(QString const& configFile, std::function<bool()> importFunction)
: file(configFile), import(importFunction) {}
QString file;
std::function<bool()> import;
};
// Warning: Config. file and import function of a new release must be placed as first element of
// vector initializer list below in order.
std::vector<ConfigFileAndImportFunction> const SettingsManager::_configFilesAndImportFunctions
{
ConfigFileAndImportFunction(CONFIG_FILE_1_0_RC1, SettingsManager::importConnectionsFrom_1_0_RC1_to_1_0_0),
ConfigFileAndImportFunction(CONFIG_FILE_0_9, SettingsManager::importConnectionsFrom_0_9_to_1_0_RC1),
ConfigFileAndImportFunction(CONFIG_FILE_0_8_5, SettingsManager::importConnectionsFrom_0_8_5_to_0_9)
};
std::vector<ConnectionSettings*> SettingsManager::_connections;
// Temporarily disabling Recent Connections feature
// std::vector<RecentConnection> SettingsManager::_recentConnections;
/**
* Creates SettingsManager for config file in default location
* ~/.config/robomongo/robomongo.json
@@ -43,20 +103,28 @@ namespace Robomongo
_timeZone(Utc),
_viewMode(Robomongo::Tree),
_autocompletionMode(AutocompleteAll),
_batchSize(50),
_loadMongoRcJs(false),
_minimizeToTray(false),
_lineNumbers(false),
_disableConnectionShortcuts(false),
_batchSize(50),
_textFontFamily(""),
_textFontPointSize(-1),
_lineNumbers(false),
_loadMongoRcJs(false)
_mongoTimeoutSec(10),
_shellTimeoutSec(15),
_imported(false)
{
load();
LOG_MSG("SettingsManager initialized in " + _configPath, mongo::logger::LogSeverity::Info(), false);
if (!load()) { // if load fails (probably due to non-existing config. file or directory)
save(); // create empty settings file
load(); // try loading again for the purpose of import from previous Robomongo versions
}
LOG_MSG("SettingsManager initialized in " + ConfigFilePath, mongo::logger::LogSeverity::Info(), false);
}
SettingsManager::~SettingsManager()
{
std::for_each(_connections.begin() ,_connections.end(), stdutils::default_delete<ConnectionSettings *>());
std::for_each(_connections.begin(), _connections.end(), stdutils::default_delete<ConnectionSettings *>());
}
/**
@@ -65,10 +133,10 @@ namespace Robomongo
*/
bool SettingsManager::load()
{
if (!QFile::exists(_configPath))
if (!QFile::exists(ConfigFilePath))
return false;
QFile f(_configPath);
QFile f(ConfigFilePath);
if (!f.open(QIODevice::ReadOnly))
return false;
@@ -91,14 +159,14 @@ namespace Robomongo
{
QVariantMap map = convertToMap();
if (!QDir().mkpath(_configDir)) {
LOG_MSG("ERROR: Could not create settings path: " + _configDir, mongo::logger::LogSeverity::Error());
if (!QDir().mkpath(ConfigDir)) {
LOG_MSG("ERROR: Could not create settings path: " + ConfigDir, mongo::logger::LogSeverity::Error());
return false;
}
QFile f(_configPath);
QFile f(ConfigFilePath);
if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
LOG_MSG("ERROR: Could not write settings to: " + _configPath, mongo::logger::LogSeverity::Error());
LOG_MSG("ERROR: Could not write settings to: " + ConfigFilePath, mongo::logger::LogSeverity::Error());
return false;
}
@@ -107,11 +175,21 @@ namespace Robomongo
s.setIndentMode(QJson::IndentFull);
s.serialize(map, &f, &ok);
LOG_MSG("Settings saved to: " + _configPath, mongo::logger::LogSeverity::Info());
LOG_MSG("Settings saved to: " + ConfigFilePath, mongo::logger::LogSeverity::Info());
return ok;
}
void SettingsManager::addCacheData(QString const& key, QVariant const& value)
{
_cacheData.insert(key, value);
}
QVariant SettingsManager::cacheData(QString const& key) const
{
return _cacheData.value(key);
}
/**
* Load settings from the map. Existings settings will be overwritten.
*/
@@ -125,7 +203,7 @@ namespace Robomongo
if (encoding > 3 || encoding < 0)
encoding = 0;
_uuidEncoding = (UUIDEncoding) encoding;
_uuidEncoding = (UUIDEncoding)encoding;
// 3. Load view mode
@@ -133,36 +211,41 @@ namespace Robomongo
int viewMode = map.value("viewMode").toInt();
if (viewMode > 2 || viewMode < 0)
viewMode = Custom; // Default View Mode
_viewMode = (ViewMode) viewMode;
} else {
_viewMode = (ViewMode)viewMode;
}
else {
_viewMode = Custom; // Default View Mode
}
_autoExpand = map.contains("autoExpand") ?
map.value("autoExpand").toBool() : true;
_autoExec = map.contains("autoExec") ?
map.value("autoExec").toBool() : true;
_lineNumbers = map.contains("lineNumbers") ?
map.value("lineNumbers").toBool() : false;
_autoExpand = map.contains("autoExpand") ? map.value("autoExpand").toBool() : true;
_autoExec = map.contains("autoExec") ? map.value("autoExec").toBool() : true;
_minimizeToTray = map.contains("minimizeToTray") ? map.value("minimizeToTray").toBool() : false;
_lineNumbers = map.contains("lineNumbers") ? map.value("lineNumbers").toBool() : false;
_imported = map.contains("imported") ? map.value("imported").toBool() : false;
// 4. Load TimeZone
int timeZone = map.value("timeZone").toInt();
if (timeZone > 1 || timeZone < 0)
timeZone = 0;
_timeZone = (SupportedTimes) timeZone;
_timeZone = (SupportedTimes)timeZone;
_loadMongoRcJs = map.value("loadMongoRcJs").toBool();
_disableConnectionShortcuts = map.value("disableConnectionShortcuts").toBool();
if (map.contains("acceptedEulaVersions"))
_acceptedEulaVersions = map.value("acceptedEulaVersions").toStringList().toSet();
// Load anonymousID
_anonymousID = getOrCreateAnonymousID(map);
// Load AutocompletionMode
if (map.contains("autocompletionMode")) {
int autocompletionMode = map.value("autocompletionMode").toInt();
if (autocompletionMode < 0 || autocompletionMode > 2)
autocompletionMode = AutocompleteAll; // Default Mode
_autocompletionMode = (AutocompletionMode) autocompletionMode;
} else {
_autocompletionMode = (AutocompletionMode)autocompletionMode;
}
else {
_autocompletionMode = AutocompleteAll; // Default Mode
}
@@ -170,6 +253,10 @@ namespace Robomongo
_batchSize = map.value("batchSize").toInt();
if (_batchSize == 0)
_batchSize = 50;
if (map.contains("checkForUpdates"))
_checkForUpdates = map.value("checkForUpdates").toBool();
_currentStyle = map.value("style").toString();
if (_currentStyle.isEmpty()) {
_currentStyle = AppStyle::StyleName;
@@ -179,32 +266,59 @@ namespace Robomongo
_textFontFamily = map.value("textFontFamily").toString();
_textFontPointSize = map.value("textFontPointSize").toInt();
if (map.contains("mongoTimeoutSec")) {
_mongoTimeoutSec = map.value("mongoTimeoutSec").toInt();
}
if (map.contains("shellTimeoutSec")) {
_shellTimeoutSec = map.value("shellTimeoutSec").toInt();
}
// 5. Load connections
_connections.clear();
QVariantList list = map.value("connections").toList();
for (QVariantList::iterator it = list.begin(); it != list.end(); ++it) {
ConnectionSettings *record = new ConnectionSettings();
record->fromVariant((*it).toMap());
_connections.push_back(record);
QVariantList const& list = map.value("connections").toList();
for (auto const& conn : list) {
auto connSettings = new ConnectionSettings(false);
connSettings->fromVariant(conn.toMap());
addConnection(connSettings);
}
/* Temporarily disabling Recent Connections feature
// Load recent connections
_recentConnections.clear();
QVariantList const& rlist = map.value("recentConnections").toList();
for (auto const& rconn : rlist) {
_recentConnections.push_back(RecentConnection(rconn.toMap().value("uuid").toString(),
rconn.toMap().value("name").toString().toStdString()));
}
*/
_toolbars = map.value("toolbars").toMap();
ToolbarSettingsContainerType::const_iterator it = _toolbars.find("connect");
if (_toolbars.end() == it)
_toolbars["connect"] = true;
it = _toolbars.find("open_save");
if (_toolbars.end() == it)
_toolbars["open_save"] = true;
it = _toolbars.find("exec");
if (_toolbars.end() == it)
_toolbars["exec"] = true;
it = _toolbars.find("explorer");
if (_toolbars.end() == it)
_toolbars["explorer"] = true;
it = _toolbars.find("logs");
if (_toolbars.end() == it)
_toolbars["logs"] = false;
_cacheData = map.value("cacheData").toMap();
// Load connection settings from previous versions of Robomongo
importConnections();
}
/**
@@ -236,36 +350,84 @@ namespace Robomongo
// 7. Save disableConnectionShortcuts
map.insert("disableConnectionShortcuts", _disableConnectionShortcuts);
// 8. Save acceptedEulaVersions array
QJsonArray arr;
for (auto const& str : _acceptedEulaVersions)
arr.push_back(str);
// 8. Save batchSize
map.insert("acceptedEulaVersions", arr.toVariantList());
// 9. Save batchSize
map.insert("batchSize", _batchSize);
map.insert("checkForUpdates", _checkForUpdates);
map.insert("mongoTimeoutSec", _mongoTimeoutSec);
map.insert("shellTimeoutSec", _shellTimeoutSec);
// 9. Save style
// 10. Save style
map.insert("style", _currentStyle);
// 10. Save font information
// 11. Save font information
map.insert("textFontFamily", _textFontFamily);
map.insert("textFontPointSize", _textFontPointSize);
// 11. Save connections
// 12. Save connections
QVariantList list;
for (ConnectionSettingsContainerType::const_iterator it = _connections.begin(); it!=_connections.end(); ++it) {
QVariantMap rm = (*it)->toVariant().toMap();
list.append(rm);
}
for (auto const conn : _connections)
list.append(conn->toVariant().toMap());
map.insert("connections", list);
map.insert("autoExec", _autoExec);
/* Temporarily disabling Recent Connections feature
// 13. Save recent connections
QVariantList recentConnsList;
QVariantMap recentConnMap;
for (auto const& rconn : _recentConnections) {
recentConnMap.insert("uuid", rconn.uuid);
recentConnMap.insert("name", QString::fromStdString(rconn.name));
recentConnsList.append(recentConnMap);
}
map.insert("recentConnections", recentConnsList);
*/
map.insert("autoExec", _autoExec);
map.insert("minimizeToTray", _minimizeToTray);
map.insert("toolbars", _toolbars);
map.insert("imported", _imported);
map.insert("anonymousID", _anonymousID);
map.insert("cacheData", _cacheData);
return map;
}
QString SettingsManager::getOrCreateAnonymousID(QVariantMap const& map) const
{
QString anonymousID = "";
// If anonymousID has never been created or is empty, create a new one. Otherwise load the existing.
if (map.contains("anonymousID")) {
QUuid id = map.value("anonymousID").toString();
if (!id.isNull())
anonymousID = id.toString();
}
for (auto const& zipFileAndConfigFile : S_3T_ZipFile_And_ConfigFile_List) {
if (!anonymousID.isEmpty())
break;
QUuid const& id = extractAnonymousID(zipFileAndConfigFile.first, zipFileAndConfigFile.second);
if (!id.isNull())
anonymousID = id.toString();
}
if (anonymousID.isEmpty())
anonymousID = QUuid::createUuid().toString();
return anonymousID;
}
/**
* Adds connection to the end of list
* Adds connection to the end of list and set it's uniqueID
*/
void SettingsManager::addConnection(ConnectionSettings *connection)
{
@@ -277,13 +439,57 @@ 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;
}
}
/* Temporarily disabling Recent Connections feature
void SettingsManager::addRecentConnection(ConnectionSettings *connection)
{
_recentConnections.push_back(RecentConnection(connection->uuid(), connection->connectionName()));
}
void SettingsManager::deleteRecentConnection(ConnectionSettings *conn)
{
for (int i = 0; i < _recentConnections.size(); ++i) {
if (_recentConnections[i].uuid == conn->uuid())
_recentConnections.erase(_recentConnections.begin() + i);
}
}
void SettingsManager::setRecentConnections(std::vector<ConnectionSettings const*> const& recentConns)
{
_recentConnections.clear();
for(auto rconn : recentConns)
_recentConnections.push_back(RecentConnection(rconn->uuid(), rconn->connectionName()));
}
void SettingsManager::clearRecentConnections()
{
_recentConnections.clear();
}
*/
ConnectionSettings* SettingsManager::getConnectionSettingsByUuid(QString const& uuid) const
{
for (auto const connSettings : _connections){
if (connSettings->uuid() == uuid)
return connSettings;
}
LOG_MSG("Failed to find connection settings object by UUID.", mongo::logger::LogSeverity::Warning());
return nullptr;
}
ConnectionSettings* SettingsManager::getConnectionSettingsByUuid(std::string const& uuid) const
{
return getConnectionSettingsByUuid(QString::fromStdString(uuid));
}
void SettingsManager::setCurrentStyle(const QString& style)
{
_currentStyle = style;
@@ -308,4 +514,198 @@ namespace Robomongo
_toolbars[toolbarName] = visible;
}
void SettingsManager::importConnections()
{
// Skip when settings already imported
if (_imported)
return;
// Find and import 'only' from the latest version
for (auto const& configFileAndImportFunction : _configFilesAndImportFunctions) {
if (QFile::exists(configFileAndImportFunction.file)) {
configFileAndImportFunction.import();
setImported(true); // Mark as imported
return;
}
}
}
bool SettingsManager::importConnectionsFrom_0_8_5_to_0_9()
{
// Load old configuration file (used till version 0.8.5)
if (!QFile::exists(CONFIG_FILE_0_8_5))
return false;
QFile oldConfigFile(CONFIG_FILE_0_8_5);
if (!oldConfigFile.open(QIODevice::ReadOnly))
return false;
bool ok;
QJson::Parser parser;
QVariantMap vmap = parser.parse(oldConfigFile.readAll(), &ok).toMap();
if (!ok)
return false;
QVariantList vconns = vmap.value("connections").toList();
for (QVariantList::iterator itconn = vconns.begin(); itconn != vconns.end(); ++itconn)
{
QVariantMap vconn = (*itconn).toMap();
auto conn = new ConnectionSettings(false);
conn->setImported(true);
conn->setConnectionName(QtUtils::toStdString(vconn.value("connectionName").toString()));
conn->setServerHost(QtUtils::toStdString(vconn.value("serverHost").toString().left(300)));
conn->setServerPort(vconn.value("serverPort").toInt());
conn->setDefaultDatabase(QtUtils::toStdString(vconn.value("defaultDatabase").toString()));
// SSH settings
if (vconn.contains("sshAuthMethod")) {
SshSettings *ssh = conn->sshSettings();
ssh->setHost(QtUtils::toStdString(vconn.value("sshHost").toString()));
ssh->setUserName(QtUtils::toStdString(vconn.value("sshUserName").toString()));
ssh->setPort(vconn.value("sshPort").toInt());
ssh->setUserPassword(QtUtils::toStdString(vconn.value("sshUserPassword").toString()));
ssh->setPublicKeyFile(QtUtils::toStdString(vconn.value("sshPublicKey").toString()));
ssh->setPrivateKeyFile(QtUtils::toStdString(vconn.value("sshPrivateKey").toString()));
ssh->setPassphrase(QtUtils::toStdString(vconn.value("sshPassphrase").toString()));
int auth = vconn.value("sshAuthMethod").toInt();
ssh->setEnabled(auth == 1 || auth == 2);
ssh->setAuthMethod(auth == 2 ? "publickey" : "password");
}
// SSL settings
if (vconn.contains("sshEnabled")) {
SslSettings *ssl = conn->sslSettings();
ssl->enableSSL(vconn.value("enabled").toBool());
ssl->setPemKeyFile(QtUtils::toStdString(vconn.value("sslPemKeyFile").toString()));
}
// Credentials
QVariantList vcreds = vconn.value("credentials").toList();
for (QVariantList::const_iterator itcred = vcreds.begin(); itcred != vcreds.end(); ++itcred) {
QVariantMap vcred = (*itcred).toMap();
auto cred = new CredentialSettings();
cred->setUserName(QtUtils::toStdString(vcred.value("userName").toString()));
cred->setUserPassword(QtUtils::toStdString(vcred.value("userPassword").toString()));
cred->setDatabaseName(QtUtils::toStdString(vcred.value("databaseName").toString()));
cred->setMechanism("MONGODB-CR");
cred->setEnabled(vcred.value("enabled").toBool());
conn->addCredential(cred);
}
// Check that we didn't have similar connection
bool matched = false;
for (std::vector<ConnectionSettings*>::const_iterator it = _connections.begin(); it != _connections.end(); ++it) {
ConnectionSettings *econn = *it; // Existing connection
if (conn->serverPort() != econn->serverPort() ||
conn->serverHost() != econn->serverHost() ||
conn->defaultDatabase() != econn->defaultDatabase())
continue;
CredentialSettings *cred = conn->primaryCredential();
CredentialSettings *ecred = econn->primaryCredential();
if (cred->databaseName() != ecred->databaseName() ||
cred->userName() != ecred->userName() ||
cred->userPassword() != ecred->userPassword() ||
cred->enabled() != ecred->enabled())
continue;
SshSettings *ssh = conn->sshSettings();
SshSettings *essh = econn->sshSettings();
if (ssh->enabled() != essh->enabled() ||
ssh->port() != essh->port() ||
ssh->host() != essh->host() ||
ssh->privateKeyFile() != essh->privateKeyFile() ||
ssh->userPassword() != essh->userPassword() ||
ssh->userName() != essh->userName())
continue;
matched = true;
break;
}
// Import connection only if we didn't find similar one
if (!matched)
addConnection(conn);
}
return true;
}
bool SettingsManager::importConnectionsFrom_0_9_to_1_0_RC1()
{
return importConnectionsFromOldVersion(CONFIG_FILE_0_9);
}
bool SettingsManager::importConnectionsFrom_1_0_RC1_to_1_0_0()
{
return importConnectionsFromOldVersion(CONFIG_FILE_1_0_RC1);
}
bool SettingsManager::importConnectionsFromOldVersion(QString const& oldConfigFilePath)
{
if (!QFile::exists(oldConfigFilePath))
return false;
QFile oldConfigFile(oldConfigFilePath);
if (!oldConfigFile.open(QIODevice::ReadOnly))
return false;
bool ok;
QJson::Parser parser;
QVariantMap vmap = parser.parse(oldConfigFile.readAll(), &ok).toMap();
if (!ok)
return false;
QVariantList const& vconns = vmap.value("connections").toList();
for (auto const& vcon : vconns)
{
QVariantMap const& vconn = vcon.toMap();
auto connSettings = new ConnectionSettings(false);
connSettings->fromVariant(vconn);
connSettings->setImported(true);
addConnection(connSettings);
}
return true;
}
int SettingsManager::importedConnectionsCount() {
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;
}
QString extractAnonymousID(QString const& zipFile, QString const& propfile)
{
QZipReader zipReader(zipFile);
if (!zipReader.exists() || !zipReader.isReadable())
return QString("");
QXmlStreamReader reader(zipReader.fileData(propfile));
while (!reader.atEnd()) {
reader.readNext();
if (reader.text().toString() == "AnonymousID") {
reader.readNext();
reader.readNext();
reader.readNext();
reader.readNext();
return reader.text().toString();
}
}
return QString("");
}
}
+134 -12
View File
@@ -2,12 +2,46 @@
#include <QString>
#include <QVariantMap>
#include <QSet>
#include <QDir>
#include <vector>
#include "robomongo/core/Enums.h"
namespace Robomongo
{
class ConnectionSettings;
struct ConfigFileAndImportFunction;
// Current cache directory
QString const CacheDir = QString("%1/.3T/robomongo/%2/cache/").arg(QDir::homePath())
.arg(PROJECT_VERSION);
// Current config file
QString const ConfigFilePath = QString("%1/.3T/robomongo/%2/robomongo.json").arg(QDir::homePath())
.arg(PROJECT_VERSION);
// Current config file directory
QString const ConfigDir = QString("%1/.3T/robomongo/%2/").arg(QDir::homePath())
.arg(PROJECT_VERSION);
/* Temporarily disabling Recent Connections feature
struct RecentConnection
{
RecentConnection(QString const& uuid, std::string const& name)
: uuid(uuid), name(name) {}
// Not directly used but required by std::vector<T>::erase()
RecentConnection& operator=(const RecentConnection& other)
{
uuid = other.uuid;
name = other.name;
return *this;
}
QString uuid;
std::string name;
};
*/
/* ----------------------------- SettingsManager ------------------------------ */
/**
* @brief SettingsManager gives you access to all settings, that is used
* by Robomongo. It can load() and save() them. Config file usually
@@ -23,6 +57,7 @@ namespace Robomongo
public:
typedef std::vector<ConnectionSettings *> ConnectionSettingsContainerType;
typedef QMap<QString, QVariant> ToolbarSettingsContainerType;
/**
* @brief Creates SettingsManager for config file in default location
* (usually ~/.config/robomongo/robomongo.json)
@@ -50,13 +85,32 @@ namespace Robomongo
* @brief Adds connection to the end of list.
* Connection now will be owned by SettingsManager.
*/
void addConnection(ConnectionSettings *connection);
static void addConnection(ConnectionSettings *connection);
/**
* @brief Removes connection by index
*/
void removeConnection(ConnectionSettings *connection);
/**
* @brief Functions of Recent Connections feature
*/
/* Temporarily disabling Recent Connections feature
static void addRecentConnection(ConnectionSettings *connection);
static void deleteRecentConnection(ConnectionSettings *connection);
static void setRecentConnections(std::vector<ConnectionSettings const*> const& recentConns);
static void clearRecentConnections();
*/
/**
* @brief Finds and returns original (non-clone) connection settings which is
* loaded/saved from/into Robomongo config. file.
* @return If uniqueID is valid returns original connection settings,
* nullptr otherwise.
*/
ConnectionSettings* getConnectionSettingsByUuid(QString const& uuid) const;
ConnectionSettings* getConnectionSettingsByUuid(std::string const& uuid) const;
void reorderConnections(const ConnectionSettingsContainerType &connections);
void setToolbarSettings(QString toolbarName, bool visible);
@@ -65,6 +119,9 @@ namespace Robomongo
* @brief Returns list of connections
*/
ConnectionSettingsContainerType connections() const { return _connections; }
// Temporarily disabling Recent Connections feature
// std::vector<RecentConnection>& recentConnections() const { return _recentConnections; }
ToolbarSettingsContainerType toolbars() const { return _toolbars; }
@@ -72,7 +129,7 @@ namespace Robomongo
UUIDEncoding uuidEncoding() const { return _uuidEncoding; }
void setTimeZone(SupportedTimes timeZ) { _timeZone = timeZ; }
SupportedTimes timeZone() const { return _timeZone;}
SupportedTimes timeZone() const { return _timeZone; }
void setViewMode(ViewMode viewMode) { _viewMode = viewMode; }
ViewMode viewMode() const { return _viewMode; }
@@ -86,6 +143,9 @@ namespace Robomongo
void setAutoExec(bool isAutoExec) { _autoExec = isAutoExec; }
bool autoExec() const { return _autoExec; }
void setMinimizeToTray(bool isMinimizingToTray) { _minimizeToTray = isMinimizingToTray; }
bool minimizeToTray() const { return _minimizeToTray; }
void setLineNumbers(bool showLineNumbers) { _lineNumbers = showLineNumbers; }
bool lineNumbers() const { return _lineNumbers; }
@@ -95,10 +155,16 @@ namespace Robomongo
void setDisableConnectionShortcuts(bool isDisable) { _disableConnectionShortcuts = isDisable; }
bool disableConnectionShortcuts() const { return _disableConnectionShortcuts; }
void addAcceptedEulaVersion(QString const& version) { _acceptedEulaVersions.insert(version); }
QSet<QString> const& acceptedEulaVersions() const { return _acceptedEulaVersions; }
void setCheckForUpdates(bool checkForUpdates) { _checkForUpdates = checkForUpdates; }
bool checkForUpdates() const { return _checkForUpdates; }
void setBatchSize(int batchSize) { _batchSize = batchSize; }
int batchSize() const { return _batchSize; }
QString currentStyle() const {return _currentStyle; }
QString currentStyle() const { return _currentStyle; }
void setCurrentStyle(const QString& style);
QString textFontFamily() const { return _textFontFamily; }
@@ -107,47 +173,103 @@ 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; }
QString anonymousID() const { return _anonymousID; }
void addCacheData(QString const& key, QVariant const& value);
QVariant cacheData(QString const& key) const;
/**
* Returns number of imported connections
*/
int importedConnectionsCount();
private:
/**
* @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;
// Find existing anonymousID from Robomongo and 3T config files, if not found create
// a new anonymousID.
QString getOrCreateAnonymousID(QVariantMap const& map) const;
/**
* Load connection settings from previous versions of Robomongo
*/
void importConnections();
// Imports connections from oldConfigFilePath into current config file
static bool importConnectionsFromOldVersion(QString const& oldConfigFilePath);
static bool importConnectionsFrom_0_8_5_to_0_9();
static bool importConnectionsFrom_0_9_to_1_0_RC1();
static bool importConnectionsFrom_1_0_RC1_to_1_0_0();
/**
* @brief Version of settings schema currently loaded
*/
QString _version;
/**
* @brief UUID encoding
*/
UUIDEncoding _uuidEncoding;
SupportedTimes _timeZone;
/**
* @brief view mode
*/
ViewMode _viewMode;
AutocompletionMode _autocompletionMode;
bool _loadMongoRcJs;
bool _autoExpand;
bool _autoExec;
bool _minimizeToTray;
bool _lineNumbers;
bool _disableConnectionShortcuts;
QSet<QString> _acceptedEulaVersions;
int _batchSize;
bool _checkForUpdates = true;
QString _currentStyle;
QString _textFontFamily;
int _textFontPointSize;
int _mongoTimeoutSec;
int _shellTimeoutSec;
// True when settings from previous versions of Robomongo are imported
bool _imported;
/**
* @brief This is an anonymous string taken from QUuid that is generated when Robomongo
* is first installed on a user's machine and then launched for the first time
* It stays the same throughout all upgrades.
*/
QString _anonymousID;
// Various cache data
QMap<QString, QVariant> _cacheData;
/**
* @brief List of connections
*/
ConnectionSettingsContainerType _connections;
static std::vector<ConnectionSettings*> _connections;
/* Temporarily disabling Recent Connections feature
// List of recent connections that are shown on Welcome Tab
static std::vector<RecentConnection> _recentConnections;
*/
ToolbarSettingsContainerType _toolbars;
// List of config. file absolute paths and related import functions.
// Must be updated with care and with every new version. Details on cpp file.
static const std::vector<ConfigFileAndImportFunction> _configFilesAndImportFunctions;
};
}
+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;
};
}
+45 -32
View File
@@ -80,23 +80,25 @@ 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 ? "]" : "}");
@@ -109,7 +111,8 @@ namespace Robomongo
return s.str();
}
std::string jsonString(const BSONElement &elem, JsonStringFormat format, bool includeFieldNames, int pretty, UUIDEncoding uuidEncoding, SupportedTimes timeFormat, bool isArray)
std::string jsonString(const BSONElement &elem, JsonStringFormat format, bool includeFieldNames,
int pretty, UUIDEncoding uuidEncoding, SupportedTimes timeFormat, bool isArray)
{
using namespace std;
BSONType t = elem.type();
@@ -130,15 +133,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 +175,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 +189,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 +199,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 +269,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 +284,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);
@@ -325,12 +334,11 @@ namespace Robomongo
break;
case bsonTimestamp:
if ( format == TenGen ) {
s << "Timestamp(" << ( elem.timestampValue() / 1000 ) << ", " << elem.timestampInc() << ")";
}
else {
s << "{ \"$timestamp\" : { \"t\" : " << ( elem.timestampValue() / 1000 ) << ", \"i\" : " << elem.timestampInc() << " } }";
}
if ( format == TenGen )
s << "Timestamp(" << elem.timestamp().getSecs() << ", " << elem.timestampInc() << ")";
else
s << "{ \"$timestamp\" : { \"t\" : " << elem.timestamp().getSecs() << ", \"i\" : "
<< elem.timestampInc() << " } }";
break;
case MinKey:
@@ -563,13 +571,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 +586,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 +639,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 +665,7 @@ namespace Robomongo
case 'g':
case 'i':
case 'm':
con+=*f;
con += *f;
default:
break;
}
@@ -678,15 +691,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 +708,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 +719,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 +735,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);
+14 -3
View File
@@ -2,13 +2,16 @@
#include <QDir>
#include <QMetaType>
#include "robomongo/core/utils/QtUtils.h"
namespace
{
std::string getLoggerPath()
{
static std::string path = Robomongo::QtUtils::toStdString(QString("%1/" PROJECT_NAME_LOWERCASE ".log").arg(QDir::tempPath()));
static std::string path =
Robomongo::QtUtils::toStdString(QString("%1/" PROJECT_NAME_LOWERCASE ".log").arg(QDir::tempPath()));
return path;
}
}
@@ -46,7 +49,15 @@ namespace Robomongo
{
// v0.9
// LOG(level) << "[" PROJECT_NAME_TITLE "] " << QtUtils::toStdString(mess) << std::endl;
if (notify)
emit printed(mess, level);
if (notify) {
// Make uniform log level strings e.g "Error: ", "Info: " etc...
auto logLevelStr = QString::fromStdString(level.toStringData().toString());
if (!logLevelStr.isEmpty()) {
logLevelStr = logLevelStr.toLower();
logLevelStr[0] = logLevelStr[0].toUpper();
logLevelStr += ": ";
}
emit printed(logLevelStr + mess, level);
}
}
}
+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;
};
}
+127 -8
View File
@@ -38,6 +38,36 @@ namespace Robomongo
return serverIc;
}
const QIcon &GuiRegistry::serverImportedIcon() const
{
static const QIcon importedIcon = QIcon(":/robomongo/icons/server_imported_16x16.png");
return importedIcon;
}
const QIcon &GuiRegistry::serverPrimaryIcon() const
{
static const QIcon icon(":/robomongo/icons/server_primary_16x16.png");
return icon;
}
const QIcon &GuiRegistry::serverSecondaryIcon() const
{
static const QIcon icon(":/robomongo/icons/server_secondary_16x16.png");
return icon;
}
const QIcon &GuiRegistry::replicaSetIcon() const
{
static const QIcon replicaSetIc(":/robomongo/icons/replica_set_16x16.png");
return replicaSetIc;
}
const QIcon &GuiRegistry::replicaSetOfflineIcon() const
{
static const QIcon icon(":/robomongo/icons/replica_set_offline_16x16.png");
return icon;
}
const QIcon &GuiRegistry::openIcon() const
{
static const QIcon openIc = qApp->style()->standardIcon(QStyle::SP_DialogOpenButton);
@@ -84,14 +114,26 @@ namespace Robomongo
const QIcon &GuiRegistry::maximizeIcon() const
{
static const QIcon maximizeIc = QIcon(":/robomongo/icons/maximize.gif");
static const QIcon maximizeIc(":/robomongo/icons/maximize.png");
return maximizeIc;
}
const QIcon &GuiRegistry::maximizeHighlightedIcon() const
const QIcon &GuiRegistry::minimizeIcon() const
{
static const QIcon maximizeHighlightedIc = QIcon(":/robomongo/icons/maximize_highlighted_16x16.png");
return maximizeHighlightedIc;
static const QIcon minimizeIc(":/robomongo/icons/minimize.png");
return minimizeIc;
}
const QIcon &GuiRegistry::undockIcon() const
{
static const QIcon undockIc(":/robomongo/icons/undock.png");
return undockIc;
}
const QIcon &GuiRegistry::dockIcon() const
{
static const QIcon dockIc(":/robomongo/icons/dock.png");
return dockIc;
}
const QIcon &GuiRegistry::textIcon() const
@@ -223,16 +265,28 @@ namespace Robomongo
const QIcon &GuiRegistry::noMarkIcon() const
{
static const QIcon noMarkIc = QIcon(":/robomongo/icons/no_mark_16x16.png");
static const QIcon noMarkIc = QIcon(":/robomongo/icons/no_mark_24x24.png");
return noMarkIc;
}
const QIcon &GuiRegistry::yesMarkIcon() const
{
static const QIcon yesMarkIc = QIcon(":/robomongo/icons/yes_mark_16x16.png");
static const QIcon yesMarkIc = QIcon(":/robomongo/icons/yes_mark_24x24.png");
return yesMarkIc;
}
const QIcon &GuiRegistry::skipMarkIcon() const
{
static const QIcon skipMarkIc = QIcon(":/robomongo/icons/skip_mark_24x24.png");
return skipMarkIc;
}
const QIcon &GuiRegistry::questionMarkIcon() const
{
static const QIcon questionMarkIc = QIcon(":/robomongo/icons/question_mark_24x24.png");
return questionMarkIc;
}
const QIcon &GuiRegistry::timeIcon() const
{
static const QIcon timeIc = QIcon(":/robomongo/icons/time_16x16.png");
@@ -245,9 +299,33 @@ namespace Robomongo
return keyIc;
}
const QIcon &GuiRegistry::showIcon() const
{
static const QIcon icon = QIcon(":/robomongo/icons/show_64x64.png");
return icon;
}
const QIcon &GuiRegistry::hideIcon() const
{
static const QIcon icon = QIcon(":/robomongo/icons/hide_64x64.png");
return icon;
}
const QIcon &GuiRegistry::plusIcon() const
{
static const QIcon icon {":/robomongo/icons/plus_sign.png"};
return icon;
}
const QIcon &GuiRegistry::minusIcon() const
{
static const QIcon icon {":/robomongo/icons/minus_sign.png"};
return icon;
}
const QBrush &GuiRegistry::typeBrush() const
{
static const QBrush typeBrush = QBrush(QColor(150,150, 150));
static const QBrush typeBrush = QBrush(QColor(150, 150, 150));
return typeBrush;
}
@@ -269,6 +347,12 @@ namespace Robomongo
static const QIcon mongodbIc = QIcon(":/robomongo/icons/mongodb_16x16.png");
return mongodbIc;
}
const QIcon &GuiRegistry::mongodbIconForMAC() const
{
static const QIcon mongodbIc = QIcon(":/robomongo/icons/mongodb_icon_for_MAC.png");
return mongodbIc;
}
const QIcon &GuiRegistry::connectIcon() const
{
@@ -288,13 +372,48 @@ namespace Robomongo
return stopIc;
}
const QIcon &GuiRegistry::exportIcon() const
{
static const QIcon exportIc = QIcon(":/robomongo/icons/export_64x64.png");
return exportIc;
}
const QIcon &GuiRegistry::importIcon() const
{
static const QIcon importIc = QIcon(":/robomongo/icons/import_64x64.png");
return importIc;
}
const QIcon &GuiRegistry::deleteIcon() const
{
static const QIcon icon(":/robomongo/icons/delete.png");
return icon;
}
const QIcon &GuiRegistry::deleteIconRed() const
{
static const QIcon icon(":/robomongo/icons/delete_red_color.png");
return icon;
}
const QIcon &GuiRegistry::deleteIconMouseHovered() const
{
static const QIcon icon(":/robomongo/icons/delete_mouse_hovered.png");
return icon;
}
const QIcon &GuiRegistry::mainWindowIcon() const
{
static const QIcon mainWindowIc = QIcon(":/robomongo/icons/logo-256x256.png");
// static const QIcon mainWindowIc = QIcon(":/robomongo/icons/main_window_icon.png");
return mainWindowIc;
}
const QIcon& GuiRegistry::welcomeTabIcon() const
{
static const QIcon icon(":/robomongo/icons/welcome_tab_icon.png");
return icon;
}
const QFont &GuiRegistry::font() const
{
QString family = AppRegistry::instance().settingsManager()->textFontFamily();
+65 -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,72 @@ namespace Robomongo
/**
* @brief Functions that provide access to various icons
*/
const QIcon &serverIcon() const;
const QIcon &saveIcon() const;
const QIcon &openIcon() const;
const QIcon &databaseIcon() const;
const QIcon &collectionIcon() const;
const QIcon &indexIcon() const;
const QIcon &userIcon() const;
const QIcon &functionIcon() const;
const QIcon &maximizeIcon() const;
const QIcon &maximizeHighlightedIcon() const;
const QIcon &textIcon() const;
const QIcon &textHighlightedIcon() const;
const QIcon &treeIcon() const;
const QIcon &treeHighlightedIcon() const;
const QIcon &tableIcon() const;
const QIcon &tableHighlightedIcon() const;
const QIcon &customIcon() const;
const QIcon &customHighlightedIcon() const;
const QIcon &rotateIcon() const;
const QIcon &visualIcon() const;
const QIcon &folderIcon() const;
const QIcon &circleIcon() const;
const QIcon &leftIcon() const;
const QIcon &rightIcon() const;
const QIcon &mongodbIcon() const;
const QIcon &connectIcon() const;
const QIcon &executeIcon() const;
const QIcon &stopIcon() const;
const QIcon &mainWindowIcon() const;
const QIcon &bsonObjectIcon() const;
const QIcon &bsonArrayIcon() const;
const QIcon &bsonStringIcon() const;
const QIcon &bsonIntegerIcon() const;
const QIcon &bsonDoubleIcon() const;
const QIcon &bsonDateTimeIcon() const;
const QIcon &bsonBinaryIcon() const;
const QIcon &bsonNullIcon() const;
const QIcon &bsonBooleanIcon() const;
const QIcon &noMarkIcon() const;
const QIcon &yesMarkIcon() const;
const QIcon &timeIcon() const;
const QIcon &keyIcon() const;
const QIcon& serverIcon() const;
const QIcon& serverImportedIcon() const;
const QIcon& serverPrimaryIcon() const;
const QIcon& serverSecondaryIcon() const;
const QIcon& replicaSetIcon() const;
const QIcon& replicaSetOfflineIcon() const;
const QIcon& saveIcon() const;
const QIcon& openIcon() const;
const QIcon& databaseIcon() const;
const QIcon& collectionIcon() const;
const QIcon& indexIcon() const;
const QIcon& userIcon() const;
const QIcon& functionIcon() const;
const QIcon& maximizeIcon() const;
const QIcon& minimizeIcon() const;
const QIcon& undockIcon() const;
const QIcon& dockIcon() const;
const QIcon& textIcon() const;
const QIcon& textHighlightedIcon() const;
const QIcon& treeIcon() const;
const QIcon& treeHighlightedIcon() const;
const QIcon& tableIcon() const;
const QIcon& tableHighlightedIcon() const;
const QIcon& customIcon() const;
const QIcon& customHighlightedIcon() const;
const QIcon& rotateIcon() const;
const QIcon& visualIcon() const;
const QIcon& folderIcon() const;
const QIcon& circleIcon() const;
const QIcon& leftIcon() const;
const QIcon& rightIcon() const;
const QIcon& mongodbIcon() const;
const QIcon& mongodbIconForMAC() const;
const QIcon& connectIcon() const;
const QIcon& executeIcon() const;
const QIcon& stopIcon() const;
const QIcon& exportIcon() const;
const QIcon& importIcon() const;
const QIcon& deleteIcon() const;
const QIcon& deleteIconRed() const;
const QIcon& deleteIconMouseHovered() const;
const QIcon& mainWindowIcon() const;
const QIcon& bsonObjectIcon() const;
const QIcon& bsonArrayIcon() const;
const QIcon& bsonStringIcon() const;
const QIcon& bsonIntegerIcon() const;
const QIcon& bsonDoubleIcon() const;
const QIcon& bsonDateTimeIcon() const;
const QIcon& bsonBinaryIcon() const;
const QIcon& bsonNullIcon() const;
const QIcon& bsonBooleanIcon() const;
const QIcon& noMarkIcon() const;
const QIcon& yesMarkIcon() const;
const QIcon& skipMarkIcon() const;
const QIcon& questionMarkIcon() const;
const QIcon& timeIcon() const;
const QIcon& keyIcon() const;
const QIcon& showIcon() const;
const QIcon& hideIcon() const;
const QIcon& plusIcon() const;
const QIcon& minusIcon() const;
const QIcon& welcomeTabIcon() const;
const QBrush &typeBrush() const;
const QBrush& typeBrush() const;
const QFont &font() const;
const QFont& font() const;
private:
/**
* @brief Private, because this is singleton
+485 -47
View File
@@ -16,6 +16,13 @@
#include <QLabel>
#include <QStatusBar>
#include <QHBoxLayout>
#include <QSettings>
#include <QSystemTrayIcon>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include <QTextDocument>
#include <mongo/logger/log_severity.h>
#include "robomongo/core/settings/SettingsManager.h"
@@ -29,14 +36,20 @@
#include "robomongo/gui/widgets/LogWidget.h"
#include "robomongo/gui/widgets/explorer/ExplorerWidget.h"
#include "robomongo/gui/widgets/explorer/ExplorerCollectionTreeItem.h"
#include "robomongo/gui/widgets/explorer/ExplorerTreeWidget.h"
#include "robomongo/gui/widgets/workarea/WorkAreaTabWidget.h"
#include "robomongo/gui/widgets/workarea/QueryWidget.h"
#include "robomongo/gui/widgets/workarea/QueryWidget.h"
#include "robomongo/gui/widgets/workarea/WelcomeTab.h"
#include "robomongo/gui/dialogs/ConnectionsDialog.h"
#include "robomongo/gui/dialogs/AboutDialog.h"
#include "robomongo/gui/dialogs/PreferencesDialog.h"
#include "robomongo/gui/dialogs/ExportDialog.h"
#include "robomongo/gui/GuiRegistry.h"
#include "robomongo/gui/AppStyle.h"
namespace
{
void setToolBarIconSize(QToolBar *toolBar)
@@ -67,21 +80,32 @@ namespace
Robomongo::AppRegistry::instance().settingsManager()->save();
}
void saveMinimizeToTraySettings(bool isMinimizingToTray)
{
Robomongo::AppRegistry::instance().settingsManager()->setMinimizeToTray(isMinimizingToTray);
Robomongo::AppRegistry::instance().settingsManager()->save();
}
void saveLineNumbers(bool showLineNumbers)
{
Robomongo::AppRegistry::instance().settingsManager()->setLineNumbers(showLineNumbers);
Robomongo::AppRegistry::instance().settingsManager()->save();
}
}
}
/* End of anonymous namespace */
namespace Robomongo
{
/* -------------------------------- ConnectionMenu ---------------------------- */
class ConnectionMenu : public QMenu
{
public:
ConnectionMenu(QWidget *parent) : QMenu(parent) {}
protected:
virtual void keyPressEvent(QKeyEvent *event)
virtual void keyPressEvent(QKeyEvent *event) override
{
if (event->key() == Qt::Key_F12) {
hide();
@@ -92,12 +116,19 @@ namespace Robomongo
}
};
/* -------------------------------- MainWindow --------------------------------- */
MainWindow::MainWindow()
: BaseClass(),
_app(AppRegistry::instance().app()),
_workArea(NULL),
_connectionsMenu(NULL)
{
_logDock(nullptr), _workArea(nullptr), _explorer(nullptr), _app(AppRegistry::instance().app()),
_connectionsMenu(nullptr), _connectButton(nullptr), _viewMenu(nullptr), _toolbarsMenu(nullptr),
_connectAction(nullptr), _openAction(nullptr), _saveAction(nullptr), _saveAsAction(nullptr),
_executeAction(nullptr), _stopAction(nullptr), _orientationAction(nullptr), _execToolBar(nullptr),
//_exportAction(nullptr), _importAction(nullptr), // Temporarily disabling export/import feature
#if defined(Q_OS_WIN)
_trayIcon(nullptr),
#endif
_allowExit(false)
{
QColor background = palette().window().color();
QString controlKey = "Ctrl";
@@ -134,7 +165,7 @@ namespace Robomongo
// Exit action
QAction *exitAction = new QAction("&Exit", this);
exitAction->setShortcuts(QKeySequence::Quit);
VERIFY(connect(exitAction, SIGNAL(triggered()), this, SLOT(close())));
VERIFY(connect(exitAction, SIGNAL(triggered()), this, SLOT(exit())));
// Connect action
_connectAction = new QAction("&Connect...", this);
@@ -165,6 +196,26 @@ namespace Robomongo
QWidgetAction *connectButtonAction = new QWidgetAction(this);
connectButtonAction->setDefaultWidget(_connectButton);
// Tray icon
#if defined(Q_OS_WIN)
_trayIcon = new QSystemTrayIcon(GuiRegistry::instance().mainWindowIcon());
VERIFY(connect(_trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
this, SLOT(trayActivated(QSystemTrayIcon::ActivationReason))));
auto trayMinimizeAction = new QAction("Minimize to Tray", _trayIcon);
VERIFY(connect(trayMinimizeAction, SIGNAL(triggered()), this, SLOT(toggleMinimize())));
auto trayExitAction = new QAction("Exit", _trayIcon);
VERIFY(connect(trayExitAction, SIGNAL(triggered()), this, SLOT(exit())));
auto contextMenu = new QMenu();
contextMenu->addAction(trayMinimizeAction);
contextMenu->addSeparator();
contextMenu->addAction(trayExitAction);
_trayIcon->setContextMenu(contextMenu);
#endif
// Orientation action
_orientationAction = new QAction("&Rotate", this);
_orientationAction->setShortcut(Qt::Key_F10);
@@ -176,7 +227,7 @@ namespace Robomongo
ViewMode viewMode = AppRegistry::instance().settingsManager()->viewMode();
// Text mode action
QAction *textModeAction = new QAction("&Text Mode", this);
auto textModeAction = new QAction("&Text Mode", this);
textModeAction->setShortcut(Qt::Key_F4);
textModeAction->setIcon(GuiRegistry::instance().textHighlightedIcon());
textModeAction->setToolTip("Show current tab in text mode, and make this mode default for all subsequent queries <b>(F4)</b>");
@@ -323,17 +374,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 +399,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 +420,33 @@ namespace Robomongo
VERIFY(connect(showLineNumbers, SIGNAL(triggered()), this, SLOT(toggleLineNumbers())));
optionsMenu->addAction(showLineNumbers);
QAction *disabelConnectionShortcuts = new QAction("Disable Connection Shortcuts",this);
QAction *disabelConnectionShortcuts = new QAction("Disable Connection Shortcuts", this);
disabelConnectionShortcuts->setCheckable(true);
disabelConnectionShortcuts->setChecked(AppRegistry::instance().settingsManager()->disableConnectionShortcuts());
VERIFY(connect(disabelConnectionShortcuts, SIGNAL(triggered()), this, SLOT(setDisableConnectionShortcuts())));
optionsMenu->addAction(disabelConnectionShortcuts);
QAction *autoExec = new QAction(tr("Automatically execute code in new tab"),this);
QAction *autoExec = new QAction(tr("Automatically execute code in new tab"), this);
autoExec->setCheckable(true);
autoExec->setChecked(AppRegistry::instance().settingsManager()->autoExec());
VERIFY(connect(autoExec, SIGNAL(triggered()), this, SLOT(toggleAutoExec())));
optionsMenu->addAction(autoExec);
QAction *preferencesAction = new QAction("Preferences",this);
#if defined(Q_OS_WIN)
QAction *minimizeTray = new QAction("Close button should minimize to system tray");
minimizeTray->setCheckable(true);
minimizeTray->setChecked(AppRegistry::instance().settingsManager()->minimizeToTray());
VERIFY(connect(minimizeTray, SIGNAL(triggered()), this, SLOT(toggleMinimizeToTray())));
optionsMenu->addAction(minimizeTray);
#endif
auto *checkUpdates = new QAction(tr("Check for updates"), this);
checkUpdates->setCheckable(true);
checkUpdates->setChecked(AppRegistry::instance().settingsManager()->checkForUpdates());
VERIFY(connect(checkUpdates, SIGNAL(triggered()), this, SLOT(toggleCheckUpdates())));
optionsMenu->addAction(checkUpdates);
QAction *preferencesAction = new QAction("Preferences", this);
VERIFY(connect(preferencesAction, SIGNAL(triggered()), this, SLOT(openPreferences())));
preferencesAction->setVisible(false);
optionsMenu->addAction(preferencesAction);
@@ -433,6 +498,12 @@ namespace Robomongo
duplicateAction->setVisible(true);
VERIFY(connect(duplicateAction, SIGNAL(triggered()), SLOT(duplicateTab())));
// Open welcome tab action
auto openWelcomeTabAction = new QAction("Open/Refresh Welcome Tab", this);
openWelcomeTabAction->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_W);
openWelcomeTabAction->setVisible(true);
VERIFY(connect(openWelcomeTabAction, SIGNAL(triggered()), SLOT(openWelcomeTab())));
// Window menu
QMenu *windowMenu = menuBar()->addMenu("Window");
//minimize
@@ -444,8 +515,10 @@ namespace Robomongo
windowMenu->addSeparator();
windowMenu->addAction(reloadAction);
windowMenu->addAction(duplicateAction);
windowMenu->addSeparator();
windowMenu->addAction(openWelcomeTabAction);
SettingsManager::ToolbarSettingsContainerType toolbarsSettings = AppRegistry::instance().settingsManager()->toolbars();
auto toolbarsSettings = AppRegistry::instance().settingsManager()->toolbars();
/*** About menu ***/
@@ -489,21 +562,92 @@ namespace Robomongo
setToolBarIconSize(_execToolBar);
addToolBar(_execToolBar);
_updateLabel = new QLabel;
_updateLabel->setWordWrap(true);
_updateLabel->setOpenExternalLinks(true);
_updateLabel->setTextFormat(Qt::TextFormat::RichText);
_updateLabel->setIndent(_updateLabel->fontMetrics().width("T"));
_closeButton = new QPushButton;
_closeButton->setIcon(QIcon(":/robomongo/icons/close_hover_16x16.png"));
_closeButton->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Minimum);
_closeButton->setMouseTracking(true);
_closeButton->setAttribute(Qt::WA_Hover);
_closeButton->installEventFilter(this);
VERIFY(connect(_closeButton, SIGNAL(clicked()), this, SLOT(on_closeButton_clicked())));
auto updateBarLay = new QHBoxLayout;
updateBarLay->addWidget(_updateLabel);
updateBarLay->addWidget(_closeButton, Qt::AlignRight);
updateBarLay->setSpacing(0);
updateBarLay->setMargin(0);
auto updateBarWid = new QWidget;
updateBarWid->setLayout(updateBarLay);
_updateBar = new QToolBar("Updates Toolbar");
_updateBar->addWidget(updateBarWid);
_updateBar->setStyleSheet("background-color: #b3e0ff; border: none;"); // blue
addToolBarBreak();
addToolBar(_updateBar);
_updateBar->setHidden(true);
_updateBar->setMovable(false);
_toolbarsMenu->addAction(_execToolBar->toggleViewAction());
VERIFY(connect(_execToolBar->toggleViewAction(), SIGNAL(triggered(bool)), this, SLOT(onExecToolbarVisibilityChanged(bool))));
/* --- Temporarily disabling export/import feature
// Export/Import Toolbar
auto expImpToolBar = new QToolBar(tr("Export/Import Toolbar"), this);
expImpToolBar->setToolButtonStyle(Qt::ToolButtonIconOnly);
expImpToolBar->setMovable(false);
// Add export action
_exportAction = new QAction(this);
_exportAction->setData("Export");
_exportAction->setIcon(GuiRegistry::instance().exportIcon());
_exportAction->setDisabled(true);
VERIFY(connect(_exportAction, SIGNAL(triggered()), this, SLOT(openExportDialog())));
addToolBar(expImpToolBar);
expImpToolBar->addAction(_exportAction);
// Add import action
_importAction = new QAction(this);
_importAction->setData("Import");
_importAction->setIcon(GuiRegistry::instance().importIcon());
_importAction->setDisabled(true);
addToolBar(expImpToolBar);
expImpToolBar->addAction(_importAction);
*/
createTabs();
createStatusBar();
setWindowTitle(PROJECT_NAME_TITLE " " PROJECT_VERSION);
setWindowTitle("Robomongo 1.0.3");
setWindowIcon(GuiRegistry::instance().mainWindowIcon());
QTimer::singleShot(0, this, SLOT(manageConnections()));
QTimer::singleShot(0, this, SLOT(manageConnections()));
updateMenus();
_updateMenusAtStart = false;
AppRegistry::instance().bus()->subscribe(this, ConnectionFailedEvent::Type);
AppRegistry::instance().bus()->subscribe(this, ScriptExecutedEvent::Type);
AppRegistry::instance().bus()->subscribe(this, ScriptExecutingEvent::Type);
AppRegistry::instance().bus()->subscribe(this, QueryWidgetUpdatedEvent::Type);
AppRegistry::instance().bus()->subscribe(this, OperationFailedEvent::Type);
restoreWindowSettings();
// Catch application windows focus changes
VERIFY(connect(qApp, SIGNAL(focusChanged(QWidget*, QWidget*)), this, SLOT(on_focusChanged())));
_networkAccessManager = new QNetworkAccessManager;
VERIFY(connect(_networkAccessManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(on_networkReply(QNetworkReply*))));
// First check for updates 30 secs after program start
QTimer::singleShot(30000, this, SLOT(checkUpdates())); // 30000 for 30secs
// Check for updates every 1 hour
auto timer = new QTimer(this);
VERIFY(connect(timer, SIGNAL(timeout()), this, SLOT(checkUpdates())));
timer->start(3600000); // 1 hour = (3600000 msec = 60 * 60 * 1000 msec)
}
void MainWindow::createStylesMenu()
@@ -516,7 +660,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,18 +709,78 @@ namespace Robomongo
AppRegistry::instance().settingsManager()->save();
}
void MainWindow::exit()
{
_allowExit = true;
close();
}
void MainWindow::restoreWindowSettings()
{
QSettings settings("3T", "Robomongo");
// Restore settings if registery key exists, otherwise resize as app started for the first time.
if (settings.contains("MainWindow/geometry"))
{
restoreGeometry(settings.value("MainWindow/geometry").toByteArray());
}
else
{
// Resize main window. We are trying to keep it "almost" maximized.
QRect screenGeometry = QApplication::desktop()->availableGeometry();
int horizontalMargin = static_cast<int>(screenGeometry.width() * 0.1);
int verticalMargin = static_cast<int>(screenGeometry.height() * 0.1);
int _width = screenGeometry.width() - horizontalMargin;
int _height = screenGeometry.height() - verticalMargin;
resize(QSize(_width, _height));
// Center main window
int x = (screenGeometry.width() - width()) / 2;
int y = (screenGeometry.height() - height()) / 2;
move(x, y);
}
}
void MainWindow::saveWindowSettings() const
{
QSettings settings("3T", "Robomongo");
settings.setValue("MainWindow/geometry", saveGeometry());
}
void MainWindow::adjustUpdatesBarHeight()
{
if (!AppRegistry::instance().settingsManager()->checkForUpdates() || !_updateBar->isVisible())
return;
QTextDocument doc;
doc.setHtml(_updateLabel->text());
int const strWidth = _updateLabel->fontMetrics().width(doc.toPlainText());
int const lineHeight = _updateLabel->fontMetrics().height();
int const widthForUpdateStr = width() - _closeButton->width();
if (0 == widthForUpdateStr)
return;
#ifdef __APPLE__
_updateLabel->setFixedHeight((strWidth / widthForUpdateStr + 1) * lineHeight * 1.3);
#else
_updateLabel->setFixedHeight((strWidth / widthForUpdateStr + 1) * lineHeight);
#endif
}
void MainWindow::open()
{
QueryWidget *wid = _workArea->currentQueryWidget();
if (wid) {
wid->openFile();
}
else {
SettingsManager::ConnectionSettingsContainerType connections = AppRegistry::instance().settingsManager()->connections();
if (connections.size() == 1) {
else { // todo: currently this case not handled properly, since "Open" button is
// disabled when no tabs exist
auto connectionsCopy = AppRegistry::instance().settingsManager()->connections();
if (connectionsCopy.size() == 1) {
ScriptInfo inf = ScriptInfo(QString());
if (inf.loadFromFile()) {
_app->openShell(connections.at(0), inf);
// todo: for now do not open new shell
_app->openShell(nullptr, connectionsCopy.at(0), inf);
}
}
}
@@ -598,23 +802,13 @@ namespace Robomongo
}
}
void MainWindow::keyPressEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_F12) {
_connectButton->showMenu();
return;
}
BaseClass::keyPressEvent(event);
}
void MainWindow::updateConnectionsMenu()
{
_connectionsMenu->clear();
int number = 1;
// Populate list with connections
SettingsManager::ConnectionSettingsContainerType connections = AppRegistry::instance().settingsManager()->connections();
for(SettingsManager::ConnectionSettingsContainerType::const_iterator it = connections.begin(); it!= connections.end(); ++it) {
for (SettingsManager::ConnectionSettingsContainerType::const_iterator it = connections.begin(); it != connections.end(); ++it) {
ConnectionSettings *connection = *it;
QAction *action = new QAction(QtUtils::toQString(connection->getReadableName()), this);
action->setData(QVariant::fromValue(connection));
@@ -639,10 +833,21 @@ namespace Robomongo
_connectionsMenu->addAction(connectAction);
}
WelcomeTab* MainWindow::getWelcomeTab()
{
return _workArea->getWelcomeTab();
}
void MainWindow::manageConnections()
{
ConnectionsDialog dialog(AppRegistry::instance().settingsManager(), this);
#if defined(Q_OS_WIN)
_trayIcon->hide(); // hide the tray icon so the main window can't be hidden behind the connections dialog
#endif
static bool checkForImported = true;
ConnectionsDialog dialog(AppRegistry::instance().settingsManager(), checkForImported, this);
int result = dialog.exec();
checkForImported = false;
// save settings and update connection menu
AppRegistry::instance().settingsManager()->save();
@@ -663,13 +868,17 @@ namespace Robomongo
}
try {
_app->openServer(selected, true);
_app->openServer(selected, ConnectionPrimary);
} catch(const std::exception &) {
QString message = QString("Cannot connect to MongoDB (%1)").arg(QtUtils::toQString(selected->getFullAddress()));
QMessageBox::information(this, "Error", message);
}
}
#if defined(Q_OS_WIN)
_trayIcon->show(); // show the tray icon once the connections dialog is gone
#endif
// on linux focus is lost - we need to activate main window back
activateWindow();
}
@@ -735,6 +944,13 @@ namespace Robomongo
saveAutoExec(send->isChecked());
}
void MainWindow::toggleCheckUpdates()
{
auto action = qobject_cast<QAction*>(sender());
AppRegistry::instance().settingsManager()->setCheckForUpdates(action->isChecked());
AppRegistry::instance().settingsManager()->save();
}
void MainWindow::toggleLineNumbers()
{
QAction *send = qobject_cast<QAction*>(sender());
@@ -788,7 +1004,7 @@ namespace Robomongo
void MainWindow::refreshConnections()
{
QToolTip::showText(QPoint(0,0),
QToolTip::showText(QPoint(0, 0),
QString("Refresh not working yet... : <br/> <b>Ctrl+D</b> : push Button"));
}
@@ -804,6 +1020,24 @@ namespace Robomongo
dlg.exec();
}
void MainWindow::openWelcomeTab()
{
_workArea->openWelcomeTab();
}
/* --- Temporarily disabling export/import feature
void MainWindow::openExportDialog()
{
auto selectedItem = dynamic_cast<ExplorerCollectionTreeItem*>(_explorer->getSelectedTreeItem());
auto dbName = QString::fromStdString(selectedItem->collection()->database()->name());
auto collName = QString::fromStdString(selectedItem->collection()->name());
auto dialog = new ExportDialog(dbName, collName, this);
dialog->show(); // show it mode-less so that user can perform multiple simultaneous exports
}
*/
void MainWindow::setDefaultUuidEncoding()
{
AppRegistry::instance().settingsManager()->setUuidEncoding(DefaultEncoding);
@@ -883,7 +1117,7 @@ namespace Robomongo
QVariant data = connectionAction->data();
ConnectionSettings *ptr = data.value<ConnectionSettings *>();
try {
_app->openServer(ptr, true);
_app->openServer(ptr, ConnectionPrimary);
}
catch(const std::exception &) {
QString message = QString("Cannot connect to the MongoDB at %1.")
@@ -894,7 +1128,18 @@ namespace Robomongo
void MainWindow::handle(ConnectionFailedEvent *event)
{
QMessageBox::information(this, "Error", QtUtils::toQString(event->message));
// Handle only Primary connections
if (event->connectionType != ConnectionPrimary)
return;
// Very temporary solution to prevent multiple connection error messages
// from both SshTunnelWorker and MongoWorker
static int lastServerHandle = -1;
if (event->serverHandle <= lastServerHandle)
return;
lastServerHandle = event->serverHandle;
QMessageBox::critical(this, "Error", QtUtils::toQString(event->message));
}
void MainWindow::handle(ScriptExecutingEvent *)
@@ -909,6 +1154,86 @@ namespace Robomongo
_executeAction->setDisabled(false);
}
void MainWindow::handle(OperationFailedEvent *event)
{
std::stringstream ss;
ss << event->userFriendlyErrorMessage << std::endl << std::endl
<< "Error:" << std::endl << event->technicalErrorMessage;
QMessageBox::critical(NULL, "Operation failed", QtUtils::toQString(ss.str()));
}
void MainWindow::keyPressEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_F12) {
_connectButton->showMenu();
return;
}
BaseClass::keyPressEvent(event);
}
void MainWindow::closeEvent(QCloseEvent *event)
{
saveWindowSettings();
#if defined(Q_OS_WIN)
if (AppRegistry::instance().settingsManager()->minimizeToTray() && !_allowExit) {
event->ignore();
hide(); // hide the window because it can be reopened with the tray icon
}
else {
if (_trayIcon->isVisible())
_trayIcon->hide();
QMainWindow::closeEvent(event);
}
#else
QMainWindow::closeEvent(event);
#endif
}
void MainWindow::hideEvent(QHideEvent *event)
{
#if defined(Q_OS_WIN)
if (_trayIcon->contextMenu()->actions().size() > 0 && isHidden()) {
_trayIcon->contextMenu()->actions().at(0)->setText("Show Robomongo");
}
#endif
}
void MainWindow::showEvent(QShowEvent *event)
{
#if defined(Q_OS_WIN)
if (_trayIcon->contextMenu()->actions().size() > 0) {
_trayIcon->contextMenu()->actions().at(0)->setText("Minimize to Tray");
}
#endif
}
bool MainWindow::eventFilter(QObject *target, QEvent *event)
{
auto closeUpdatesBarButton = qobject_cast<QPushButton*>(target);
if (!closeUpdatesBarButton)
return false;
if (event->type() == QEvent::HoverEnter) {
closeUpdatesBarButton->setIcon(QIcon(":/robomongo/icons/close_hover_16x16_original.png"));
return true;
}
else if (event->type() == QEvent::HoverLeave) {
closeUpdatesBarButton->setIcon(QIcon(":/robomongo/icons/close_hover_16x16.png"));
return true;
}
return QWidget::eventFilter(target, event);
}
void MainWindow::resizeEvent(QResizeEvent* event)
{
QMainWindow::resizeEvent(event);
adjustUpdatesBarHeight();
}
void MainWindow::handle(QueryWidgetUpdatedEvent *event)
{
_orientationAction->setEnabled(event->numOfResults() > 1);
@@ -916,14 +1241,14 @@ namespace Robomongo
void MainWindow::createDatabaseExplorer()
{
ExplorerWidget *explorer = new ExplorerWidget(this);
AppRegistry::instance().bus()->subscribe(explorer, ConnectingEvent::Type);
AppRegistry::instance().bus()->subscribe(explorer, ConnectionFailedEvent::Type);
AppRegistry::instance().bus()->subscribe(explorer, ConnectionEstablishedEvent::Type);
_explorer = new ExplorerWidget(this);
AppRegistry::instance().bus()->subscribe(_explorer, ConnectingEvent::Type);
AppRegistry::instance().bus()->subscribe(_explorer, ConnectionFailedEvent::Type);
AppRegistry::instance().bus()->subscribe(_explorer, ConnectionEstablishedEvent::Type);
QDockWidget *explorerDock = new QDockWidget(tr("Database Explorer"));
explorerDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
explorerDock->setWidget(explorer);
explorerDock->setWidget(_explorer);
explorerDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable);
QWidget *titleWidget = new QWidget(this); // this lines simply remove
@@ -950,7 +1275,7 @@ namespace Robomongo
_logDock = new QDockWidget(tr("Logs"));
_logDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea | Qt::BottomDockWidgetArea | Qt::TopDockWidgetArea);
_logDock->setWidget(log);
_logDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable);
_logDock->setFeatures(QDockWidget::DockWidgetClosable);
_logDock->setVisible(false);
QAction *action = _logDock->toggleViewAction();
@@ -966,8 +1291,20 @@ namespace Robomongo
void MainWindow::updateMenus()
{
int contTab = _workArea->count();
bool isEnable = _workArea && contTab > 0;
if (!_workArea)
return;
bool isEnable = false;
if (_updateMenusAtStart)
isEnable = false;
else {
if (getWelcomeTab() && getWelcomeTab()->isVisible())
isEnable = false;
else if (_workArea->count() == 0)
isEnable = false;
else
isEnable = true;
}
_execToolBar->setEnabled(isEnable);
_openAction->setEnabled(isEnable);
@@ -980,6 +1317,7 @@ namespace Robomongo
_workArea = new WorkAreaTabWidget(this);
AppRegistry::instance().bus()->subscribe(_workArea, OpeningShellEvent::Type);
VERIFY(connect(_workArea, SIGNAL(currentChanged(int)), this, SLOT(updateMenus())));
VERIFY(connect(_workArea, SIGNAL(currentChanged(int)), this, SLOT(on_tabChange())));
QHBoxLayout *hlayout = new QHBoxLayout;
hlayout->setContentsMargins(0, 3, 0, 0);
@@ -1013,4 +1351,104 @@ namespace Robomongo
AppRegistry::instance().settingsManager()->setToolbarSettings("explorer", isVisible);
AppRegistry::instance().settingsManager()->save();
}
/* --- Temporarily disabling export/import feature
void MainWindow::onExplorerItemSelected(QTreeWidgetItem *selectedItem)
{
auto collectionItem = dynamic_cast<ExplorerCollectionTreeItem*>(selectedItem);
if (collectionItem) {
_exportAction->setEnabled(true);
}
else {
_exportAction->setDisabled(true);
}
}
*/
void MainWindow::on_tabChange()
{
auto activeTab = dynamic_cast<QueryWidget*>(_workArea->currentWidget());
if (activeTab) {
activeTab->bringDockToFront();
}
}
void MainWindow::toggleMinimize()
{
if (isHidden()) {
show();
}
else {
hide();
}
}
void MainWindow::trayActivated(QSystemTrayIcon::ActivationReason reason)
{
if (QSystemTrayIcon::DoubleClick == reason && isHidden()) {
show();
}
}
void MainWindow::toggleMinimizeToTray()
{
QAction *send = qobject_cast<QAction*>(sender());
saveMinimizeToTraySettings(send->isChecked());
}
void MainWindow::on_focusChanged()
{
// If focus is on floating output window, make it's parent (which is a QueryWidget tab) as active tab
auto const activeDock = dynamic_cast<QueryWidget::CustomDockWidget*>(qApp->activeWindow());
if (activeDock) {
_workArea->setCurrentWidget(activeDock->getParentQueryWidget());
}
}
void MainWindow::on_networkReply(QNetworkReply* reply)
{
QString str(QUrl::fromPercentEncoding(reply->readAll()));
if ("NO-UPDATES-ANNOUNCED.\n" == str || reply->error() != QNetworkReply::NoError || str.isEmpty()) {
_updateLabel->setText("");
_updateBar->setVisible(false);
return;
}
str.replace('+', ' ');
str.remove("Update,");
_updateLabel->setText(str);
_updateBar->setVisible(true);
adjustUpdatesBarHeight();
}
void MainWindow::on_closeButton_clicked()
{
_updateBar->setVisible(false);
}
void MainWindow::checkUpdates()
{
if (!Robomongo::AppRegistry::instance().settingsManager()->checkForUpdates())
return;
#ifdef _WIN32
QString const OS = "win";
#elif __APPLE__
QString const OS = "osx";
#elif __linux__
QString const OS = "linux";
#else
QString const OS = "unknown";
#endif
// softwareId=8: Robomongo product ID
QUrl url("http://updates.3tsoftwarelabs.com/check.php?os=" + OS + "&softwareId=8&softwareVersion=" +
QString(PROJECT_VERSION) + "&licenseInfo=FREE&setup=" +
AppRegistry::instance().settingsManager()->anonymousID() + "&notify=true#");
_networkAccessManager->get(QNetworkRequest(url));
}
}
+77 -7
View File
@@ -1,11 +1,17 @@
#pragma once
#include <QMainWindow>
#include <QSystemTrayIcon>
QT_BEGIN_NAMESPACE
class QLabel;
class QToolBar;
class QDockWidget;
class QToolButton;
class QPushButton;
class QTreeWidgetItem;
class QNetworkReply;
class QNetworkAccessManager;
QT_END_NAMESPACE
namespace Robomongo
@@ -13,10 +19,14 @@ namespace Robomongo
class ConnectionFailedEvent;
class ScriptExecutingEvent;
class ScriptExecutedEvent;
class OperationFailedEvent;
class QueryWidgetUpdatedEvent;
class WorkAreaTabWidget;
class ConnectionMenu;
class App;
class ExplorerWidget;
class WelcomeTab;
class MainWindow : public QMainWindow
{
@@ -25,7 +35,8 @@ namespace Robomongo
public:
typedef QMainWindow BaseClass;
MainWindow();
void keyPressEvent(QKeyEvent *event);
WelcomeTab* getWelcomeTab();
public Q_SLOTS:
void manageConnections();
@@ -49,6 +60,7 @@ namespace Robomongo
void save();
void saveAs();
void changeStyle(QAction *);
void exit();
void setDefaultUuidEncoding();
void setJavaUuidEncoding();
@@ -66,22 +78,65 @@ namespace Robomongo
void handle(ScriptExecutingEvent *event);
void handle(ScriptExecutedEvent *event);
void handle(QueryWidgetUpdatedEvent *event);
void handle(OperationFailedEvent *event);
protected:
void keyPressEvent(QKeyEvent *event) override;
void closeEvent(QCloseEvent *event) override;
void hideEvent(QHideEvent *event) override;
void showEvent(QShowEvent *event) override;
bool eventFilter(QObject *target, QEvent *event) override;
void resizeEvent(QResizeEvent* event) override;
private Q_SLOTS:
void updateMenus();
void setUtcTimeZone();
void setLocalTimeZone();
void openPreferences();
void openWelcomeTab();
// Temporarily disabling export/import feature
//void openExportDialog();
void onConnectToolbarVisibilityChanged(bool isVisisble);
void onOpenSaveToolbarVisibilityChanged(bool isVisisble);
void onExecToolbarVisibilityChanged(bool isVisisble);
void onExplorerVisibilityChanged(bool isVisisble);
// Temporarily disabling export/import feature
//void onExplorerItemSelected(QTreeWidgetItem *selectedItem);
void on_tabChange();
void toggleMinimize();
void trayActivated(QSystemTrayIcon::ActivationReason reason);
void toggleMinimizeToTray();
// On application focus changes
void on_focusChanged();
void on_networkReply(QNetworkReply* reply);
void on_closeButton_clicked();
void checkUpdates();
void toggleCheckUpdates();
private:
void updateConnectionsMenu();
void createDatabaseExplorer();
void createTabs();
void createStylesMenu();
void createStatusBar();
void restoreWindowSettings();
void saveWindowSettings() const;
void adjustUpdatesBarHeight();
QDockWidget *_logDock;
WorkAreaTabWidget *_workArea;
ExplorerWidget* _explorer;
App *_app;
ConnectionMenu *_connectionsMenu;
@@ -89,19 +144,34 @@ namespace Robomongo
QMenu *_viewMenu;
QMenu *_toolbarsMenu;
QAction *_connectAction;
// Open/Save tool bar
QAction *_openAction;
QAction *_saveAction;
QAction *_saveAsAction;
// Execution tool bar
QAction *_executeAction;
QAction *_stopAction;
QAction *_orientationAction;
QToolBar *_execToolBar;
QToolBar *_updateBar;
QLabel *_updateLabel;
QPushButton* _closeButton;
void updateConnectionsMenu();
void createDatabaseExplorer();
void createTabs();
void createStylesMenu();
void createStatusBar();
QNetworkAccessManager *_networkAccessManager;
// Temporarily disabling export/import feature
/*
// Export/import tool bar
QAction *_exportAction;
QAction *_importAction;
*/
#if defined(Q_OS_WIN)
QSystemTrayIcon *_trayIcon;
#endif
bool _allowExit;
bool _updateMenusAtStart = true;
};
}
+53 -17
View File
@@ -4,6 +4,9 @@
#include <QGridLayout>
#include <QLabel>
#include <QPushButton>
#include <QTabWidget>
#include <QTextBrowser>
#include <QFile>
#include "robomongo/gui/GuiRegistry.h"
#include "robomongo/core/utils/QtUtils.h"
@@ -15,15 +18,21 @@ namespace
"Shell-centric MongoDB management tool."
"<br/>"
"<br/>"
"Visit " PROJECT_NAME_TITLE " website: <a href=\"http://" PROJECT_DOMAIN "\">" PROJECT_DOMAIN "</a> <br/>"
"Visit " PROJECT_NAME_TITLE " website: <a href=\"https://" PROJECT_DOMAIN "\">" PROJECT_DOMAIN "</a> <br/>"
"<br/>"
"<a href=\"https://" PROJECT_GITHUB_FORK "\">Fork</a> project or <a href=\"https://" PROJECT_GITHUB_ISSUES "\">submit</a> issues/proposals on GitHub. <br/>"
"<a href=\"https://" PROJECT_GITHUB_ISSUES "\">Submit</a> issues/proposals on GitHub. <br/>"
"<br/>"
"Copyright 2013-2016 <a href=\"http:// " PROJECT_COMPANYNAME_DOMAIN "\">" PROJECT_COMPANYNAME "</a>. All rights reserved.<br/>"
"Copyright 2014-2017 <a href= " PROJECT_COMPANYNAME_DOMAIN " >" PROJECT_COMPANYNAME "</a>. All rights reserved.<br/>"
"<br/>"
"The program is provided AS IS with NO WARRANTY OF ANY KIND, "
"INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A "
"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
@@ -31,30 +40,57 @@ namespace Robomongo
AboutDialog::AboutDialog(QWidget *parent)
: QDialog(parent)
{
setWindowIcon(GuiRegistry::instance().mainWindowIcon());
setWindowTitle("About " PROJECT_NAME_TITLE);
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
QGridLayout *layout = new QGridLayout(this);
//// About tab
auto aboutTab = new QWidget;
aboutTab->setWindowIcon(GuiRegistry::instance().mainWindowIcon());
aboutTab->setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
auto layout = new QGridLayout(this);
layout->setSizeConstraint(QLayout::SetFixedSize);
QLabel *copyRightLabel = new QLabel(description);
auto copyRightLabel = new QLabel(description);
copyRightLabel->setWordWrap(true);
copyRightLabel->setOpenExternalLinks(true);
copyRightLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close);
QPushButton *closeButton = buttonBox->button(QDialogButtonBox::Close);
buttonBox->addButton(closeButton, QDialogButtonBox::ButtonRole(QDialogButtonBox::RejectRole | QDialogButtonBox::AcceptRole));
VERIFY(connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())));
QIcon icon = GuiRegistry::instance().mainWindowIcon();
QPixmap iconPixmap = icon.pixmap(128, 128);
QLabel *logoLabel = new QLabel;
auto logoLabel = new QLabel;
logoLabel->setPixmap(iconPixmap);
layout->addWidget(logoLabel , 0, 0, 1, 1);
layout->addWidget(logoLabel, 0, 0, 1, 1);
layout->addWidget(copyRightLabel, 0, 1, 4, 4);
layout->addWidget(buttonBox, 4, 0, 1, 5);
aboutTab->setLayout(layout);
//// License Agreement tab
auto licenseTab = new QWidget;
auto textBrowser = new QTextBrowser;
textBrowser->setOpenExternalLinks(true);
textBrowser->setOpenLinks(true);
QFile file(":gnu_gpl3_license.html");
if (file.open(QFile::ReadOnly | QFile::Text))
textBrowser->setText(file.readAll());
auto licenseTabLay = new QVBoxLayout;
licenseTabLay->addWidget(textBrowser);
licenseTab->setLayout(licenseTabLay);
//// Button box
auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Close);
QPushButton *closeButton = buttonBox->button(QDialogButtonBox::Close);
buttonBox->addButton(closeButton, QDialogButtonBox::ButtonRole(QDialogButtonBox::RejectRole |
QDialogButtonBox::AcceptRole));
VERIFY(connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())));
//// Main layout
auto tabWidget = new QTabWidget;
tabWidget->addTab(aboutTab, "About");
tabWidget->addTab(licenseTab, "License Agreement");
auto mainLayout = new QVBoxLayout;
mainLayout->addWidget(tabWidget);
mainLayout->addWidget(buttonBox);
setLayout(mainLayout);
}
}
@@ -3,9 +3,25 @@
#include <QLabel>
#include <QGridLayout>
#include <QLineEdit>
/* --- Disabling unfinished export URI connection string feature
#include <QPushButton>
#include <QMessageBox>
#include <QClipboard>
#include <QApplication>
#include <QCheckBox>
#include <QToolTip>
*/
#include <mongo/client/mongo_uri.h>
#include "robomongo/core/settings/ConnectionSettings.h"
#include "robomongo/core/utils/QtUtils.h"
/* --- Disabling unfinished export URI connection string feature
#include "robomongo/core/settings/CredentialSettings.h"
#include "robomongo/core/settings/ReplicaSetSettings.h"
#include "robomongo/core/settings/SslSettings.h"
#include "robomongo/gui/utils/GuiConstants.h"
*/
namespace Robomongo
{
@@ -16,23 +32,123 @@ namespace Robomongo
QLabel *defaultDatabaseDescriptionLabel = new QLabel(
"Database, that will be default (<code>db</code> shell variable will point to this database). "
"By default, default database will be the one you authenticate on, or <code>test</code> otherwise. "
"Leave this field empty, if you want default behaviour.");
"Leave this field empty, if you want default behavior.");
defaultDatabaseDescriptionLabel->setWordWrap(true);
defaultDatabaseDescriptionLabel->setContentsMargins(0, -2, 0, 20);
_defaultDatabaseName = new QLineEdit(QtUtils::toQString(_settings->defaultDatabase()));
QGridLayout *connectionLayout = new QGridLayout;
connectionLayout->setAlignment(Qt::AlignTop);
connectionLayout->addWidget(new QLabel("Default Database:"), 1, 0);
connectionLayout->addWidget(_defaultDatabaseName, 1, 1, 1, 1);
connectionLayout->addWidget(defaultDatabaseDescriptionLabel, 2, 1, 1, 1);
/* --- Disabling unfinished export URI connection string feature
_uriString = new QLineEdit;
_uriString->setReadOnly(true);
setLayout(connectionLayout);
_includePasswordCheckBox = new QCheckBox("Include passwords");
VERIFY(connect(_includePasswordCheckBox, SIGNAL(toggled(bool)),
this, SLOT(on_includePasswordsCheckBox_toggle(bool))));
auto generateButton = new QPushButton("Generate");
generateButton->setFixedWidth(70);
generateButton->setMaximumHeight(HighDpiConstants::WIN_HIGH_DPI_BUTTON_HEIGHT);
VERIFY(connect(generateButton, SIGNAL(clicked()), this, SLOT(on_generateButton_clicked())));
_copyButton = new QPushButton("Copy");
_copyButton->setFixedWidth(50);
_copyButton->setMaximumHeight(HighDpiConstants::WIN_HIGH_DPI_BUTTON_HEIGHT);
VERIFY(connect(_copyButton, SIGNAL(clicked()), this, SLOT(on_copyButton_clicked())));
auto hlay = new QHBoxLayout;
hlay->addWidget(_includePasswordCheckBox);
hlay->addWidget(generateButton, Qt::AlignLeft);
hlay->addWidget(_copyButton, Qt::AlignLeft);
*/
auto mainLayout = new QGridLayout;
mainLayout->setAlignment(Qt::AlignTop);
mainLayout->addWidget(new QLabel("Default Database:"), 1, 0);
mainLayout->addWidget(_defaultDatabaseName, 1, 1, 1, 2);
mainLayout->addWidget(defaultDatabaseDescriptionLabel, 2, 1, 1, 2);
/* --- Disabling unfinished export URI connection string feature
mainLayout->addWidget(new QLabel{ "URI Connection String:" }, 3, 0);
mainLayout->addWidget(_uriString, 3, 1);
mainLayout->addLayout(hlay, 4, 1);
*/
setLayout(mainLayout);
}
void ConnectionAdvancedTab::accept()
{
_settings->setDefaultDatabase(QtUtils::toStdString(_defaultDatabaseName->text()));
}
/* --- Disabling unfinished export URI connection string feature
void ConnectionAdvancedTab::on_generateButton_clicked()
{
if (_settings->isReplicaSet()) {
// todo:
// handle set name will be empty initially. It will be available(cached) after first
// successful connection.
std::string connStr = { "mongodb://" };
// todo: which credential to use if there are more than one?
if (_settings->hasEnabledPrimaryCredential() && !_settings->credentials().isEmpty()) {
connStr.append(_settings->primaryCredential()->userName() + ":");
connStr.append(_includePasswordCheckBox->isChecked() ?
_settings->primaryCredential()->userPassword() : "PASSWORD");
connStr.append("@");
}
for (auto const& member : _settings->replicaSetSettings()->membersToHostAndPort()) {
connStr.append(member.toString() + ",");
}
connStr.pop_back(); // delete last ","
connStr.append("/admin?");
connStr.append("replicaSet=" + _settings->replicaSetSettings()->setName());
if (_settings->sslSettings()->sslEnabled())
connStr.append("&ssl=true");
if (_settings->hasEnabledPrimaryCredential() && !_settings->credentials().isEmpty())
connStr.append("&authSource=" + _settings->primaryCredential()->databaseName());
// todo: validate connStr
_uriString->setText(QString::fromStdString(connStr));
_uriString->setCursorPosition(QTextCursor::Start);
}
else { // standalone server
mongo::HostAndPort server = _settings->hostAndPort();
mongo::ConnectionString connStr{ server };
auto str1 = connStr.toString();
auto uriWithStatus = mongo::StatusWith<mongo::MongoURI>(mongo::MongoURI::parse(connStr.toString()));
auto str2 = uriWithStatus.getValue().toString();
_uriString->setText(QString::fromStdString("mongodb://" + str1 + "/admin"));
_uriString->setCursorPosition(QTextCursor::Start);
}
}
void ConnectionAdvancedTab::on_copyButton_clicked()
{
if (_uriString->text().isEmpty())
return;
QClipboard *clipboard = qApp->clipboard();
clipboard->setText(_uriString->text());
auto posUnderGenerateButton = QPoint(_copyButton->pos().x()-100, _copyButton->y()+20);
QToolTip::showText(mapToGlobal(posUnderGenerateButton), "Copied into clipboard", nullptr, QRect(), 2000);
}
void ConnectionAdvancedTab::on_includePasswordsCheckBox_toggle(bool checked)
{
if (_uriString->text().isEmpty())
return;
on_generateButton_clicked();
}
*/
}
@@ -4,6 +4,8 @@
QT_BEGIN_NAMESPACE
class QLineEdit;
class QCheckBox;
class QPushButton;
QT_END_NAMESPACE
namespace Robomongo
@@ -16,10 +18,24 @@ namespace Robomongo
public:
ConnectionAdvancedTab(ConnectionSettings *settings);
QLineEdit *_defaultDatabaseName;
void accept();
/* --- Disabling unfinished export URI connection string feature
private Q_SLOTS :
void on_generateButton_clicked();
void on_copyButton_clicked();
void on_includePasswordsCheckBox_toggle(bool checked);
*/
private:
QLineEdit *_defaultDatabaseName;
/* --- Disabling unfinished export URI connection string feature
QLineEdit *_uriString;
QCheckBox *_includePasswordCheckBox;
QPushButton *_copyButton;
*/
ConnectionSettings *_settings;
};
}
+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(HighDpiConstants::WIN_HIGH_DPI_BUTTON_HEIGHT);
#endif
VERIFY(connect(_echoModeButton, SIGNAL(clicked()), this, SLOT(toggleEchoMode())));
_useAuth->setChecked(_settings->hasEnabledPrimaryCredential());
@@ -57,23 +69,20 @@ namespace Robomongo
_mechanismComboBox->setCurrentText(QtUtils::toQString(primaryCredential->mechanism()));
}
QGridLayout *_authLayout = new QGridLayout;
_authLayout->addWidget(_useAuth, 0, 0, 1, 3);
_authLayout->addWidget(_databaseNameLabel, 1, 0);
_authLayout->addWidget(_databaseName, 1, 1, 1, 2);
_authLayout->addWidget(_databaseNameDescriptionLabel, 2, 1, 1, 2);
_authLayout->addWidget(_userNameLabel, 3, 0);
_authLayout->addWidget(_userName, 3, 1, 1, 2);
_authLayout->addWidget(_userPasswordLabel, 4, 0);
_authLayout->addWidget(_userPassword, 4, 1);
_authLayout->addWidget(_echoModeButton, 4, 2);
_authLayout->addWidget(_mechanismLabel, 5, 0);
_authLayout->addWidget(_mechanismComboBox, 5, 1, 1, 2);
_authLayout->setAlignment(Qt::AlignTop);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(_authLayout);
setLayout(mainLayout);
QGridLayout *authLayout = new QGridLayout;
authLayout->addWidget(_useAuth, 0, 0, 1, 3);
authLayout->addWidget(_databaseNameLabel, 1, 0);
authLayout->addWidget(_databaseName, 1, 1, 1, 2);
authLayout->addWidget(_databaseNameDescriptionLabel, 2, 1, 1, 2);
authLayout->addWidget(_userNameLabel, 3, 0);
authLayout->addWidget(_userName, 3, 1, 1, 2);
authLayout->addWidget(_userPasswordLabel, 4, 0);
authLayout->addWidget(_userPassword, 4, 1);
authLayout->addWidget(_echoModeButton, 4, 2);
authLayout->addWidget(_mechanismLabel, 5, 0);
authLayout->addWidget(_mechanismComboBox, 5, 1, 1, 2);
authLayout->setAlignment(Qt::AlignTop);
setLayout(authLayout);
}
void ConnectionAuthTab::accept()
@@ -99,7 +108,7 @@ namespace Robomongo
{
bool isPassword = _userPassword->echoMode() == QLineEdit::Password;
_userPassword->setEchoMode(isPassword ? QLineEdit::Normal: QLineEdit::Password);
_echoModeButton->setText(isPassword ? "Hide": "Show");
_echoModeButton->setIcon(isPassword ? GuiRegistry::instance().showIcon() : GuiRegistry::instance().hideIcon());
}
void ConnectionAuthTab::authChecked(bool checked)
+259 -26
View File
@@ -1,4 +1,4 @@
#include "robomongo/gui/dialogs/ConnectionBasicTab.h"
#include "robomongo/gui/dialogs/ConnectionBasicTab.h"
#include <QLabel>
#include <QLineEdit>
@@ -7,54 +7,287 @@
#include <QCheckBox>
#include <QPushButton>
#include <QFileDialog>
#include <QComboBox>
#include <QTreeWidget>
#include <QTreeWidgetItem>
#include <QMessageBox>
#include <QDialogButtonBox>
#include "robomongo/core/utils/QtUtils.h"
#include "robomongo/core/settings/ConnectionSettings.h"
#include "robomongo/core/settings/ReplicaSetSettings.h"
#include "robomongo/gui/GuiRegistry.h"
namespace Robomongo
{
ConnectionBasicTab::ConnectionBasicTab(ConnectionSettings *settings) :
_settings(settings)
{
QLabel *connectionDescriptionLabel = new QLabel(
"Choose any connection name that will help you to identify this connection.");
connectionDescriptionLabel->setWordWrap(true);
connectionDescriptionLabel->setContentsMargins(0, -2, 0, 20);
QLabel *serverDescriptionLabel = new QLabel(
"Specify host and port of MongoDB server. Host can be either IPv4, IPv6 or domain name.");
serverDescriptionLabel->setWordWrap(true);
serverDescriptionLabel->setContentsMargins(0, -2, 0, 20);
_typeLabel = new QLabel("Type:");
_connectionType = new QComboBox;
_connectionType->addItem(tr("Direct Connection"));
_connectionType->addItem(tr("Replica Set"));
_connectionType->setCurrentIndex(static_cast<int>(_settings->isReplicaSet()));
VERIFY(connect(_connectionType, SIGNAL(currentIndexChanged(int)), this, SLOT(on_ConnectionTypeChange(int))));
_nameLabel = new QLabel("Name:");
_connectionName = new QLineEdit(QtUtils::toQString(_settings->connectionName()));
_connInfoLabel = new QLabel("Choose any connection name that will help you to identify this connection.");
_connInfoLabel->setWordWrap(true);
_connInfoLabel->setContentsMargins(0, -2, 0, 20);
_addressLabel = new QLabel("Address:");
_serverAddress = new QLineEdit(QtUtils::toQString(_settings->serverHost()));
_colon = new QLabel(":");
_serverPort = new QLineEdit(QString::number(_settings->serverPort()));
_serverPort->setFixedWidth(80);
QRegExp rx("\\d+");//(0-65554)
_serverPort->setValidator(new QRegExpValidator(rx, this));
QRegExp rx("\\d+"); //(0-65554)
_serverPort->setValidator(new QRegExpValidator(rx, this));
_addInfoLabel = new QLabel("Specify host and port of MongoDB server. Host can be either IPv4, IPv6 or domain name.");
_addInfoLabel->setWordWrap(true);
_addInfoLabel->setContentsMargins(0, -2, 0, 20);
QGridLayout *connectionLayout = new QGridLayout;
_membersLabel = new QLabel("Members:");
_membersLabel->setFixedWidth(_membersLabel->sizeHint().width());
_members = new QTreeWidget;
_members->setHeaderHidden(true);
_members->setIndentation(0);
#ifdef _WIN32
auto lineHeight = _members->fontMetrics().height();
_members->setFixedHeight(lineHeight * 8);
#endif
VERIFY(connect(_members, SIGNAL(itemChanged(QTreeWidgetItem*, int)),
this, SLOT(on_replicaMemberItemEdit(QTreeWidgetItem*, int))));
if (_settings->isReplicaSet() && _settings->replicaSetSettings()->members().size() > 0) {
for (const std::string& str : _settings->replicaSetSettings()->members()) {
if (!str.empty()) {
auto item = new QTreeWidgetItem;
item->setText(0, QString::fromStdString(str));
item->setFlags(item->flags() | Qt::ItemIsEditable);
_members->addTopLevelItem(item);
}
}
// To fix strange MAC alignment issue
#ifndef _WIN32
auto lineHeight = _members->fontMetrics().height();
_members->setFixedHeight(lineHeight * 8);
#endif
}
else { // No members
auto item = new QTreeWidgetItem;
item->setText(0, "localhost:27017");
item->setFlags(item->flags() | Qt::ItemIsEditable);
_members->addTopLevelItem(item);
}
int const BUTTON_SIZE = 60;
_addButton = new QPushButton;
_addButton->setIcon(GuiRegistry::instance().plusIcon());
_removeButton = new QPushButton;
_removeButton->setIcon(GuiRegistry::instance().minusIcon());
VERIFY(connect(_addButton, SIGNAL(clicked()), this, SLOT(on_addButton_clicked())));
VERIFY(connect(_removeButton, SIGNAL(clicked()), this, SLOT(on_removeButton_clicked())));
_minusPlusButtonBox = new QDialogButtonBox(this);
_minusPlusButtonBox->setOrientation(Qt::Horizontal);
#ifdef _WIN32
_minusPlusButtonBox->addButton(_addButton, QDialogButtonBox::NoRole);
_minusPlusButtonBox->addButton(_removeButton, QDialogButtonBox::NoRole);
#else
_minusPlusButtonBox->addButton(_removeButton, QDialogButtonBox::NoRole);
_minusPlusButtonBox->addButton(_addButton, QDialogButtonBox::NoRole);
#endif
_setNameLabel = new QLabel("Set Name:<br><i><font color=\"gray\">(Optional)</font></i>");
_setNameEdit = new QLineEdit(QString::fromStdString(_settings->replicaSetSettings()->setNameUserEntered()));
auto _optionalLabel = new QLabel("<i><font color=\"gray\">(Optional)</font></i>");
auto connectionLayout = new QGridLayout;
connectionLayout->setVerticalSpacing(10);
connectionLayout->setAlignment(Qt::AlignTop);
connectionLayout->addWidget(new QLabel("Name:"), 1, 0);
connectionLayout->addWidget(_connectionName, 1, 1, 1, 3);
connectionLayout->addWidget(connectionDescriptionLabel, 2, 1, 1, 3);
connectionLayout->addWidget(new QLabel("Address:"), 3, 0);
connectionLayout->addWidget(_serverAddress, 3, 1);
connectionLayout->addWidget(new QLabel(":"), 3, 2);
connectionLayout->addWidget(_serverPort, 3, 3);
connectionLayout->addWidget(serverDescriptionLabel, 4, 1, 1, 3);
connectionLayout->addWidget(_typeLabel, 1, 0);
connectionLayout->addWidget(_connectionType, 1, 1, 1, 3);
connectionLayout->addWidget(new QLabel(""), 2, 0);
connectionLayout->addWidget(_nameLabel, 3, 0);
connectionLayout->addWidget(_connectionName, 3, 1, 1, 3);
connectionLayout->addWidget(_connInfoLabel, 4, 1, 1, 3);
connectionLayout->addWidget(_addressLabel, 5, 0);
connectionLayout->addWidget(_serverAddress, 5, 1);
connectionLayout->addWidget(_colon, 5, 2);
connectionLayout->addWidget(_serverPort, 5, 3);
connectionLayout->addWidget(_addInfoLabel, 6, 1, 1, 3);
connectionLayout->addWidget(_membersLabel, 7, 0, Qt::AlignTop);
connectionLayout->addWidget(_members, 7, 1, 1, 3);
connectionLayout->addWidget(_minusPlusButtonBox, 8, 3, Qt::AlignRight | Qt::AlignTop);
connectionLayout->addWidget(new QLabel(""), 9, 0);
connectionLayout->addWidget(_setNameLabel, 10, 0/*, Qt::AlignBottom*/);
connectionLayout->addWidget(_setNameEdit, 10, 1, 1, 3, Qt::AlignTop);
//connectionLayout->addWidget(_optionalLabel, 11, 1, 1, 3, Qt::AlignLeft | Qt::AlignTop);
connectionLayout->addWidget(new QLabel(""), 12, 0);
QVBoxLayout *mainLayout = new QVBoxLayout;
auto mainLayout = new QVBoxLayout;
mainLayout->addLayout(connectionLayout);
setLayout(mainLayout);
_connectionName->setFocus();
on_ConnectionTypeChange(_connectionType->currentIndex());
}
void ConnectionBasicTab::accept()
bool ConnectionBasicTab::accept()
{
_settings->setReplicaSet(static_cast<bool>(_connectionType->currentIndex()));
_settings->setConnectionName(QtUtils::toStdString(_connectionName->text()));
_settings->setServerHost(QtUtils::toStdString(_serverAddress->text()));
_settings->setServerPort(_serverPort->text().toInt());
if (_settings->isReplicaSet() && _members->topLevelItemCount() == 0) {
QMessageBox::critical(this, "Error", "Replica set members cannot be empty. "
"Please enter at least one member.");
return false;
}
// Check and warn if there is duplicate member or
// if any of the replica set member items does not contain ":" character between hostname and port.
if (_settings->isReplicaSet() && _members->topLevelItemCount() > 1) {
QStringList members;
for (int i = 0; i < _members->topLevelItemCount(); ++i) {
QTreeWidgetItem const* item = _members->topLevelItem(i);
QStringList const hostAndPort = item->text(0).split(":");
if (hostAndPort.size() < 2) {
QMessageBox::critical(this, "Error", "Replica set member items must all contain ':' between"
" hostname and port.");
return false;
}
if (!item->text(0).isEmpty())
members.push_back(item->text(0));
}
if (members.size() > 1) {
if (members.removeDuplicates() > 0) {
QMessageBox::critical(this, "Error", "Please remove duplicate member, two replica"
" set members cannot have the same hostname and port.");
return false;
}
}
}
// Save to settings
if (_settings->isReplicaSet() && _members->topLevelItemCount() > 0) {
QStringList const hostAndPort = _members->topLevelItem(0)->text(0).split(":");
_settings->setServerHost(hostAndPort[0].toStdString());
_settings->setServerPort(hostAndPort[1].toInt());
}
else { // Single server
_settings->setServerHost(QtUtils::toStdString(_serverAddress->text()));
_settings->setServerPort(_serverPort->text().toInt());
}
if (_settings->isReplicaSet()) {
// Save replica members
std::vector<std::string> members;
for (int i = 0; i < _members->topLevelItemCount(); ++i)
{
QTreeWidgetItem const* item = _members->topLevelItem(i);
if (!item->text(0).isEmpty())
members.push_back(item->text(0).toStdString());
}
_settings->replicaSetSettings()->setMembers(members);
_settings->replicaSetSettings()->setSetNameUserEntered(_setNameEdit->text().toStdString());
// Clear cached set name
_settings->replicaSetSettings()->setCachedSetName("");
}
return true;
}
void ConnectionBasicTab::on_ConnectionTypeChange(int index)
{
bool const isReplica = static_cast<bool>(index);
// Replica set
_membersLabel->setVisible(isReplica);
_members->setVisible(isReplica);
_minusPlusButtonBox->setVisible(isReplica);
_setNameLabel->setVisible(isReplica);
_setNameEdit->setVisible(isReplica);
// Direct Connection
_addressLabel->setVisible(!isReplica);
_serverAddress->setVisible(!isReplica);
_serverPort->setVisible(!isReplica);
_colon->setVisible(!isReplica);
_addInfoLabel->setVisible(!isReplica);
}
void ConnectionBasicTab::deleteItem()
{
delete _members->currentItem(); // todo: refactor
}
void ConnectionBasicTab::on_addButton_clicked()
{
auto item = new QTreeWidgetItem;
// Make member addition little smarter than expected
if (_members->topLevelItemCount() < 1) {
item->setText(0, "localhost:27017");
}
else { // Add the next member using last entered hostname and incremented port by one
QString const& lastMember = _members->topLevelItem(_members->topLevelItemCount()-1)->text(0);
QStringList const& hostAndPort = lastMember.split(':');
if (hostAndPort.size() == 2) {
auto const& hostName = hostAndPort[0];
auto const& port = hostAndPort[1].toInt();
item->setText(0, hostName + ':' + QString::number(port + 1));
}
else {
item->setText(0, "localhost:" + QString::number(_members->topLevelItemCount() + 27017));
}
}
item->setFlags(item->flags() | Qt::ItemIsEditable);
_members->addTopLevelItem(item);
}
void ConnectionBasicTab::on_removeButton_clicked()
{
if (_members->topLevelItemCount() <= 0)
return;
if (_members->currentItem())
delete _members->currentItem();
else
delete _members->topLevelItem(_members->topLevelItemCount() - 1);
}
void ConnectionBasicTab::on_replicaMemberItemEdit(QTreeWidgetItem* item, int column)
{
if (!item)
return;
auto str = item->text(0);
// Remove white spaces
str = str.simplified();
str.remove(" ");
// Remove item from tree widget if it has empty text
if (str.isEmpty()) {
delete item;
return;
}
// Force port as integer
QStringList const& hostAndPort = str.split(':');
if (hostAndPort.size() >= 2) {
auto const& hostName = hostAndPort[0];
auto portStr = hostAndPort[1];
portStr.remove(QRegExp("[^\\d]"));
str = hostName + ':' + QString::number(portStr.toInt());
}
else
str += ":27017";
item->setText(0, str);
}
}

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