Skip to content
Permalink
Browse files

Added ability to lookup users

Includes a jQuery extension called userlookup that provides SharePoint-like ctrl+k behaviour
  • Loading branch information...
robdmoore committed Nov 4, 2014
1 parent 7d5b7c2 commit 1e8b673bdcbd4327d5de64d641d03411d06f26bd
@@ -19,11 +19,19 @@ public static void RegisterBundles(BundleCollection bundles)
"~/Scripts/bootstrap.js",
"~/Scripts/respond.js"));

bundles.Add(new ScriptBundle("~/bundles/userlookup").Include(
"~/Scripts/typeahead.jquery.js",
"~/Scripts/userlookup.js"));

bundles.Add(new StyleBundle("~/Content/css").Include(
"~/Content/bootstrap.css",
"~/Content/site.css"));
"~/Content/site.css",
"~/Content/typeaheadjs.css",
"~/Content/userlookup.css"));

BundleTable.EnableOptimizations = true;
#if !DEBUG
BundleTable.EnableOptimizations = true;
#endif
}
}
}
@@ -159,6 +159,7 @@
<Compile Include="App_Start\Startup.cs" />
<Compile Include="Controllers\HomeController.cs" />
<Compile Include="Controllers\LogoutController.cs" />
<Compile Include="Controllers\UserLookupController.cs" />
<Compile Include="Global.asax.cs">
<DependentUpon>Global.asax</DependentUpon>
</Compile>
@@ -174,6 +175,9 @@
<Content Include="Content\bootstrap-theme.min.css" />
<Content Include="Content\bootstrap.css" />
<Content Include="Content\bootstrap.min.css" />
<Content Include="Content\loading.gif" />
<Content Include="Content\typeaheadjs.css" />
<Content Include="Content\userlookup.css" />
<Content Include="favicon.ico" />
<Content Include="fonts\glyphicons-halflings-regular.svg" />
<Content Include="Global.asax" />
@@ -199,6 +203,8 @@
<Content Include="Scripts\respond.matchmedia.addListener.js" />
<Content Include="Scripts\respond.matchmedia.addListener.min.js" />
<Content Include="Scripts\respond.min.js" />
<Content Include="Scripts\typeahead.jquery.js" />
<Content Include="Scripts\userlookup.js" />
<Content Include="Scripts\_references.js" />
<Content Include="Web.config" />
<Content Include="Web.Release.config">
@@ -212,6 +218,7 @@
<Content Include="Views\Home\Group1.cshtml" />
<Content Include="Views\Home\Group2.cshtml" />
<Content Include="Views\Home\Group3.cshtml" />
<Content Include="Views\UserLookup\Index.cshtml" />
</ItemGroup>
<ItemGroup />
<ItemGroup>
@@ -2,16 +2,8 @@
padding-top: 50px;
padding-bottom: 20px;
}

/* Set padding to keep content from hitting the edges */
.body-content {
padding-left: 15px;
padding-right: 15px;
}

/* Set width on the form input elements since they're 100% wide by default */
input,
select,
textarea {
max-width: 280px;
}
Binary file not shown.
@@ -0,0 +1,57 @@
span.twitter-typeahead .tt-dropdown-menu {
position: absolute;
top: 100%;
left: 0;
z-index: 1000;
display: none;
float: left;
min-width: 160px;
padding: 5px 0;
margin: 2px 0 0;
list-style: none;
font-size: 14px;
text-align: left;
background-color: #ffffff;
border: 1px solid #cccccc;
border: 1px solid rgba(0, 0, 0, 0.15);
border-radius: 4px;
-webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
background-clip: padding-box;
}
span.twitter-typeahead .tt-suggestion > p {
display: block;
padding: 3px 20px;
clear: both;
font-weight: normal;
line-height: 1.42857143;
color: #333333;
white-space: nowrap;
}
span.twitter-typeahead .tt-suggestion > p:hover,
span.twitter-typeahead .tt-suggestion > p:focus {
color: #ffffff;
text-decoration: none;
outline: 0;
background-color: #428bca;
}
span.twitter-typeahead .tt-suggestion.tt-cursor {
color: #ffffff;
background-color: #428bca;
}
span.twitter-typeahead {
width: 100%;
}
.input-group span.twitter-typeahead {
display: block !important;
}
.input-group span.twitter-typeahead .tt-dropdown-menu {
top: 32px !important;
}
.input-group.input-group-lg span.twitter-typeahead .tt-dropdown-menu {
top: 44px !important;
}
.input-group.input-group-sm span.twitter-typeahead .tt-dropdown-menu {
top: 28px !important;
}

@@ -0,0 +1,25 @@
.loading {
background-image: url("loading.gif");
background-repeat: no-repeat;
background-position: 100% 50%;
}

.matchfound {
text-decoration: underline;
}

