From 7966058928cc1ab9a282bd7e166fd2c08046c1d0 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Wed, 25 Mar 2015 22:01:18 -0700 Subject: [PATCH 1/3] GUAC-1138 Implemented global active session filter against name, username, and remote host. --- .../controllers/manageSessionsController.js | 60 ++++++++++++++---- .../webapp/app/manage/styles/sessions.css | 9 +++ .../app/manage/templates/manageSessions.html | 6 +- .../src/main/webapp/images/magnifier.png | Bin 0 -> 1058 bytes .../src/main/webapp/translations/en_US.json | 2 + 5 files changed, 64 insertions(+), 13 deletions(-) create mode 100644 guacamole/src/main/webapp/images/magnifier.png diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageSessionsController.js b/guacamole/src/main/webapp/app/manage/controllers/manageSessionsController.js index 17603e209..892c6d244 100644 --- a/guacamole/src/main/webapp/app/manage/controllers/manageSessionsController.js +++ b/guacamole/src/main/webapp/app/manage/controllers/manageSessionsController.js @@ -54,6 +54,13 @@ angular.module('manage').controller('manageSessionsController', ['$scope', '$inj */ $scope.wrappers = null; + /** + * The filter search string to use to restrict the displayed active sessions + * + * @type String + */ + $scope.filterSearchString = null; + /** * StableSort instance which maintains the sort order of the visible * connection wrappers. @@ -67,13 +74,6 @@ angular.module('manage').controller('manageSessionsController', ['$scope', '$inj 'name' ]); - /** - * The root connection group of the connection group hierarchy. - * - * @type ConnectionGroup - */ - var rootGroup = null; - /** * All active connections, if known, or null if active connections have not * yet been loaded. @@ -83,11 +83,12 @@ angular.module('manage').controller('manageSessionsController', ['$scope', '$inj var activeConnections = null; /** - * Map of all visible connections by object identifier. + * Map of all visible connections by object identifier, or null if visible + * connections have not yet been loaded. * * @type Object. */ - var connections = {}; + var connections = null; /** * Map of all currently-selected active connection wrappers by identifier. @@ -118,7 +119,7 @@ angular.module('manage').controller('manageSessionsController', ['$scope', '$inj * The connection group whose descendant connections should be added to * the internal set of connections. */ - var addDescendantConnections = function addDescendantConnections(connectionGroup) { + var addDescendantConnections = function addDescendantConnections(connectionGroup) { // Add all child connections if (connectionGroup.childConnections) @@ -168,8 +169,8 @@ angular.module('manage').controller('manageSessionsController', ['$scope', '$inj .success(function connectionGroupReceived(retrievedRootGroup) { // Load connections from retrieved group tree - rootGroup = retrievedRootGroup; - addDescendantConnections(rootGroup); + connections = {}; + addDescendantConnections(retrievedRootGroup); // Attempt to produce wrapped list of active connections wrapActiveConnections(); @@ -351,5 +352,40 @@ angular.module('manage').controller('manageSessionsController', ['$scope', '$inj delete selectedWrappers[wrapper.activeConnection.identifier]; }; + + /** + * A predicate to be used for filtering the active sessions based on a plain + * text search string. A wrapper will be considered a match iff the search string + * appears (case insensitive) in the connection name, username, or remote host. + * + * @param {ActiveConnectionWrapper} wrapper + * The wrapper to match against the search string.. + * + * @returns {Boolean} + * true if the wrapper matches the specified search string, false otherwise. + */ + $scope.globalFilterPredicate = function globalFilterPredicate(wrapper) { + + // If no search term is provided, always consider it a match + if (!$scope.filterSearchString) + return true; + + // Convert to lower case for case insensitive matching + var searchString = $scope.filterSearchString.toLowerCase(); + + // Check to see if the search string matches the connection name + if (wrapper.name.toLowerCase().indexOf(searchString) !== -1) + return true; + + // Check to see if the search string matches the username + if (wrapper.activeConnection.username.toLowerCase().indexOf(searchString) !== -1) + return true; + + // Check to see if the search string matches the remote host + if (wrapper.activeConnection.remoteHost.toLowerCase().indexOf(searchString) !== -1) + return true; + + return false; + }; }]); diff --git a/guacamole/src/main/webapp/app/manage/styles/sessions.css b/guacamole/src/main/webapp/app/manage/styles/sessions.css index 30b486828..0a0fbd8e9 100644 --- a/guacamole/src/main/webapp/app/manage/styles/sessions.css +++ b/guacamole/src/main/webapp/app/manage/styles/sessions.css @@ -76,3 +76,12 @@ .manage table.session-list th.sort-primary.sort-descending:after { background-image: url('images/arrows/up.png'); } + +.manage .sessions .filter{ + background-image: url('images/magnifier.png'); + background-repeat: no-repeat; + background-size: 1.75em; + padding-left: 1.75em; + width: 100%; + max-width: none; +} diff --git a/guacamole/src/main/webapp/app/manage/templates/manageSessions.html b/guacamole/src/main/webapp/app/manage/templates/manageSessions.html index d2b29adc8..f925d6e00 100644 --- a/guacamole/src/main/webapp/app/manage/templates/manageSessions.html +++ b/guacamole/src/main/webapp/app/manage/templates/manageSessions.html @@ -35,6 +35,10 @@ THE SOFTWARE.
+ +
+ +
@@ -78,7 +82,7 @@ THE SOFTWARE.

- + \ No newline at end of file diff --git a/guacamole/src/main/webapp/images/magnifier.png b/guacamole/src/main/webapp/images/magnifier.png new file mode 100644 index 0000000000000000000000000000000000000000..15261209e842bde1924bd7c83070e5b64c809651 GIT binary patch literal 1058 zcmV+-1l{|IP)>Ds39z4(?-e$j@)!N8ur@ z(S`x0@fkiykhPC*u$3UIk!%A@;Y+dCOOEjZe_|U4z3*SdGOl5!;d6)h7MsbsBG&*D z_(Jo_KNkD?BzK;~b+K(`i)V-UN(%wYw^Y7myJQG`EsWLy;d-ig8HR}u;p-Vn8Xv|}=^4mkoJHq$) ztC+tnPiK;ZO^Ll*WnBcjQ8>hR#oR1zjwpA43)WA~%?eju5uUJTuy~s6J-drfHTM(O@JphuZ)u||yefKw zW$>!9@1ImQ=j2$lA)2DCkm&(^-iS+$RqxIOQzFI9IDHY0@JE_<4T`kEzEJu=xb!I5 zW>y&Wb%I_s+A=_&E&MLB#FTLNrWHRHMd`>$PtE4wAKM!9WPL?N`Fsm#A&fYay~I`0?r6XIB$ zdKMT*0}%DZb@jW#Md2Z7-*n@&JgVT!Vt`}ukFhET_+eDRmthO^*%g6rNx1z(E&h9~qobpvqobpv cqvJo$-$k;gRcIppj{pDw07*qoM6N<$f;~p|@Bjb+ literal 0 HcmV?d00001 diff --git a/guacamole/src/main/webapp/translations/en_US.json b/guacamole/src/main/webapp/translations/en_US.json index 7348958d9..46876aa5b 100644 --- a/guacamole/src/main/webapp/translations/en_US.json +++ b/guacamole/src/main/webapp/translations/en_US.json @@ -243,6 +243,8 @@ "DIALOG_HEADER_CONFIRM_DELETE" : "Kill Sessions", "DIALOG_HEADER_ERROR" : "Error", + "FIELD_PLACEHOLDER_FILTER" : "Filter", + "HELP_SESSIONS" : "All currently-active Guacamole sessions are listed here. If you wish to kill one or more sessions, check the box next to those sessions and click \"Kill Sessions\". Killing a session will immediately disconnect the user from the associated connection.", "INFO_NO_SESSIONS" : "No active sessions", From 770078fc6e96f09d6fdda29c29fe9b8cfea01f63 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Wed, 25 Mar 2015 22:36:18 -0700 Subject: [PATCH 2/3] GUAC-1138 Fix modal.min.js warning during build. --- guacamole/pom.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/guacamole/pom.xml b/guacamole/pom.xml index ff35797ed..ff75a224e 100644 --- a/guacamole/pom.xml +++ b/guacamole/pom.xml @@ -137,7 +137,6 @@ lib/plugins/angular-translate.js lib/plugins/angular-translate-loader-static-files.js lib/plugins/angular-translate-interpolation-messageformat.js - lib/plugins/modal.min.js lib/blob/blob.js lib/filesaver/filesaver.js lib/messageformat/messageformat.js From d26d354ff9a0d86188f9bc25e3e485fee8d60039 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Wed, 25 Mar 2015 22:38:01 -0700 Subject: [PATCH 3/3] GUAC-1138 Migrate to FilterPattern class. --- .../controllers/manageSessionsController.js | 46 ++------ .../app/manage/templates/manageSessions.html | 3 +- .../webapp/app/manage/types/FilterPattern.js | 106 ++++++++++++++++++ 3 files changed, 120 insertions(+), 35 deletions(-) create mode 100644 guacamole/src/main/webapp/app/manage/types/FilterPattern.js diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageSessionsController.js b/guacamole/src/main/webapp/app/manage/controllers/manageSessionsController.js index 892c6d244..aa81a16ff 100644 --- a/guacamole/src/main/webapp/app/manage/controllers/manageSessionsController.js +++ b/guacamole/src/main/webapp/app/manage/controllers/manageSessionsController.js @@ -29,6 +29,7 @@ angular.module('manage').controller('manageSessionsController', ['$scope', '$inj // Required types var ActiveConnectionWrapper = $injector.get('ActiveConnectionWrapper'); var ConnectionGroup = $injector.get('ConnectionGroup'); + var FilterPattern = $injector.get('FilterPattern'); var StableSort = $injector.get('StableSort'); // Required services @@ -61,6 +62,13 @@ angular.module('manage').controller('manageSessionsController', ['$scope', '$inj */ $scope.filterSearchString = null; + /** + * The pattern object to use when filtering active sessions. + * + * @type FilterPattern + */ + $scope.filterPattern = new FilterPattern(); + /** * StableSort instance which maintains the sort order of the visible * connection wrappers. @@ -353,39 +361,9 @@ angular.module('manage').controller('manageSessionsController', ['$scope', '$inj }; - /** - * A predicate to be used for filtering the active sessions based on a plain - * text search string. A wrapper will be considered a match iff the search string - * appears (case insensitive) in the connection name, username, or remote host. - * - * @param {ActiveConnectionWrapper} wrapper - * The wrapper to match against the search string.. - * - * @returns {Boolean} - * true if the wrapper matches the specified search string, false otherwise. - */ - $scope.globalFilterPredicate = function globalFilterPredicate(wrapper) { - - // If no search term is provided, always consider it a match - if (!$scope.filterSearchString) - return true; - - // Convert to lower case for case insensitive matching - var searchString = $scope.filterSearchString.toLowerCase(); - - // Check to see if the search string matches the connection name - if (wrapper.name.toLowerCase().indexOf(searchString) !== -1) - return true; - - // Check to see if the search string matches the username - if (wrapper.activeConnection.username.toLowerCase().indexOf(searchString) !== -1) - return true; - - // Check to see if the search string matches the remote host - if (wrapper.activeConnection.remoteHost.toLowerCase().indexOf(searchString) !== -1) - return true; - - return false; - }; + // Recompile the filter pattern when changed + $scope.$watch('filterSearchString', function recompilePredicate(searchString) { + $scope.filterPattern.compile(searchString); + }); }]); diff --git a/guacamole/src/main/webapp/app/manage/templates/manageSessions.html b/guacamole/src/main/webapp/app/manage/templates/manageSessions.html index f925d6e00..26fc52b6c 100644 --- a/guacamole/src/main/webapp/app/manage/templates/manageSessions.html +++ b/guacamole/src/main/webapp/app/manage/templates/manageSessions.html @@ -82,7 +82,8 @@ THE SOFTWARE.

- + \ No newline at end of file diff --git a/guacamole/src/main/webapp/app/manage/types/FilterPattern.js b/guacamole/src/main/webapp/app/manage/types/FilterPattern.js new file mode 100644 index 000000000..2aaeddc2e --- /dev/null +++ b/guacamole/src/main/webapp/app/manage/types/FilterPattern.js @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * A service for defining the FilterPattern class. + */ +angular.module('manage').factory('FilterPattern', [ + function defineFilterPattern() { + + /** + * Object which handles compilation of filtering predicates as used by + * the Angular "filter" filter. Predicates are compiled from a user- + * specified search string. + * + * @constructor + */ + var FilterPattern = function FilterPattern() { + + /** + * Reference to this instance. + * + * @type FilterPattern + */ + var filterPattern = this; + + /** + * Filter predicate which simply matches everything. This function + * always returns true. + * + * @returns {Boolean} + * true. + */ + var nullPredicate = function nullPredicate() { + return true; + }; + + /** + * The current filtering predicate. + * + * @type Function + */ + this.predicate = nullPredicate; + + /** + * Compiles the given pattern string, assigning the resulting filter + * predicate. The resulting predicate will accept only objects that + * match the given pattern. + * + * @param {String} pattern + * The pattern to compile. + */ + this.compile = function compile(pattern) { + + // If no pattern provided, everything matches + if (!pattern) { + filterPattern.predicate = nullPredicate; + return; + } + + // Convert to lower case for case insensitive matching + pattern = pattern.toLowerCase(); + + // TODONT: Return predicate specific to a type of object this class should know nothing about + filterPattern.predicate = function oddlySpecificPredicate(wrapper) { + + // Check to see if the search string matches the connection name + if (wrapper.name.toLowerCase().indexOf(pattern) !== -1) + return true; + + // Check to see if the search string matches the username + if (wrapper.activeConnection.username.toLowerCase().indexOf(pattern) !== -1) + return true; + + // Check to see if the search string matches the remote host + if (wrapper.activeConnection.remoteHost.toLowerCase().indexOf(pattern) !== -1) + return true; + + return false; + }; + + }; + + }; + + return FilterPattern; + +}]); \ No newline at end of file