PHP form with Active Directory attribute lookup

With the Mindtouch wiki I have set up, I occasionally use a small form and mysql database to meet someone’s needs. Right now that happens to be a few sign-up tables for some training sessions.

I do this with an html form right in the page, pointing to a php file within the wiki’s OS, to a new mysql database on the linux VM that the wiki is packaged with. This way this form and database is externally available to my users without having to open up additional resources through our reverse proxy.

 

One problem I came across is that the usenames are first initial last name, such as jmiles. However for readability I wanted this to be Jeff Miles. Despite using Active Directory single sign on for Mindtouch, it doesn’t record or use the AD displayname attribute unless you specifically tell it to, and then only for new users.

Instead I have just incorporated an extra lookup into my PHP file to do the lookup for me.

HTML Form:
This form is placed within the source editor on your wiki page.
 

<form action="/config/qms_training.php" method="post">
	<table align="center" border="0" cellpadding="1" cellspacing="1" frame="box" style="width: 550px; border-style: solid; border-width: 1px;">
		<thead>
			<tr>
				<th scope="col">Training Session:</th>
				<th scope="col">Manager Name:</th>
				<th scope="col">&nbsp;</th>
			</tr>
		</thead>
		<tbody>
			<tr>
				<td><select name="session"><option selected="selected" value=""></option><option value="sp_nov17_830">SP - November 17 - 8:30 AM</option><option value="sp_nov17_100">SP - November 17 - 1:00 PM</option><option value="sp_nov18_100">SP - November 18 - 1:00 PM</option><option value="sp_nov23_830">SP - November 23 - 8:30 AM</option><option value="sp_nov23_100">SP - November 23 - 1:00 PM</option><option value="sp_nov24_830">SP - November 24 - 8:30 AM</option><option value="sp_dec2_100">SP - December 2 - 1:00 PM</option><option value="sp_dec8_830">SP - December 8 - 8:30 AM</option><option value="sp_dec19_100">SP - December 19 - 1:00 PM</option><option value="sp_dec20_100">SP - December 20 - 1:00 PM</option><option value="bv_dec6">BV - December 6 - 5:30 PM</option><option value="cg_dec22_1100">CG - December 22 - 11:00 AM</option><option value="cg_dec22_100">CG - December 22 - 2:00 PM</option><option value="gp_nov29_100">GP - November 29 - 1:00 PM</option><option value="gp_nov30_800">GP - November 30 - 8:00 AM</option><option value="lb_dec23_1000">LB - December 23 - 10:00 AM</option><option value="online">Online Session (Exam Required)</option></select></td>
				<td><input name="manager_name" /></td>
				<td><input type="submit" value="Submit" />
                                <!-- This line below is the important part, to take the current wiki user -->
                                <input name="username" type="hidden" value="{{user.name}}" /></td>
			</tr>
		</tbody>
	</table>
</form>

 

PHP file:
This php file is placed on the wiki, in /var/www/dekiwiki/config
 

<?php
// variable takes current logged on user from the Dekiscript in the form.
$wikiusername = $_REQUEST['username'];
 
// Active Directory Username to Display name conversion
// Takes the wiki username, checks AD and returns full name
        $dn = "OU=UserAccounts,DC=domain,DC=ca";
    //$username = $_SERVER['REMOTE_USER'];
    $username = $wikiusername;
    $attributes = array("displayname");
    $filter = "(samaccountname=".$username.")";
    $ad = ldap_connect("ldap://server.domain.ca") or die("Couldn't connect to AD!");
    ldap_set_option($ad, LDAP_OPT_PROTOCOL_VERSION, 3);
    $bind = ldap_bind($ad,"user@domain.ca","password") or die("Couldn't bind to AD!");
    $result = ldap_search($ad, $dn, $filter, $attributes) or die ("ldap search failed");
    $entries = ldap_get_entries($ad, $result);
                if ($entries["count"] > 0)
                        {
                                for ($i=0; $i<$entries["count"]; $i++)
                                        {
                                                $fullname = $entries[$i]["displayname"][0];
                                        }
                        }
        // Close the connection
        ldap_unbind($ad);
 
 
// Remainder of the variables from the form
$session = $_REQUEST['session'] ;
$manager_name = $_REQUEST['manager_name'] ;
$username = $fullname;
 
// Define the connection parameters to connect to the MySQL database.
// This is a local database on the wiki VM.
$conn = mysql_connect("localhost", "root", "password");
mysql_select_db("qms_training",$conn);
 
// If connection does not work, let the user know
 
if (!$conn)
 {
  die('Could not connect: ' . mysql_error());
 }
// Actual SQL command to insert new data into database. The On DUPLICATE KEY UPDATE command ensures
// That if the key exists, it will be updated instead of a new one created.
 
$username = mysql_real_escape_string($username);
$manager_name = mysql_real_escape_string($manager_name);
 
$sql="INSERT INTO session_tracking (username, manager_name, session)
VALUES ('$username','$manager_name','$session')
ON DUPLICATE KEY UPDATE session=VALUES(session), manager_name=VALUES(manager_name)";
 
