How to hide columns in table view based on user group?

I got a question from an AppGini user on how to hide specific columns in the table view based on the user group. This blog post explains how to do this through hooks.

As an example, we’ll modify the orders table of the Northwind demo to hide the ‘Customer’ column for ‘Suppliers’ and ‘anonymous’ groups, while keeping it visible for all others, as demonstrated in the following 2 screenshots:

Orders table, with all columns visible, including the ‘Customer’ column.
Orders table, with the ‘Customer’ column hidden for specific user groups as we’re going to see how in this tutorial.

Now, we could do this in a trivial way through JavaScript, or in a more robust way through server-side PHP code. The JavaScript method would work until some ‘geek’ user figures out how to use the browser console to unhide the column. The more robust server-side method is a more secure way that users can’t circumvent. And that’s what we’re going to explain here.

Step 1: Copy a few lines from the generated {tablename}_view.php file

{tablename} in our example is ‘orders’. So, let’s open the generated orders_view.php file in a text editor and search for a line starting with $x->ColWidth. We should copy that line and the following ones until the line starting with $x->SelectedTemplate. For the Orders table of Northwind, these lines read:

$x->ColWidth   = array(  75, 200, 150, 100, 150, 150);
$x->ColCaption = array("Order ID", "Customer", "Employee", "Order Date", "Ship Via", "Ship Country");
$x->ColFieldName = array('OrderID', 'CustomerID', 'EmployeeID', 'OrderDate', 'ShipVia', 'ShipCountry');
$x->ColNumber  = array(1, 2, 3, 4, 7, 14);

// template paths below are based on the app main directory
$x->Template = 'templates/orders_templateTV.html';
$x->SelectedTemplate = 'templates/orders_templateTVS.html';

Step 2: Paste the copied lines into the {tablename}_init hook

For our example again, that would be the orders_init hook in the generated hooks/orders.php file. After pasting the code we copied in step 1, that hook function would read:

function orders_init(&$options, $memberInfo, &$args){
		$x->ColWidth   = array(  75, 200, 150, 100, 150, 150);
		$x->ColCaption = array("Order ID", "Customer", "Employee", "Order Date", "Ship Via", "Ship Country");
		$x->ColFieldName = array('OrderID', 'CustomerID', 'EmployeeID', 'OrderDate', 'ShipVia', 'ShipCountry');
		$x->ColNumber  = array(1, 2, 3, 4, 7, 14);

		// template paths below are based on the app main directory
		$x->Template = 'templates/orders_templateTV.html';
		$x->SelectedTemplate = 'templates/orders_templateTVS.html';

	return TRUE;
}

Note that we added an extra indentation level (via Tab or spaces, whichever you prefer) while pasting the code. This is for a reason that will be revealed below.

Step 3: Replace ‘$x’ with ‘$options’, and ‘templates’ with ‘hooks’

After pasting the code in step 2, we need to change $x into $options and templates into hooks:

function orders_init(&$options, $memberInfo, &$args){
		$options->ColWidth   = array(  75, 200, 150, 100, 150, 150);
		$options->ColCaption = array("Order ID", "Customer", "Employee", "Order Date", "Ship Via", "Ship Country");
		$options->ColFieldName = array('OrderID', 'CustomerID', 'EmployeeID', 'OrderDate', 'ShipVia', 'ShipCountry');
		$options->ColNumber  = array(1, 2, 3, 4, 7, 14);

		// template paths below are based on the app main directory
		$options->Template = 'hooks/orders_templateTV.html';
		$options->SelectedTemplate = 'hooks/orders_templateTVS.html';

	return TRUE;
}

Step 4: Remove the references to the column we want to hide

In our example, we’d like to hide the ‘Customer’ column. This is the second column in the table view. To remove references to it in the init hook, we should remove the second item in each of the 4 arrays present in lines 2-5 in our code:

function orders_init(&$options, $memberInfo, &$args){
		$options->ColWidth   = array(  75, 150, 100, 150, 150);
		$options->ColCaption = array("Order ID", "Employee", "Order Date", "Ship Via", "Ship Country");
		$options->ColFieldName = array('OrderID', 'EmployeeID', 'OrderDate', 'ShipVia', 'ShipCountry');
		$options->ColNumber  = array(1, 3, 4, 7, 14);

		// template paths below are based on the app main directory
		$options->Template = 'hooks/orders_templateTV.html';
		$options->SelectedTemplate = 'hooks/orders_templateTVS.html';

	return TRUE;
}

If we need to hide more columns, we should do the same for each.

Step 5: Add a conditional check for user group

So far, we’ve hidden the ‘Customer’ column header for all users. We need to wrap our code inside a conditional check to hide the column only for specific user groups:

function orders_init(&$options, $memberInfo, &$args){
	$hide_groups = array('anonymous', 'Suppliers');
	if(in_array($memberInfo['group'], $hide_groups)) {
		$options->ColWidth   = array(  75, 150, 100, 150, 150);
		$options->ColCaption = array("Order ID", "Employee", "Order Date", "Ship Via", "Ship Country");
		$options->ColFieldName = array('OrderID', 'EmployeeID', 'OrderDate', 'ShipVia', 'ShipCountry');
		$options->ColNumber  = array(1, 3, 4, 7, 14);

		// template paths below are based on the app main directory
		$options->Template = 'hooks/orders_templateTV.html';
		$options->SelectedTemplate = 'hooks/orders_templateTVS.html';
	}
	return TRUE;
}

