Merge pull request #24 from patriot1burke/master

idle timeout and app create
This commit is contained in:
Bill Burke 2013-08-07 18:53:23 -07:00
commit 562e402ac3
13 changed files with 598 additions and 298 deletions

View file

@ -5,6 +5,12 @@
<meta charset="utf-8">
<title>Keycloak</title>
<style type="text/css">
#idletimeout { background:#CC5100; border:3px solid #FF6500; color:#fff; font-family:arial, sans-serif; text-align:center; font-size:12px; padding:10px; position:relative; top:0px; left:0; right:0; z-index:100000; display:none; }
#idletimeout a { color:#fff; font-weight:bold }
#idletimeout span { font-weight:bold }
</style>
<link href="lib/bootstrap/css/bootstrap.css" rel="stylesheet">
<link href="lib/bootstrap/css/bootstrap-responsive.css" rel="stylesheet">
<link href="css/admin.css" rel="stylesheet">
@ -12,8 +18,6 @@
<link href="css/styles.css" rel="stylesheet">
<script src="lib/jquery/jquery-1.10.2.js"></script>
<script src="lib/angular/angular.js"></script>
<script src="lib/angular/angular-resource.js"></script>
<script src="lib/angular/ui-bootstrap-tpls-0.4.0.js"></script>
@ -23,9 +27,71 @@
<script src="js/controllers.js"></script>
<script src="js/loaders.js"></script>
<script src="js/services.js"></script>
</head>
<body data-ng-controller="GlobalCtrl">
<div id="idletimeout">
You will be logged off in <span></span>&nbsp;seconds due to inactivity.
<a id="idletimeout-resume" href="#">Click here to continue using this web page</a>.
</div>
<div class="alert-container" data-ng-show="notification" data-ng-click="notification = null">
<div class="alert alert-{{notification.type}}">{{notification.message}}</div>
</div>
<div id="wrap">
<div data-ng-include data-src="'partials/menu.html'"></div>
<div data-ng-view id="view" data-ng-hide="httpProviderError"></div>
<div id="httpProviderError" data-ng-show="httpProviderError">
<button class="btn btn-danger" data-ng-click="httpProviderError=null">
<strong>Error</strong> {{httpProviderError}}
</button>
</div>
<div id="loading">
<i class="icon-spinner icon-spin"></i> Loading...
</div>
</div>
<script src="lib/jquery/jquery-1.10.2.js" type="text/javascript"></script>
<script src="lib/jquery/jquery.idletimer.js" type="text/javascript"></script>
<script src="lib/jquery/jquery.idletimeout.js" type="text/javascript"></script>
<script type="text/javascript">
$.idleTimeout('#idletimeout', '#idletimeout a', {
idleAfter: 5,
pollingInterval: 0,
keepAliveURL: '',
serverResponseEquals: '',
onTimeout: function(){
$(this).slideUp();
window.location = "timeout.html";
},
onIdle: function(){
$(this).slideDown(); // show the warning bar
},
onCountdown: function( counter ){
$(this).find("span").html( counter ); // update the counter
},
onResume: function(){
$(this).slideUp(); // hide the warning bar
}
});
</script>
</body>
<!--
<body data-ng-controller="GlobalCtrl">
<div id="idletimeout">
You will be logged off in <span></span>&nbsp;seconds due to inactivity.
<a id="idletimeout-resume" href="#">Click here to continue using this web page</a>.
</div>
<div class="alert-container" data-ng-show="notification" data-ng-click="notification = null">
<div class="alert alert-{{notification.type}}">{{notification.message}}</div>
@ -48,4 +114,5 @@
</div>
</body>
-->
</html>

View file