// Provides feedback for the user to know their submission was successful
if (!mysql_query($sql,$conn))
{
die('Error: ' . mysql_error());
}
echo $username . ", your training session booking has been added.\n Please wait while the page is refreshed.";
mysql_close($con)
 
?>
<!-- redirects user back to the wiki page -->
<meta http-equiv="refresh" content="4;url=http://wiki.domain.ca/Resources/QMS/QMS_Training/QMS_Training_Sign_Up" />

 

Output in the wiki page:
Place this text in the WYSIWYG editor on your wiki page. It will pull from your mysql database and display in table format. This requires the setup of an additional mysql extension, described here: http://developer.mindtouch.com/App_Catalog/MySql

{{ mysql_qms_training.table{query: "SET @rank=0; SELECT @rank:=@rank+1 AS Count, username as 'Name', manager_name as Manager FROM session_tracking WHERE session = 'sp_nov17_830' ORDER BY username"} }}

 

Now, you’ll have output on your wiki page, with proper attributes from AD!
(table shrunk for this image, it’ll actually be wider)

Mindtouch – Show additional Active Directory information for user

Beginning with Mindtouch 10.0, a feature for User Dashboards was added. Effectively this is an additional set of tabs within the “My Page” section for each user page on your wiki.

Display dashboard tabs.

 

Included in this Mindtouch has created an “Activity Dashboard”, which is a pretty great feature. For me and my company, it didn’t quite fit what we needed, so we have modified it, as well as added an additional tab within the dashboard.

This post details how to add those additional tabs. In a future post I’ll show how I’ve modified the Activity Dashboard.

What I wanted to provide for my users was a central spot to view additional Active Directory information about the user who’s page they were viewing. I originally wanted to place this information within the Activity Dashboard, but couldn’t get that working for some reason I can’t remember. Instead I ended up with a separate tab within the user dashboard.

None of this would have been possible without this post from crb on the Mindtouch developer forums.

To start you’ll need to have ssh or console access to your wiki. Navigate to /var/www/dekiwiki/deki/plugins/user_dashboard.

Create a new folder with a related name to your new tab (“adpull” for example).

Inside that folder, create a php file with the same name as the folder: adpull.php

Fill out that php file with the following code. Take note of the comments separating the common code for additional dashboard tabs and the actual content of that tab.

<?php
// Replace references of "adpull" with your chosen name, but don't change anything else in this code.
// This can probably be made easier with a php variable rather than repeated instances of the text.
 
DekiPlugin::registerHook(Hooks::DATA_GET_USER_DASHBOARD_PLUGINS, array('adpull', 'getDashboardPluginsHook'));
 
class adpull extends UserDashboardPage
{
        protected $pluginFolder = 'adpull';
 
public static function getDashboardPluginsHook(&$plugins, $User) {
                $Plugin = new self($User);
                $plugins[] = $Plugin;
        }
 
        public function initPlugin() {
                $this->displayTitle = 'Info';
  //            $this->pagePath = 'Template:MindTouch_UserWelcome';
//              parent::initPlugin();
        }
 
        public function getPluginId() {
                return 'adpull';
        }
 
// Fill out the following function with what you want to populate your tab page with. 		
  public function getHtml() {
 
	// This sets up a connection to an Active Directory server to pull various attribues for the user page
   $dn = "OU=UserAccounts,DC=domain,DC=ca";
    $attributes = array("title", "department", "l", "telephonenumber", "mail");
    $username = $this->User->getUsername();
    $filter = "(samaccountname=$username)";
    $ad = ldap_connect("ldap://server.domain.ca") or die("Couldn't connect to AD!");
 
    ldap_set_option($ad, LDAP_OPT_PROTOCOL_VERSION, 3);
 
    $bd = ldap_bind($ad,"user@domain.ca","password") or die("Couldn't bind to AD!");
    $result = ldap_search($ad, $dn, $filter, $attributes);
    $entries = ldap_get_entries($ad, $result);
 
		// For the results in the array, put them into this html output.
    for ($i=0; $i<$entries["count"]; $i++)
    {
        $html = '<div style="float:left; font-weight:bold;padding-right:5px;">Title: <br /> City: <br /> Telephone: <br /></div>'
        .$entries[$i]["title"][0].
        "<br />"
        .$entries[$i]["l"][0].
        "<br />"
        .$entries[$i]["telephonenumber"][0].
        //"<br />"
        //'<a href="mailto:'.$entries[$i]["mail"][0].'">'.$entries[$i]["mail"][0].'</a>'
        //.$entries[$i]["mail"][0].
        "<br />";
        $html .='
        <div style="float:left; font-weight:bold;padding-right:32px;">Email: </div>
        <a href="mailto:'.$entries[$i]["mail"][0].'">'.$entries[$i]["mail"][0].'</a>';
    }
    ldap_unbind($ad);
        //$html .=$template;
    return $html;
    }
}

 

The example above pulls out AD attributes for the page that’s being viewed. You could just as well fill the php function with:

$html .='Hello World';
return $html;

Now all you need to do is enable this page in the configuration of Mindtouch.
Navigate to the control panel, and choose Configuration

 

Choose “Advanced Config”

 

