I have an issue, where by each user in my user list is showing as on-line when I know they're not.
When the page is loaded, they show as offline, but if I refresh the page, they all show as on-line. I'm assuming this is because I'm programmatically accessing their profile information (CommonProfile) to get the data to show on the gridview?
Is there any way to get the profile information without triggering the IsOnline property to be true?
Update:
Sorry, code is here. Please be gentle, I'm relatively new to c# & asp.net and I'm still learning.
The code is collecting information from Membership user and the common profiles and adding the fields to a datatable so that i can display the results in a gridview.
MembershipUserCollection usersList = Membership.GetAllUsers();
MembershipUserCollection filteredUsers = new MembershipUserCollection();
foreach (MembershipUser user in usersList)
{
if (!Roles.IsUserInRole(user.UserName, "Admin") && !Roles.IsUserInRole(user.UserName, "Engineering"))
{
if (txtFilterCustomerNo.Text.Length > 0)
{
ProfileCommon PC = Profile.GetProfile(user.UserName);
if (PC.CompanyAccountNo == txtFilterCustomerNo.Text.ToUpper())
{
filteredUsers.Add(user);
}
}
else
{
filteredUsers.Add(user);
}
}
}
txtFilterCustomerNo.Text = null;
foreach (MembershipUser user in filteredUsers)
{
userProfile = Profile.GetProfile(user.UserName);
string[] userRoles = Roles.GetRolesForUser(user.UserName);
DataRow orderLine = dataSet.Tables["UserAccounts"].NewRow();
orderLine["USER_NAME"] = user.UserName;
orderLine["CREATED"] = user.CreationDate;
orderLine["LAST_LOGIN"] = user.LastLoginDate;
orderLine["PASSWORD_CHANGED"] = user.LastLoginDate;
orderLine["ACTIVE"] = user.IsApproved;
orderLine["ONLINE"] = user.IsOnline;
orderLine["LOCKED"] = user.IsLockedOut;
orderLine["CUSTOMER_NO"] = userProfile.CompanyAccountNo;
orderLine["HAS_INVENTORY"] = userProfile.HasOwnInventory;
orderLine["ORDER"] = userRoles.Contains("Order");
orderLine["REPAIR"] = userRoles.Contains("Repair");
orderLine["WARRANTY"] = userRoles.Contains("Warranty");
orderLine["COMMISSIONING"] = userRoles.Contains("Commissioning");
orderLine["ACCOUNT"] = userRoles.Contains("Account");
dataSet.Tables["UserAccounts"].Rows.Add(orderLine);
}
if (dataSet.Tables.Contains("UserAccounts"))
{
GridView1.DataSource = dataSet.Tables["UserAccounts"];
}
If you simply looked at the different overloads of GetUser, you would see that some of them take a Boolean called userIsOnline. If you specify this as false, it will not update the last online timestamp, and will not list them as online.
var user = Membership.GetUser(userid, false);
EDIT:
I see you are using GetAllUsers() rather than GetUser(). There are some problems with GetAllUsers() and you cannot rely on the IsOnline property. Instead, you need to check the LastActivityDate field and figure out the difference between that and the current DateTime. If the amount of time is greater than what you consider "Online" to be, then they are offline, otherwise online.
Related
I am making a windows application that sync the source data to Active Directory.
This application works like this.
Choose Source Data(Department, User)
Mapping user or department attributes from source data
When Application service is run, it create groups and users in Active Directory
And also it sets the attributes to users and groups.
When I try to set group or user attributes(properties), it throws exception message like this.
in DirectoryEntry.CommitChanges(); block
The directory
service cannot perform the requested operation on the RDN attribute of an object.
I tried to solve it, but it's really hard to me because I`m not good at Active directory...
Code is below, Please share your knowledge.
//ppk: department key column, pk:user key column, row : Source DataTable's row
void CreateADUser(string ppk,string pk,DataRow row)
{
//password
string pass = GetPass(pk,row,LogSections.AD);
//OU
DirectoryEntry addept = adm.FindOU(ppk);
//principal path
string sOU = adm.GetPrincipalPath(addept);
var aduser = adm.CreateNewUser(sOU, pk, pass, pk, null, null, adm.sDomain);
SetAdUserProperties(pk, pass, row);
MoveUser(ppk,pk);
}
void SetAdUserProperties(string pk,string pass,DataRow row)
{
if (row == null) return;
//list of mapped column(AD User attributes)
List<ADMapping> MappingPatterns = GetAdMappings(Words.User,false);
//Columns name of Source Data table's row
var colnames = Tool.GetColNames(row);
//get user proterties
var aduser = adm.GetUser(pk);
//directory entry of users
var de=aduser.GetUnderlyingObject() as DirectoryEntry;
//looping mapped column of user attributes
foreach (var ADMap in MappingPatterns)
{
string val = ADMap.Mapping;
//mapped columns value
val=Util.ReplaceColPattern(val, row);
SetProperty(de, ADMap.CN, val);
}
if (!string.IsNullOrWhiteSpace(pass))
{
var UserPkColumn = AppConfigHelper.GetAppString(Words.SourceUserPKColumn);
UserPkColumn = Util.GetActualColName(UserPkColumn);
aduser.SetPassword(pass);
QueryHelper.Update(QueryHelper.ConnectionString, Words.ShadowUserTable
,new SqlParameter[] { new SqlParameter("#passwd", pass) }
, new SqlParameter("#"+UserPkColumn,pk));
}
aduser.Save();
}
public void SetProperty(DirectoryEntry oDE, string sPropertyName, object sPropertyValue)
{
if (sPropertyValue != null && !string.IsNullOrWhiteSpace(sPropertyValue.ToString()))
{
if (oDE.Properties.Contains(sPropertyName))
{
oDE.Properties[sPropertyName].Value = sPropertyValue;
}
else
{
oDE.Properties[sPropertyName].Add(sPropertyValue);
}
try
{
oDE.CommitChanges(); //exception here.
oDE.Close();
}
catch (Exception)
{
}
}
}
I also asked this question to other forums, and finally got it.
Before DirectoryEntry.CommitChanges(); set UserPropertyCache property to true
and call the RefreshCache method.
It's hard to see what's the cause of the issue here as we're not seeing what attributes you are trying to set.
That said, you can't just add an attribute if it doesn't exist on your AD object so this part of your code definitely has an issue :
if (oDE.Properties.Contains(sPropertyName))
{
oDE.Properties[sPropertyName].Value = sPropertyValue;
}
else
{
//The following line will never work in this context
oDE.Properties[sPropertyName].Add(sPropertyValue);
}
If I had to make an educated guess, I'd say you're either trying to set an attribute that can't be set, or the User you're adding doesn't have all it's mandatory attributes set.
I have a table for messages and another table for messageviews. I'm looking to show unread messages to an individual user. With my message table, I've sent up a new field that looks to see if any messageviews exist for the current message. If they do, then my Viewed bool should return true otherwise false(if they haven't viewed the message). Everything works fine, except that I'm unable to find the currently logged in user with User.Identity.GetUser() as I normally would. I've added the correct usings as well. Is there some limitation within a model to restrict this type of call. If so, how can I find the current user within a model?
public bool Viewed
{
get
{
ApplicationDbContext db = new ApplicationDbContext();
//var _userId = User.Identity.GetUserId();
var _userId = Thread.CurrentPrincipal.Identity.GetUserId();
List<MessageView> m_List = db.MessageView.Where(u => u.UserId == _userId && u.MessageId == O_MessageId).ToList();
var count = m_List.Count();
if (count >= 1)
{
return true;
}
else
{
return false;
}
}
}
Resolved: Outside of a controller you can find the current user with this -
var _userId = System.Web.HttpContext.Current.User.Identity.GetUserId();
Outside of a controller you can find the current user with this:
var _userId = System.Web.HttpContext.Current.User.Identity.GetUserId();
Hi there I am trying to create a textbox that when a user types into it they get a list of users with the specific name:
Example: If I started to type Jane.Doe, and I had only typed in Ja a list would come up with users from the Active Directory who start with Ja. I need to figure out how I can get the Users to a list each time a user types. I pretty much have the ajax side done. Its just getting the list of users updated each time.
My current idea:
[HttpPost]
public ActionResult RemoteData(string query)
{
List<string> lstADUsers = new List<string>();
using (var context = new PrincipalContext(ContextType.Domain, null, "LDAPPATH"))
{
using (var searcher = new PrincipalSearcher(new UserPrincipal(context)))
{
foreach (var result in searcher.FindAll())
{
DirectoryEntry de = result.GetUnderlyingObject() as DirectoryEntry;
string usersWithName;
if (!String.IsNullOrEmpty((String)de.Properties["samaccountname"].Value))
{
usersWithName = de.Properties["samaccountname"].Value.ToString();
lstADUsers.Add(usersWithName);
}
}
}
}
List<string> listData = null;
if (!string.IsNullOrEmpty(query))
{
listData = lstADUsers.Where(q => q.ToLower().StartsWith(query.ToLower())).ToList();
}
return Json(new { Data = listData });
}
So this allows us to get EVERY user in the Active Directory but I don't want this because the issue at hand gets that there are too many users and the search takes FOREVER to load this before it even displays the list of names. I only want to be able to take a parameter and only search for user that starts with that. How would I go about doing this?
You need to populate the Name property of UserPrincipal with a wildcard to limit the result set:
// assume 'query' is 'Ja'
UserPrincipal user = new UserPrincipal(context);
user.Name = query + "*"; // builds 'Ja*', which finds names starting with 'Ja'
using (var searcher = new PrincipalSearcher(user))
// ...
I am making an Active Directry managment tool but I am having trouble getting somethings working.
While I made a class where I want to find a specific user and return als his information(Name,Fullname,CN,...). I can find all this information but when I don't know the best way to return all the values from my class.
Here is the code I use so far:
DirectorySearcher search = new DirectorySearcher(ldapConnectie);
search.Filter = "(cn=" + username + ")";
SearchResult result = search.FindOne();
if (result != null)
{
List<string> listLdapFields = new List<string>();
List<Object> listLdapValues = new List<Object>();
ResultPropertyCollection fields = result.Properties;
foreach (String ldapField in fields.PropertyNames)
{
listLdapFields.Add(ldapField);
foreach (Object myCollection in fields[ldapField])
{
listLdapValues.Add(myCollection);
}
}
}
The program add everything well to a list. But if I return this I cannot search the list on "CN" or "Name". I can only find the information on Index Number.
Hope you can help me out.
If you're on .NET 3.5 and up, you should check out the System.DirectoryServices.AccountManagement (S.DS.AM) namespace. Read all about it here:
Managing Directory Security Principals in the .NET Framework 3.5
MSDN docs on System.DirectoryServices.AccountManagement
Basically, you can define a domain context and easily find users and/or groups in AD:
// set up domain context
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain))
{
// find a user
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, username);
if(user != null)
{
// do something here....
// the most often used attributes are available as nice, strongly-typed properties
string value = user.GivenName;
value = user.Surname;
value = user.EmailAddress;
value = user.VoiceTelephoneNumber;
}
}
The new S.DS.AM makes it really easy to play around with users and groups in AD!
Here's an approach where you can call the property by name. It also uses the authenticated account making the request.
string domainPath = String.Format("LDAP://{0},DC=site,DC=com", domain);
using (DirectoryEntry searchRoot = new DirectoryEntry(domainPath))
{
using (DirectorySearcher search =
filterSearch(new DirectorySearcher(searchRoot), username))
{
SearchResult result = null;
try
{
result = search.FindOne();
}
catch (DirectoryServicesCOMException e)
{
//handle the error
}
if (result != null)
{
string givenname = result.Properties["givenname"].Count > 0 ?
(string)result.Properties["givenname"][0] : "";
string sn = result.Properties["sn"].Count > 0 ?
(string)result.Properties["sn"][0] : "";
var samaccount= result.Properties["samaccountname"].Count > 0 ?
(string)result.Properties["samaccountname"][0] : "";
var name = String.Format("{0}, {1}", sn, givenname);
var email = result.Properties["mail"].Count > 0 ?
(string)result.Properties["mail"][0] : "";
}
}
}
//Apply a filter to search only specific classes and categories.
//Add the specific properties to be retrieved
private DirectorySearcher filterSearch(DirectorySearcher search, string username)
{
DirectorySearcher filteredSearch = search;
filteredSearch.Filter = "(&(objectClass=user)(objectCategory=person)(samaccountname=" + username + "))";
filteredSearch.PropertiesToLoad.Add("givenname");
filteredSearch.PropertiesToLoad.Add("sn");
filteredSearch.PropertiesToLoad.Add("samaccountname");
filteredSearch.PropertiesToLoad.Add("department");
filteredSearch.PropertiesToLoad.Add("physicalDeliveryOfficeName");
filteredSearch.PropertiesToLoad.Add("mail");
return filteredSearch;
}
This may be helpful if looking for specific properties, but if you want to retrieve a list of all attributes/values, take a look at this other SO question.
Google has a list of common filters you can use. Take a look through them and modify the filterSearch method appropriately.
I am playing around with a SharePoint server and I am trying to programmatically add a service request to microsoft's call center application template. So far, I have had pretty good success. I can add a call for a specified customer and assign a specific support tech:
private enum FieldNames
{
[EnumExtension.Value("Service Request")]
ServiceRequest,
[EnumExtension.Value("Customer")]
Customer,
[EnumExtension.Value("Service Representative")]
ServiceRepresentative,
[EnumExtension.Value("Assigned To")]
AssignedTo,
[EnumExtension.Value("Software")]
Software,
[EnumExtension.Value("Category")]
Category
}
private void CreateServiceCall(string serviceCallTitle, string customerName, string serviceRep)
{
SPSite allSites = new SPSite(siteURL);
SPWeb site = allSites.AllWebs[siteName];
SPListItemCollection requestsList = site.Lists[serviceRequests].Items;
SPListItem item = requestsList.Add();
SPFieldLookup customerLookup = item.Fields[FieldNames.Customer.Value()] as SPFieldLookup;
item[FieldNames.ServiceRequest.Value()] = serviceCallTitle;
if (customerLookup != null)
{
using (SPWeb lookupWeb = allSites.OpenWeb(customerLookup.LookupWebId))
{
SPList lookupList = lookupWeb.Lists.GetList(new Guid(customerLookup.LookupList), false);
foreach (SPListItem listItem in lookupList.Items)
{
if (listItem[customerLookup.LookupField].ToString() != customerName) continue;
item[FieldNames.Customer.Value()] = new SPFieldLookupValue(listItem.ID, customerName);
break;
}
}
}
SPUserCollection userCollection = site.SiteUsers;
if (userCollection != null)
{
foreach (SPUser user in userCollection)
{
if (user.Name != serviceRep) continue;
item[FieldNames.AssignedTo.Value()] = user;
break;
}
}
item.Update();
site.Close();
allSites.Close();
}
I added two custom columns (category, software) to the default list:
I populated both of these columns inside of SharePoint, now I want to retrieve that data so I can use it in the code snippet I posted to assign the proper category/software etc to the call. I have not been able to get the list in the code, I have tried using a item["Software"], site.Lists["Software"] and a couple of others, but so far all I have come up is null.
Can anyone point me in the right direction for this? Thanks!
SPFieldMultiChoice and related fields have a Choices property:
SPFieldMultiChoice software = item.Fields[FieldNames.Software.Value()] as SPFieldMultiChoice;
StringCollection softwareChoices = software.Choices;
If you need to set a value on the field, use the SPFieldMultiChoiceValue type:
SPFieldMultiChoiceValue values = new SPFieldMultiChoiceValue();
values.Add("Choice 1");
values.Add("Choice 2");
item[FieldNames.Software.Value()] = values;