Fixed table header on User Permissions page

This feature was requested recently, and does make sense - especially as the
number of tables in a project increases.The following enhancement is also fully compatible with the “Control export
options via userpriv.php” tip that I submitted previously
(viewtopic.php?f=18&t=45378).As before, avoid confusion by edit the userpriv.php file from the bottom
up…So, start by opening userpriv.php in the root folder:1) Locate this line of code:<?php include_once "footer.php"; ?>…and insert the following code block immediately before it:<?php if (!$user_groups->isExport()) { ?>loadjs.ready(“fixedheadertable”, function() {ew.fixedHeaderTable({delay: 0,scrollbars: false,container: “gmp_user_groups”,width: “”,height: “”});});</scr ipt><?php } ?>2) Next, locate this block of code (just a few lines above the block you
just added):loadjs.ready(“load”, function() {// Startup script…and edit as follows:loadjs.ready(“load”, function() {// Apply fixed header style to table$(‘table’).addClass(‘ew-table table-head-fixed
ew-fixed-header-table’);// Startup script3) Now locate this block of code (somewhere around line 50 in the file):loadjs.ready(“head”, function() {// Client script…and insert the following code block immediately before it:ew.ready(“head”, “js/ewfixedheadertable.js”, “fixedheadertable”);</scr ipt>4) Save the edited version (and make a copy in case it ever gets
overwritten)Finally:1) ensure that the “Fixed Header Table” extension is enabled under Tools >
Extensions (no tables need to be selected under the extension’s Advanced
settings)2) click “Generate” after ensuring that both userpriv rows are unchecked (or
you’ll overwrite the file(s) you modified!)…and that’s it!Log in to your site as Admin and open a Permissions page - as you scroll
down the page, the table header will stay in focus.

