GUACAMOLE-5: Merge contextual create link changes.

This commit is contained in:
James Muehlner
2016-08-10 16:44:31 -07:00
17 changed files with 321 additions and 176 deletions

View File

@@ -46,37 +46,18 @@ angular.module('groupList').directive('guacGroupList', [function guacGroupList()
context : '=', context : '=',
/** /**
* The URL or ID of the Angular template to use when rendering a * The map of @link{GroupListItem} type to the URL or ID of the
* connection. The @link{GroupListItem} associated with that * Angular template to use when rendering a @link{GroupListItem} of
* connection will be exposed within the scope of the template * that type. The @link{GroupListItem} itself will be within the
* as <code>item</code>, and the arbitrary context object, if any, * scope of the template as <code>item</code>, and the arbitrary
* will be exposed as <code>context</code>. * context object, if any, will be exposed as <code>context</code>.
* If the template for a type is omitted, items of that type will
* not be rendered. All standard types are defined by
* @link{GroupListItem.Type}, but use of custom types is legal.
* *
* @type String * @type Object.<String, String>
*/ */
connectionTemplate : '=', templates : '=',
/**
* The URL or ID of the Angular template to use when rendering a
* connection group. The @link{GroupListItem} associated with that
* connection group will be exposed within the scope of the
* template as <code>item</code>, and the arbitrary context object,
* if any, will be exposed as <code>context</code>.
*
* @type String
*/
connectionGroupTemplate : '=',
/**
* The URL or ID of the Angular template to use when rendering a
* sharing profile. The @link{GroupListItem} associated with that
* sharing profile will be exposed within the scope of the template
* as <code>item</code>, and the arbitrary context object, if any,
* will be exposed as <code>context</code>.
*
* @type String
*/
sharingProfileTemplate : '=',
/** /**
* Whether the root of the connection group hierarchy given should * Whether the root of the connection group hierarchy given should
@@ -92,7 +73,18 @@ angular.module('groupList').directive('guacGroupList', [function guacGroupList()
* *
* @type Number * @type Number
*/ */
pageSize : '=' pageSize : '=',
/**
* A callback which accepts an array of GroupListItems as its sole
* parameter. If provided, the callback will be invoked whenever an
* array of root-level GroupListItems is about to be rendered.
* Changes may be made by this function to that array or to the
* GroupListItems themselves.
*
* @type Function
*/
decorator : '='
}, },
@@ -145,51 +137,20 @@ angular.module('groupList').directive('guacGroupList', [function guacGroupList()
}; };
/** /**
* Returns whether the given item represents a connection that can * Returns whether a @link{GroupListItem} of the given type can be
* be displayed. If there is no connection template, then no * displayed. If there is no template associated with the given
* connection is visible. * type, then a @link{GroupListItem} of that type cannot be
* * displayed.
* @param {GroupListItem} item *
* The item to check. * @param {String} type
* The type to check.
* *
* @returns {Boolean} * @returns {Boolean}
* true if the given item is a connection that can be * true if the given @link{GroupListItem} type can be displayed,
* displayed, false otherwise. * false otherwise.
*/ */
$scope.isVisibleConnection = function isVisibleConnection(item) { $scope.isVisible = function isVisible(type) {
return item.isConnection && !!$scope.connectionTemplate; return !!$scope.templates[type];
};
/**
* Returns whether the given item represents a connection group
* that can be displayed. If there is no connection group template,
* then no connection group is visible.
*
* @param {GroupListItem} item
* The item to check.
*
* @returns {Boolean}
* true if the given item is a connection group that can be
* displayed, false otherwise.
*/
$scope.isVisibleConnectionGroup = function isVisibleConnectionGroup(item) {
return item.isConnectionGroup && !!$scope.connectionGroupTemplate;
};
/**
* Returns whether the given item represents a sharing profile that
* can be displayed. If there is no sharing profile template, then
* no sharing profile is visible.
*
* @param {GroupListItem} item
* The item to check.
*
* @returns {Boolean}
* true if the given item is a sharing profile that can be
* displayed, false otherwise.
*/
$scope.isVisibleSharingProfile = function isVisibleSharingProfile(item) {
return item.isSharingProfile && !!$scope.sharingProfileTemplate;
}; };
// Set contents whenever the connection group is assigned or changed // Set contents whenever the connection group is assigned or changed
@@ -212,7 +173,8 @@ angular.module('groupList').directive('guacGroupList', [function guacGroupList()
// Create root item for current connection group // Create root item for current connection group
var rootItem = GroupListItem.fromConnectionGroup(dataSource, connectionGroup, var rootItem = GroupListItem.fromConnectionGroup(dataSource, connectionGroup,
!!$scope.connectionTemplate, !!$scope.sharingProfileTemplate, $scope.isVisible(GroupListItem.Type.CONNECTION),
$scope.isVisible(GroupListItem.Type.SHARING_PROFILE),
countActiveConnections); countActiveConnections);
// If root group is to be shown, add it as a root item // If root group is to be shown, add it as a root item
@@ -255,6 +217,10 @@ angular.module('groupList').directive('guacGroupList', [function guacGroupList()
} }
// Invoke item decorator, if provided
if ($scope.decorator)
$scope.decorator($scope.rootItems);
}); });
/** /**
@@ -265,7 +231,7 @@ angular.module('groupList').directive('guacGroupList', [function guacGroupList()
* connection group. * connection group.
*/ */
$scope.toggleExpanded = function toggleExpanded(groupListItem) { $scope.toggleExpanded = function toggleExpanded(groupListItem) {
groupListItem.isExpanded = !groupListItem.isExpanded; groupListItem.expanded = !groupListItem.expanded;
}; };
}] }]

