Fixed table header on User Permissions page

Tips submitted by PHPMaker users
Post Reply
Adam
User
Posts: 480

Fixed table header on User Permissions page

Post by Adam »

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()) { ?>

<scr ipt>

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

3) 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>

<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.


Adam
User
Posts: 480

Post by Adam »

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()) { ?>
<scr ipt>
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

3) 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>
<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.


mobhar
User
Posts: 11729

Post by mobhar »

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.


Adam
User
Posts: 480

Post by Adam »

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')) { ?>
<scr ipt>
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

3) Now locate this block of code (somewhere around line 50 in the file):

<scr ipt>
loadjs.ready("head", function() {

// Client script

...and insert the following code block immediately before it:

<?php if (file_exists('js/ewfixedheadertable.js')) { ?>
<scr ipt>
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)


mobhar
User
Posts: 11729

Post by mobhar »

Sorry, still not working.


Adam
User
Posts: 480

Post by Adam »

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.


mobhar
User
Posts: 11729

Post by mobhar »

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.


Adam
User
Posts: 480

Post by Adam »

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.


mobhar
User
Posts: 11729

Post by mobhar »

No, Adam. Still not working.


Adam
User
Posts: 480

Post by Adam »

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.


mobhar
User
Posts: 11729

Post by mobhar »

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.


Adam
User
Posts: 480

Post by Adam »

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"; ?>
<scr ipt>
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')) { ?>
<scr ipt>
ew.ready("head", "js/ewfixedheadertable.js", "fixedheadertable");
</scr ipt>
<?php } ?>
<scr ipt>
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();
?>
<form name="fuserpriv" id="fuserpriv" class="form-inline ew-form ew-user-priv-form" action="<?php echo CurrentPageName() ?>" method="post">
<?php if ($Page->CheckToken) { ?>
<in put type="hidden" name="<?php echo Config("TOKEN_NAME") ?>" value="<?php echo $Page->Token ?>">
<?php } ?>
<in put type="hidden" name="t" value="user_groups">
<in put type="hidden" name="action" id="action" value="update">
<in put type="hidden" name="x_User_Group_ID" id="x_User_Group_ID" value="<?php echo $userpriv->User_Group_ID->CurrentValue ?>">
<div class="ew-desktop">
<div class="card ew-card ew-user-priv">
<div class="card-header">
<h3 class="card-title"><?php echo $Language->phrase("UserLevel") ?><?php echo $Security->getUserLevelName((int)$userpriv->User_Group_ID->CurrentValue) ?> (<?php echo $userpriv->User_Group_ID->CurrentValue ?>)</h3>
<div class="card-tools">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-search"></i></span>
</div>
<in put type="search" name="table-name" id="table-name" class="form-control form-control-sm" placeholder="<?php echo HtmlEncode($Language->phrase("Search")) ?>">
</div>
</div>
</div>
<div class="<?php echo ResponsiveTableClass() ?>card-body ew-card-body p-0"></div>
</div>
<div class="ew-desktop-button">
<button class="btn btn-primary ew-btn" name="btn-submit" id="btn-submit" type="submit"<?php echo $userpriv->Disabled ?>><?php echo $Language->phrase("Update") ?></button>
<button class="btn btn-default ew-btn" name="btn-cancel" id="btn-cancel" type="button" data-href="<?php echo $userpriv->getReturnUrl() ?>"><?php echo $Language->phrase("CancelBtn") ?></button>
</div>
</div>
</form>
<scr ipt>
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>
<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')) { ?>
<scr ipt>
loadjs.ready("fixedheadertable", function() {
ew.fixedHeaderTable({
delay: 0,
scrollbars: false,
container: "ewjtable-main-container",
width: "",
height: ""
});
});
</scr ipt>
<?php } ?>
<?php include_once "footer.php"; ?>
<?php
$userpriv->terminate();
?>


Adam
User
Posts: 480

Post by Adam »

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 :)


mobhar
User
Posts: 11729

Post by mobhar »

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.


Adam
User
Posts: 480

Post by Adam »

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.


mobhar
User
Posts: 11729

Post by mobhar »

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


Post Reply