Here’s a more readable version…This feature was requested recently, and does make sense - especially as the number of tables in a project increases.The following enhancement is also fully compatible with the “Control export options via userpriv.php” tip that I submitted previously (viewtopic.php?f=18&t=45378).As before, avoid confusion by editing the userpriv.php file from the bottom up (and remember to remove the spaces within the “scr ipt” tags)…So, start by opening userpriv.php in the root folder:1) Locate this line of code:<?php include_once "footer.php"; ?>…and insert the following code block immediately before it:<?php if (!$user_groups->isExport()) { ?>

loadjs.ready(“fixedheadertable”, function() {
ew.fixedHeaderTable({
delay: 0,
scrollbars: false,
container: “gmp_user_groups”,
width: “”,
height: “”
});
});
</scr ipt>

<?php } ?>2) Next, locate this block of code (just a few lines above the block you just added):loadjs.ready("load", function() {
// Startup script

…and edit as follows:loadjs.ready(“load”, function() {

// Apply fixed header style to table
$('table').addClass('ew-table table-head-fixed ew-fixed-header-table');

// Startup script
  1. Now locate this block of code (somewhere around line 50 in the file):loadjs.ready(“head”, function() {
// Client script

…and insert the following code block immediately before it:ew.ready(“head”, “js/ewfixedheadertable.js”, “fixedheadertable”);
</scr ipt>
4) Save the edited version (and make a copy in case it ever gets overwritten)Finally:1) ensure that the “Fixed Header Table” extension is enabled under Tools > Extensions (no tables need to be selected under the extension’s Advanced settings)2) click “Generate” after ensuring that both userpriv rows are unchecked (or you’ll overwrite the file(s) you modified!)…and that’s it!Log in to your site as Admin and open a Permissions page - as you scroll down the page, the table header will stay in focus.

Your customization/code above won’t work, because there is no “gmp_user_groups” container in the generated userpriv.php page in v2020.

In addition, you used $user_groups variable object which does not exist in that file.

Thanks for your thoughts, though the code presented does work 100% with no PHP or console errors - I had already tested it before posting.However, in light of your comments, I’ve made a few changes - mostly to improve stability:So, start by opening userpriv.php in the root folder:1) Locate this line of code:<?php include_once "footer.php"; ?>…and insert the following code block immediately before it:<?php if (file_exists('js/ewfixedheadertable.js')) { ?>

loadjs.ready(“fixedheadertable”, function() {
ew.fixedHeaderTable({
delay: 0,
scrollbars: false,
container: “ewjtable-main-container”,
width: “”,
height: “”
});
});
</scr ipt>

<?php } ?>2) Next, locate this block of code (a few lines above the block you just added):loadjs.ready("load", function() {
// Startup script

…and edit as follows:loadjs.ready(“load”, function() {

<?php if (file_exists('js/ewfixedheadertable.js')) { ?>

// Apply fixed header style to table
$(‘table’).addClass(‘table-head-fixed ew-fixed-header-table’);

<?php } ?>
// Startup script
  1. Now locate this block of code (somewhere around line 50 in the file):
    loadjs.ready(“head”, function() {
// Client script

…and insert the following code block immediately before it:<?php if (file_exists('js/ewfixedheadertable.js')) { ?>

ew.ready(“head”, “js/ewfixedheadertable.js”, “fixedheadertable”);
</scr ipt>

<?php } ?>4) Save the edited version (and make a copy in case it ever gets overwritten)

Sorry, still not working.

This is very strange - the code works perfectly for me… as I scroll down the page of permissions, the table header remains visible at all times and the global enable/disable/sort features work too.Please post your edits + a few lines above & below each edited block.

What PHPMaker version are you using? I implemented your code in v2020.0.6, and it does not work at all.Please note, there is no “ewjtable-main-container” in the generated userpriv.php file under the root folder of web app.

I’m using v2020.0.6 …and the “ewjtable-main-container” is created on the fly by the ewjtable code - inspect the table in your browser and you’ll see it.

No, Adam. Still not working.

That’s so odd - it literally worked first time for me and was probably the simplest mod I’ve made to PHPM!I was planning to paste you my userpriv.php to try but it won’t go through despite me breaking all the SCRIPT tags.

I suspicious, there are another customization that you did in that file so that it’s working at your side, but no at mine. Thanks.

I do have another customisation in userpriv (export options permissions), but it’s not related in any way.Actually, since my mods are implemented via an extension using args.code.replace(), I disabled the export permissions mod and regenerated both userpriv files to check for issues but the fixed header still worked fine.All I can suggest at this point is that you post your edits (plus 2-3 lines of code before and after) - maybe I’ll spot something in your implementation.//*****

<?php namespace PHPMaker2020\DFX_2020;// Session if (session_status() !== PHP_SESSION_ACTIVE) session_start(); // Init session data// Output buffering ob_start();// Autoload include_once "autoload.php"; ?> <?php// Write header WriteHeader(FALSE);// Create page object $userpriv = new userpriv();// Run the page $userpriv->run();// Setup login status SetupLoginStatus(); SetClientVar("login", LoginStatus());// Global Page Rendering event (in userfn*.php) Page_Rendering();// Page Rendering event $userpriv->Page_Render(); ?> <?php include_once "header.php"; ?> var fuserpriv, currentPageID; loadjs.ready("head", function() {
// Form object
currentPageID = ew.PAGE_ID = "userpriv";
fuserpriv = currentForm = new ew.Form("fuserpriv", "userpriv");
loadjs.done("fuserpriv");

});
</scr ipt>

<?php if (file_exists('js/ewfixedheadertable.js')) { ?> ew.ready("head", "js/ewfixedheadertable.js", "fixedheadertable"); <?php } ?> loadjs.ready("head", function() {
// Client scr ipt
// Write your client scr ipt here, no need to add scr ipt tags.

});
</scr ipt>

<?php $userpriv->showMessage(); ?> <?php if ($Page->CheckToken) { ?> " value="<?php echo $Page->Token ?>"> <?php } ?>

<?php echo $Language->phrase("UserLevel") ?><?php echo $Security->getUserLevelName((int)$userpriv->User_Group_ID->CurrentValue) ?> (<?php echo $userpriv->User_Group_ID->CurrentValue ?>)

">
Disabled ?>><?php echo $Language->phrase("Update") ?> <?php echo $Language->phrase("CancelBtn") ?>
loadjs.ready("makerjs", function() { var $ = jQuery, priv = <?php echo JsonEncode($userpriv->Privileges) ?>;
function getDisplayFn(name, trueValue) {
	return function(data) {
		var row = data.record, id = name + '_' + row.index,
			checked = (row.permission & trueValue) == trueValue;
		row.checked = checked;
		return '<div class="custom-control custom-checkbox d-inline-block"><in put type="checkbox" class="custom-control-input ew-priv ew-multi-select" name="' + id + '" id="' + id +
			'" value="' + trueValue + '" data-index="' + row.index + '"' +
			((checked) ? ' checked' : '') +
			(((row.allowed & trueValue) != trueValue) ? ' disabled' : '') + '><label class="custom-control-label" for="' + id + '"></label></div>';
	};
}

function displayTableName(data) {
	var row = data.record;
	return row.table + '<in put type="hidden" name="table_' + row.index + '" value="1">';
}

function getRecords(data, params) {
	var rows = priv.permissions.slice(0);
	if (data && data.table) {
		var table = data.table.toLowerCase();
		rows = jQuery.map(rows, function(row) {
			if (row.table.toLowerCase().includes(table))
				return row;
			return null;
		});
	}
	if (params && params.sorting) {
		var asc = params.sorting.match(/ASC$/);
		rows.sort(function(a, b) { // Case-insensitive
			if (b.table.toLowerCase() > a.table.toLowerCase())
				return (asc) ? -1 : 1;
			else if (b.table.toLowerCase() === a.table.toLowerCase())
				return 0
			else if (b.table.toLowerCase() < a.table.toLowerCase())
				return (asc) ? 1 : -1;
		});
	}
	return {
		result: "OK",
		params: jQuery.extend({}, data, params),
		records: rows
	};
}

function getTitleHtml(id, phraseId) {
	return '<div class="custom-control custom-checkbox"><in put type="checkbox" class="custom-control-input ew-priv" name="' + id + '" id="' + id + '" onclick="ew.selectAll(this);">' +
		'<label class="custom-control-label" for="' + id + '">' + ew.language.phrase("Permission" + (phraseId || id)) + '</label></div>'
}

// Fields
var _fields = {
	table: {
		title: '<span class="font-weight-normal">' + ew.language.phrase("TableOrView") + '</span>',
		display: displayTableName,
		sorting: true
	}
};
["add", "delete", "edit", "list", "lookup", "view", "search", "import", "admin"].forEach(function(id) {
	_fields[id] = {
		title: getTitleHtml(id),
		display: getDisplayFn(id, priv[id]),
		sorting: false
	};
});

// Init
$(".ew-card.ew-user-priv .ew-card-body").ewjtable({
	paging: false,
	sorting: true,
	defaultSorting: "table ASC",
	fields: _fields,
	actions: { listAction: getRecords },
	rowInserted: function(event, data) {
		var $row = data.row;
		$row.find("input[type=checkbox]").on("click", function() {
			var $this = $(this), index = parseInt($this.data("index"), 10), value = parseInt($this.data("value"), 10);
			if (this.checked)
				priv.permissions[index].permission = priv.permissions[index].permission | value;
			else
				priv.permissions[index].permission = priv.permissions[index].permission ^ value;
		});
	},
	recordsLoaded: function(event, data) {
		var sorting = data.serverResponse.params.sorting,
			$c = $(this).find(".ewjtable-column-header-container:first");
		if (!$c.find(".ew-table-header-sort")[0])
			$c.append('<span class="ew-table-header-sort"><i class="fas fa-sort-down"></i></span>');
		$c.find(".ew-table-header-sort i.fas").toggleClass("fa-sort-up", !!sorting.match(/ASC$/)).toggleClass("fa-sort-down", !!sorting.match(/DESC$/));
		ew.initMultiSelectCheckboxes();
		ew.fixLayoutHeight();
	}
});

// Re-load records when user click 'Search' button.
var _timer;
$("#table-name").on("keydown keypress cut paste", function(e) {
	if (_timer)
		_timer.cancel();
	_timer = $.later(200, null, function() {
		$(".ew-card.ew-user-priv .ew-card-body").ewjtable("load", {
			table: $("#table-name").val()
		});
	});
});

// Load all records
$("#table-name").keydown();

});
</scr ipt>

loadjs.ready(“load”, function() {

<?php if (file_exists('js/ewfixedheadertable.js')) { ?>

// Apply fixed header style to table
$(‘table’).addClass(‘table-head-fixed ew-fixed-header-table’);

<?php } ?>
// Startup scr ipt
// Write your startup scr ipt here
// console.log("page loaded");

});
</scr ipt>

<?php if (file_exists('js/ewfixedheadertable.js')) { ?> loadjs.ready("fixedheadertable", function() { ew.fixedHeaderTable({ delay: 0, scrollbars: false, container: "ewjtable-main-container", width: "", height: "" }); }); <?php } ?> <?php include_once "footer.php"; ?> <?php $userpriv->terminate(); ?>

Okay… I found the issue - having “Use Bootstrap responsive tables” enabled breaks things, but it’s a simple fix.Change:$(‘table’).addClass(‘table-head-fixed ew-fixed-header-table’);…to…$(‘table’).addClass(‘table-head-fixed ew-fixed-header-table’).parent().parent().removeClass(‘table-responsive table-responsive-sm table-responsive-md table-responsive-lg table-responsive-xl’);…and it should then work fine :slight_smile:

Adam, it works only if the “AdminLTE layout class” is setup other than “layout-navbar-fixed”.

If you choose “layout-navbar-fixed”, it won’t work, because the navbar/header will overlay the header column itself; and we cannot see the fixed table header effect.

Yes, mobhar - good point! The (kinda) simple fix is the edit the middle code block to this (remember to remove the space in both instances of “win dow”):loadjs.ready(“load”, function() {

<?php if (file_exists('js/ewfixedheadertable.js')) { ?> <?php if (strpos(Config('BODY_CLASS'), 'layout-navbar-fixed') > 0) { ?>

// Accommodate layout-fixed-navbar style
$(win dow).on(‘scroll’, function () {
if (typeof self.rowHeight == ‘undefined’)
self.rowHeight = Math.round($(‘thead > tr’).height());

	if (typeof self.rowTop == 'undefined')
		self.rowTop = Math.round($('thead > tr').offset().top);

	$('thead > tr').height(self.rowHeight + Math.min(Math.max($(win dow).scrollTop() + $('nav.main-header').outerHeight() - self.rowTop, 0), $('nav.main-header').outerHeight()));
});
<?php } ?>
// Remove responsive table class from card body (if present)
$('.card-body').removeClass('table-responsive table-responsive-sm table-responsive-md table-responsive-lg table-responsive-xl');

// Apply fixed header style to table
$('table').addClass('table-head-fixed ew-fixed-header-table');
<?php } ?>
// Startup script
// Write your startup script here
// console.log("page loaded");

});If you try this version of the code, you’ll see that the table scrolls up smoothly until the table header reaches the bottom of the fixed navbar, then it freezes until you have scrolled down 68px (the height of the navbar in my browser), then scrolling continues normally and the table header remains in view. This “pause” was the only issue I found and couldn’t find a solution to - perhaps you’ll spot the cause… either way, you should find the mod works in all situations now.

Thanks, Adam. Now it works properly for the “layout-navbar-fixed” condition, too.