View File

@@ -1,55 +1,31 @@
<div class="group-list"> <div class="group-list">
<script type="text/ng-template" id="nestedItem.html"> <script type="text/ng-template" id="nestedItem.html">
<div class="{{item.type}}" ng-if="isVisible(item.type)"
ng-class="{
expanded : item.expanded,
expandable : item.expandable,
empty : !item.children.length
}">
<!-- Connection --> <!-- Item caption -->
<div class="connection expandable" ng-if="isVisibleConnection(item)"
ng-class="{expanded: item.isExpanded, empty: !item.children.length}">
<div class="caption"> <div class="caption">
<!-- Expand/collapse icon --> <!-- Expand/collapse icon -->
<div class="icon expand" ng-click="toggleExpanded(item)" <div class="icon expand" ng-click="toggleExpanded(item)"
ng-if="sharingProfileTemplate"></div> ng-if="item.expandable"></div>
<ng-include src="connectionTemplate"/> <ng-include src="templates[item.type]"/>
</div> </div>
<!-- Children of this connection --> <!-- Children of item (if any) -->
<div class="children" ng-show="item.isExpanded"> <div class="children" ng-if="item.expanded">
<div class="list-item" ng-repeat="item in item.children | orderBy : 'name'" <div class="list-item" ng-repeat="item in item.children | orderBy : 'name'"
ng-include="'nestedItem.html'"></div> ng-include="'nestedItem.html'"></div>
</div> </div>
</div> </div>
<!-- Connection group -->
<div class="group expandable" ng-if="isVisibleConnectionGroup(item)"
ng-class="{expanded: item.isExpanded, empty: !item.children.length, balancer: item.isBalancing}">
<div class="caption">
<!-- Expand/collapse icon -->
<div class="icon expand" ng-click="toggleExpanded(item)"></div>
<ng-include src="connectionGroupTemplate"/>
</div>
<!-- Children of this group -->
<div class="children" ng-if="item.isExpanded">
<div class="list-item" ng-repeat="item in item.children | orderBy : 'name'"
ng-include="'nestedItem.html'"></div>
</div>
</div>
<!-- Sharing profile -->
<div class="sharing-profile" ng-show="isVisibleSharingProfile(item)">
<div class="caption">
<ng-include src="sharingProfileTemplate"/>
</div>
</div>
</script> </script>
<!-- Root-level connections / groups --> <!-- Root-level connections / groups -->
@@ -58,7 +34,7 @@
</div> </div>
<!-- Pager for connections / groups --> <!-- Pager for connections / groups -->
<guac-pager page="childrenPage" items="rootItems | orderBy : 'name'" <guac-pager page="childrenPage" items="rootItems | orderBy : ['weight', 'name']"
page-size="pageSize"></guac-pager> page-size="pageSize"></guac-pager>
</div> </div>

View File