@ -5,37 +5,7 @@ var resourceRequests = 0;
module.config([ '$routeProvider', function($routeProvider) {
$routeProvider.when('/create/application', {
templateUrl : 'partials/application-detail.html',
resolve : {
application : function(ApplicationLoader) {
return {};
},
realms : function(RealmListLoader) {
return RealmListLoader();
}
},
controller : 'ApplicationDetailCtrl'
}).when('/applications/:application', {
templateUrl : 'partials/application-detail.html',
resolve : {
application : function(ApplicationLoader) {
return ApplicationLoader();
},
realms : function(RealmListLoader) {
return RealmListLoader();
}
},
controller : 'ApplicationDetailCtrl'
}).when('/applications', {
templateUrl : 'partials/application-list.html',
resolve : {
applications : function(ApplicationListLoader) {
return ApplicationListLoader();
}
},
controller : 'ApplicationListCtrl'
})
$routeProvider
.when('/create/realm', {
templateUrl : 'partials/realm-detail.html',
@ -128,36 +98,47 @@ module.config([ '$routeProvider', function($routeProvider) {
controller : 'RoleListCtrl'
})
.when('/applications/:application/roles', {
templateUrl : 'partials/role-mapping.html',
resolve : {
realm : function(ApplicationLoader) {
return ApplicationLoader();
},
users : function() {
return null;
},
role : function() {
return null;
}
},
controller : 'RoleMappingCtrl'
}).when('/applications/:application/roles/:role', {
templateUrl : 'partials/role-mapping.html',
resolve : {
realm : function(ApplicationLoader) {
return ApplicationLoader();
},
role : function($route) {
return $route.current.params.role;
},
users : function(RoleMappingLoader) {
return RoleMappingLoader();
}
},
controller : 'RoleMappingCtrl'
})
.when('/create/application/:realm', {
templateUrl : 'partials/application-detail.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
applications : function(ApplicationListLoader) {
return ApplicationListLoader();
},
application : function() {
return {};
}
},
controller : 'ApplicationDetailCtrl'
}).when('/realms/:realm/applications/:application', {
templateUrl : 'partials/application-detail.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
applications : function(ApplicationListLoader) {
return ApplicationListLoader();
},
application : function(ApplicationLoader) {
return ApplicationLoader();
}
},
controller : 'ApplicationDetailCtrl'
}).when('/realms/:realm/applications', {
templateUrl : 'partials/application-list.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
applications : function(ApplicationListLoader) {
return ApplicationListLoader();
}
},
controller : 'ApplicationListCtrl'
})
.otherwise({
templateUrl : 'partials/home.html'
});

View file

@ -43,118 +43,6 @@ module.controller('GlobalCtrl', function($scope, $http, Auth, Current, $location
});
});
module.controller('ApplicationListCtrl', function($scope, Application) {
$scope.applications = Application.query();
});
module.controller('ApplicationDetailCtrl', function($scope, application, Application, realms, $location, $window, Dialog,
Notifications) {
$scope.application = angular.copy(application);
$scope.realms = realms;
$scope.create = !application.id;
$scope.changed = $scope.create;
$scope.$watch('application', function() {
if (!angular.equals($scope.application, application)) {
$scope.changed = true;
}
}, true);
$scope.addRole = function() {
if ($scope.newRole) {
if ($scope.application.roles) {
for ( var i = 0; i < $scope.application.roles.length; i++) {
if ($scope.application.roles[i] == $scope.newRole) {
Notifications.warn("Role already exists");
$scope.newRole = null;
return;
}
}
}
if (!$scope.application.roles) {
$scope.application.roles = [];
}
$scope.application.roles.push($scope.newRole);
$scope.newRole = null;
}
}
$scope.removeRole = function(role) {
Dialog.confirmDelete(role, 'role', function() {
var i = $scope.application.roles.indexOf(role);
if (i > -1) {
$scope.application.roles.splice(i, 1);
}
if ($scope.application.initialRoles) {
$scope.removeInitialRole(role);
}
});
};
$scope.addInitialRole = function() {
if ($scope.newInitialRole) {
if (!$scope.application.initialRoles) {
$scope.application.initialRoles = [];
}
$scope.application.initialRoles.push($scope.newInitialRole);
$scope.newInitialRole = null;
}
}
$scope.removeInitialRole = function(role) {
var i = $scope.application.initialRoles.indexOf(role);
if (i > -1) {
$scope.application.initialRoles.splice(i, 1);
}
};
$scope.save = function() {
if ($scope.applicationForm.$valid) {
if ($scope.create) {
Application.save($scope.application, function(data, headers) {
var l = headers().location;
var id = l.substring(l.lastIndexOf("/") + 1);
$location.url("/applications/" + id);
Notifications.success("Created application");
});
} else {
Application.update($scope.application, function() {
$scope.changed = false;
application = angular.copy($scope.application);
Notifications.success("Saved changes to the application");
});
}
} else {
$scope.applicationForm.showErrors = true;
}
};
$scope.reset = function() {
$scope.application = angular.copy(application);
$scope.changed = false;
$scope.applicationForm.showErrors = false;
};
$scope.cancel = function() {
$location.url("/applications");
};
$scope.remove = function() {
Dialog.confirmDelete($scope.application.name, 'application', function() {
$scope.application.$remove(function() {
$location.url("/applications");
Notifications.success("Deleted application");
});
});
};
});
module.controller('RealmListCtrl', function($scope, Realm, Current) {
$scope.realms = Realm.get();
Current.realms = $scope.realms;
@ -439,7 +327,7 @@ module.controller('RoleDetailCtrl', function($scope, realm, role, Role, $locatio
};
$scope.reset = function() {
$scope.role = angular.copy(user);
$scope.role = angular.copy(role);
$scope.changed = false;
$scope.roleForm.showErrors = false;
};
@ -461,6 +349,118 @@ module.controller('RoleDetailCtrl', function($scope, realm, role, Role, $locatio
};
});
module.controller('ApplicationListCtrl', function($scope, realm, applications, Application, $location, Dialog, Notifications) {
console.log('ApplicationListCtrl');
$scope.realm = realm;
$scope.selection = {
applications : angular.copy(applications),
application : null
};
$scope.create = false;
$scope.changeApplication = function() {
console.log('ApplicationListCtrl.changeApplication() - ' + $scope.selection.application.name);
$location.url("/realms/" + realm.id + "/applications/" + $scope.selection.application.id);
};
});
module.controller('ApplicationDetailCtrl', function($scope, realm, applications, application, Application, $location, Dialog, Notifications) {
console.log('ApplicationDetailCtrl');
$scope.realm = realm;
$scope.create = !application.id;
var selection = {
applications : null,
application : null
};
selection.applications = angular.copy(applications);
for (var i=0;i < selection.applications.length; i++) {
if (selection.applications[i].name == application.name) {
console.log('app name: ' + application.name);
selection.application = selection.applications[i];
break;
}
}
$scope.selection = selection;
$scope.application = angular.copy(application);
$scope.changeApplication = function() {
console.log('ApplicationDetailCtrl.changeApplication() - ' + $scope.selection.application.name);
$location.url("/realms/" + realm.id + "/applications/" + $scope.selection.application.id);
};
$scope.$watch('application', function() {
if (!angular.equals($scope.application, application)) {
$scope.changed = true;
}
}, true);
$scope.save = function() {
if ($scope.applicationForm.$valid) {
if ($scope.create) {
Application.save({
realm: realm.id
}, $scope.application, function (data, headers) {
$scope.changed = false;
var l = headers().location;
var id = l.substring(l.lastIndexOf("/") + 1);
$location.url("/realms/" + realm.id + "/applications/" + id);
Notifications.success("Created application");
});
} else {
Application.update({
realm : realm.id,
id : application.id
}, $scope.application, function() {
$scope.changed = false;
application = angular.copy($scope.application);
Notifications.success("Saved changes to application");
});
}
} else {
$scope.applicationForm.showErrors = true;
}
};
$scope.reset = function() {
$scope.application = angular.copy(application);
$scope.changed = false;
$scope.applicationForm.showErrors = false;
};
$scope.cancel = function() {
$location.url("/realms/" + realm.id + "/applications");
};
$scope.remove = function() {
Dialog.confirmDelete($scope.application.name, 'application', function() {
$scope.application.$remove({
realm : realm.id,
id : $scope.applicatino.id
}, function() {
$location.url("/realms/" + realm.id + "/applications");
Notifications.success("Deleted application");
});
});
};
});
module.controller('RoleMappingCtrl', function($scope, realm, User, users, role, RoleMapping, Notifications) {
$scope.realm = realm;
$scope.realmId = realm.realm || realm.id;

View file

@ -31,18 +31,6 @@ module.factory('Loader', function($q) {
return loader;
});
module.factory('ApplicationListLoader', function(Loader, Application, $q) {
return Loader.query(Application);
});
module.factory('ApplicationLoader', function(Loader, Application, $route, $q) {
return Loader.get(Application, function() {
return {
id : $route.current.params.application
}
});
});
module.factory('RealmListLoader', function(Loader, Realm, $q) {
return Loader.get(Realm);
});
@ -90,6 +78,24 @@ module.factory('RoleListLoader', function(Loader, Role, $route, $q) {
});
module.factory('ApplicationLoader', function(Loader, Application, $route, $q) {
return Loader.get(Application, function() {
return {
realm : $route.current.params.realm,
id : $route.current.params.application
}
});
});
module.factory('ApplicationListLoader', function(Loader, Application, $route, $q) {
return Loader.query(Application, function() {
return {
realm : $route.current.params.realm
}
});
});
module.factory('RoleMappingLoader', function(Loader, RoleMapping, $route, $q) {
var realm = $route.current.params.realm || $route.current.params.application;

View file

@ -101,16 +101,6 @@ module.factory('Notifications', function($rootScope, $timeout) {
return notifications;
});
module.factory('Application', function($resource) {
return $resource('/keycloak-server/ui/api/applications/:id', {
id : '@id'
}, {
update : {
method : 'PUT'
}
});
});
module.factory('Provider', function($resource) {
return $resource('/ejs-identity/api/admin/providers');
});
@ -159,9 +149,23 @@ module.factory('Role', function($resource) {
});
});
module.factory('Application', function($resource) {
return $resource('/auth-server/rest/saas/admin/realms/:realm/resources/:id', {
realm : '@realm',
id : '@id'
}, {
update : {
method : 'PUT'
}
});
});
module.factory('Current', function($resource) {
return {
realm : null,
realms : {}
realms : {},
application : null,
applications : {}
};
});

View file

@ -0,0 +1,180 @@
/*
* jQuery Idle Timeout 1.1
* Copyright (c) 2011 Eric Hynds
*
* http://www.erichynds.com/jquery/a-new-and-improved-jquery-idle-timeout-plugin/
*
* Depends:
* - jQuery 1.4.2+
* - jQuery Idle Timer (by Paul Irish, http://paulirish.com/2009/jquery-idletimer-plugin/)
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
(function($, win){
var idleTimeout = {
init: function( element, resume, options ){
var self = this, elem;
this.warning = elem = $(element);
this.resume = $(resume);
this.options = options;
this.countdownOpen = false;
this.failedRequests = options.failedRequests;
this._startTimer();
// expose obj to data cache so peeps can call internal methods
$.data( elem[0], 'idletimout', this );
// start the idle timer
$.idleTimer(options.idleAfter * 1000);
// once the user becomes idle
$(document).bind("idle.idleTimer", function(){
// if the user is idle and a countdown isn't already running
if( $.data(document, 'idleTimer') === 'idle' && !self.countdownOpen ){
self._stopTimer();
self.countdownOpen = true;
self._idle();
}
});
// bind continue link
this.resume.bind("click", function(e){
e.preventDefault();
win.clearInterval(self.countdown); // stop the countdown
self.countdownOpen = false; // stop countdown
self._startTimer(); // start up the timer again
self._keepAlive( false ); // ping server
options.onResume.call( self.warning ); // call the resume callback
});
},
_idle: function(){
var self = this,
options = this.options,
warning = this.warning[0],
counter = options.warningLength;
// fire the onIdle function
options.onIdle.call(warning);
// set inital value in the countdown placeholder
options.onCountdown.call(warning, counter);
// create a timer that runs every second
this.countdown = win.setInterval(function(){
if(--counter === 0){
window.clearInterval(self.countdown);
options.onTimeout.call(warning);
} else {
options.onCountdown.call(warning, counter);
}
}, 1000);
},
_startTimer: function(){
var self = this;
this.timer = win.setTimeout(function(){
self._keepAlive();
}, this.options.pollingInterval * 1000);
},
_stopTimer: function(){
// reset the failed requests counter
this.failedRequests = this.options.failedRequests;
win.clearTimeout(this.timer);
},
_keepAlive: function( recurse ){
var self = this,
options = this.options;
if( typeof recurse === "undefined" ){
recurse = true;
}
// if too many requests failed, abort
if( !this.failedRequests ){
this._stopTimer();
options.onAbort.call( this.warning[0] );
return;
}
$.ajax({
timeout: options.AJAXTimeout,
url: options.keepAliveURL,
error: function(){
self.failedRequests--;
},
success: function(response){
if($.trim(response) !== options.serverResponseEquals){
self.failedRequests--;
}
},
complete: function(){
if( recurse ){
self._startTimer();
}
}
});
}
};
// expose
$.idleTimeout = function(element, resume, options){
idleTimeout.init( element, resume, $.extend($.idleTimeout.options, options) );
return this;
};
// options
$.idleTimeout.options = {
// number of seconds after user is idle to show the warning
warningLength: 30,
// url to call to keep the session alive while the user is active
keepAliveURL: "",
// the response from keepAliveURL must equal this text:
serverResponseEquals: "OK",
// user is considered idle after this many seconds. 10 minutes default
idleAfter: 600,
// a polling request will be sent to the server every X seconds
pollingInterval: 60,
// number of failed polling requests until we abort this script
failedRequests: 5,
// the $.ajax timeout in MILLISECONDS!
AJAXTimeout: 250,
/*
Callbacks
"this" refers to the element found by the first selector passed to $.idleTimeout.
*/
// callback to fire when the session times out
onTimeout: $.noop,
// fires when the user becomes idle
onIdle: $.noop,
// fires during each second of warningLength
onCountdown: $.noop,
// fires when the user resumes the session
onResume: $.noop,
// callback to fire when the script is aborted due to too many failed requests
onAbort: $.noop
};
})(jQuery, window);

View file

@ -0,0 +1,116 @@
(function($){
$.idleTimer = function f(newTimeout){
//$.idleTimer.tId = -1 //timeout ID
var idle = false, //indicates if the user is idle
enabled = true, //indicates if the idle timer is enabled
timeout = 30000, //the amount of time (ms) before the user is considered idle
events = 'mousemove keydown DOMMouseScroll mousewheel mousedown', // activity is one of these events
//f.olddate = undefined, // olddate used for getElapsedTime. stored on the function
/* (intentionally not documented)
* Toggles the idle state and fires an appropriate event.
* @return {void}
*/
toggleIdleState = function(){
//toggle the state
idle = !idle;
// reset timeout counter
f.olddate = +new Date;
//fire appropriate event
$(document).trigger( $.data(document,'idleTimer', idle ? "idle" : "active" ) + '.idleTimer');
},
/**
* Stops the idle timer. This removes appropriate event handlers
* and cancels any pending timeouts.
* @return {void}
* @method stop
* @static
*/
stop = function(){
//set to disabled
enabled = false;
//clear any pending timeouts
clearTimeout($.idleTimer.tId);
//detach the event handlers
$(document).unbind('.idleTimer');
},
/* (intentionally not documented)
* Handles a user event indicating that the user isn't idle.
* @param {Event} event A DOM2-normalized event object.
* @return {void}
*/
handleUserEvent = function(){
//clear any existing timeout
clearTimeout($.idleTimer.tId);
//if the idle timer is enabled
if (enabled){
//if it's idle, that means the user is no longer idle
if (idle){
toggleIdleState();
}
//set a new timeout
$.idleTimer.tId = setTimeout(toggleIdleState, timeout);
}
};
/**
* Starts the idle timer. This adds appropriate event handlers
* and starts the first timeout.
* @param {int} newTimeout (Optional) A new value for the timeout period in ms.
* @return {void}
* @method $.idleTimer
* @static
*/
f.olddate = f.olddate || +new Date;
//assign a new timeout if necessary
if (typeof newTimeout == "number"){
timeout = newTimeout;
} else if (newTimeout === 'destroy') {
stop();
return this;
} else if (newTimeout === 'getElapsedTime'){
return (+new Date) - f.olddate;
}
//assign appropriate event handlers
$(document).bind($.trim((events+' ').split(' ').join('.idleTimer ')),handleUserEvent);
//set a timeout to toggle state
$.idleTimer.tId = setTimeout(toggleIdleState, timeout);
// assume the user is active for the first x seconds.
$.data(document,'idleTimer',"active");
}; // end of $.idleTimer()
})(jQuery);

View file

@ -7,11 +7,11 @@
<h1 data-ng-show="create"><span class="gray">New Application</span></h1>
<h1 data-ng-hide="create">
<span class="gray">{{application.name}}</span> configuration
<span class="gray">Application Settings</span>
</h1>
<div data-ng-show="applicationForm.showErrors && applicationForm.$error.required" class="alert alert-error">
Please fill in all required fields
<div data-ng-show="applicationForm.showErrors && applicationForm.$error.required" class="alert alert-error">Please fill
in all required fields
</div>
<p class="subtitle subtitle-right"><span class="required">*</span> Required fields</p>
@ -30,58 +30,21 @@
<input class="input-xlarge" type="checkbox" name="enabled" data-ng-model="application.enabled">
</div>
<div data-kc-input>
<label>User registration</label>
<input class="input-xlarge" type="checkbox" name="social"
data-ng-model="application.userRegistration">
</div>
<div class="control-group">
<label class="control-label" for="realm">Realm <span class="required">*</span></label>
<label for="applicationForm-adminUrl" class="control-label">Admin URL</label>
<div class="controls">
<select data-ng-model="application.realm" id="realm" name="realm" data-ng-required>
<option data-ng-repeat="r in realms" value="{{r.id}}"
data-ng-selected="r.id == application.realm">{{r.name}}
</option>
</select>
<input class="input-small" type="text" name="adminUrl"
data-ng-model="application.adminUrl">
</div>
</div>
</fieldset>
<fieldset>
<legend>Roles</legend>
<div class="control-group">
<label class="control-label">Roles</label>
<div class="controls">
<span class="label" style="margin-right: 1em;"
data-ng-repeat="r in (application.roles|orderBy:'toString()')">{{r}} <button
data-ng-click="removeRole(r)"><i class="icon-remove icon-white"></i></button></span>
<div class="input-append">
<input class="input-small" type="text" data-ng-model="newRole" placeHolder="Role"
data-kc-enter="addRole()"/>
<button class="btn" type="button" data-ng-click="addRole()">Add</button>
</div>
</div>
</div>
<div class="control-group">
<label class="control-label">Initial Roles</label>
<div class="controls">
<span class="label" style="margin-right: 1em;"
data-ng-repeat="r in (application.initialRoles|orderBy:'toString()')">{{r}} <button
data-ng-click="removeInitialRole(r)"><i class="icon-remove icon-white"></i></button></span>
<div class="input-append">
<select style="width: auto;" data-ng-model="newInitialRole"
data-ng-click="addInitialRole()">
<option data-ng-repeat="r in (application.roles|remove:application.initialRoles|orderBy:'toString()')"
value="{{r}}">{{r}}
</option>
</select>
</div>
</div>
</div>
</fieldset>
<div class="form-actions" data-ng-show="create">
<button type="submit" data-ng-click="save()" class="btn btn-primary" data-ng-show="changed">Save
</button>
@ -96,14 +59,13 @@
</button>
<button type="submit" data-ng-click="reset()" class="btn" data-ng-show="changed">Clear changes
</button>
<a href="#/applications" data-ng-hide="changed">View applications &#187;</a>
<button type="submit" data-ng-click="remove()" class="btn btn-danger" data-ng-hide="changed">
Delete
</button>
</div>
</form>
</div>
<div id="container-right-bg"></div>
</div>
</div>

View file

@ -4,22 +4,7 @@
<div id="actions-bg"></div>
<div id="container-right" class="span9">
<a class="btn btn-small pull-right" href="#/create/application">Add Application</a>
<h1>
<span class="gray">Applications</span>
</h1>
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>Application</th>
</tr>
</thead>
<tr data-ng-repeat="application in applications">
<td><a href="#/applications/{{application.id}}">{{application.name}}</a></td>
</tr>
</table>
</div>
<div id="container-right-bg"></div>
</div>

View file

@ -1,20 +1,25 @@
<nav id="local-nav" data-ng-controller="ApplicationListCtrl">
<ul class="nav nav-list">
<li>
<div>
<span class="toggle">Applications</span>
<div data-ng-hide="create">
<nav id="local-nav">
<div data-ng-show="selection.applications.length > 0"><span class="divider-vertical-right">Application </span>
<select ng-change="changeApplication()" ng-model="selection.application" ng-options="a.name for a in selection.applications">
<option value="">-- chose an application --</option>
</select>
</div>
<ul>
<li data-ng-repeat="a in applications" data-ng-class="path[1] == a.id && 'active'">
<a href="#/applications/{{a.id}}">{{a.name}}</a>
<ul class="sub-items" data-ng-show="path[1] == a.id">
<li data-ng-class="!path[2] && 'active'"><a href="#/applications/{{a.id}}">Configuration</a>
</li>
<li data-ng-class="path[2] == 'roles' && 'active'"><a href="#/applications/{{a.id}}/roles">Role
mapping</a></li>
</ul>
</li>
<ul class="nav nav-list" data-ng-show="selection.application">
<li data-ng-class="path[4] == 'roles' && 'active'"><a
href="#/realms/{{realm.id}}/applications/{{selection.application.id}}/roles">Application Roles</a>
<ul class="sub-items">
<li data-ng-class="path[0] == 'create' && path[1] == 'role' && 'active'"><a
href="#/create/role/realms/{{realm.id}}/applications/{{selection.application.id}}">Add Role</a></li>
</ul>
</li>
</ul>
<hr/>
<div>
<a href="#/realms/{{realm.id}}">Back to realm management...</a>
</div>
<div>
<a href="#/create/application/{{realm.id}}">New Application...</a>
</div>
</nav>
</div>

View file

@ -3,15 +3,7 @@
<div class="container">
<div class="nav-collapse">
<nav id="global-nav">
<div data-ng-controller="RealmDropdownCtrl" > <!--
<ul class="nav pull-left" data-ng-show="auth.loggedIn">
<li class="dropdown"><a data-toggle="dropdown" class="dropdown-toggle" href="#"> Realms <i class="caret"></i></a>
<ul class="dropdown-menu">
<li ng-repeat="(id, name) in current.realms"><a href="#/realms/{{id}}">{{name}}</a>
</li>
</ul>
</li>
</ul> -->
<div data-ng-controller="RealmDropdownCtrl" >
<ul class="nav pull-left" data-ng-show="showNav()">
<li class="divider-vertical-right"><a href="#/realms/{{currentRealmId}}">Realm</a></li>
</ul>
@ -20,7 +12,7 @@
<!-- <select class="nav pull-left" ng-options="r.name for r in current.realms"></select> -->
</div>
<ul class="nav pull-right" data-ng-hide="auth.loggedIn">
<li><a href="/auth-server/rest/saas/login">Login</a></li>
<li><a href="/auth-server/rest/saas/loginPage.html">Login</a></li>
<li><a href="/auth-server/saas/saas-register.jsp">Register</a></li>
</ul>
<ul class="nav pull-right" data-ng-show="auth.loggedIn">

View file

@ -16,6 +16,8 @@
href="#/find/user/{{realm.id}}">Find User</a></li>
</ul>
</li>
<li data-ng-class="path[2] == 'resources' && 'active'"><a href="#/realms/{{realm.id}}/applications">Manage Applications</a></li>
</ul>
</nav>
</div>

View file

@ -72,7 +72,7 @@ public class RealmResourcesResource {
@Path("{id}")
public RealmResourceResource getResource(final @PathParam("id") String id) {
return new Transaction() {
return new Transaction(false) {
@Override
protected RealmResourceResource callImpl() {
ResourceModel resourceModel = realm.getResourceById(id);