Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backport PR #4990 - Adds pagination of tokens on user profile #5062

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
149 changes: 134 additions & 15 deletions rundeckapp/grails-app/assets/javascripts/user/profile.js
Expand Up @@ -29,6 +29,7 @@ function TokenCreator(data) {
self.tokenTimeUnit = ko.observable(data.tokenTimeUnit || 'm');
self.tokenUser = ko.observable(data.tokenUser || data.user);
self.tokenRolesStr = ko.observable(data.tokenRoles);
self.tokenTableSummaryText = ko.observable(data.tokenTableSummaryText);
self.generateData = ko.computed(function () {
return {
login: self.user(),
Expand Down Expand Up @@ -81,6 +82,92 @@ function RoleSet(list) {
}
}

function TokenTableHandler(ctx) {

var self = this;
var context = ctx;
var tbody = jQuery('#apiTokenTableBody');

self.pageOffset = context.tokenPagingOffset;
self.pageMax = context.tokenPagingMax;
self.totalTokens = context.tokenTotal;

self.getTableBody = function () {
return tbody;
};

var updateSummary = function () {
tokencreator.tokenTableSummaryText(message("userController.page.profile.pager.summary", [
self.pageOffset + 1,
Math.min((self.pageOffset + self.pageMax), self.totalTokens),
self.totalTokens
]));
};

var rows = function () {
return tbody.find(".apitokenform");
};

self.getNumRows = function () {
return rows().length;
};

self.currentPage = function () {
return Math.ceil(self.pageOffset / self.pageMax) + 1;
};

var calcPages = function (total) {
return Math.ceil(total / self.pageMax);
};

self.totalPages = function () {
return calcPages(self.totalTokens);
};

/**
* Determines whenever the number of pages changes if the total number of tokens changes.
* @param totalDiff Increment (or decrement if negative) on current total to check.
* @return {boolean} true if number of pages would change.
*/
self.numPagesChanges = function (totalDiff) {
return self.totalPages() !== calcPages(self.totalTokens + totalDiff);
};

self.insertRow = function (tokenid, rowContent) {
var table = tokenTable.getTableBody();
table.prepend(rowContent);
var row = $('token-' + tokenid);
row.style.opacity = 0;
jQuery($(row)).fadeTo(1000, 1);

self.totalTokens++;
updateSummary();

// if numrows > max then drop last row.
if(self.getNumRows() > self.pageMax) {
rows().last().remove();
}
};

self.removeRow = function(elem) {
jQuery(elem).fadeOut("slow", function () {
$(this).remove();
self.totalTokens--;
updateSummary();
});
};

self.goToPage = function (pageNum) {
var params = {
login: context.user,
max: self.pageMax,
offset: self.pageMax * (pageNum - 1)
};
window.location = _genUrl(appLinks.userProfilePage, params);
};
}


function highlightNew() {
jQuery(' .apitokenform.newtoken').fadeTo('slow', 1);
}
Expand All @@ -89,18 +176,39 @@ function tokenAjaxError(msg) {
jQuery('.gentokenerror').show();
}


function getNumRows() {
return jQuery('.apitokenform').length;
}

function addTokenRow(elem, login, tokenid) {
var table = $(elem).down('.apitokentable');
var row = new Element('tbody');
table.insert(row);
$(row).addClassName('apitokenform');
$(row).style.opacity = 0;
jQuery(row).load(
_genUrl(appLinks.userRenderApiToken, {login: login, tokenid: tokenid}),
function (resp, status, jqxhr) {
jQuery($(row)).fadeTo("slow",1);
}
);

// add the row dynamically only if we are at the first page, and the
// number of pages will not change.
if(tokenTable.currentPage() !== 1 || tokenTable.numPagesChanges(1)) {
tokenTable.goToPage(1);
return;
}

jQuery.get(_genUrl(appLinks.userRenderApiToken, {login: login, tokenid: tokenid}), function (response) {
tokenTable.insertRow(tokenid, response);
});

}

function removeTokenRow(elem, data) {

// Remove dynamically only if we are in last page
// and number of pages will not change.
if (tokenTable.currentPage() !== tokenTable.totalPages()
|| tokenTable.numPagesChanges(-1)) {

// reload page.
window.location.reload();
return;
}

tokenTable.removeRow(elem);
}

function clearToken(elem) {
Expand All @@ -122,7 +230,7 @@ function clearToken(elem) {
tokenAjaxError(data.error);
} else if (data.result) {
//remove row element
jQuery(elem).fadeOut("slow");
removeTokenRow(elem, data);
}
},
error: function (jqxhr, status, error) {
Expand All @@ -132,6 +240,8 @@ function clearToken(elem) {
}
}).success(_ajaxReceiveTokens.curry('api_req_tokens'));
}


function generateUserToken(login, elem, data) {
var dom = jQuery('#' + elem);
jQuery.ajax({
Expand Down Expand Up @@ -197,8 +307,17 @@ jQuery(function () {
var dom = jQuery('#gentokensection');
if (dom.length == 1) {
var roleset = new RoleSet(data.roles);
window.tokencreator = new TokenCreator({roleset: roleset, user: data.user, adminAuth: data.adminAuth,
userTokenAuth: data.userTokenAuth, svcTokenAuth: data.svcTokenAuth});
window.tokencreator = new TokenCreator({
roleset: roleset,
user: data.user,
adminAuth: data.adminAuth,
userTokenAuth: data.userTokenAuth,
svcTokenAuth: data.svcTokenAuth,
tokenTableSummaryText: data.tokenTableSummaryText
});
ko.applyBindings(tokencreator, dom[0]);

// Token table handler
window.tokenTable = new TokenTableHandler(data);
}
});
});
Expand Up @@ -33,6 +33,8 @@ import javax.servlet.http.HttpServletResponse

class UserController extends ControllerBase{

private static final int DEFAULT_TOKEN_PAGE_SIZE = 50

UserService userService
FrameworkService frameworkService
GrailsApplication grailsApplication
Expand Down Expand Up @@ -98,28 +100,78 @@ class UserController extends ControllerBase{
def profile() {
//check auth to view profile
//default to current user profile
if(!params.login){
params.login=session.user
if (!params.login) {
params.login = session.user
}
UserAndRolesAuthContext authContext = frameworkService.getAuthContextForSubject(session.subject)
if(unauthorizedResponse(params.login == session.user || frameworkService.authorizeApplicationResourceType
(authContext, AuthConstants.TYPE_USER, AuthConstants.ACTION_ADMIN), AuthConstants.ACTION_ADMIN,'Users',
params.login)){

def tokenAdmin = frameworkService.authorizeApplicationResourceType(
authContext,
AuthConstants.TYPE_USER,
AuthConstants.ACTION_ADMIN)

if (unauthorizedResponse(
params.login == session.user || tokenAdmin,
AuthConstants.ACTION_ADMIN, 'Users', params.login)) {
return
}

def User u = User.findByLogin(params.login)
if(!u && params.login==session.user){
if (!u && params.login == session.user) {
//redirect to profile edit page, so user can setup their profile
flash.message="Please fill out your profile"
return redirect(action:'register')
flash.message = "Please fill out your profile"
return redirect(action: 'register')
}
if(notFoundResponse(u, 'User', params['login'])){
if (notFoundResponse(u, 'User', params['login'])) {
return
}

def tokenTotal = AuthToken.createCriteria().count {
if (!tokenAdmin) {
eq("creator", u.login)
}
}

int max = (params.max && params.max.isInteger()) ? params.max.toInteger() :
grailsApplication.config.getProperty(
"rundeck.gui.user.profile.paginatetoken.max.per.page",
Integer.class,
DEFAULT_TOKEN_PAGE_SIZE)

int offset = (params.offset && params.offset.isInteger()) ? params.offset.toInteger() : 0

if(offset >= tokenTotal) {
def diff = (tokenTotal % max)
if( diff == 0 && tokenTotal > 0) {
diff = max
}
offset = tokenTotal - diff
}

def tokenList = AuthToken.createCriteria().list {
if (!tokenAdmin) {
eq("creator", u.login)
}
if (offset) {
firstResult(offset)
}
if (max) {
maxResults(max)
}
order("dateCreated", "desc")
}
params.max = max
params.offset = offset

[
user : u,
authRoles : authContext.getRoles(),
tokenMaxExpiration: apiService.maxTokenDurationConfig()
tokenMaxExpiration: apiService.maxTokenDurationConfig(),
tokenAdmin : tokenAdmin,
tokenList : tokenList,
tokenTotal : tokenTotal,
max : max,
offset : offset
]
}
def create={
Expand Down Expand Up @@ -647,7 +699,15 @@ class UserController extends ControllerBase{
return
}

return redirect(controller: 'user', action: 'profile', params: [login: login])
def redirParams = [login: login]
if (params.tokenPagingMax) {
redirParams.max = params.tokenPagingMax
}
if (params.tokenPagingOffset) {
redirParams.offset = params.tokenPagingOffset
}

return redirect(controller: 'user', action: 'profile', params: redirParams)
}
def clearApiToken(User user) {
boolean valid = false
Expand Down Expand Up @@ -744,7 +804,11 @@ class UserController extends ControllerBase{
if (result.error) {
flash.error = result.error
}
return redirect(controller: 'user', action: 'profile', params: [login: login])
return redirect(controller: 'user', action: 'profile', params: [
login: login,
max: params.tokenPagingMax,
offset: params.tokenPagingOffset
])

}
def setDashboardPref={
Expand Down
1 change: 1 addition & 0 deletions rundeckapp/grails-app/i18n/messages.properties
Expand Up @@ -1165,6 +1165,7 @@ security.groups.description=The list of groups/roles names provided by the login
userController.page.create.title=Create User Profile
userController.page.profile.description=Email and Name can be used in Job executions, notifications, or by other plugins.
userController.page.profile.title=User Profile
userController.page.profile.pager.summary=Showing tokens {0} to {1} of {2} total.
not.set=Not set
scmController.action.saveCommit.userInfoMissing.message=SCM Export could not be performed
scmController.action.saveCommit.userInfoMissing.errorHelp=Please update {{user/profile}} and enter the missing values.
Expand Down
1 change: 1 addition & 0 deletions rundeckapp/grails-app/i18n/messages_es_419.properties
Expand Up @@ -1135,6 +1135,7 @@ security.groups.description=La lista de grupos / nombres de funciones proporcion
userController.page.create.title=Crear Perfil de Usuario
userController.page.profile.description=Email y nombre se pueden utilizados en las ejecuciones de Trabajo, notificaciones, o por otros plugins.
userController.page.profile.title=Perfil de Usuario
userController.page.profile.pager.summary=Mostrando tokens {0} a {1} de {2} en total.
not.set=No establecido
scmController.action.saveCommit.userInfoMissing.message=SCM Exportación no pudo ser ejecutado
scmController.action.saveCommit.userInfoMissing.errorHelp=Por favor, actualice {{user/profile}} e ingrese los valores faltantes.
Expand Down
1 change: 1 addition & 0 deletions rundeckapp/grails-app/i18n/messages_fr_FR.properties
Expand Up @@ -1094,6 +1094,7 @@ security.groups.description=La liste des noms de groupes / r\u00f4les fournis pa
userController.page.create.title=Cr\u00e9er un profil utilisateur
userController.page.profile.description=L\u02bcemail et le nom peuvent \u00eatre employ\u00e9s dans les ex\u00e9cutions de travail, les notifications, ou par d\u02bcautres plugins.
userController.page.profile.title=Profil de l\u02bcutilisateur
userController.page.profile.pager.summary=Montrant les jetons {0} à {1} sur un total de {2}
not.set=Pas encore d\u00e9fini
scmController.action.saveCommit.userInfoMissing.message=L\u02bcexportation SCM n\u02bca pas pu \u00eatre effectu\u00e9e
scmController.action.saveCommit.userInfoMissing.errorHelp=Veuillez mettre \u00e0 jour {{user / profile}} et entrer les valeurs manquantes.
Expand Down