@@ -77,43 +77,36 @@ angular.module('groupList').factory('GroupListItem', ['ConnectionGroup', functio
this.children = template.children || []; this.children = template.children || [];
/** /**
* Whether this item represents a connection. If this item represents * The type of object represented by this GroupListItem. Standard types
* a connection group or sharing profile, this MUST be false. * are defined by GroupListItem.Type, but custom types are also legal.
* *
* @type Boolean * @type String
*/ */
this.isConnection = template.isConnection; this.type = template.type;
/** /**
* Whether this item represents a connection group. If this item * Whether this item, or items of the same type, can contain children.
* represents a connection or sharing profile, this MUST be false. * This may be true even if this particular item does not presently
* contain children.
* *
* @type Boolean * @type Boolean
*/ */
this.isConnectionGroup = template.isConnectionGroup; this.expandable = template.expandable;
/**
* Whether this item represents a sharing profile. If this item
* represents a connection or connection group, this MUST be false.
*
* @type Boolean
*/
this.isSharingProfile = template.isSharingProfile;
/** /**
* Whether this item represents a balancing connection group. * Whether this item represents a balancing connection group.
* *
* @type Boolean * @type Boolean
*/ */
this.isBalancing = template.isBalancing; this.balancing = template.balancing;
/** /**
* Whether the children items should be displayed. * Whether the children items should be displayed.
* *
* @type Boolean * @type Boolean
*/ */
this.isExpanded = template.isExpanded; this.expanded = template.expanded;
/** /**
* Returns the number of currently active users for this connection, * Returns the number of currently active users for this connection,
* connection group, or sharing profile, if known. * connection group, or sharing profile, if known.
@@ -126,12 +119,24 @@ angular.module('groupList').factory('GroupListItem', ['ConnectionGroup', functio
/** /**
* The connection, connection group, or sharing profile whose data is * The connection, connection group, or sharing profile whose data is
* exposed within this GroupListItem. * exposed within this GroupListItem. If the type of this GroupListItem
* is not one of the types defined by GroupListItem.Type, then this
* value may be anything.
* *
* @type Connection|ConnectionGroup|SharingProfile * @type Connection|ConnectionGroup|SharingProfile|*
*/ */
this.wrappedItem = template.wrappedItem; this.wrappedItem = template.wrappedItem;
/**
* The sorting weight to apply when displaying this GroupListItem. This
* weight is relative only to other sorting weights. If two items have
* the same weight, they will be sorted based on their names.
*
* @type Number
* @default 0
*/
this.weight = template.weight || 0;
}; };
/** /**
@@ -182,9 +187,8 @@ angular.module('groupList').factory('GroupListItem', ['ConnectionGroup', functio
dataSource : dataSource, dataSource : dataSource,
// Type information // Type information
isConnection : true, expandable : includeSharingProfiles,
isConnectionGroup : false, type : GroupListItem.Type.CONNECTION,
isSharingProfile : false,
// Already-converted children // Already-converted children
children : children, children : children,
@@ -277,10 +281,9 @@ angular.module('groupList').factory('GroupListItem', ['ConnectionGroup', functio
dataSource : dataSource, dataSource : dataSource,
// Type information // Type information
isConnection : false, type : GroupListItem.Type.CONNECTION_GROUP,
isConnectionGroup : true, balancing : connectionGroup.type === ConnectionGroup.Type.BALANCING,
isSharingProfile : false, expandable : true,
isBalancing : connectionGroup.type === ConnectionGroup.Type.BALANCING,
// Already-converted children // Already-converted children
children : children, children : children,
@@ -331,9 +334,7 @@ angular.module('groupList').factory('GroupListItem', ['ConnectionGroup', functio
dataSource : dataSource, dataSource : dataSource,
// Type information // Type information
isConnection : false, type : GroupListItem.Type.SHARING_PROFILE,
isConnectionGroup : false,
isSharingProfile : true,
// Wrapped item // Wrapped item
wrappedItem : sharingProfile wrappedItem : sharingProfile
@@ -342,6 +343,42 @@ angular.module('groupList').factory('GroupListItem', ['ConnectionGroup', functio
}; };
/**
* All pre-defined types of GroupListItems. Note that, while these are the
* standard types supported by GroupListItem and the related guacGroupList
* directive, the type string is otherwise arbitrary and custom types are
* legal.
*
* @type Object.<String, String>
*/
GroupListItem.Type = {
/**
* The standard type string of a GroupListItem which represents a
* connection.
*
* @type String
*/
CONNECTION : 'connection',
/**
* The standard type string of a GroupListItem which represents a
* connection group.
*
* @type String
*/
CONNECTION_GROUP : 'connection-group',
/**
* The standard type string of a GroupListItem which represents a
* sharing profile.
*
* @type String
*/
SHARING_PROFILE : 'sharing-profile'
};
return GroupListItem; return GroupListItem;
}]); }]);

