mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 13:17:41 +00:00
Merge 1.0.0 changes back to master.
This commit is contained in:
@@ -30,9 +30,10 @@ angular.module('groupList').directive('guacGroupListFilter', [function guacGroup
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The property to which a subset of the provided map of connection
|
* The property to which a subset of the provided map of connection
|
||||||
* groups will be assigned.
|
* groups will be assigned. The type of each item within the
|
||||||
|
* original map is preserved within the filtered map.
|
||||||
*
|
*
|
||||||
* @type Array
|
* @type Object.<String, ConnectionGroup|GroupListItem>
|
||||||
*/
|
*/
|
||||||
filteredConnectionGroups : '=',
|
filteredConnectionGroups : '=',
|
||||||
|
|
||||||
@@ -155,6 +156,94 @@ angular.module('groupList').directive('guacGroupListFilter', [function guacGroup
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flattens the connection group hierarchy of the given
|
||||||
|
* GroupListItem such that all descendants are copied as immediate
|
||||||
|
* children. The hierarchy of nested items is otherwise completely
|
||||||
|
* preserved. A connection or connection group nested two or more
|
||||||
|
* levels deep within the hierarchy will thus appear within the
|
||||||
|
* returned item in two places: in its original location AND as an
|
||||||
|
* immediate child.
|
||||||
|
*
|
||||||
|
* @param {GroupListItem} item
|
||||||
|
* The GroupListItem whose descendents should be copied as
|
||||||
|
* first-level children.
|
||||||
|
*
|
||||||
|
* @returns {GroupListItem}
|
||||||
|
* A new GroupListItem completely identical to the provided
|
||||||
|
* item, except that absolutely all descendents have been
|
||||||
|
* copied into the first level of children.
|
||||||
|
*/
|
||||||
|
var flattenGroupListItem = function flattenGroupListItem(item) {
|
||||||
|
|
||||||
|
// Replace item with shallow copy
|
||||||
|
item = new GroupListItem(item);
|
||||||
|
|
||||||
|
// Ensure children are defined and independent copies
|
||||||
|
item.children = angular.copy(item.children) || [];
|
||||||
|
|
||||||
|
// Flatten all children to the top-level group
|
||||||
|
angular.forEach(item.children, function flattenChild(child) {
|
||||||
|
if (child.type === GroupListItem.Type.CONNECTION_GROUP) {
|
||||||
|
|
||||||
|
var flattenedChild = flattenConnectionGroup(child);
|
||||||
|
|
||||||
|
// Merge all children
|
||||||
|
Array.prototype.push.apply(
|
||||||
|
item.children,
|
||||||
|
flattenedChild.children
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return item;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces the set of children within the given GroupListItem such
|
||||||
|
* that only children which match the filter predicate for the
|
||||||
|
* current search string are present.
|
||||||
|
*
|
||||||
|
* @param {GroupListItem} item
|
||||||
|
* The GroupListItem whose children should be filtered.
|
||||||
|
*/
|
||||||
|
var filterGroupListItem = function filterGroupListItem(item) {
|
||||||
|
item.children = item.children.filter(function applyFilterPattern(child) {
|
||||||
|
|
||||||
|
// Filter connections and connection groups by
|
||||||
|
// given pattern
|
||||||
|
switch (child.type) {
|
||||||
|
|
||||||
|
case GroupListItem.Type.CONNECTION:
|
||||||
|
return connectionFilterPattern.predicate(child.wrappedItem);
|
||||||
|
|
||||||
|
case GroupListItem.Type.CONNECTION_GROUP:
|
||||||
|
return connectionGroupFilterPattern.predicate(child.wrappedItem);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Include all other children
|
||||||
|
return true;
|
||||||
|
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces the set of child connections and connection groups
|
||||||
|
* within the given connection group such that only children which
|
||||||
|
* match the filter predicate for the current search string are
|
||||||
|
* present.
|
||||||
|
*
|
||||||
|
* @param {ConnectionGroup} connectionGroup
|
||||||
|
* The connection group whose children should be filtered.
|
||||||
|
*/
|
||||||
|
var filterConnectionGroup = function filterConnectionGroup(connectionGroup) {
|
||||||
|
connectionGroup.childConnections = connectionGroup.childConnections.filter(connectionFilterPattern.predicate);
|
||||||
|
connectionGroup.childConnectionGroups = connectionGroup.childConnectionGroups.filter(connectionGroupFilterPattern.predicate);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies the current filter predicate, filtering all provided
|
* Applies the current filter predicate, filtering all provided
|
||||||
* connection groups and storing the result in
|
* connection groups and storing the result in
|
||||||
@@ -177,16 +266,17 @@ angular.module('groupList').directive('guacGroupListFilter', [function guacGroup
|
|||||||
if (connectionGroups) {
|
if (connectionGroups) {
|
||||||
angular.forEach(connectionGroups, function updateFilteredConnectionGroup(connectionGroup, dataSource) {
|
angular.forEach(connectionGroups, function updateFilteredConnectionGroup(connectionGroup, dataSource) {
|
||||||
|
|
||||||
// Unwrap GroupListItem
|
var filteredGroup;
|
||||||
if (connectionGroup instanceof GroupListItem)
|
|
||||||
connectionGroup = connectionGroup.wrappedItem;
|
|
||||||
|
|
||||||
// Flatten hierarchy of connection group
|
// Flatten and filter depending on type
|
||||||
var filteredGroup = flattenConnectionGroup(connectionGroup);
|
if (connectionGroup instanceof GroupListItem) {
|
||||||
|
filteredGroup = flattenGroupListItem(connectionGroup);
|
||||||
// Filter all direct children
|
filterGroupListItem(filteredGroup);
|
||||||
filteredGroup.childConnections = filteredGroup.childConnections.filter(connectionFilterPattern.predicate);
|
}
|
||||||
filteredGroup.childConnectionGroups = filteredGroup.childConnectionGroups.filter(connectionGroupFilterPattern.predicate);
|
else {
|
||||||
|
filteredGroup = flattenConnectionGroup(connectionGroup);
|
||||||
|
filterConnectionGroup(filteredGroup);
|
||||||
|
}
|
||||||
|
|
||||||
// Store now-filtered root
|
// Store now-filtered root
|
||||||
$scope.filteredConnectionGroups[dataSource] = filteredGroup;
|
$scope.filteredConnectionGroups[dataSource] = filteredGroup;
|
||||||
|
@@ -74,6 +74,10 @@ h2 {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header.tabbed {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.header ~ * .header,
|
.header ~ * .header,
|
||||||
.header ~ .header {
|
.header ~ .header {
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
|
@@ -84,6 +84,67 @@ angular.module('manage').directive('connectionPermissionEditor', ['$injector',
|
|||||||
|
|
||||||
directive.controller = ['$scope', function connectionPermissionEditorController($scope) {
|
directive.controller = ['$scope', function connectionPermissionEditorController($scope) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A map of data source identifiers to all root connection groups
|
||||||
|
* within those data sources, regardless of the permissions granted for
|
||||||
|
* the items within those groups. As only one data source is applicable
|
||||||
|
* to any particular permission set being edited/created, this will only
|
||||||
|
* contain a single key. If the data necessary to produce this map has
|
||||||
|
* not yet been loaded, this will be null.
|
||||||
|
*
|
||||||
|
* @type Object.<String, GroupListItem>
|
||||||
|
*/
|
||||||
|
var allRootGroups = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A map of data source identifiers to the root connection groups within
|
||||||
|
* those data sources, excluding all items which are not explicitly
|
||||||
|
* readable according to $scope.permissionFlags. As only one data
|
||||||
|
* source is applicable to any particular permission set being
|
||||||
|
* edited/created, this will only contain a single key. If the data
|
||||||
|
* necessary to produce this map has not yet been loaded, this will be
|
||||||
|
* null.
|
||||||
|
*
|
||||||
|
* @type Object.<String, GroupListItem>
|
||||||
|
*/
|
||||||
|
var readableRootGroups = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the tab within the connection permission editor which
|
||||||
|
* displays currently selected (readable) connections only.
|
||||||
|
*
|
||||||
|
* @constant
|
||||||
|
* @type String
|
||||||
|
*/
|
||||||
|
var CURRENT_CONNECTIONS = 'CURRENT_CONNECTIONS';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the tab within the connection permission editor which
|
||||||
|
* displays all connections, regardless of whether they are readable.
|
||||||
|
*
|
||||||
|
* @constant
|
||||||
|
* @type String
|
||||||
|
*/
|
||||||
|
var ALL_CONNECTIONS = 'ALL_CONNECTIONS';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The names of all tabs which should be available within the
|
||||||
|
* connection permission editor, in display order.
|
||||||
|
*
|
||||||
|
* @type String[]
|
||||||
|
*/
|
||||||
|
$scope.tabs = [
|
||||||
|
CURRENT_CONNECTIONS,
|
||||||
|
ALL_CONNECTIONS
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the currently selected tab.
|
||||||
|
*
|
||||||
|
* @type String
|
||||||
|
*/
|
||||||
|
$scope.currentTab = ALL_CONNECTIONS;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Array of all connection properties that are filterable.
|
* Array of all connection properties that are filterable.
|
||||||
*
|
*
|
||||||
@@ -104,31 +165,53 @@ angular.module('manage').directive('connectionPermissionEditor', ['$injector',
|
|||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A map of data source identifiers to the root connection groups within
|
* Returns the root groups which should be displayed within the
|
||||||
* thost data sources. As only one data source is applicable to any
|
* connection permission editor.
|
||||||
* particular permission set being edited/created, this will only
|
|
||||||
* contain a single key.
|
|
||||||
*
|
*
|
||||||
* @type Object.<String, GroupListItem>
|
* @returns {Object.<String, GroupListItem>}
|
||||||
|
* The root groups which should be displayed within the connection
|
||||||
|
* permission editor as a map of data source identifiers to the
|
||||||
|
* root connection groups within those data sources.
|
||||||
*/
|
*/
|
||||||
$scope.rootGroups = null;
|
$scope.getRootGroups = function getRootGroups() {
|
||||||
|
return $scope.currentTab === CURRENT_CONNECTIONS ? readableRootGroups : allRootGroups;
|
||||||
|
};
|
||||||
|
|
||||||
// Retrieve all connections for which we have ADMINISTER permission
|
/**
|
||||||
dataSourceService.apply(
|
* Returns whether the given PermissionFlagSet declares explicit READ
|
||||||
connectionGroupService.getConnectionGroupTree,
|
* permission for the connection, connection group, or sharing profile
|
||||||
[$scope.dataSource],
|
* represented by the given GroupListItem.
|
||||||
ConnectionGroup.ROOT_IDENTIFIER,
|
*
|
||||||
[PermissionSet.ObjectPermissionType.ADMINISTER]
|
* @param {GroupListItem} item
|
||||||
)
|
* The GroupListItem which should be checked against the
|
||||||
.then(function connectionGroupReceived(rootGroups) {
|
* PermissionFlagSet.
|
||||||
|
*
|
||||||
|
* @param {PemissionFlagSet} flags
|
||||||
|
* The set of permissions which should be used to determine whether
|
||||||
|
* explicit READ permission is granted for the given item.
|
||||||
|
*
|
||||||
|
* @returns {Boolean}
|
||||||
|
* true if explicit READ permission is granted for the given item
|
||||||
|
* according to the given permission set, false otherwise.
|
||||||
|
*/
|
||||||
|
var isReadable = function isReadable(item, flags) {
|
||||||
|
|
||||||
// Convert all received ConnectionGroup objects into GroupListItems
|
switch (item.type) {
|
||||||
$scope.rootGroups = {};
|
|
||||||
angular.forEach(rootGroups, function addGroupListItem(rootGroup, dataSource) {
|
|
||||||
$scope.rootGroups[dataSource] = GroupListItem.fromConnectionGroup(dataSource, rootGroup);
|
|
||||||
});
|
|
||||||
|
|
||||||
}, requestService.WARN);
|
case GroupListItem.Type.CONNECTION:
|
||||||
|
return flags.connectionPermissions.READ[item.identifier];
|
||||||
|
|
||||||
|
case GroupListItem.Type.CONNECTION_GROUP:
|
||||||
|
return flags.connectionGroupPermissions.READ[item.identifier];
|
||||||
|
|
||||||
|
case GroupListItem.Type.SHARING_PROFILE:
|
||||||
|
return flags.sharingProfilePermissions.READ[item.identifier];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expands all items within the tree descending from the given
|
* Expands all items within the tree descending from the given
|
||||||
@@ -144,6 +227,9 @@ angular.module('manage').directive('connectionPermissionEditor', ['$injector',
|
|||||||
* @param {PemissionFlagSet} flags
|
* @param {PemissionFlagSet} flags
|
||||||
* The set of permissions which should be used to determine whether
|
* The set of permissions which should be used to determine whether
|
||||||
* the given item and its descendants are expanded.
|
* the given item and its descendants are expanded.
|
||||||
|
*
|
||||||
|
* @returns {Boolean}
|
||||||
|
* true if the given item has been expanded, false otherwise.
|
||||||
*/
|
*/
|
||||||
var expandReadable = function expandReadable(item, flags) {
|
var expandReadable = function expandReadable(item, flags) {
|
||||||
|
|
||||||
@@ -152,29 +238,10 @@ angular.module('manage').directive('connectionPermissionEditor', ['$injector',
|
|||||||
if (item.expandable && item.children) {
|
if (item.expandable && item.children) {
|
||||||
angular.forEach(item.children, function expandReadableChild(child) {
|
angular.forEach(item.children, function expandReadableChild(child) {
|
||||||
|
|
||||||
// Determine whether the permission set contains READ
|
|
||||||
// permission for the current child object
|
|
||||||
var readable = false;
|
|
||||||
switch (child.type) {
|
|
||||||
|
|
||||||
case GroupListItem.Type.CONNECTION:
|
|
||||||
readable = flags.connectionPermissions.READ[child.identifier];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case GroupListItem.Type.CONNECTION_GROUP:
|
|
||||||
readable = flags.connectionGroupPermissions.READ[child.identifier];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case GroupListItem.Type.SHARING_PROFILE:
|
|
||||||
readable = flags.sharingProfilePermissions.READ[child.identifier];
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// The parent should be expanded by default if the child is
|
// The parent should be expanded by default if the child is
|
||||||
// expanded by default OR the permission set contains READ
|
// expanded by default OR the permission set contains READ
|
||||||
// permission on the child
|
// permission on the child
|
||||||
item.expanded |= expandReadable(child, flags) || readable;
|
item.expanded |= expandReadable(child, flags) || isReadable(child, flags);
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -183,22 +250,105 @@ angular.module('manage').directive('connectionPermissionEditor', ['$injector',
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Update default expanded state whenever connection groups and
|
/**
|
||||||
// associated permissions change
|
* Creates a deep copy of all items within the tree descending from the
|
||||||
$scope.$watchGroup(['rootGroups', 'permissionFlags'], function updateDefaultExpandedStates() {
|
* given GroupListItem which have at least one descendant for which
|
||||||
|
* explicit READ permission is granted. Items which lack explicit READ
|
||||||
|
* permission and which have no descendants having explicit READ
|
||||||
|
* permission are omitted from the copy.
|
||||||
|
*
|
||||||
|
* @param {GroupListItem} item
|
||||||
|
* The GroupListItem which should be conditionally copied
|
||||||
|
* depending on whether READ permission is granted for any of its
|
||||||
|
* descendants.
|
||||||
|
*
|
||||||
|
* @param {PemissionFlagSet} flags
|
||||||
|
* The set of permissions which should be used to determine whether
|
||||||
|
* the given item or any of its descendants are copied.
|
||||||
|
*
|
||||||
|
* @returns {GroupListItem}
|
||||||
|
* A new GroupListItem containing a deep copy of the given item,
|
||||||
|
* omitting any items which lack explicit READ permission and whose
|
||||||
|
* descendants also lack explicit READ permission, or null if even
|
||||||
|
* the given item would not be copied.
|
||||||
|
*/
|
||||||
|
var copyReadable = function copyReadable(item, flags) {
|
||||||
|
|
||||||
if (!$scope.rootGroups || !$scope.permissionFlags)
|
// Produce initial shallow copy of given item
|
||||||
return;
|
item = new GroupListItem(item);
|
||||||
|
|
||||||
angular.forEach($scope.rootGroups, function updateExpandedStates(rootGroup) {
|
// Replace children array with an array containing only readable
|
||||||
|
// children (or children with at least one readable descendant),
|
||||||
|
// flagging the current item for copying if any such children exist
|
||||||
|
if (item.children) {
|
||||||
|
|
||||||
// Automatically expand all objects with any descendants for
|
var children = [];
|
||||||
// which the permission set contains READ permission
|
angular.forEach(item.children, function copyReadableChildren(child) {
|
||||||
expandReadable(rootGroup, $scope.permissionFlags);
|
|
||||||
|
// Reduce child tree to only explicitly readable items and
|
||||||
|
// their parents
|
||||||
|
child = copyReadable(child, flags);
|
||||||
|
|
||||||
|
// Include child only if they are explicitly readable, they
|
||||||
|
// have explicitly readable descendants, or their parent is
|
||||||
|
// readable (and thus all children are relevant)
|
||||||
|
if ((child.children && child.children.length)
|
||||||
|
|| isReadable(item, flags)
|
||||||
|
|| isReadable(child, flags))
|
||||||
|
children.push(child);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
item.children = children;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return item;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// Retrieve all connections for which we have ADMINISTER permission
|
||||||
|
dataSourceService.apply(
|
||||||
|
connectionGroupService.getConnectionGroupTree,
|
||||||
|
[$scope.dataSource],
|
||||||
|
ConnectionGroup.ROOT_IDENTIFIER,
|
||||||
|
[PermissionSet.ObjectPermissionType.ADMINISTER]
|
||||||
|
)
|
||||||
|
.then(function connectionGroupReceived(rootGroups) {
|
||||||
|
|
||||||
|
// Update default expanded state and the all / readable-only views
|
||||||
|
// when associated permissions change
|
||||||
|
$scope.$watchGroup(['permissionFlags'], function updateDefaultExpandedStates() {
|
||||||
|
|
||||||
|
if (!$scope.permissionFlags)
|
||||||
|
return;
|
||||||
|
|
||||||
|
allRootGroups = {};
|
||||||
|
readableRootGroups = {};
|
||||||
|
|
||||||
|
angular.forEach(rootGroups, function addGroupListItem(rootGroup, dataSource) {
|
||||||
|
|
||||||
|
// Convert all received ConnectionGroup objects into GroupListItems
|
||||||
|
var item = GroupListItem.fromConnectionGroup(dataSource, rootGroup);
|
||||||
|
allRootGroups[dataSource] = item;
|
||||||
|
|
||||||
|
// Automatically expand all objects with any descendants for
|
||||||
|
// which the permission set contains READ permission
|
||||||
|
expandReadable(item, $scope.permissionFlags);
|
||||||
|
|
||||||
|
// Create a duplicate view which contains only readable
|
||||||
|
// items
|
||||||
|
readableRootGroups[dataSource] = copyReadable(item, $scope.permissionFlags);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// Display only readable connections by default if at least one
|
||||||
|
// readable connection exists
|
||||||
|
$scope.currentTab = !!readableRootGroups[$scope.dataSource].children.length ? CURRENT_CONNECTIONS : ALL_CONNECTIONS;
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
}, requestService.WARN);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the permissionsAdded and permissionsRemoved permission sets
|
* Updates the permissionsAdded and permissionsRemoved permission sets
|
||||||
|
@@ -17,10 +17,6 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.manage-user .username.header {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.manage-user .page-tabs .page-list li.read-only a[href],
|
.manage-user .page-tabs .page-list li.read-only a[href],
|
||||||
.manage-user .page-tabs .page-list li.unlinked a[href],
|
.manage-user .page-tabs .page-list li.unlinked a[href],
|
||||||
.manage-user .page-tabs .page-list li.linked a[href] {
|
.manage-user .page-tabs .page-list li.linked a[href] {
|
||||||
|
@@ -1,12 +1,13 @@
|
|||||||
<div class="connection-permissions">
|
<div class="connection-permissions">
|
||||||
<div class="header">
|
<div class="header tabbed">
|
||||||
<h2>{{'MANAGE_USER.SECTION_HEADER_CONNECTIONS' | translate}}</h2>
|
<h2>{{'MANAGE_USER.SECTION_HEADER_CONNECTIONS' | translate}}</h2>
|
||||||
<guac-group-list-filter connection-groups="rootGroups"
|
<guac-group-list-filter connection-groups="getRootGroups()"
|
||||||
filtered-connection-groups="filteredRootGroups"
|
filtered-connection-groups="filteredRootGroups"
|
||||||
placeholder="'MANAGE_USER.FIELD_PLACEHOLDER_FILTER' | translate"
|
placeholder="'MANAGE_USER.FIELD_PLACEHOLDER_FILTER' | translate"
|
||||||
connection-properties="filteredConnectionProperties"
|
connection-properties="filteredConnectionProperties"
|
||||||
connection-group-properties="filteredConnectionGroupProperties"></guac-group-list-filter>
|
connection-group-properties="filteredConnectionGroupProperties"></guac-group-list-filter>
|
||||||
</div>
|
</div>
|
||||||
|
<guac-section-tabs namespace="MANAGE_USER" current="currentTab" tabs="tabs"></guac-section-tabs>
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<guac-group-list
|
<guac-group-list
|
||||||
context="groupListContext"
|
context="groupListContext"
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
<div class="manage-user view" ng-class="{loading: !isLoaded()}">
|
<div class="manage-user view" ng-class="{loading: !isLoaded()}">
|
||||||
|
|
||||||
<!-- User header and data source tabs -->
|
<!-- User header and data source tabs -->
|
||||||
<div class="username header">
|
<div class="header tabbed">
|
||||||
<h2>{{'MANAGE_USER.SECTION_HEADER_EDIT_USER' | translate}}</h2>
|
<h2>{{'MANAGE_USER.SECTION_HEADER_EDIT_USER' | translate}}</h2>
|
||||||
<guac-user-menu></guac-user-menu>
|
<guac-user-menu></guac-user-menu>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -0,0 +1,143 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Directive which displays a set of tabs dividing a section of a page into
|
||||||
|
* logical subsections or views. The currently selected tab is communicated
|
||||||
|
* through assignment to the variable bound to the <code>current</code>
|
||||||
|
* attribute. No navigation occurs as a result of selecting a tab.
|
||||||
|
*/
|
||||||
|
angular.module('navigation').directive('guacSectionTabs', ['$injector',
|
||||||
|
function guacSectionTabs($injector) {
|
||||||
|
|
||||||
|
// Required services
|
||||||
|
var translationStringService = $injector.get('translationStringService');
|
||||||
|
|
||||||
|
var directive = {
|
||||||
|
|
||||||
|
restrict : 'E',
|
||||||
|
replace : true,
|
||||||
|
templateUrl : 'app/navigation/templates/guacSectionTabs.html',
|
||||||
|
|
||||||
|
scope : {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The translation namespace to use when producing translation
|
||||||
|
* strings for each tab. Tab translation strings will be of the
|
||||||
|
* form:
|
||||||
|
*
|
||||||
|
* <code>NAMESPACE.SECTION_HEADER_NAME<code>
|
||||||
|
*
|
||||||
|
* where <code>NAMESPACE</code> is the namespace provided to this
|
||||||
|
* attribute and <code>NAME</code> is one of the names within the
|
||||||
|
* array provided to the <code>tabs</code> attribute and
|
||||||
|
* transformed via translationStringService.canonicalize().
|
||||||
|
*/
|
||||||
|
namespace : '@',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the currently selected tab. This name MUST be one of
|
||||||
|
* the names present in the array given via the <code>tabs</code>
|
||||||
|
* attribute. This directive will not automatically choose an
|
||||||
|
* initially selected tab, and a default value should be manually
|
||||||
|
* assigned to <code>current</code> to ensure a tab is initially
|
||||||
|
* selected.
|
||||||
|
*
|
||||||
|
* @type String
|
||||||
|
*/
|
||||||
|
current : '=',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The unique names of all tabs which should be made available, in
|
||||||
|
* display order. These names will be assigned to the variable
|
||||||
|
* bound to the <code>current</code> attribute when the current
|
||||||
|
* tab changes.
|
||||||
|
*
|
||||||
|
* @type String[]
|
||||||
|
*/
|
||||||
|
tabs : '='
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
directive.controller = ['$scope', function dataSourceTabsController($scope) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Produces the translation string for the section header representing
|
||||||
|
* the tab having the given name. The translation string will be of the
|
||||||
|
* form:
|
||||||
|
*
|
||||||
|
* <code>NAMESPACE.SECTION_HEADER_NAME<code>
|
||||||
|
*
|
||||||
|
* where <code>NAMESPACE</code> is the namespace provided to the
|
||||||
|
* directive and <code>NAME</code> is the given name transformed
|
||||||
|
* via translationStringService.canonicalize().
|
||||||
|
*
|
||||||
|
* @param {String} name
|
||||||
|
* The name of the tab.
|
||||||
|
*
|
||||||
|
* @returns {String}
|
||||||
|
* The translation string which produces the translated header
|
||||||
|
* of the tab having the given name.
|
||||||
|
*/
|
||||||
|
$scope.getSectionHeader = function getSectionHeader(name) {
|
||||||
|
|
||||||
|
// If no name, then no header
|
||||||
|
if (!name)
|
||||||
|
return '';
|
||||||
|
|
||||||
|
return translationStringService.canonicalize($scope.namespace || 'MISSING_NAMESPACE')
|
||||||
|
+ '.SECTION_HEADER_' + translationStringService.canonicalize(name);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selects the tab having the given name. The name of the currently
|
||||||
|
* selected tab will be communicated outside the directive through
|
||||||
|
* $scope.current.
|
||||||
|
*
|
||||||
|
* @param {String} name
|
||||||
|
* The name of the tab to select.
|
||||||
|
*/
|
||||||
|
$scope.selectTab = function selectTab(name) {
|
||||||
|
$scope.current = name;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the tab having the given name is currently
|
||||||
|
* selected. A tab is currently selected if its name is stored within
|
||||||
|
* $scope.current, as assigned externally or by selectTab().
|
||||||
|
*
|
||||||
|
* @param {String} name
|
||||||
|
* The name of the tab to test.
|
||||||
|
*
|
||||||
|
* @returns {Boolean}
|
||||||
|
* true if the tab having the given name is currently selected,
|
||||||
|
* false otherwise.
|
||||||
|
*/
|
||||||
|
$scope.isSelected = function isSelected(name) {
|
||||||
|
return $scope.current === name;
|
||||||
|
};
|
||||||
|
|
||||||
|
}];
|
||||||
|
|
||||||
|
return directive;
|
||||||
|
|
||||||
|
}]);
|
@@ -17,23 +17,27 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.page-tabs .page-list ul {
|
.page-tabs .page-list ul,
|
||||||
|
.section-tabs ul {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
background: rgba(0, 0, 0, 0.0125);
|
background: rgba(0, 0, 0, 0.0125);
|
||||||
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-tabs .page-list ul + ul {
|
.page-tabs .page-list ul + ul,
|
||||||
|
.section-tabs ul + ul {
|
||||||
font-size: 0.75em;
|
font-size: 0.75em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-tabs .page-list li {
|
.page-tabs .page-list li,
|
||||||
|
.section-tabs li {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-tabs .page-list li a[href] {
|
.page-tabs .page-list li a[href],
|
||||||
|
.section-tabs li a {
|
||||||
display: block;
|
display: block;
|
||||||
color: black;
|
color: black;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
@@ -44,12 +48,16 @@
|
|||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-tabs .page-list li a[href]:hover {
|
.page-tabs .page-list li a[href]:hover,
|
||||||
|
.section-tabs li a:hover {
|
||||||
background-color: #CDA;
|
background-color: #CDA;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-tabs .page-list li a[href].current,
|
.page-tabs .page-list li a[href].current,
|
||||||
.page-tabs .page-list li a[href].current:hover {
|
.page-tabs .page-list li a[href].current:hover,
|
||||||
|
.section-tabs li a.current,
|
||||||
|
.section-tabs li a.current:hover {
|
||||||
background: rgba(0,0,0,0.3);
|
background: rgba(0,0,0,0.3);
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
@@ -0,0 +1,10 @@
|
|||||||
|
<div class="section-tabs" ng-show="tabs.length">
|
||||||
|
<ul>
|
||||||
|
<li ng-repeat="name in tabs">
|
||||||
|
<a ng-click="selectTab(name)"
|
||||||
|
ng-class="{ current : isSelected(name) }">
|
||||||
|
{{ getSectionHeader(name) | translate }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
@@ -17,10 +17,6 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.settings .header {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.settings table.properties th {
|
.settings table.properties th {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
<div class="view">
|
<div class="view">
|
||||||
|
|
||||||
<div class="header">
|
<div class="header tabbed">
|
||||||
<h2>{{'SETTINGS.SECTION_HEADER_SETTINGS' | translate}}</h2>
|
<h2>{{'SETTINGS.SECTION_HEADER_SETTINGS' | translate}}</h2>
|
||||||
<guac-user-menu></guac-user-menu>
|
<guac-user-menu></guac-user-menu>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -303,9 +303,11 @@
|
|||||||
|
|
||||||
"INFO_READ_ONLY" : "Sorry, but this user account cannot be edited.",
|
"INFO_READ_ONLY" : "Sorry, but this user account cannot be edited.",
|
||||||
|
|
||||||
"SECTION_HEADER_CONNECTIONS" : "Connections",
|
"SECTION_HEADER_ALL_CONNECTIONS" : "All Connections",
|
||||||
"SECTION_HEADER_EDIT_USER" : "Edit User",
|
"SECTION_HEADER_CONNECTIONS" : "Connections",
|
||||||
"SECTION_HEADER_PERMISSIONS" : "Permissions",
|
"SECTION_HEADER_CURRENT_CONNECTIONS" : "Current Connections",
|
||||||
|
"SECTION_HEADER_EDIT_USER" : "Edit User",
|
||||||
|
"SECTION_HEADER_PERMISSIONS" : "Permissions",
|
||||||
|
|
||||||
"TEXT_CONFIRM_DELETE" : "Users cannot be restored after they have been deleted. Are you sure you want to delete this user?"
|
"TEXT_CONFIRM_DELETE" : "Users cannot be restored after they have been deleted. Are you sure you want to delete this user?"
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user