We’ve added a line listing the user groups we want to hide the ‘Customer’ column from: ‘anonymous’ and ‘Suppliers’. Then we wrapped the code we pasted in the previous steps inside an if check. This would cause the code for hiding the column to apply only if the user belongs to any of the groups we specified. Note that because we double-indented the pasted code in step 2, it’s now properly formatted after wrapping it inside the if block.

We’ve now finished all the necessary edits for the init hook. In the following steps, we’ll make a copy of the table view templates to remove the ‘Customer’ table from there as well.

Step 6: Copy the table view templates to the hooks folder

The table view templates are stored in the templates folder, and named {tablename}_templateTV.html and {tablename}_templateTVS.html. For our example, these are orders_templateTV.html and orders_templateTVS.html. We should copy those 2 files to the hooks folder. We’ll then edit the copied templates to remove the ‘Customer’ column.

To clarify this step a little more, we now have 2 sets of templates for the table view of the orders table: the default one in the templates folder would apply to all groups except ‘anonymous’ and ‘Suppliers’, and the copied one inside hooks that would apply to those 2 groups.

Step 7: Edit the copied templates to remove the column

Let’s open the copied hooks/orders_templateTV.html file in our text editor. It should read:

<td id="orders-OrderID-<%%VALUE(OrderID)%%>" class="orders-OrderID text-right"><%%SELECT%%><%%VALUE(OrderID)%%><%%ENDSELECT%%></td>
<td id="orders-CustomerID-<%%VALUE(OrderID)%%>" class="orders-CustomerID"><%%SELECT%%><%%VALUE(CustomerID)%%><%%ENDSELECT%%></td>
<td id="orders-EmployeeID-<%%VALUE(OrderID)%%>" class="orders-EmployeeID"><%%SELECT%%><%%VALUE(EmployeeID)%%><%%ENDSELECT%%></td>
<td id="orders-OrderDate-<%%VALUE(OrderID)%%>" class="orders-OrderDate"><%%SELECT%%><%%VALUE(OrderDate)%%><%%ENDSELECT%%></td>
<td id="orders-ShipVia-<%%VALUE(OrderID)%%>" class="orders-ShipVia"><%%SELECT%%><%%VALUE(ShipVia)%%><%%ENDSELECT%%></td>
<td id="orders-ShipCountry-<%%VALUE(OrderID)%%>" class="orders-ShipCountry"><%%SELECT%%><%%VALUE(ShipCountry)%%><%%ENDSELECT%%></td>

To remove the ‘Customer’ column, we should remove the second line:

<td id="orders-OrderID-<%%VALUE(OrderID)%%>" class="orders-OrderID text-right"><%%SELECT%%><%%VALUE(OrderID)%%><%%ENDSELECT%%></td>
<td id="orders-EmployeeID-<%%VALUE(OrderID)%%>" class="orders-EmployeeID"><%%SELECT%%><%%VALUE(EmployeeID)%%><%%ENDSELECT%%></td>
<td id="orders-OrderDate-<%%VALUE(OrderID)%%>" class="orders-OrderDate"><%%SELECT%%><%%VALUE(OrderDate)%%><%%ENDSELECT%%></td>
<td id="orders-ShipVia-<%%VALUE(OrderID)%%>" class="orders-ShipVia"><%%SELECT%%><%%VALUE(ShipVia)%%><%%ENDSELECT%%></td>
<td id="orders-ShipCountry-<%%VALUE(OrderID)%%>" class="orders-ShipCountry"><%%SELECT%%><%%VALUE(ShipCountry)%%><%%ENDSELECT%%></td>

Finally, we’ll do the same for hooks/orders_templateTVS.html:

<td id="orders-OrderID-<%%VALUE(OrderID)%%>" class="orders-OrderID text-right"><%%VALUE(OrderID)%%></td>
<td id="orders-EmployeeID-<%%VALUE(OrderID)%%>" class="orders-EmployeeID"><%%VALUE(EmployeeID)%%></td>
<td id="orders-OrderDate-<%%VALUE(OrderID)%%>" class="orders-OrderDate"><%%VALUE(OrderDate)%%></td>
<td id="orders-ShipVia-<%%VALUE(OrderID)%%>" class="orders-ShipVia"><%%VALUE(ShipVia)%%></td>
<td id="orders-ShipCountry-<%%VALUE(OrderID)%%>" class="orders-ShipCountry"><%%VALUE(ShipCountry)%%></td>

By now, we’re done! If a guest (anonymous) user, or a user from the ‘Suppliers’ group visits the orders table, they won’t be able to see the ‘Customer’ field in the table view. For all other user groups, the ‘Customer’ field would be visible.

Further improvements

The above steps hide the ‘Customer’ column for ‘anonymous’ and ‘Suppliers’ groups, but only in the table view (this includes the print view as well). However, if users from those 2 groups have access to the detail view, they would be able to see the hidden ‘Customer’ column. So we need to hide it from the detail view as well, but we’ll leave that to another future post.

In addition, if we enable the option Allow saving data to CSV files in AppGini, those users would be able to click the ‘Save CSV’ button, and they’d be able to access the ‘Customer’ field in the exported dump. To avoid this, we could disable saving CSV for specific user groups (see the example for tablename_init hook), or we could modify the CSV query to remove a certain field for specific groups in a method similar to this, but for the $options->QueryFieldsCSV property.

Published by Genedy

I'm the founder of BigProf Software. We're a tiny team of developers who create tools that make it easy (and affordable) for anyone to create connected business applications that work from any device with a browser.

Leave a comment

Your email address will not be published. Required fields are marked *