diff --git a/guacamole/src/main/webapp/app/manage/controllers/connectionEditModalController.js b/guacamole/src/main/webapp/app/manage/controllers/connectionEditModalController.js index 3364dfcb0..4ad156dd0 100644 --- a/guacamole/src/main/webapp/app/manage/controllers/connectionEditModalController.js +++ b/guacamole/src/main/webapp/app/manage/controllers/connectionEditModalController.js @@ -29,12 +29,14 @@ angular.module('manage').controller('connectionEditModalController', ['$scope', var connectionEditModal = $injector.get('connectionEditModal'); var connectionService = $injector.get('connectionService'); var displayObjectPreparationService = $injector.get('displayObjectPreparationService'); + var Connection = $injector.get('Connection'); + var HistoryEntryWrapper = $injector.get('HistoryEntryWrapper'); // Make a copy of the old connection so that we can copy over the changes when done var oldConnection = $scope.connection; // Copy data into a new conection object in case the user doesn't want to save - $scope.connection = angular.copy($scope.connection); + $scope.connection = new Connection($scope.connection); var newConnection = !$scope.connection.identifier; if(newConnection) @@ -45,6 +47,13 @@ angular.module('manage').controller('connectionEditModalController', ['$scope', if(!$scope.connection.protocol) $scope.connection.protocol = "vnc"; + $scope.historyEntryWrappers = []; + + // Wrap all the history entries + $scope.connection.history.forEach(function wrapHistoryEntry(historyEntry) { + $scope.historyEntryWrappers.push(new HistoryEntryWrapper(historyEntry)); + }); + /** * Close the modal. */ @@ -103,7 +112,8 @@ angular.module('manage').controller('connectionEditModalController', ['$scope', // Close the modal connectionEditModal.deactivate(); }); - } + }; + }]); diff --git a/guacamole/src/main/webapp/app/manage/directives/connectionParameter.js b/guacamole/src/main/webapp/app/manage/directives/connectionParameter.js index fcbd64cc1..f3f931017 100644 --- a/guacamole/src/main/webapp/app/manage/directives/connectionParameter.js +++ b/guacamole/src/main/webapp/app/manage/directives/connectionParameter.js @@ -24,7 +24,7 @@ /** * A directive that allows editing of a connection parameter. */ -angular.module('manage').directive('guacConnectionParameter', [function locationChooser() { +angular.module('manage').directive('guacConnectionParameter', [function connectionParameter() { return { // Element only @@ -41,14 +41,44 @@ angular.module('manage').directive('guacConnectionParameter', [function location $scope.parameterName = $scope.parameter.name; // Coerce numeric strings to numbers - if($scope.parameterType === 'NUMERIC') { - $scope.connectionParameters[$scope.parameterName] = - Number($scope.connectionParameters[$scope.parameterName]) || 0; - // Coerce boolean strings to boolean values - } else if($scope.parameterType === 'BOOLEAN') { - $scope.connectionParameters[$scope.parameterName] = - $scope.connectionParameters[$scope.parameterName] === 'true'; + if ($scope.parameterType === 'NUMERIC') { + + // If a value exists, coerce it to a number + if ($scope.connectionParameters[$scope.parameterName]) + $scope.parameterValue = Number($scope.connectionParameters[$scope.parameterName]); + else + $scope.parameterValue = null; + } + + // Coerce boolean strings to boolean values + else if ($scope.parameterType === 'BOOLEAN') { + // TODO: Use defined checked value from protocol description + $scope.parameterValue = $scope.connectionParameters[$scope.parameterName] === 'true'; + } + + // All other parameter types are represented internally as strings + else + $scope.parameterValue = $scope.connectionParameters[$scope.parameterName]; + + // Update internal representation as model is changed + $scope.$watch('parameterValue', function parameterValueChanges(value) { + + // Convert numeric values back into strings + if ($scope.parameterType === 'NUMERIC') { + if (value === null || typeof value === 'undefined') + $scope.connectionParameters[$scope.parameterName] = ''; + else + $scope.connectionParameters[$scope.parameterName] = value.toString(); + } + + // TODO: Transform BOOLEAN input fields back into strings based on protocol description + + // All other parameter types are already strings + else + $scope.connectionParameters[$scope.parameterName] = value; + + }); }] }; diff --git a/guacamole/src/main/webapp/app/manage/manageModule.js b/guacamole/src/main/webapp/app/manage/manageModule.js index 1f469b40c..9ea80c415 100644 --- a/guacamole/src/main/webapp/app/manage/manageModule.js +++ b/guacamole/src/main/webapp/app/manage/manageModule.js @@ -23,5 +23,5 @@ /** * The module for the administration functionality. */ -angular.module('manage', ['btford.modal', 'protocol', 'connectionGroup', 'util']); +angular.module('manage', ['btford.modal', 'protocol', 'connection', 'connectionGroup', 'util']); diff --git a/guacamole/src/main/webapp/app/manage/templates/connectionParameter.html b/guacamole/src/main/webapp/app/manage/templates/connectionParameter.html index 30cefd3db..4249f3a3a 100644 --- a/guacamole/src/main/webapp/app/manage/templates/connectionParameter.html +++ b/guacamole/src/main/webapp/app/manage/templates/connectionParameter.html @@ -20,10 +20,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --> - - - - - - + + + + + + \ No newline at end of file diff --git a/guacamole/src/main/webapp/app/manage/templates/editableConnection.html b/guacamole/src/main/webapp/app/manage/templates/editableConnection.html index 4a3755faa..2d8e5805a 100644 --- a/guacamole/src/main/webapp/app/manage/templates/editableConnection.html +++ b/guacamole/src/main/webapp/app/manage/templates/editableConnection.html @@ -94,12 +94,12 @@ THE SOFTWARE. {{'manage.edit.connection.history.duration' | translate}} - - {{record.username}} - {{record.startDate | date:'short'}} - {{record.endDate - record.startDate}} - {{'manage.edit.connection.history.unknownEnd' | translate}} - {{'manage.edit.connection.history.activeNow' | translate}} + + {{wrapper.entry.username}} + {{wrapper.entry.startDate | date:'short'}} + {{'manage.edit.connection.history.formattedDuration' | translate:"{VALUE: wrapper.duration.value, UNIT: wrapper.duration.unit}"}} + {{'manage.edit.connection.history.unknownEnd' | translate}} + {{'manage.edit.connection.history.activeNow' | translate}} diff --git a/guacamole/src/main/webapp/app/manage/types/HistoryEntryWrapper.js b/guacamole/src/main/webapp/app/manage/types/HistoryEntryWrapper.js new file mode 100644 index 000000000..f40f40a42 --- /dev/null +++ b/guacamole/src/main/webapp/app/manage/types/HistoryEntryWrapper.js @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2014 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 generating new guacClient properties objects. + */ +angular.module('manage').factory('HistoryEntryWrapper', [function defineHistoryEntryWrapper() { + + /** + * Given a number of milliseconds, returns an object containing a unit and value + * for that history entry duration. + * + * @param {Number} milliseconds The number of milliseconds. + * @return {Object} A unit and value pair representing a history entry duration. + */ + var formatMilliseconds = function formatMilliseconds(milliseconds) { + + var seconds = milliseconds / 1000; + + /** + * Rounds the given value to the nearest tenth. + * + * @param {Number} value The value to round. + * @returns {Number} The given value, rounded to the nearest tenth. + */ + var round = function round(value) { + return Math.round(value * 10) / 10; + }; + + // Seconds + if (seconds < 60) { + return { + value : round(seconds), + unit : "second" + }; + } + + // Minutes + if (seconds < 3600) { + return { + value : round(seconds / 60 ), + unit : "minute" + }; + } + + // Hours + if (seconds < 86400) { + return { + value : round(seconds / 3600), + unit : "hour" + }; + } + + // Days + return { + value : round(seconds / 86400), + unit : "day" + }; + + }; + + /** + * Wrapper for ConnectionHistoryEntry which adds display-specific + * properties, such as the connection duration. + * + * @constructor + * @param {ConnectionHistoryEntry} historyEntry + * The history entry to wrap. + */ + var HistoryEntryWrapper = function HistoryEntryWrapper(historyEntry) { + + /** + * The wrapped ConnectionHistoryEntry. + * + * @type ConnectionHistoryEntry + */ + this.entry = historyEntry; + + /** + * An object providing value and unit properties, denoting the duration + * and its corresponding units. + * + * @type Object + */ + this.duration = null; + + // Set the duration if the necessary information is present + if (historyEntry.endDate && historyEntry.startDate) + this.duration = formatMilliseconds(historyEntry.endDate - historyEntry.startDate); + + }; + + return HistoryEntryWrapper; + +}]); \ No newline at end of file diff --git a/guacamole/src/main/webapp/translations/en_US.json b/guacamole/src/main/webapp/translations/en_US.json index 5860af733..fd7f20e06 100644 --- a/guacamole/src/main/webapp/translations/en_US.json +++ b/guacamole/src/main/webapp/translations/en_US.json @@ -53,7 +53,8 @@ "startTime" : "Start Time", "duration" : "Duration", "activeNow" : "Active Now", - "unknownEnd" : "--" + "unknownEnd" : "--", + "formattedDuration" : "{VALUE} {UNIT, select, second{{VALUE, plural, one{second} other{seconds}}} minute{{VALUE, plural, one{minute} other{minutes}}} hour{{VALUE, plural, one{hour} other{hours}}} day{{VALUE, plural, one{day} other{days}}} other{}}" } }, "connectionGroup": {