.nomatchfound {
color: red;
text-decoration: underline;
font-style: italic;
-moz-text-decoration-line: underline;
-moz-text-decoration-color: red;
-moz-text-decoration-style: wavy;
-webkit-text-decoration-line: underline;
-webkit-text-decoration-color: red;
-webkit-text-decoration-style: dotted;
-webkit-text-decoration-style: wavy;
text-decoration-line: underline;
text-decoration-color: red;
text-decoration-style: wavy;
}
@@ -0,0 +1,73 @@
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Configuration;
using System.Web.Mvc;
using AzureAdMvcExample.Infrastructure.Auth;
using Microsoft.Azure.ActiveDirectory.GraphClient;

namespace AzureAdMvcExample.Controllers
{
public class UserLookupController : Controller
{
private readonly AzureADGraphConnection _graphConnection;

public UserLookupController()
{
// This should be injected in via a DI container, but for simplicity of the demo I'll leave it as a new statement
_graphConnection = new AzureADGraphConnection(
ConfigurationManager.AppSettings["AzureADTenant"],
ConfigurationManager.AppSettings["ida:ClientId"],
ConfigurationManager.AppSettings["ida:Password"]);
}

public ActionResult Index()
{
return View(new UserLookupViewModel());
}

[HttpPost]
public ActionResult Index(UserLookupViewModel vm)
{
if (ModelState.IsValid)
vm.User = _graphConnection.GetUser(vm.UserId.Value);
else
vm.UserId = null;

return View(vm);
}

public ActionResult Search(string q)
{
var users = _graphConnection.SearchUsers(q);

return Json(users, JsonRequestBehavior.AllowGet);
}
}

public class UserLookupViewModel
{
private User _user;

[Required]
public Guid? UserId { get; set; }

[Required]
public string UserName { get; set; }

[ReadOnly(true)]
public User User
{
get
{
return _user;
}
set
{
_user = value;
UserId = Guid.Parse(_user.ObjectId);
UserName = _user.DisplayName;
}
}
}
}
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Security.Claims;
using Humanizer;
using Microsoft.Azure.ActiveDirectory.GraphClient;
@@ -11,6 +12,8 @@ namespace AzureAdMvcExample.Infrastructure.Auth
public interface IAzureADGraphConnection
{
IList<AppRoles> GetRolesForUser(ClaimsPrincipal userPrincipal);
IList<User> SearchUsers(string query);
User GetUser(Guid id);
}

public class AzureADGraphConnection : IAzureADGraphConnection
@@ -39,5 +42,44 @@ public IList<AppRoles> GetRolesForUser(ClaimsPrincipal userPrincipal)
.Select(r => r.Value)
.ToList();
}

public IList<User> SearchUsers(string query)
{
var displayNameFilter = ExpressionHelper.CreateStartsWithExpression(typeof(User), GraphProperty.DisplayName, query);
var surnameFilter = ExpressionHelper.CreateStartsWithExpression(typeof(User), GraphProperty.Surname, query);
var usersByDisplayName = _graphConnection
.List<User>(null, new FilterGenerator { QueryFilter = displayNameFilter })
.Results;
var usersBySurname = _graphConnection
.List<User>(null, new FilterGenerator { QueryFilter = surnameFilter })
.Results;

return usersByDisplayName.Union(usersBySurname, new UserComparer()).ToArray();
}

public User GetUser(Guid id)
{
try
{
return _graphConnection.Get<User>(id.ToString());
}
catch (ObjectNotFoundException)
{
return null;
}
}

class UserComparer : IEqualityComparer<User>
{
public bool Equals(User x, User y)
{
return x.ObjectId == y.ObjectId;
}

public int GetHashCode(User obj)
{
return obj.ObjectId.GetHashCode();
}
}
}
}
@@ -1,7 +1,11 @@
/// <reference path="jquery-2.1.1.js" />
/// <autosync enabled="true" />
/// <reference path="jquery-2.1.1.js" />
/// <reference path="modernizr-2.8.3.js" />
/// <autosync enabled="true" />
਍⼀⼀⼀ 㰀爀攀昀攀爀攀渀挀攀 瀀愀琀栀㴀∀洀漀搀攀爀渀椀稀爀ⴀ㈀⸀㘀⸀㈀⸀樀猀∀ ⼀㸀ഀഀ
਍⼀⼀⼀ 㰀爀攀昀攀爀攀渀挀攀 瀀愀琀栀㴀∀戀漀漀琀猀琀爀愀瀀⸀樀猀∀ ⼀㸀ഀഀ
/// <reference path="bootstrap.js" />
/// <reference path="jquery.validate.js" />
/// <reference path="jquery.validate.unobtrusive.js" />
/// <reference path="respond.js" />
/// <reference path="respond.matchmedia.addlistener.js" />
/// <reference path="userlookup.js" />
/// <reference path="respond.js" />

0 comments on commit 1e8b673

Please sign in to comment.
You can’t perform that action at this time.