View File

@@ -26,6 +26,7 @@ angular.module('home').controller('homeController', ['$scope', '$injector',
// Get required types // Get required types
var ConnectionGroup = $injector.get('ConnectionGroup'); var ConnectionGroup = $injector.get('ConnectionGroup');
var ClientIdentifier = $injector.get('ClientIdentifier'); var ClientIdentifier = $injector.get('ClientIdentifier');
var GroupListItem = $injector.get('GroupListItem');
// Get required services // Get required services
var authenticationService = $injector.get('authenticationService'); var authenticationService = $injector.get('authenticationService');
@@ -95,15 +96,15 @@ angular.module('home').controller('homeController', ['$scope', '$injector',
getClientIdentifier : function getClientIdentifier(item) { getClientIdentifier : function getClientIdentifier(item) {
// If the item is a connection, generate a connection identifier // If the item is a connection, generate a connection identifier
if (item.isConnection) if (item.type === GroupListItem.Type.CONNECTION)
return ClientIdentifier.toString({ return ClientIdentifier.toString({
dataSource : item.dataSource, dataSource : item.dataSource,
type : ClientIdentifier.Types.CONNECTION, type : ClientIdentifier.Types.CONNECTION,
id : item.identifier id : item.identifier
}); });
// If the item is a connection, generate a connection group identifier // If the item is a connection group, generate a connection group identifier
if (item.isConnectionGroup) if (item.type === GroupListItem.Type.CONNECTION_GROUP)
return ClientIdentifier.toString({ return ClientIdentifier.toString({
dataSource : item.dataSource, dataSource : item.dataSource,
type : ClientIdentifier.Types.CONNECTION_GROUP, type : ClientIdentifier.Types.CONNECTION_GROUP,

View File

@@ -1,5 +1,4 @@
<span class="name"> <span class="name">
<a ng-show="item.balancing" ng-href="#/client/{{context.getClientIdentifier(item)}}">{{item.name}}</a>
<a ng-show="item.isBalancing" ng-href="#/client/{{context.getClientIdentifier(item)}}">{{item.name}}</a> <span ng-show="!item.balancing">{{item.name}}</span>
<span ng-show="!item.isBalancing">{{item.name}}</span>
</span> </span>

View File

@@ -25,8 +25,10 @@
<guac-group-list <guac-group-list
context="context" context="context"
connection-groups="filteredRootConnectionGroups" connection-groups="filteredRootConnectionGroups"
connection-template="'app/home/templates/connection.html'" templates="{
connection-group-template="'app/home/templates/connectionGroup.html'" 'connection' : 'app/home/templates/connection.html',
'connection-group' : 'app/home/templates/connectionGroup.html'
}"
page-size="20"></guac-group-list> page-size="20"></guac-group-list>
</div> </div>

View File

@@ -18,28 +18,28 @@
*/ */
.user, .user,
.group, .connection-group,
.connection { .connection {
cursor: pointer; cursor: pointer;
} }
.user a, .user a,
.connection a, .connection a,
.group a { .connection-group a {
text-decoration:none; text-decoration:none;
color: black; color: black;
} }
.user a:hover, .user a:hover,
.connection a:hover, .connection a:hover,
.group a:hover { .connection-group a:hover {
text-decoration:none; text-decoration:none;
color: black; color: black;
} }
.user a:visited, .user a:visited,
.connection a:visited, .connection a:visited,
.group a:visited { .connection-group a:visited {
text-decoration:none; text-decoration:none;
color: black; color: black;
} }

View File

@@ -178,11 +178,11 @@ div.section {
background-position: center center; background-position: center center;
} }
.group > .caption .icon { .connection-group > .caption .icon {
background-image: url('images/folder-closed.png'); background-image: url('images/folder-closed.png');
} }
.group.expanded > .caption .icon { .connection-group.expanded > .caption .icon {
background-image: url('images/folder-open.png'); background-image: url('images/folder-open.png');
} }
@@ -213,7 +213,7 @@ div.section {
padding-left: 13px; padding-left: 13px;
} }
.group.empty.balancer .icon { .connection-group.empty.balancer .icon {
background-image: url('images/protocol-icons/guac-monitor.png'); background-image: url('images/protocol-icons/guac-monitor.png');
} }

View File

@@ -291,7 +291,10 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i
// If we are creating a new connection, populate skeleton connection data // If we are creating a new connection, populate skeleton connection data
else { else {
$scope.connection = new Connection({ protocol: 'vnc' }); $scope.connection = new Connection({
protocol : 'vnc',
parentIdentifier : $location.search().parent
});
$scope.historyEntryWrappers = []; $scope.historyEntryWrappers = [];
$scope.parameters = {}; $scope.parameters = {};
} }

View File

@@ -175,7 +175,9 @@ angular.module('manage').controller('manageConnectionGroupController', ['$scope'
// If we are creating a new connection group, populate skeleton connection group data // If we are creating a new connection group, populate skeleton connection group data
else else
$scope.connectionGroup = new ConnectionGroup(); $scope.connectionGroup = new ConnectionGroup({
parentIdentifier : $location.search().parent
});
/** /**
* Available connection group types, as translation string / internal value * Available connection group types, as translation string / internal value

View File

@@ -9,7 +9,9 @@
context="groupListContext" context="groupListContext"
show-root-group="true" show-root-group="true"
connection-groups="rootGroups" connection-groups="rootGroups"
connection-group-template="'app/manage/templates/locationChooserConnectionGroup.html'"/> templates="{
'connection-group' : 'app/manage/templates/locationChooserConnectionGroup.html'
}"/>
</div> </div>
</div> </div>

View File

@@ -78,9 +78,11 @@
<guac-group-list <guac-group-list
context="groupListContext" context="groupListContext"
connection-groups="filteredRootGroups" connection-groups="filteredRootGroups"
connection-template="'app/manage/templates/connectionPermission.html'" templates="{
sharing-profile-template="'app/manage/templates/sharingProfilePermission.html'" 'connection' : 'app/manage/templates/connectionPermission.html',
connection-group-template="'app/manage/templates/connectionGroupPermission.html'" 'sharing-profile' : 'app/manage/templates/sharingProfilePermission.html',
'connection-group' : 'app/manage/templates/connectionGroupPermission.html'
}"
page-size="20"/> page-size="20"/>
</div> </div>
</div> </div>

View File

@@ -35,6 +35,7 @@ angular.module('settings').directive('guacSettingsConnections', [function guacSe
// Required types // Required types
var ConnectionGroup = $injector.get('ConnectionGroup'); var ConnectionGroup = $injector.get('ConnectionGroup');
var GroupListItem = $injector.get('GroupListItem');
var PermissionSet = $injector.get('PermissionSet'); var PermissionSet = $injector.get('PermissionSet');
// Required services // Required services
@@ -205,6 +206,112 @@ angular.module('settings').directive('guacSettingsConnections', [function guacSe
}; };
/**
* Returns whether the current user can update the connection group
* having the given identifier within the current data source.
*
* @param {String} identifier
* The identifier of the connection group to check.
*
* @return {Boolean}
* true if the current user can update the connection group
* having the given identifier within the current data source,
* false otherwise.
*/
$scope.canUpdateConnectionGroup = function canUpdateConnectionGroup(identifier) {
// Abort if permissions have not yet loaded
if (!$scope.permissions)
return false;
// Can update the connection if adminstrator or have explicit permission
if (PermissionSet.hasSystemPermission($scope.permissions, PermissionSet.SystemPermissionType.ADMINISTER)
|| PermissionSet.hasConnectionGroupPermission($scope.permissions, PermissionSet.ObjectPermissionType.UPDATE, identifier))
return true;
// Current data sources does not allow the connection group to be updated
return false;
};
/**
* Adds connection-group-specific contextual actions to the given
* array of GroupListItems. Each contextual action will be
* represented by a new GroupListItem.
*
* @param {GroupListItem[]} items
* The array of GroupListItems to which new GroupListItems
* representing connection-group-specific contextual actions
* should be added.
*
* @param {GroupListItem} [parent]
* The GroupListItem representing the connection group which
* contains the given array of GroupListItems, if known.
*/
var addConnectionGroupActions = function addConnectionGroupActions(items, parent) {
// Do nothing if we lack permission to modify the parent at all
if (parent && !$scope.canUpdateConnectionGroup(parent.identifier))
return;
// Add action for creating a child connection, if the user has
// permission to do so
if ($scope.canCreateConnections())
items.push(new GroupListItem({
type : 'new-connection',
dataSource : $scope.dataSource,
weight : 1,
wrappedItem : parent
}));
// Add action for creating a child connection group, if the user
// has permission to do so
if ($scope.canCreateConnectionGroups())
items.push(new GroupListItem({
type : 'new-connection-group',
dataSource : $scope.dataSource,
weight : 1,
wrappedItem : parent
}));
};
/**
* Decorates the given GroupListItem, including all descendants,
* adding contextual actions.
*
* @param {GroupListItem} item
* The GroupListItem which should be decorated with additional
* GroupListItems representing contextual actions.
*/
var decorateItem = function decorateItem(item) {
// If the item is a connection group, add actions specific to
// connection groups
if (item.type === GroupListItem.Type.CONNECTION_GROUP)
addConnectionGroupActions(item.children, item);
// Decorate all children
angular.forEach(item.children, decorateItem);
};
/**
* Callback which decorates all items within the given array of
* GroupListItems, including their descendants, adding contextual
* actions.
*
* @param {GroupListItem[]} items
* The array of GroupListItems which should be decorated with
* additional GroupListItems representing contextual actions.
*/
$scope.rootItemDecorator = function rootItemDecorator(items) {
// Decorate each root-level item
angular.forEach(items, decorateItem);
};
// Retrieve current permissions // Retrieve current permissions
permissionService.getPermissions($scope.dataSource, currentUsername) permissionService.getPermissions($scope.dataSource, currentUsername)
.success(function permissionsRetrieved(permissions) { .success(function permissionsRetrieved(permissions) {
@@ -219,19 +326,19 @@ angular.module('settings').directive('guacSettingsConnections', [function guacSe
if (!$scope.canManageConnections()) if (!$scope.canManageConnections())
$location.path('/'); $location.path('/');
}); // Retrieve all connections for which we have UPDATE or DELETE permission
dataSourceService.apply(
// Retrieve all connections for which we have UPDATE or DELETE permission connectionGroupService.getConnectionGroupTree,
dataSourceService.apply( [$scope.dataSource],
connectionGroupService.getConnectionGroupTree, ConnectionGroup.ROOT_IDENTIFIER,
[$scope.dataSource], [PermissionSet.ObjectPermissionType.UPDATE, PermissionSet.ObjectPermissionType.DELETE]
ConnectionGroup.ROOT_IDENTIFIER, )
[PermissionSet.ObjectPermissionType.UPDATE, PermissionSet.ObjectPermissionType.DELETE] .then(function connectionGroupsReceived(rootGroups) {
) $scope.rootGroups = rootGroups;
.then(function connectionGroupsReceived(rootGroups) { });
$scope.rootGroups = rootGroups;
}); }); // end retrieve permissions
}] }]
}; };

View File

@@ -0,0 +1,34 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
.settings.connections .connection-list .new-connection,
.settings.connections .connection-list .new-connection-group {
opacity: 0.5;
font-style: italic;
}
.settings.connections .connection-list .new-connection a,
.settings.connections .connection-list .new-connection a:hover,
.settings.connections .connection-list .new-connection a:visited,
.settings.connections .connection-list .new-connection-group a,
.settings.connections .connection-list .new-connection-group a:hover,
.settings.connections .connection-list .new-connection-group a:visited {
text-decoration:none;
color: black;
}

View File

@@ -0,0 +1,3 @@
<a ng-href="#/manage/{{item.dataSource}}/connections/?parent={{item.wrappedItem.identifier}}">
<span class="name">{{'SETTINGS_CONNECTIONS.ACTION_NEW_CONNECTION' | translate}}</span>
</a>

View File

@@ -0,0 +1,3 @@
<a ng-href="#/manage/{{item.dataSource}}/connectionGroups/?parent={{item.wrappedItem.identifier}}">
<span class="name">{{'SETTINGS_CONNECTIONS.ACTION_NEW_CONNECTION_GROUP' | translate}}</span>
</a>

View File

@@ -33,7 +33,15 @@
<guac-group-list <guac-group-list
page-size="25" page-size="25"
connection-groups="filteredRootGroups" connection-groups="filteredRootGroups"
connection-template="'app/settings/templates/connection.html'" decorator="rootItemDecorator"
connection-group-template="'app/settings/templates/connectionGroup.html'"/> templates="{
'connection' : 'app/settings/templates/connection.html',
'connection-group' : 'app/settings/templates/connectionGroup.html',
'new-connection' : 'app/settings/templates/newConnection.html',
'new-connection-group' : 'app/settings/templates/newConnectionGroup.html'
}"/>
</div> </div>
</div> </div>