Show menu items to users in MySQL database on a per user permission basis with PHP?
Asked Answered
H

3

6

I have a MySQL database table for users and a new one for menu links. Using PHP, more specifically I am also using Laravel.

I need to build a User Menu that displays menu items on a per user permission basis.

Typically this is done using user groups however my requirement is to build it on a per-user-basis!

Meaning every menu item needs to have a yes or no value saved somewhere for every single user in the database.

I then need to display this menu to each user, showing only the menu items they are allowed to view.

I have the user and links MySQL database schema below.

What I need help with is, I believe I need to add another 3rd table user_link_permissions that will store the setting for each user and menu item to determine if the user can view the menu item or not.

I am not sure how to build the PHP to show only the menu items a user is allowed to view and could also use some help in how that 3rd table might need to look like please?

I deally in the PHP code that will be building the menu HTML output, I think it would be nice to possibbly have a method that checks each menu item record in a loop to see if the current user has permission to view it or not.... example

// Array of menu items from MySQL Database or even just a MySQL result?
$menuItems = array();

foreach ($menuItems as $key => $value) {

    // can cureent user view this menu item record or not?
    if($this->user->canViewMenuItem($value)){
        // show menu item
    }

}

Users Table

CREATE TABLE IF NOT EXISTS `users` (
  `user_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `username` varchar(64) COLLATE utf8_unicode_ci NOT NULL,
  `password` varchar(160) COLLATE utf8_unicode_ci NOT NULL,
  `email` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL,
  `first_name` varchar(64) COLLATE utf8_unicode_ci NOT NULL,
  `last_name` varchar(64) COLLATE utf8_unicode_ci NOT NULL,
  `phone` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL,
  `address_street` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL,
  `address_city` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL,
  `address_state` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL,
  `address_postal_code` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL,
  `address_country` varchar(64) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'USA',
  `job_position` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL,
  `user_role` enum('admin','manager','employee') COLLATE utf8_unicode_ci NOT NULL DEFAULT 'employee',
  `payday_group` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL,
  `default_user_photo_url` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `last_user_photo_url` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `created_date` datetime NOT NULL,
  `last_login_date` datetime DEFAULT NULL,
  `updated_date` datetime DEFAULT NULL,
  `login_counter` bigint(20) NOT NULL DEFAULT '0',
  `total_time_worked` bigint(20) DEFAULT NULL,
  `user_notes` text COLLATE utf8_unicode_ci,
  `time_zone` varchar(30) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'US/Central',
  `clocked_in` tinyint(1) NOT NULL DEFAULT '0',
  `status` tinyint(1) NOT NULL DEFAULT '1',
  `webcam` tinyint(1) NOT NULL DEFAULT '1',
  PRIMARY KEY (`user_id`),
  UNIQUE KEY `users_username_unique` (`username`),
  UNIQUE KEY `users_email_unique` (`email`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=64 ;

Links table

CREATE TABLE IF NOT EXISTS `intranet_links` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(255) DEFAULT NULL,
  `description` text NOT NULL,
  `url` varchar(255) DEFAULT NULL,
  `permission` varchar(50) NOT NULL DEFAULT 'admin',
  `notes` text,
  `active` int(2) NOT NULL DEFAULT '1',
  `sort_order` int(11) DEFAULT NULL,
  `parent_id` int(10) NOT NULL DEFAULT '1',
  PRIMARY KEY (`id`),
  UNIQUE KEY `id` (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=2 ;

To slightly complicate things a little more, my menu will also have a hierachy like categories/folders/directories. So that 1 menu item can have child menu items o my actual menu output would like like the image below, except that each menu items will also be shown or not shown on a per user permission setting basis!

enter image description here

For now my question is just about how to structure the 3rd DB table and then how to query and show the correct links to each user. After that part is complete though I will then also have a settings page in which each menu item can be set to yes/no for each user in the database. Similar to this SugarCRM permission page... with the exception that the top horizontal columns will be links and the vertical records will be users....

enter image description here

Harlequinade answered 23/4, 2015 at 17:19 Comment(2)
How many menu items? Maximum.Innocent
@Innocent I would guess between 20-40 maxHarlequinade
I
7

Make your permissions an integer and use 1 bit for each menu item or menu items grouped by permission.

Then use a bit wise AND to determine if permissions match.

Both user and links have a permission column.

permissions is an integer

permissions A = 1
permissions B = 2
permissions C = 4
permissions D = 8
permissions E = 16
permissions F = 32

If a menu item is displayed for both Group B and D, then:

if (link_permission & user_permission) is not zero then the user has permission.

The value for the link permissions if only B and D would be:
permissions B + permissions D, or 2+8 (00000010 + 00001000) = 00001010 (10 decimal, A hex)

Now if a user's permissions = 2 (0010) or a user's permission = 8 (1000)
When ANDed with the Link permission of 00001010 the result of a bit wise AND of user permission and link permission will not be zero (true where non-zero = true).

define('LINK_PERMISSION_ACCESS' ,1);  // 000000001
define('LINK_PERMISSION_DELETE' ,2) ; // 000000010
define('LINK_PERMISSION_EDIT'   ,4) ; // 000000100
define('LINK_PERMISSION_EXPORT' ,8) ; // 000001000
define('LINK_PERMISSION_IMPORT',16) ; // 000010000
define('LINK_PERMISSION_UPDATE',32) ; // 000100000
define('LINK_PERMISSION_VIEW'  ,64) ; // 001000000

$linkPermission =  LINK_PERMISSION_B + LINK_PERMISSION_D;  // 0010 + 01000 

$userPermission = LINK_PERMISSION_D;  // 01000

You can define group level values as well

define('LINK_PERMISSION_ADMIN' ,255); // 11111111

You can define multiple premissions

I'm going to go Hex rather than Decimal or the number will be unmanagable

define('LINK_PERMISSION_ACCOUNTS'  ,0x8000); 
define('LINK_PERMISSION_AUDIT'     ,0x4000); 
define('LINK_PERMISSION_WORKFLOW'  ,0x2000); 
define('LINK_PERMISSION_BUGTRACKER',0x1000); 

A user with only account access would be

`user.permission` = LINK_PERMISSION_ACCOUNTS + LINK_PERMISSION_ACCESS ;

A user with account access, edit, and delete, would be

`user.permission` = LINK_PERMISSION_ACCOUNTS 
                  + LINK_PERMISSION_ACCESS 
                  + LINK_PERMISSION_DELETE 
                  + LINK_PERMISSION_EDIT;

If you would need a user permissions column for each area:

CREATE TABLE IF NOT EXISTS `user` (
  ...
`accountPermission`  int(11) NOT NULL DEFAULT '0',
`workFlowPermission` int(11) NOT NULL DEFAULT '0',
`contactsPermission` int(11) NOT NULL DEFAULT '0',
`campaignPermission` int(11) NOT NULL DEFAULT '0',

But if the number of permissions is 4 or less for example:

define('LINK_PERMISSION_ACCESS' ,1);  // 000000001
define('LINK_PERMISSION_DELETE' ,2) ; // 000000010
define('LINK_PERMISSION_EDIT'   ,4) ; // 000000100
define('LINK_PERMISSION_VIEW'   ,8) ; // 000001000

`permission`  int(11) NOT NULL DEFAULT '0', 

Where account, workflow, contacts, and campaign are grouped into 4 bits:

account  workflow  contacts campaign
 0000     0000       0000     0000

PERMISSION_ACCOUNT_ACCESS,  0x1000
PERMISSION_WORKFLOW_ACCESS, 0x0100
PERMISSION_CONTACTS_ACCESS, 0x0010
PERMISSION_CAMPAIGN_ACCESS, 0x0001

PERMISSION_ACCOUNT_DELETE,  0x2000
PERMISSION_WORKFLOW_DELETE, 0x0200
PERMISSION_CONTACTS_DELETE, 0x0020
PERMISSION_CAMPAIGN_DELETE, 0x0002

PERMISSION_ACCOUNT_EDIT,  0x4000
PERMISSION_WORKFLOW_EDIT, 0x0400
PERMISSION_CONTACTS_EDIT, 0x0040
PERMISSION_CAMPAIGN_EDIT, 0x0004

PERMISSION_ACCOUNT_VIEW,  0x8000
PERMISSION_WORKFLOW_VIEW, 0x0800
PERMISSION_CONTACTS_VIEW, 0x0080
PERMISSION_CAMPAIGN_VIEW, 0x0008

BACK TO YOUR LINKS

Define Constants

define ('SERVER_ADMIN',2);
define ('UBUNTU_DASHBOARD',4);
define ('REDIS_CACHE_ADMIN',8);
define ('MYSQL_DB_MANAGEMENT',16);
define ('NEON_AND_MORE',32);
define ('NEON_AND_MORE_(NAM)',64);
define ('SUGARCRM',128);
define ('NAM_MAGENTO_ADMIN',256);
define ('NAM_TIME_CLOCK',512);
define ('NEONANDMORE_BLOG_ADMIN',1024);
define ('ORDER_REPORTS',2048);
define ('WORK_ORDERS',4096);
define ('UPDATE_ORDER_STATUS',8192);
define ('CHANNEL_LETTER',16384);
define ('CHANNEL_LETTER',32768);
define ('MAGENTO_ADMIN',65536);
define ('BORDER_TUBING',131072);
define ('BORDER_TUBING',262144);
define ('SIGN_PARTS_AND_MORE',524288);
define ('SIGN_PARTS_AND_MORE',1048576);
define ('OTHER_SERVICES',2097152);
define ('PUSHER_REALTIME_EVENTS',4194304);
define ('ZOPIM_CUSTOMER_SUPPORT_CHAT',8388608);
define ('GOOGLE_ANALYTICS',16777216);
define ('GITLAB_(PRIVATE_GITHUB_CLONE)',33554432);
define ('LABS_/_PROJECTS',67108864);
define ('NAM_LABS',134217728);
define ('CAMERA_PHONE',268435456);
define ('SERVER_EMAIL_VERIFICATION',536870912);

Both links and users have a permissions column:

`permissions` int(11) NOT NULL DEFAULT '0',

define('LINK_PERMISSION_ACCOUNTS'  ,0x8000); 
define('LINK_PERMISSION_AUDIT'     ,0x4000); 
define('LINK_PERMISSION_WORKFLOW'  ,0x2000); 
define('LINK_PERMISSION_BUGTRACKER',0x1000); 

If a user has accounts and bug tracker access:

$userPermission = LINK_PERMISSION_ACCOUNTS + LINK_PERMISSION_BUGTRACKER;
UPDATE `users` SET `permissions`= $userPermission WHERE `id` = $user  

Then the required links permissions:

$linkPermission = LINK_PERMISSION_ACCOUNTS;

We do a Bit wise AND (&) on the links permissions with the user permissions

SELECT * FROM `links` WHERE (`permissions` & $userPermission) 

It does not matter if the link is a sub-menu link

This is your typical hierarchical table:

CREATE TABLE IF NOT EXISTS `links` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `parent` int(11) NOT NULL DEFAULT '0',
  `sort` int(11) NOT NULL DEFAULT '0',
  `text` char(32) COLLATE utf8_bin NOT NULL,
  `link` text COLLATE utf8_bin NOT NULL,
  `permission` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=1 ;

Instead we can eliminate, parent and sort, the id column will do it all.

a menu structure with 2 levels: main and sub-menu, 'id' is broken down

menu sub-menu

menu numbers are 0x0100 through 0xFF00

sub-menu numbers are 0x0002 through 0x00FE

For this menu:

enter image description here

SQL to Create Links Table:

CREATE TABLE IF NOT EXISTS `links` (
  `id` int(11) NOT NULL,
  `text` char(64) COLLATE utf8_bin NOT NULL,
  `link` text COLLATE utf8_bin NOT NULL,
  `permission` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

INSERT INTO `links` (`id`, `text`, `link`, `permission`) VALUES
(512, 'Server Admin', '#', 1),
(514, 'Ubuntu Dashboard', '#', 2),
(518, 'Redis Cache Admin', '#', 4),
(522, 'MySQL dB Management', '#', 8),
(1024, 'Neon and More', '#', 16),
(1026, 'Neon and More (NAM)', '#', 32),
(1030, 'SugarCRM', '#', 64),
(1034, 'NAM Magento Admin', '#', 128),
(1038, 'NAM Time Clock', '#', 256),
(1042, 'NeonAndMore Blog Admin', '#', 512),
(1046, 'Order Reports', '#', 1024),
(1050, 'Work Orders', '#', 2048),
(1054, 'Update Order Status', '#', 4096),
(1536, 'Channel Letter', '#', 8192),
(1538, 'Channel Letter', '#', 16384),
(1542, 'Magento Admin', '#', 32768),
(2048, 'Border Tubing', '#', 65536),
(2050, 'Border Tubing', '#', 131072),
(2560, 'Sign Parts And More', '#', 262144),
(2562, 'Sign Parts And More', '#', 524288),
(3072, 'Other Services', '#', 1048576),
(3074, 'Pusher Realtime Events<br/>Instant Caller ID Alerts', '#', 2097152),
(3078, 'Zopim Customer Support Chat', '#', 4194304),
(3082, 'Google Analytics', '#', 8388608),
(3086, 'GitLab (Private GitHub Clone)', '#', 16777216),
(3584, 'Labs / Projects', '#', 33554432),
(3586, 'NAM LABS', '#', 67108864),
(3590, 'Camera Phone', '#', 134217728),
(3594, 'Server Email Verification', '#', 268435456);

Now to create the HTML for the links menu:

SQL

SELECT `id`, `text`, `link`, `permission` 
FROM `links` 
WHERE (`permission` & $userpermission )

PHP

HEAD and CSS

<?php 
ob_start("ob_gzhandler");
header('Content-Type: text/html; charset=utf-8');
header('Connection: Keep-Alive');
header('Keep-Alive: timeout=5, max=100');
header('Cache-Control: max-age=84600');
header('Vary: Accept-Encoding');
echo <<<EOT
<!DOCTYPE html>
<html lang="en"><head><title>Daily Rx</title><meta name="viewport" content="width=device-width, initial-scale=1.0" />
<style type="text/css">
.submenu,.mainmenu{text-align:left;border-radius: 3px 3px 3px 3px;font: 700 1.1em Arial,Helvetica,Calibri,sans-serif;overflow: visible;}
.submenu{border:1px solid #0f0;color: #fff;margin:.2em 0 .2em .8em;width:16.8em;padding: 0 0 0 .8em;
background-image: -o-linear-gradient(bottom, #3d5 0%, #370 100%);
background-image: -moz-linear-gradient(bottom, #3d5 0%, #370 100%);
background-image: -webkit-linear-gradient(bottom, #3d5 0%, #370 100%);
background-image: -ms-linear-gradient(bottom, #3d5 0%, #370 100%);
background-image: linear-gradient(to bottom, #3d5 0%, #370 100%);}
.mainmenu{font-size:1.2em;margin:.2em .2em .2em .2em ;width:16em;padding-left:1em;border:1px solid #00f;color: #fff;
background-image: -o-linear-gradient(bottom, #2ef 0%, #02f 100%);
background-image: -moz-linear-gradient(bottom, #2ef 0%, #02f 100%);
background-image: -webkit-linear-gradient(bottom, #2ef 0%, #02f 100%);
background-image: -ms-linear-gradient(bottom, #2ef 0%, #02f 100%);
background-image: linear-gradient(to bottom, #2ef 0%, #02f 100%);}
.hide{display:none;}
#x{height:40em;}
#page{margin:0;padding:0;}
hr{font-size:.1em;padding:0;margin:0 0 0 1em;width:50em;opacity:0;}
</style></head><body><div id="page">
EOT;
ob_flush();

Create Menus

$userpermission = 4294967295; // 0xffffffff
$sql = "SELECT `id`, `text`, `link`, `permission` FROM `links` WHERE (`permission` & $userpermission ) > 0";
$results = mysqli_query($conn,$sql);
if (mysqli_errno($conn) > 0){echo mysqli_error($conn) . "<br>\n$sql\n";}
while($row = mysqli_fetch_array($results, MYSQL_NUM)){
  $class = $row[0] & 1;
  if($class == 0){
    $i++;
    echo "$closeSubmenu\n<button class=\"mainmenu\" onclick=\"show($i)\">$row[1]</button>\n<div class=\"hide\" id=\"d$i\">\n";
  }
  else{
    echo "<form action=\"$row[2]\"><div><input type=\"hidden\" name=\"user\" value=\"$user\" /><input type=\"hidden\" name=\"id\" value=\"$row[0]\" /><input type=\"hidden\" name=\"permission\" value=\"$userpermission\" /><button class=\"submenu\">$row[1]</button></div></form>\n";
  }
  $closeSubmenu = '</div><hr/>';
}

JavaScript to expand and contract sub menus

ob_flush();
echo <<<EOT
</div><div id="x"><p>&#x2003;</p></div>
<script type="text/javascript">
//<![CDATA[
var toggle = new Array();
toggle[''] ='block';
toggle['none'] ='block';
toggle['block'] ='none';
var div,disp;
var prev = document.getElementById('d1');
prev.style.display='none';
function show(id){
  div = document.getElementById('d' + id);
  disp = div.style.display;
  prev.style.display='none';
  div.style.display=toggle[disp];
  prev=div;
  var y=div.offsetTop;
  window.scrollTo(0, y-32);
}
//]]>
</script></div></body></html>
EOT;
ob_end_flush();
?>

FAST Page Load, Just 132 milliseconds

This PHP page loads in the Browse in just over 100 milliseconds.
That is just the time it takes to the TPC/IP connection.

The time it takes the HTML to be transmitted from the Server to the Browser is just 2 milliseconds.

The below image is from http://www.webpagetest.org

enter image description here

DNS Lookup: 20 ms
Initial Connection: 35 ms
Time to First Byte: 95 ms
Content Download: 2 ms

W3C MobileOK Checker Score: 100%

W3C mobileOK Checker

You will not find many web pages that can do this:

enter image description here


Google PageSpeed Insights 100%
Mobile and Desktop Speed and Usability

Google PageSpeed Insights

enter image description here

enter image description here

Snippet

This snippet was made using the PHP above and pasting the View Source here:

var toggle = new Array();
toggle[''] ='block';
toggle['none'] ='block';
toggle['block'] ='none';
var div,disp;
var prev = document.getElementById('x');
function show(id){
  div = document.getElementById('d' + id);
  disp = div.style.display;
  prev.style.display='none';
  div.style.display=toggle[disp];
  prev=div;
  var y=div.offsetTop;
  window.scrollTo(0, y-32);    }
.submenu,.mainmenu{text-align:left;border-radius: 3px 3px 3px 3px;font: 700 1.1em Arial,Helvetica,Calibri,sans-serif;overflow: visible;}
.submenu{border:1px solid #0f0;color: #fff;margin:.2em 0 .2em .8em;width:16.8em;padding: 0 0 0 .8em;
background-image: -o-linear-gradient(bottom, #3d5 0%, #370 100%);
background-image: -moz-linear-gradient(bottom, #3d5 0%, #370 100%);
background-image: -webkit-linear-gradient(bottom, #3d5 0%, #370 100%);
background-image: -ms-linear-gradient(bottom, #3d5 0%, #370 100%);
background-image: linear-gradient(to bottom, #3d5 0%, #370 100%);}
.mainmenu{font-size:1.2em;margin:.2em .2em .2em .2em ;width:16em;padding-left:1em;border:1px solid #00f;color: #fff;
background-image: -o-linear-gradient(bottom, #2ef 0%, #02f 100%);
background-image: -moz-linear-gradient(bottom, #2ef 0%, #02f 100%);
background-image: -webkit-linear-gradient(bottom, #2ef 0%, #02f 100%);
background-image: -ms-linear-gradient(bottom, #2ef 0%, #02f 100%);
background-image: linear-gradient(to bottom, #2ef 0%, #02f 100%);}
.hide{display:none;}
#x{height:40em;}
#page{margin:0;padding:0;}
hr{font-size:.1em;padding:0;margin:0 0 0 1em;width:50em;opacity:0;}
<div id="page">
<button class="mainmenu" onclick="show(1)">Server Admin</button>
<div class="hide" id="d1">
<form action="#"><div><input type="hidden" name="user" value="123" /><input type="hidden" name="id" value="257" /><input type="hidden" name="permission" value="4294967295" /><button class="submenu">Ubuntu Dashboard</button></div></form>
<form action="#"><div><input type="hidden" name="user" value="123" /><input type="hidden" name="id" value="259" /><input type="hidden" name="permission" value="4294967295" /><button class="submenu">Redis Cache Admin</button></div></form>
<form action="#"><div><input type="hidden" name="user" value="123" /><input type="hidden" name="id" value="261" /><input type="hidden" name="permission" value="4294967295" /><button class="submenu">MySQL dB Management</button></div></form>
</div><hr/>
<button class="mainmenu" onclick="show(2)">Neon and More</button>
<div class="hide" id="d2">
<form action="#"><div><input type="hidden" name="user" value="123" /><input type="hidden" name="id" value="513" /><input type="hidden" name="permission" value="4294967295" /><button class="submenu">Neon and More (NAM)</button></div></form>
<form action="#"><div><input type="hidden" name="user" value="123" /><input type="hidden" name="id" value="515" /><input type="hidden" name="permission" value="4294967295" /><button class="submenu">SugarCRM</button></div></form>
<form action="#"><div><input type="hidden" name="user" value="123" /><input type="hidden" name="id" value="517" /><input type="hidden" name="permission" value="4294967295" /><button class="submenu">NAM Magento Admin</button></div></form>
<form action="#"><div><input type="hidden" name="user" value="123" /><input type="hidden" name="id" value="519" /><input type="hidden" name="permission" value="4294967295" /><button class="submenu">NAM Time Clock</button></div></form>
<form action="#"><div><input type="hidden" name="user" value="123" /><input type="hidden" name="id" value="521" /><input type="hidden" name="permission" value="4294967295" /><button class="submenu">NeonAndMore Blog Admin</button></div></form>
<form action="#"><div><input type="hidden" name="user" value="123" /><input type="hidden" name="id" value="523" /><input type="hidden" name="permission" value="4294967295" /><button class="submenu">Order Reports</button></div></form>
<form action="#"><div><input type="hidden" name="user" value="123" /><input type="hidden" name="id" value="525" /><input type="hidden" name="permission" value="4294967295" /><button class="submenu">Work Orders</button></div></form>
<form action="#"><div><input type="hidden" name="user" value="123" /><input type="hidden" name="id" value="527" /><input type="hidden" name="permission" value="4294967295" /><button class="submenu">Update Order Status</button></div></form>
</div><hr/>
<button class="mainmenu" onclick="show(3)">Channel Letter</button>
<div class="hide" id="d3">
<form action="#"><div><input type="hidden" name="user" value="123" /><input type="hidden" name="id" value="769" /><input type="hidden" name="permission" value="4294967295" /><button class="submenu">Channel Letter</button></div></form>
<form action="#"><div><input type="hidden" name="user" value="123" /><input type="hidden" name="id" value="771" /><input type="hidden" name="permission" value="4294967295" /><button class="submenu">Magento Admin</button></div></form>
</div><hr/>
<button class="mainmenu" onclick="show(4)">Border Tubing</button>
<div class="hide" id="d4">
<form action="#"><div><input type="hidden" name="user" value="123" /><input type="hidden" name="id" value="1025" /><input type="hidden" name="permission" value="4294967295" /><button class="submenu">Border Tubing</button></div></form>
</div><hr/>
<button class="mainmenu" onclick="show(5)">Sign Parts And More</button>
<div class="hide" id="d5">
<form action="#"><div><input type="hidden" name="user" value="123" /><input type="hidden" name="id" value="1281" /><input type="hidden" name="permission" value="4294967295" /><button class="submenu">Sign Parts And More</button></div></form>
</div><hr/>
<button class="mainmenu" onclick="show(6)">Other Services</button>
<div class="hide" id="d6">
<form action="#"><div><input type="hidden" name="user" value="123" /><input type="hidden" name="id" value="1537" /><input type="hidden" name="permission" value="4294967295" /><button class="submenu">Pusher Realtime Events<br/>Instant Caller ID Alerts</button></div></form>
<form action="#"><div><input type="hidden" name="user" value="123" /><input type="hidden" name="id" value="1539" /><input type="hidden" name="permission" value="4294967295" /><button class="submenu">Zopim Customer Support Chat</button></div></form>
<form action="#"><div><input type="hidden" name="user" value="123" /><input type="hidden" name="id" value="1541" /><input type="hidden" name="permission" value="4294967295" /><button class="submenu">Google Analytics</button></div></form>
<form action="#"><div><input type="hidden" name="user" value="123" /><input type="hidden" name="id" value="1543" /><input type="hidden" name="permission" value="4294967295" /><button class="submenu">GitLab (Private GitHub Clone)</button></div></form>
</div><hr/>
<button class="mainmenu" onclick="show(7)">Labs / Projects</button>
<div class="hide" id="d7">
<form action="#"><div><input type="hidden" name="user" value="123" /><input type="hidden" name="id" value="1793" /><input type="hidden" name="permission" value="4294967295" /><button class="submenu">NAM LABS</button></div></form>
<form action="#"><div><input type="hidden" name="user" value="123" /><input type="hidden" name="id" value="1795" /><input type="hidden" name="permission" value="4294967295" /><button class="submenu">Camera Phone</button></div></form>
<form action="#"><div><input type="hidden" name="user" value="123" /><input type="hidden" name="id" value="1797" /><input type="hidden" name="permission" value="4294967295" /><button class="submenu">Server Email Verification</button></div></form>
</div><div id="x"><p>&#x2003;</p></div>
Innocent answered 23/4, 2015 at 17:34 Comment(12)
I have to say that bit wise based permissions have always fascinated me a lot, however have always still been a bit difficult to fully understand 100%. Is this practical though with a system with like 50 links and 60 users? IF it is, I am still a bit confused on how to query the DB to get the permission of each link, could you explain a bit more please?Harlequinade
I am continuing to explain, I'm giving it to you one piece at a time. My job is not done until you understand.Innocent
Also, you mention the word group...i'm not sure if you are just using the word which in my case would be a user record or if you actually mean using user groups, which my boss won't allow me to do in this case?Harlequinade
I appreciate your time, I think this will be helpful to a lot of people too, thank you!Harlequinade
Would the value for permissions A = 1 be set as a variable in PHP or from the DB?Harlequinade
Back in 2009 I had actually made this post on SO #1380545 could this be used in this example?Harlequinade
@jasondavis I'm going to take it a bit further, but thanks. Had I known sooner I would have stared with your example.Innocent
Im a bit confused on what a link permission in the Database and a user permission in the Database would need to look like. Sorry it's a complicated thing to grasp. I understand about how the values add up to determine a yes or no but not really on how to set the values for each user and each link for each user. Even my example link sent, confuses me now as it is not really set for a per user and per link setting but instead just a per user for a sort of permission groupHarlequinade
Getting there, got interrupted.Innocent
Also just to clarify we are on the same page ( I do think we are but want to make sure) every link will individually need to be turn on or off for every single user. IF that is possible with the bits then amazing as I wasn't aware it could be used on such a large scale!Harlequinade
No rush, I appreciate you trying to explain it to me, just putting in my comments to steer you towards where i'm caught up at so far, thanks again!Harlequinade
Let us continue this discussion in chat.Innocent
U
0

If I got your question you want display or not some menu itens based on user type that is requesting the php dynamic page.

I use Bootstrap to create some projects, and always PHP-oop. So the menu display status is always controled based on user status.

Look at these pictures on this link . Sequence d.jpg, e.jpg, f.jpg, g.jpg, h.jpg and ha.jpg. As you can see, some links of the left menu are locked or not. I could display or hide Based on some information that I grab from Mysql. Than PHP controls the variable to lock or display. This is portion of the menu code:

<li class="list-group-item list-toggle lv1">
         <a data-toggle="collapse" data-parent="#menuHomeUserPrivate" href="#collapse-PerfilManage"><i class="fa fa-cog"></i>Perfil Profissional</a>
         <ul id="collapse-PerfilManage" class="collapse">
             <li class="list-group-item lv2"><a id="to_CodAtivacao" class="privateMenuLinkJS "><i class="fa  fa-lock"></i> Código de Ativação</a></li>
             <li class="list-group-item lv2">$badgeInativar_Code<a id="to_EditarFoto" class="privateMenuLinkJS "><i class="fa  fa-camera-retro"></i> Editar Foto</a></li>
             <li class="list-group-item lv2">$badgeInativar_Code<a id="to_URL_Manage" class="privateMenuLinkJS "><i class="fa  fa-link"></i>Site (URL) Profissional</a></li>
             <li class="list-group-item lv2"><a id="to_NivelEcucacional" class="privateMenuLinkJS "><i class="fa   fa-university"></i>Nível Educaional</a></li>
             <li class="list-group-item lv2"><a id="to_OrdemProfissional" class="privateMenuLinkJS "><i class="fa   fa-users"></i> N&deg;Profissional</a></li>
             <li class="list-group-item lv2"><a id="to_EspecialidadeProfissional" class="privateMenuLinkJS "><i class="fa  fa-codepen"></i>Especialidade(s)</a></li>
             <li class="list-group-item lv2">$badgeInativar_Code<a id="to_EnderecoProfissional" class="privateMenuLinkJS "><i class="fa  fa-hospital-o"></i>Endereço Profissional</a></li>
             <li class="list-group-item lv2">$badgeInativar_Code<a id="to_GestaoEnderecos" class="privateMenuLinkJS "><i class="fa  fa-cogs"></i>Gestão de Endereço(s)</a></li>    
         </ul>
     </li>

As you can see I tagged the menu items to be locked or not with the variable $badgeInativar_Code and use the php code to control the behavior

if($eventMenuInactivate===0||$eventMenuInactivate===NULL||$eventMenuInactivate===""){
$badgeInativar_Code="";
} 
elseif ($eventMenuInactivate===1) {//$eventMenuInactivate===1 O usuário não preencheu o código de ativação
$badgeInativar_Code = "<span class=\" hidden spmInatCode badge rounded badge-red\"><i class=\"fa  fa-lock\"></i>Locked</span>";
}

This is a bit of the method that creates the menu

static function section_MENU_HomeUser_Private($eventMenuInactivate=FALSE) { ....
Unawares answered 24/4, 2015 at 1:44 Comment(1)
Thanks for the post however it is slightly different from my needs. I mentioned that user group based permissions are not what we need but instead on a per user, per menu item. So for example 50 menu links and 50 users would mean 50x50= 2,500 actual permission setting records! Every user, all 50 would have 50 separate permission records, 1 for each of the 50 menu items....that is why it is somewhat more of a pain. Not too hard, just more workHarlequinade
I
0

Update User Permissions

This goes along with my other answer regarding making a menu based on user permissions.

This is PHP Script will generate permissions based on which check boxes are checked.


There are permissions for the main menu but there are no check boxes for them. All the check box values = the permission value for the sub-menu + its associated main menu.

If the user permissions are passed to this script, the check boxes for the user's current permissions will be checked on page load.

This is made to submit POST values to itself. This will generate new user permissions $userpermissions and is displayed on the bottom of the page. It is up to you as to what you wnat to do with them. Insert a db Record, or put the value in a link or from to be submitted to another script to the save the value.

Retrieving the POST values is non-conventional.

For testing I pass a user permissions in a GET query string value ('up'):

updateUser.php?up=4575

$userpermissions = intval($_GET['up']);

If no GET value is passed, then the $userpermissions is set to zero by teh intval(), and check boxes are only checked if the values were pasted in the POST data.

I then scan each POST check box value:

foreach($_POST as $key => $value){
  if(substr($key,0,1) == 'c'){
    $userpermissions |= $value;
  }
}

The names of the check boxes are "c" plus a sequential number.

I find all POSTed key values that start with a "c". Then OR the value with any existing permissions.

This is why I added the main menu value to the sub menu check boxes. If no sub-menus are selected, then the permission for the main menu will not be in the user permissions either.

If multiple sub-menus are selected it does not matter because the value is ORed rather than added.

Checking the boxes with Existing Permissions:

If the bit for the menu is set, then the check box will have "checked="checked" in the HTML tag.

for($i=1;$i<33;$i++){
  if($userpermissions & $permissions[$i]){
    $checked[$i] = 'checked="checked"';
  }
}

The user permissions are ANDed with an array, $permissions, that stores the bit value for each check box, indexed with the id number of the divs and check boxes.

$permissions = array(0,1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576,2097152,4194304,8388608,16777216,33554432,67108864,134217728,268435456);

The check box is encapsulated with a div. This div's id starts with a "d" and followed by the same sequential number a the id cor the check box. the purpose of the div is top change the background color when a check box is checked.

There is a JS init() routine the examines each check box and sets the background to the checked or unchecked color.

Each check box has an onclick event to change the background color. The background colors are stored in an array with an index value of true and false.

bg = new Array;
bg[true] = '#f00';
bg[false] = '#2985EA';

The color is set to the true/false value of the check box input.

This is the code in the init() function:

c[i] = document.getElementById('c' + i);
d[i] = document.getElementById('d' + i);

d[i].style.backgroundColor=bg[c[i].checked];

All the divs and check box DOM Elements are saved in an array.

var divs = document.getElementsByTagName("div");

The int() function loops through the are looking for any divs with an id (' getAttribute("id") ') that begins with a "d"

PHP

<?php 
ob_start("ob_gzhandler");
header('Content-Type: text/html; charset=utf-8');
header('Connection: Keep-Alive');
header('Keep-Alive: timeout=5, max=100');
header('Cache-Control: max-age=84600');
header('Vary: Accept-Encoding');
echo <<<EOT
<!DOCTYPE html>
<html lang="en"><head><title>Daily Rx</title><meta name="viewport" content="width=device-width, initial-scale=1.0" />
<style type="text/css">
.link,.btn{text-align:center;border-radius: 3px 3px 3px 3px;
font: 700 1em Arial,Helvetica,Calibri,sans-serif;overflow: visible;}
.btn{border:1px solid #00f;color: #fff;background:#004;margin:.2em;width:18em;padding:.8em;
}
.link{border:1px solid #0f0;color: #fff;margin:.2em;width:18em;padding:.8em;
background-image: -o-linear-gradient(bottom, #2ef 0%, #02f 100%);
background-image: -moz-linear-gradient(bottom, #2ef 0%, #02f 100%);
background-image: -webkit-linear-gradient(bottom, #2ef 0%, #02f 100%);
background-image: -ms-linear-gradient(bottom, #2ef 0%, #02f 100%);
background-image: linear-gradient(to bottom, #2ef 0%, #02f 100%);}
.hide{display:none;}
#x{height:40em;}
#page{margin:0;padding:0;}
.dchk{font:700 1em Arial,sans-serif;color:#fff;width:18em;display:inline-block;padding:2px 0 2px 2px;margin:0 0 .5em;vertical-align: middle;position: relative;}
.chk{color:#fff;display:inline;padding:6px 0 6px 6px;margin-bottom:6px;outline:2px solid #000;}
.dchk,.chk,.component{background:#2985EA;text-align:left}
input[type="radio"],input[type="checkbox"]{width:2em;height:2em;border:2px solid #eee;outline:2px solid #eee;display: inline;margin:1px;margin:4px 0 4px 4px;vertical-align: middle;position: relative;background:#144;color:#eee;   display: inline;vertical-align: middle;position: relative;}   
.hr{font-size:.1em;padding:0;margin:0 0 0 1em;width:50em;opacity:1;}
</style></head><body><div id="page">
<form action="updateUser.php" method="post"><div>
EOT;
ob_flush();
$check = array(1 => 1,2 => 2,3 => 4,4 => 8,5 => 16,6 => 32,7 => 64,8 => 128,9 => 256,10 => 512,11 => 1024,12 => 2048,13 => 4096,14 => 8192,15 => 16384,16 => 32768,17 => 65536,18 => 131072,19 => 262144,20 => 524288,21 => 1048576,22 => 2097152,23 => 4194304,24 => 8388608,25 => 16777216,26 => 33554432,27 => 67108864,28 => 134217728,29 => 268435456);
$permissions = array(0,1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576,2097152,4194304,8388608,16777216,33554432,67108864,134217728,268435456);
$checked = array_fill(0,32,'');
$userpermissions = intval($_GET['up']);
foreach($_POST as $key => $value){
  if(substr($key,0,1) == 'c'){
    $userpermissions |= $value;
  }
}
for($i=1;$i<33;$i++){
  if($userpermissions & $permissions[$i]){$checked[$i] = 'checked="checked"';}
}
echo <<<EOT
<button class="btn" type"button">Server Admin</button><br/>
<div id="d2" class="dchk "><input type="checkbox" id="c2" class="chk" name="c2" value="3" onclick="chk('2')" $checked[2] />&#x2002;Ubuntu Dashboard</div><br/>
<div id="d3" class="dchk "><input type="checkbox" id="c3" class="chk" name="c3" value="5" onclick="chk('3')" $checked[3] />&#x2002;Redis Cache Admin</div><br/>
<div id="d4" class="dchk "><input type="checkbox" id="c4" class="chk" name="c4" value="9" onclick="chk('4')" $checked[4] />&#x2002;MySQL dB Management</div><br/>
<button class="btn" type"button">Neon and More</button><br/>
<div id="d6" class="dchk "><input type="checkbox" id="c6" class="chk" name="c6" value="48" onclick="chk('6')" $checked[6] />&#x2002;Neon and More (NAM)</div><br/>
<div id="d7" class="dchk "><input type="checkbox" id="c7" class="chk" name="c7" value="80" onclick="chk('7')" $checked[7] />&#x2002;SugarCRM</div><br/>
<div id="d8" class="dchk "><input type="checkbox" id="c8" class="chk" name="c8" value="144" onclick="chk('8')" $checked[8] />&#x2002;NAM Magento Admin</div><br/>
<div id="d9" class="dchk "><input type="checkbox" id="c9" class="chk" name="c9" value="272" onclick="chk('9')" $checked[9] />&#x2002;NAM Time Clock</div><br/>
<div id="d10" class="dchk "><input type="checkbox" id="c10" class="chk" name="c10" value="528" onclick="chk('10')" $checked[10] />&#x2002;NeonAndMore Blog Admin</div><br/>
<div id="d11" class="dchk "><input type="checkbox" id="c11" class="chk" name="c11" value="1040" onclick="chk('11')" $checked[11] />&#x2002;Order Reports</div><br/>
<div id="d12" class="dchk "><input type="checkbox" id="c12" class="chk" name="c12" value="2064" onclick="chk('12')" $checked[12] />&#x2002;Work Orders</div><br/>
<div id="d13" class="dchk "><input type="checkbox" id="c13" class="chk" name="c13" value="4112" onclick="chk('13')" $checked[13] />&#x2002;Update Order Status</div><br/>
<button class="btn" type"button">Channel Letter</button><br/>
<div id="d15" class="dchk "><input type="checkbox" id="c15" class="chk" name="c15" value="24576" onclick="chk('15')" $checked[15] />&#x2002;Channel Letter</div><br/>
<div id="d16" class="dchk "><input type="checkbox" id="c16" class="chk" name="c16" value="40960" onclick="chk('16')" $checked[16] />&#x2002;Magento Admin</div><br/>
<button class="btn" type"button">Border Tubing</button><br/>
<div id="d18" class="dchk "><input type="checkbox" id="c18" class="chk" name="c18" value="196608" onclick="chk('18')" $checked[18] />&#x2002;Border Tubing</div><br/>
<button class="btn" type"button">Sign Parts And More</button><br/>
<div id="d20" class="dchk "><input type="checkbox" id="c20" class="chk" name="c20" value="786432" onclick="chk('20')" $checked[20] />&#x2002;Sign Parts And More</div><br/>
<button class="btn" type"button">Other Services</button><br/>
<div id="d22" class="dchk "><input type="checkbox" id="c22" class="chk" name="c22" value="3145728" onclick="chk('22')" $checked[22] />&#x2002;Pusher Realtime Events</div><br/>
<div id="d23" class="dchk "><input type="checkbox" id="c23" class="chk" name="c23" value="5242880" onclick="chk('23')" $checked[23] />&#x2002;Zopim Customer Support Chat</div><br/>
<div id="d24" class="dchk "><input type="checkbox" id="c24" class="chk" name="c24" value="9437184" onclick="chk('24')" $checked[24] />&#x2002;Google Analytics</div><br/>
<div id="d25" class="dchk "><input type="checkbox" id="c25" class="chk" name="c25" value="17825792" onclick="chk('25')" $checked[25] />&#x2002;GitLab (Private GitHub Clone)</div><br/>
<button class="btn" type"button">Labs / Projects</button><br/>
<div id="d27" class="dchk "><input type="checkbox" id="c27" class="chk" name="c27" value="100663296" onclick="chk('27')" $checked[27] />&#x2002;NAM LABS</div><br/>
<div id="d28" class="dchk "><input type="checkbox" id="c28" class="chk" name="c28" value="167772160" onclick="chk('28')" $checked[28] />&#x2002;Camera Phone</div><br/>
<div id="d29" class="dchk "><input type="checkbox" id="c29" class="chk" name="c29" value="301989888" onclick="chk('29')" $checked[29] />&#x2002;Server Email Verification</div><br/>
<button class="link" type="submit">Submit</button></div></form>
<h3> $userpermissions</h3>
<script type="text/javascript"> //<![CDATA[
var d = new Array;
var c = new Array;
function chk(id){
  d[id].style.backgroundColor=bg[c[id].checked];
}
function init(){
var checked,did;
bg = new Array;
bg[true] = '#f00';
bg[false] = '#2985EA';

var divs = document.getElementsByTagName("div");
  for (div=0; div<divs.length; div++){
    did = divs[div].getAttribute("id");
    if (did != null){
      if (did.substring(0,1) == "d"){
        var i = did.substring(1,5);
        c[i] = document.getElementById('c' + i);
        d[i] = document.getElementById('d' + i);
        checked = c[i].checked;
        d[i].style.backgroundColor=bg[checked];
        //show = show + i + ',';
      }
    }
  }
}
window.onload = init;
//]]>
</script>
</body></html>
EOT;
ob_end_flush();
?>

Snippet

var d = new Array;
var c = new Array;
function chk(id){
  d[id].style.backgroundColor=bg[c[id].checked];
}
function init(){
var checked,did;
bg = new Array;
bg[true] = '#f00';
bg[false] = '#2985EA';

var divs = document.getElementsByTagName("div");
  for (div=0; div<divs.length; div++){
did = divs[div].getAttribute("id");
if (did != null){
  if (did.substring(0,1) == "d"){
    var i = did.substring(1,5);
	c[i] = document.getElementById('c' + i);
	d[i] = document.getElementById('d' + i);
	checked = c[i].checked;
	d[i].style.backgroundColor=bg[checked];
	//show = show + i + ',';
  }
}
  }
}
window.onload = init;
.link,.btn{text-align:center;border-radius: 3px 3px 3px 3px;
font: 700 1em Arial,Helvetica,Calibri,sans-serif;overflow: visible;}
.btn{border:1px solid #00f;color: #fff;background:#004;margin:.2em;width:18em;padding:.8em;
}
.link{border:1px solid #0f0;color: #fff;margin:.2em;width:18em;padding:.8em;
background-image: -o-linear-gradient(bottom, #2ef 0%, #02f 100%);
background-image: -moz-linear-gradient(bottom, #2ef 0%, #02f 100%);
background-image: -webkit-linear-gradient(bottom, #2ef 0%, #02f 100%);
background-image: -ms-linear-gradient(bottom, #2ef 0%, #02f 100%);
background-image: linear-gradient(to bottom, #2ef 0%, #02f 100%);}
.hide{display:none;}
#x{height:40em;}
#page{margin:0;padding:0;}
.dchk{font:700 1em Arial,sans-serif;color:#fff;width:18em;display:inline-block;padding:2px 0 2px 2px;margin:0 0 .5em;vertical-align: middle;position: relative;}
.chk{color:#fff;display:inline;padding:6px 0 6px 6px;margin-bottom:6px;outline:2px solid #000;}
.dchk,.chk,.component{background:#2985EA;text-align:left}
input[type="radio"],input[type="checkbox"]{width:2em;height:2em;border:2px solid #eee;outline:2px solid #eee;display: inline;margin:1px;margin:4px 0 4px 4px;vertical-align: middle;position: relative;background:#144;color:#eee;   display: inline;vertical-align: middle;position: relative;}   
.hr{font-size:.1em;padding:0;margin:0 0 0 1em;width:50em;opacity:1;}
<div id="page">
<form action="updateUser.php" method="post"><div><button class="btn" type"button">&#x2002;Server Admin</button><br/>
<div id="d2" class="dchk "><input type="checkbox" id="c2" class="chk" name="c2" value="3" onclick="chk('2')"  />&#x2002;Ubuntu Dashboard</div><br/>
<div id="d3" class="dchk "><input type="checkbox" id="c3" class="chk" name="c3" value="5" onclick="chk('3')"  />&#x2002;Redis Cache Admin</div><br/>
<div id="d4" class="dchk "><input type="checkbox" id="c4" class="chk" name="c4" value="9" onclick="chk('4')"  />&#x2002;MySQL dB Management</div><br/>
<button class="btn" type"button">&#x2002;Neon and More</button><br/>
<div id="d6" class="dchk "><input type="checkbox" id="c6" class="chk" name="c6" value="48" onclick="chk('6')"  />&#x2002;Neon and More (NAM)</div><br/>
<div id="d7" class="dchk "><input type="checkbox" id="c7" class="chk" name="c7" value="80" onclick="chk('7')"  />&#x2002;SugarCRM</div><br/>
<div id="d8" class="dchk "><input type="checkbox" id="c8" class="chk" name="c8" value="144" onclick="chk('8')"  />&#x2002;NAM Magento Admin</div><br/>
<div id="d9" class="dchk "><input type="checkbox" id="c9" class="chk" name="c9" value="272" onclick="chk('9')"  />&#x2002;NAM Time Clock</div><br/>
<div id="d10" class="dchk "><input type="checkbox" id="c10" class="chk" name="c10" value="528" onclick="chk('10')"  />&#x2002;NeonAndMore Blog Admin</div><br/>
<div id="d11" class="dchk "><input type="checkbox" id="c11" class="chk" name="c11" value="1040" onclick="chk('11')"  />&#x2002;Order Reports</div><br/>
<div id="d12" class="dchk "><input type="checkbox" id="c12" class="chk" name="c12" value="2064" onclick="chk('12')"  />&#x2002;Work Orders</div><br/>
<div id="d13" class="dchk "><input type="checkbox" id="c13" class="chk" name="c13" value="4112" onclick="chk('13')"  />&#x2002;Update Order Status</div><br/>
<button class="btn" type"button">&#x2002;Channel Letter</button><br/>
<div id="d15" class="dchk "><input type="checkbox" id="c15" class="chk" name="c15" value="24576" onclick="chk('15')"  />&#x2002;Channel Letter</div><br/>
<div id="d16" class="dchk "><input type="checkbox" id="c16" class="chk" name="c16" value="40960" onclick="chk('16')"  />&#x2002;Magento Admin</div><br/>
<button class="btn" type"button">&#x2002;Border Tubing</button><br/>
<div id="d18" class="dchk "><input type="checkbox" id="c18" class="chk" name="c18" value="196608" onclick="chk('18')"  />&#x2002;Border Tubing</div><br/>
<button class="btn" type"button">&#x2002;Sign Parts And More</button><br/>
<div id="d20" class="dchk "><input type="checkbox" id="c20" class="chk" name="c20" value="786432" onclick="chk('20')"  />&#x2002;Sign Parts And More</div><br/>
<button class="btn" type"button">&#x2002;Other Services</button><br/>
<div id="d22" class="dchk "><input type="checkbox" id="c22" class="chk" name="c22" value="3145728" onclick="chk('22')"  />&#x2002;Pusher Realtime Events</div><br/>
<div id="d23" class="dchk "><input type="checkbox" id="c23" class="chk" name="c23" value="5242880" onclick="chk('23')"  />&#x2002;Zopim Customer Support Chat</div><br/>
<div id="d24" class="dchk "><input type="checkbox" id="c24" class="chk" name="c24" value="9437184" onclick="chk('24')"  />&#x2002;Google Analytics</div><br/>
<div id="d25" class="dchk "><input type="checkbox" id="c25" class="chk" name="c25" value="17825792" onclick="chk('25')"  />&#x2002;GitLab (Private GitHub Clone)</div><br/>
<button class="btn" type"button">&#x2002;Labs / Projects</button><br/>
<div id="d27" class="dchk "><input type="checkbox" id="c27" class="chk" name="c27" value="100663296" onclick="chk('27')"  />&#x2002;NAM LABS</div><br/>
<div id="d28" class="dchk "><input type="checkbox" id="c28" class="chk" name="c28" value="167772160" onclick="chk('28')"  />&#x2002;Camera Phone</div><br/>
<div id="d29" class="dchk "><input type="checkbox" id="c29" class="chk" name="c29" value="301989888" onclick="chk('29')"  />&#x2002;Server Email Verification</div><br/>
<button class="link" type="submit">Submit</button></div></form></div>
Innocent answered 24/4, 2015 at 21:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.