Find the config key ui/user_dashboards, and then add a comma and the name of what you named your folder:

user_page,Template:MindTouch/Views/ActivityDashboard, adpull

Save that change, and now you should see an additional tab on your user dashboard:
 

 

One year in …

It has Image of number onenow been one year and one day since my first post on Faultbucket. When I was first reading about starting a blog and some common do’s and don’ts, one thing mentioned was, “Don’t post a 1 year summary”.

Screw those people, this is interesting stuff!
In my first year, I have:

  • Made 50 posts
  • Received 18,537 visits
  • Had 35 comments (excluding mine)
  • Blocked 2,753 spam comments

My most popular post so far is the Adobe Flash Installax.exe with 22% of pageviews, followed by Hyper-V Failover Cluster Setup with 15%.

 

I started Faultbucket for a variety of reasons, including sharing knowledge, having a place to document things I come across, and associating a body of work with my name. So far those things are being accomplished, so I feel pretty content with it.

Originally I was worried about running out of topics, but as of this writing I still have 3 in the queue and I come across more ideas every week. Perhaps this year I’ll even find time to make a logo and a unique identity for the site.

Looking forward to year number 2.

Locked out

On Friday I was in the office working late, because our Dell TL2000 tape autoloader decided to die. During my support call with Dell, I was back and forth between my office and the server room, and had kept my key-card in my hoodie pocket.

At one point during the evening, as I was trying to manually eject the magazines from the TL2000 (no simple task for a single person), I took off my hoodie, left it in the server room, and then promptly closed the door behind me.

Luckily I was able to unlock the door through our security server; small IT with a wide range of responsibilities saves the day again.

This isn’t the first time this has happened to me, so the moral of the story is, keep your keys and access cards on your person, in a pocket in your pants. I guess the other moral of the story is, I don’t think very clearly at 10:30 PM.

Asp.net menu – show parent as selected

I’ve been working on a group of tools for my company in asp.net, and have been using an ASP Master page with an ASP menu sourced from a Web.SiteMap xml file. Looks a little like this:

Default menu

I’ve got sub-menu’s under each heading, that look like this:

Showing submenu from parent hover

 

The problem I’ve been struggling with is that upon selecting an item from the sub-menu, the sub-menu is hidden (which is desired) but the parent doesn’t show as selected.

This is because in Net 4.0, you can only have one item in a menu set as selected, which happens to be the actual page that is displayed. Since my sub-menu is hidden except at rollover, I don’t care that the child is shown as selected.

I finally found a solution that worked for me, based on this StackOverflow question.

However, it is a bit of a hack instead of a real solution. Basically, we deselect the child, and select the parent instead. For those who don’t hide the sub-menu, this may not be appropriate (which was the original intent of the StackOverflow question above.

The other downside of this method is that due to the organization of the web.SiteMap xml file, while on the Home page it doesn’t show as selected. This is a small price to pay for the overall benefit of parent selection in my case.

aspx page:

<asp:Menu 
            ID="Menu1"
            CssClass="menu" 
            runat="server" OnMenuItemDataBound="Menu1_MenuItemDataBound"
            DataSourceID="SiteMapDataSource1" Orientation="Horizontal" 
            StaticDisplayLevels="2" RenderingMode="List" >
            <StaticSelectedStyle CssClass="selected" />
</asp:Menu>

 

aspx codebehind

protected void Menu1_MenuItemDataBound(object sender, MenuEventArgs e)
    {
        if (SiteMap.CurrentNode != null)
        {
            if (e.Item.Selected == true)
            {
                e.Item.Selected = true;
                e.Item.Parent.Selectable = true;
                e.Item.Parent.Selected = true;
            }
        }
    }

Web.SiteMap

<siteMapNode roles="*">
    <siteMapNode url="~/Default.aspx" title="Home" description="Default" roles="*">
    </siteMapNode>
    <siteMapNode  roles="*" title="Time & Equip" description="Time & Equip" >
		<siteMapNode url="~/TimeEquip/TimeSheet.aspx" title="TimeSheet" description="Timesheet entry" />
		<siteMapNode url="~/TimeEquip/WeekTmSht.aspx" title="Weekly Timesheet"  description="Weekly Timesheet" />
		<siteMapNode url="~/TimeEquip/TmShtApproval.aspx" title="Timesheet Approval" description="Timesheet Approval" />
    </siteMapNode>
    <siteMapNode roles="*"  title="Project Tools" description="Project Tools" >   
		<siteMapNode url="~/Projects/ProjectPerformance.aspx" title="Project Performance" description="Project Performance Entry Site" />
    </siteMapNode>
    <siteMapNode roles="*" title="Finance/Acct" description="Fin/Acct Dashboard">
		<siteMapNode url="~/AccFin/FinAcct_Dashboard.aspx" title="Finance/Acct" description="Finance/Accounting Main" />
    <siteMapNode roles="*" title="HR Tools" description="" >
		<siteMapNode url="~/HRTools/RecognitionApproval.aspx" title="Recognition Approval" description="" />
    </siteMapNode>
  </siteMapNode>

Final Result:

Parent selected instead of child