I need to send a temporary password via email to an user when i register a new Teacher.
i use Membership.GeneratePassword to generate random password.
here is my TeacherController
[HttpPost]
public async Task<ActionResult> Create(TeacherViewModel viewModel)
{
if (!ModelState.IsValid)
{
var model = new TeacherViewModel()
{
Courses = _context.Courses.ToList(),
SituationActuelles = _context.SituationActuelles.ToList()
};
return View("Create", model);
}
var teacher = new Teacher
{
PhoneNumber = viewModel.PhoneNumber,
Password = viewModel.Password
};
_context.Teachers.Add(teacher);
_context.SaveChanges();
var body = "<p>Merci de votre interet a NewEra Tutoring Corp.</p> <br/>" +
"Vos credentials pour acceder a votre platform est le suivant: <br/>" +
"Email: Votre email<br/>" +
"Votre mot de passe temporaire: " + viewModel.Password;
var message = new MailMessage();
message.To.Add(new MailAddress(viewModel.Email)); // replace with valid value
message.From = new MailAddress("recrutement#newera-tutoring.com"); // replace with valid value
message.Subject = "Your email subject";
message.Body = string.Format(body, "NewEra Tutoring", "recrutement#newera-tutoring.com", "Votre identifiant pour NewEra Tutoring");
message.IsBodyHtml = true;
using (var smtp = new SmtpClient())
{
var credential = new NetworkCredential
{
UserName = "recrutement#newera-tutoring.com", // replace with valid value
Password = "neweratutoring" // replace with valid value
};
smtp.Credentials = credential;
smtp.Host = "gator4123.hostgator.com";
smtp.Port = 587;
smtp.EnableSsl = true;
await smtp.SendMailAsync(message);
}
return RedirectToAction("Index", "Home");
}
But the password i receive in the email it's different from the one that it's saved in the database.
here is my model
TeacherViewModel{
public string Password => Membership.GeneratePassword(12, 1);
}
Where i am doing wrong?
You've used an expression bodied member in the view model for the password property which essentially creates a get wrapper around the call to membership password.
Each time you access the property it generates a new password as it runs the function again.
I don't see why you'd want to have it in the view model at all unless your intention is to offer a random password that they user could replace with their own. If that was the aim use a standard property get/set and create an initial password value in the view model constructor instead. Otherwise, take it out o of the view model & only create the password in the post action if every thing else is valid.
Related
I need to create a form in which the user has to fill it and to send it. So i have to create a submit button that calls another method but i couldn't find the link between the submit action and the call to another method.
The script of my form is :
public Attachment CreateAdaptiveCardwithEntry()
{
var submitActionData = JObject.Parse("{ \"Type\": \"SaveFunction\" }");
var card = new AdaptiveCard()
{
Body = new List<CardElement>()
{
// Hotels Search form
new TextBlock() { Text = "Titre de la note des frais" },
new TextInput()
{
Id = "titre",
Speak = "<s>Veuillez saisir le titre</s>",
Placeholder = "Veuillez saisir le titre",
Style = TextInputStyle.Text
},
},
Actions = new List<ActionBase>()
{
new SubmitAction()
{
DataJson = submitActionData.ToString()
}
}
};
The script of my card is :
var replyMessage = context.MakeMessage();
replyMessage.Attachments = new List<Attachment> { FraisDialog.CreateAdaptiveCardwithEntry() };
await context.PostAsync(replyMessage, CancellationToken.None);
context.Wait(MessageReceived);
the script in MessageReceivedAsync is :
public virtual async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
var message = await result;
if (message.Value != null)
{
// Got an Action Submit
dynamic value = message.Value;
string submitType = value.Type.ToString();
switch (submitType)
{
case "SaveFunction":
await context.PostAsync("Please complete all the search parameters:\n");
return;
}
}
}
In this example i need to send the information with the Id = "titre" and pprocess it afterwards, i don't know how to send it(DataJson ?) and where(MessageReceivedAsync ?). Can someone help me ? do i need to create another dialog just for the card ?
Ps : all this code is in rootDialog.
i'm not getting the message 'Please complete all the search parameters'
If all of your code is in RootDialog then please use context.Wait(MessageReceivedAsync); after sending your attachment.
i need to send the information with the Id = "titre" and process it afterwards
When clicking the submit button, the form data is send to MessageReceived method as usual. If you want to just access the fields in the adaptive card you can access the dynamic variable value. Here is an example.
var message = await result;
if (message.Value != null)
{
// Got an Action Submit
dynamic value = message.Value;
string submitType = value.Type.ToString();
switch (submitType)
{
case "SaveFunction":
if(value.titre == "")
{
await context.PostAsync("Please complete all the search parameters:\n");
}
else
{
await context.PostAsync($"You entered {value.titre}");
}
return;
}
}
I have used SOAP API to create Embedded signing. I am using template (CreateEnvelopeFromTemplates) to create envelope. I have added a payment field on template which works fine if I send envelope request from docusign website. I get a popup to enter payment information. BUT when I am sending the same template from API then Payment functionality is not working. Payment field is appearing as number.
screenshot
Here is my code
public class DS_Recipe_Signer_View_Controller {
// Embedded signing of an envelope
// Copyright (c) 2016 DocuSign, Inc.
// LICENSE: The MIT License, see https://opensource.org/licenses/MIT
// SETTINGS
// Private static string integration_key = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx';
// Private static string account_id = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx';
Private static string integration_key = 'XXXXXXXXXX-98333677e8bd';
Private static string account_id = 'XXXXXXXXXXX-5f66c17b7f9f';
// NOTE: You MUST use the long form of the account id. It's has 32 digits
// with 4 dashes (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).
// This version of the account id is shown in the APIs and Connects section
// of your DocuSign Administration tool
Public string signer_email {get;set;} // Required
Public string signer_name {get;set;} // Required
Public string email_message {get;set;} // Required
Public string signer_user_id {get;set;} // Required for embedded signing
Public string signer_return_url {get;set;} // Required. Where DS redirects to after the signing ceremony
Public string output {get;set;}
Public string envelope_id {get;set;}
Public string signer_view_url {get;set;} // Redirect to this url
Public string error_code {get;set;} // Null means no error
Public string error_message {get;set;}
// Using Legacy authentication via an SFDC Named Credential
Private static string ds_server = 'callout:DocuSign_Legacy_Demo/api/3.0/dsapi.asmx';
// If you choose to not use a named credential:
//Private static string ds_server = 'https://requestb.in/119qunn1';
//Private static string ds_server ='https://demo.docusign.net/api/3.0/dsapi.asmx';
Private static string trace_value = 'SFDC_002_SOAP_embedded_signing'; // Used for tracing API calls
Private static string trace_key = 'X-ray';
Private DocuSignTK.APIServiceSoap api_sender = new DocuSignTK.APIServiceSoap();
Public DS_Recipe_Signer_View_Controller(){}
Public void send(){
configure_sender();
send_envelope();
embedded_signing();
if (no_error()) {
output = '<p>The envelope was sent, Envelope ID: ' + envelope_id + '</p>';
output += '<p></p><p>Signer: ' + signer_name + ' <' + signer_email + '></p>';
output += '<p><b>To sign the envelope, redirect the user to the <a href = "' +
signer_view_url + '" target="_blank">DocuSign Signing Ceremony</a></b></p>';
output += '<p>The redirect address is ' + signer_view_url + '</p>';
output += '<p><b>Note:</b> the Signing Ceremony url can only be used for a couple of minutes after ' +
'it has been created. Do NOT store the url for later use. Instead, ' +
'generate the URL immediately before you redirect the user\'s browser.</p>';
output += '<p>After the signer has completed the signing ceremony, his ' +
'browser will be redirected back to your app with some query fields added. Example: </p>' +
'<p>http://www.foo.com/?event=signing_complete</p>';
} else {
output = '<h3>Problem</h3><p>' + error_message + '</p>';
}
}
Private void configure_sender(){
api_sender.endpoint_x = ds_server;
api_sender.inputHttpHeaders_x = new Map<String, String>();
String auth = '<DocuSignCredentials><Username>{!$Credential.Username}</Username>'
+ '<Password>{!$Credential.Password}</Password>'
+ '<IntegratorKey>' + integration_key + '</IntegratorKey></DocuSignCredentials>';
api_sender.inputHttpHeaders_x.put('X-DocuSign-Authentication', auth);
api_sender.inputHttpHeaders_x.put(trace_key, trace_value);
}
Private void embedded_signing() {
// Obtains the embedded Signing Ceremony URL for an envelope's recipient (the signer).
// To use embedded signing:
// 1. The signer must have been added to the envelope as a "captive signer"
// 2. You need the following values:
// 1. EnvelopeID
// 2. Signer's Email that was provided when the signer was added to the envelope.
// 3. Signer's name (UserName field)
// 4. The Signer's User ID (client id) within your app. Must uniquely identify the signer.
// 3. You also need to create an "Assertion" object where you provide information on how
// your app authenticated the signer. This information is stored by DocuSign so you can
// later use the data in case of a dispute.
// Incoming variables used:
// envelope_id, signer_user_id, signer_email, signer_name
// Maintaining state: when DocuSign redirects back to your app after the signing ceremony
// ended, how does your app know what is going on? You can include additional query parameters
// in the signer_return_url that you supply. Eg the recipient ID, envelope ID, etc.
// You can include your app's sessionID. You can use the cookie system to store either
// specific information or your stack's session id for your app.
// Step 1. Create the assertion
DocuSignTK.RequestRecipientTokenAuthenticationAssertion assertion =
new DocuSignTK.RequestRecipientTokenAuthenticationAssertion();
assertion.AssertionID = '1'; // A unique identifier of the authentication
// event executed by your application.
assertion.AuthenticationInstant = Datetime.now(); // The date/time that the end-user was authenticated.
assertion.AuthenticationMethod = 'Password'; // How did your app authenticate the signer?
// Options: Password, Email, PaperDocuments, HTTPBasicAuth, SSLMutualAuth, X509Certificate, Kerberos,
// SingleSignOn_CASiteminder, SingleSignOn_InfoCard, SingleSignOn_MicrosoftActiveDirectory, SingleSignOn_Passport,
// SingleSignOn_SAML, SingleSignOn_Other, Smartcard, RSASecureID, Biometric, None, KnowledgeBasedAuth
assertion.SecurityDomain = 'DS_Recipe_Signer_View_Controller'; // The "domain" (app, sso system, etc)
// to which the user authenticated
// Step 2. Create the redirect URLs for the different outcomes of the Signing Ceremony
DocuSignTK.RequestRecipientTokenClientURLs urls = new DocuSignTK.RequestRecipientTokenClientURLs();
String return_url_base = signer_return_url;
// The supplied url may already include one or more query parameters. In that case, we're appending
// one more query parameters. Otherwiser, we're adding the first set of query parameters.
// Look for a ? to see if the url already includes query parameters
If (return_url_base.contains('?')) {
return_url_base += '&event=';
} Else {
return_url_base += '?event=';
}
urls.OnSigningComplete = return_url_base + 'signing_complete';
urls.OnViewingComplete = return_url_base + 'viewing_complete';
urls.OnCancel = return_url_base + 'cancel';
urls.OnDecline = return_url_base + 'decline';
urls.OnSessionTimeout = return_url_base + 'session_timeout';
urls.OnTTLExpired = return_url_base + 'ttl_expired';
urls.OnException = return_url_base + 'exception';
urls.OnAccessCodeFailed = return_url_base + 'failed_access_code';
urls.OnIdCheckFailed = return_url_base + 'failed_id_check';
urls.OnFaxPending = return_url_base + 'fax_pending';
// Step 3. Make the call
try {
signer_view_url = api_sender.RequestRecipientToken(
envelope_id, signer_user_id, signer_name, signer_email, assertion, urls);
System.debug('Received signer_view_url: = ' + signer_view_url);
} catch ( CalloutException e) {
System.debug('Exception - ' + e );
error_code = 'Problem: ' + e;
error_message = error_code;
}
}
Private void send_envelope() {
// Sends an envelope. The first signer is "captive," so he can sign embedded
// Check input
if (String.isBlank(signer_email) || String.isBlank(signer_name) || !signer_email.contains('#')) {
error_message = 'Please fill in the email and name fields';
error_code = 'INPUT_PROBLEM';
return;
}
// Check configuration
if (integration_key == 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' ||
account_id == 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx') {
error_message = 'Please configure the Apex class DS_Recipe_Send_Env_Email_Controller with your integration key and account id.';
error_code = 'CONFIGURATION_PROBLEM';
return;
}
DocuSignTK.Recipient recipient = new DocuSignTK.Recipient();
recipient.Email = signer_email;
recipient.UserName = signer_name;
recipient.ID = 1;
recipient.Type_x = 'Signer';
recipient.RoutingOrder = 1;
recipient.RoleName = 'Signer';
// We want this signer to be "captive" so we can use embedded signing with him
recipient.CaptiveInfo = new DocuSignTK.RecipientCaptiveInfo();
recipient.CaptiveInfo.ClientUserID = signer_user_id; // Must uniquely identify the
DocuSignTK.ArrayOfRecipient1 recipients = new DocuSignTK.ArrayOfRecipient1();
recipients.Recipient = new DocuSignTK.Recipient[1];
recipients.Recipient[0] = recipient;
// Create the template reference from a server-side template ID
DocuSignTK.TemplateReference templateReference = new DocuSignTK.TemplateReference();
templateReference.Template = '37041580-9d59-4b41-a7c4-7c9bfa3b26c8';//'d0d80082-612b-4a04-b2a1-0672eb720491';
templateReference.TemplateLocation = 'Server';
//templateReference.Sequence = 1;
DocuSignTK.ArrayOfTemplateReferenceRoleAssignment Roles = new DocuSignTK.ArrayOfTemplateReferenceRoleAssignment();
Roles.RoleAssignment = new DocuSignTK.TemplateReferenceRoleAssignment[1];
DocuSignTK.TemplateReferenceRoleAssignment role = new DocuSignTK.TemplateReferenceRoleAssignment();
role.RoleName = 'Signer';
role.RecipientID = 1;
Roles.RoleAssignment[0] = role;
templateReference.RoleAssignments = Roles;
// Construct the envelope information
DocuSignTK.EnvelopeInformation envelopeInfo = new DocuSignTK.EnvelopeInformation();
envelopeInfo.AccountId = account_Id;
envelopeInfo.Subject = 'Subject';
envelopeInfo.EmailBlurb = 'Email content';
DocuSignTK.TemplateReferenceFieldDataDataValue fd2 = new
DocuSignTK.TemplateReferenceFieldDataDataValue();
fd2.TabLabel = 'name';
fd2.Value = 'recipient.UserName';
DocuSignTK.TemplateReferenceFieldDataDataValue fd3 = new
DocuSignTK.TemplateReferenceFieldDataDataValue();
fd3.TabLabel = 'company';
fd3.Value = 'company';
templateReference.FieldData = new DocuSignTK.TemplateReferenceFieldData();
templateReference.FieldData.DataValues = new
DocuSignTK.ArrayOfTemplateReferenceFieldDataDataValue();
templateReference.FieldData.DataValues.DataValue = new
DocuSignTK.TemplateReferenceFieldDataDataValue[2];
templateReference.FieldData.DataValues.DataValue[0] = fd2;
templateReference.FieldData.DataValues.DataValue[1] = fd3;
// Make the call
try {
//DocuSignTK.EnvelopeStatus result = api_sender.CreateAndSendEnvelope(envelope);
// Create draft with all the template information
DocuSignTK.ArrayOfTemplateReference TemplateReferenceArray = new DocuSignTK.ArrayOfTemplateReference();
TemplateReferenceArray.TemplateReference = new DocuSignTK.TemplateReference[1];
TemplateReferenceArray.TemplateReference[0] = templateReference;
DocuSignTK.EnvelopeStatus result = api_sender.CreateEnvelopeFromTemplates( TemplateReferenceArray, recipients, envelopeInfo, true);
envelope_id = result.EnvelopeID;
System.debug('Returned successfully, envelope_id = ' + envelope_id );
} catch ( CalloutException e) {
System.debug('Exception - ' + e );
error_code = 'Problem: ' + e;
error_message = error_code;
}
}
Private Boolean no_error() {
return (String.isEmpty(error_code));
}
}
DocuSign Website uses RESTAPI to send Payment details in an envelope. Payment related APIs are not available in SOAP calls, please try using REST API for payment related calls.
I have changed the AspNetUser (identity) tabe to only user with the following code.
// Change the name of the table to be Users instead of AspNetUsers
modelBuilder.Entity<IdentityUser>()
.ToTable("Users").Property(p => p.Id).HasColumnName("UserID");
modelBuilder.Entity<ApplicationUser>()
.ToTable("Users").Property(p => p.Id).HasColumnName("UserID");
My application works fine but not my seed. I have this code in Migrations/Configuration.cs
var UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context));
var RoleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(context));
string role = "Admin";
string password = "Password#1234";
//Create Role Admin if it does not exist
if (!RoleManager.RoleExists(role))
{
var roleresult = RoleManager.Create(new IdentityRole(role));
}
//Create Admin users with password=123456
var admin1 = new ApplicationUser();
admin1.UserName = "admin1#admin1.com";
admin1.Email = "admin1#admin1.com";
admin1.EmailConfirmed = true;
UserManager.Create(admin1, password);
UserManager.AddToRole(admin1.Id, role);
context.SaveChanges();
I get the error message "UserId not found". Seems like my UserManager.Create fails.
How can I change my seed code to use the UserID insted of standard Id?
Actually, when you save the user, it does not have an id given yet, you'll have to retrieve the user in between the creation of the user & addToRole:
var UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context));
var RoleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(context));
string role = "Admin";
string password = "Password#1234";
//Create Role Admin if it does not exist
if (!RoleManager.RoleExists(role))
{
var roleresult = RoleManager.Create(new IdentityRole(role));
}
//Create Admin users with password=123456
var admin1 = new ApplicationUser();
admin1.UserName = "admin1#admin1.com";
admin1.Email = "admin1#admin1.com";
admin1.EmailConfirmed = true;
UserManager.Create(admin1, password);
// Refetch user with ID:
dbAdmin1 = context.Users.FirstOrDefault(x => x.UserName == admin1.UserName);
UserManager.AddToRole(dbAdmin1.Id, role);
context.SaveChanges();
I have a ServiceStack application that coexists with mvc5 in a single web project. The only purpose of the mvc5 part is to host a single controller action that receives a callback from janrain for javascript initiated social login. I could receive this callback in a SS service request, too, but then I don't know how I would do a redirect to the returnUrl that is passed through all the way from the javascript context. Even if I was able to figure this out, my question would still be the same.
Inside of the controller action, once I verify the janrain provided token resolves to a user in my system, I need to manually tell ServiceStack "hey trust me - this person is authorized".
All my searches lead to some code along the lines of the following snippet:
var authService = AppHostBase.Resolve<AuthService>();
authService.RequestContext = System.Web.HttpContext.Current.ToRequestContext();
var AuthResponse = authService.Authenticate(new Auth
{
provider = "credentials",
UserName = user.user_id,
Password = user.password,
RememberMe = true
});
My first problem here is that I store hashed passwords (I support social login as well as manual login), so I don't know the user's password (and I shouldn't).
My second problem is that this code seems to only work for SS 3.X and not 4.X. I requires a ServiceStack.ServiceInterface.dll that is mysteriously missing from 4.X.
Is there a short and precise way to manually authenticate a user with SS on the server side?
Thanks
EDIT:
So far this is what I am doing: (This is not final code - I have commented out some things I don't know what to do with):
public class UsernameOnlyAuthorizationService : Service
{
public object Post(UsernameOnlyLoginRequest request)
{
var authProvider = new UsernameOnlyAuthProvider();
authProvider.Authenticate(this, GetSession(), new Authenticate()
{
UserName = request.username,
Password = "NotRelevant",
RememberMe = true
});
return HttpResult.Redirect(request.returnUrl);
}
}
public class UsernameOnlyAuthProvider : CredentialsAuthProvider
{
public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
{
var authRepo = authService.TryResolve<IAuthRepository>().AsUserAuthRepository(authService.GetResolver());
ReferScienceDataContext db = authService.TryResolve<ReferScienceDataContext>();
var session = authService.GetSession();
IUserAuth userAuth;
var user = db.Users.FirstOrDefault(u => u.Username == userName);
if (user != null)
{
//AssertNotLocked(userAuth);
//session.PopulateWith(userAuth);
session.Id = user.Id.ToString();
session.UserName = user.Username;
session.FirstName = user.FirstName;
session.LastName = user.LastName;
session.IsAuthenticated = true;
session.UserAuthId = user.Id.ToString(CultureInfo.InvariantCulture);
session.ProviderOAuthAccess = authRepo.GetUserAuthDetails(session.UserAuthId)
.ConvertAll(x => (IAuthTokens)x);
return true;
}
return false;
}
}
And from within my Janrain success callback code I call it so:
HostContext.ResolveService<UsernameOnlyAuthorizationService>().Post(new UsernameOnlyLoginRequest() {username = user.Username, returnUrl= returnUrl});
This seems to work nicely, however, I can't get it to remember my session across browser closes. I am hardcoding RememberMe = true - why is this not working?
I would do this by creating an internal service, which you can call from your MVC5 controller action, where you only require to pass the username of the user you have authenticated.
public class JanrainSuccessService : Service
{
public void CreateSessionFor(string username)
{
var repository = TryResolve<IAuthRepository>().AsUserAuthRepository(GetResolver());
var user = repository.GetUserAuthByUserName(username);
var session = GetSession();
session.PopulateWith(user);
session.IsAuthenticated = true;
session.UserAuthId = user.Id.ToString(CultureInfo.InvariantCulture);
session.ProviderOAuthAccess = repository.GetUserAuthDetails(session.UserAuthId).ConvertAll(x => (IAuthTokens)x);
}
}
The code in this method, is effectively the same could that is used by the CredentialsAuthProvider, but has the advantage of not requiring the password of the user. (See the TryAuthenticate method here for original code)
In your MVC5 controller action method you would need to call:
HostContext.ResolveService<JanrainSuccessService>().CreateSessionFor(user.user_id);
This assumes that you have a valid repository of users configured to match username's against.
You should update your code to be:
public class UsernameOnlyAuthorizationService : Service
{
public object Post(UsernameOnlyLoginRequest request)
{
var authProvider = new UsernameOnlyAuthProvider();
authProvider.Authenticate(this, GetSession(), new Authenticate()
{
UserName = request.username,
Password = "NotRelevant",
RememberMe = true
});
// Remember the session
base.Request.AddSessionOptions(SessionOptions.Permanent);
return HttpResult.Redirect(request.returnUrl);
}
}
public class UsernameOnlyAuthProvider : CredentialsAuthProvider
{
public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
{
var authRepo = authService.TryResolve<IAuthRepository>().AsUserAuthRepository(authService.GetResolver());
ReferScienceDataContext db = authService.TryResolve<ReferScienceDataContext>();
var session = authService.GetSession();
var user = db.Users.FirstOrDefault(u => u.Username == userName);
if (user == null)
return false;
session.Id = user.Id.ToString();
session.UserName = user.Username;
session.FirstName = user.FirstName;
session.LastName = user.LastName;
session.IsAuthenticated = true;
session.UserAuthId = user.Id.ToString(CultureInfo.InvariantCulture);
session.ProviderOAuthAccess = authRepo.GetUserAuthDetails(session.UserAuthId).ConvertAll(x => (IAuthTokens)x);
return true;
}
}
I am using managed client side object model in sharepoint 2010. And I want to get loginaName of the AssignedTo user in Task list.
In server side object model I use SPFieldUserValue.User.LoginName to get this property but in client side object model FieldUserValue.User does not exists.
How can I resolve this situation ?
Thanks
Here is the code for that. I've taken an example of AssignedTo field from Task list. I hope that helps.
public static User GetUserFromAssignedToField(string siteUrl)
{
// create site context
ClientContext ctx = new ClientContext(siteUrl);
// create web object
Web web = ctx.Web;
ctx.Load(web);
// get Tasks list
List list = ctx.Web.Lists.GetByTitle("Tasks");
ctx.Load(list);
// get list item using Id e.g. updating first item in the list
ListItem targetListItem = list.GetItemById(1);
// Load only the assigned to field from the list item
ctx.Load(targetListItem,
item => item["AssignedTo"]);
ctx.ExecuteQuery();
// create and cast the FieldUserValue from the value
FieldUserValue fuv = (FieldUserValue)targetListItem["AssignedTo"];
Console.WriteLine("Request succeeded. \n\n");
Console.WriteLine("Retrieved user Id is: {0}", fuv.LookupId);
Console.WriteLine("Retrieved login name is: {0}", fuv.LookupValue);
User user = ctx.Web.EnsureUser(fuv.LookupValue);
ctx.Load(user);
ctx.ExecuteQuery();
// display the user's email address.
Consol.writeLine("User Email: " + user.Email);
return user;
}
The fuv.LookupValue may contain the display name, not the login name, so my suggestion is (assuming you have the FieldUserValue - fuv in code (as descibed by #ekhanna):
var userId = fuv.LookupId;
var user = ctx.Web.GetUserById(userId);
ctx.Load(user);
ctx.ExecuteQuery();
You get the column which as the FieldUserValue from the list, once you have that you use the lookup id value and then query against the Sites User Info List. In the example below I cache the results to prevent looking up the same id more than once since the query can be expensive.
private readonly Dictionary<int, string> userNameCache = new Dictionary<int, string>();
public string GetUserName(object user)
{
if (user == null)
{
return string.Empty;
}
var username = string.Empty;
var spUser = user as FieldUserValue;
if (spUser != null)
{
if (!userNameCache.TryGetValue(spUser.LookupId, out username))
{
var userInfoList = context.Web.SiteUserInfoList;
context.Load(userInfoList);
var query = new CamlQuery { ViewXml = "<View Scope='RecursiveAll'><Query><Where><Eq><FieldRef Name='ID' /><Value Type='int'>" + spUser.LookupId + "</Value></Eq></Where></Query></View>" };
var users = userInfoList.GetItems(query);
context.Load(users, items => items.Include(
item => item.Id,
item => item["Name"]));
if (context.TryExecuteQuery())
{
var principal = users.GetById(spUser.LookupId);
context.Load(principal);
context.ExecuteQuery()
username = principal["Name"] as string;
userNameCache.Add(spUser.LookupId, username);
}
}
}
return username;
}
Everything above worked for me, but instead of:
FieldUserValue fuv = (FieldUserValue)targetListItem["AssignedTo"];
I used:
FieldUserValue[] fuv = targetListItem["AssignedTo"] as FieldUserValue[];