Asp.Net - Identity 2 - Attach files in emails fired by EmailService - c#

I´m using Asp.Net-Identity-2 to manage user access in my app.
I´m using EmailService (UserManager.SendEmail) to send confirmation email messages and I´d like to send a formatted HTML message, and I want attach images in it.
How can I do that??
Setup Identity User Manager EmailService
public class AppUserManager : UserManager<AppUser>
{
public AppUserManager(IUserStore<AppUser> store) : base(store) { }
public static AppUserManager Create(IdentityFactoryOptions<AppUserManager> options, IOwinContext context)
{
AppUserManager manager = new AppUserManager(new UserStore<AppUser>(db));
//Some setup code here ....
//Hook to my EmailService (see class MyEmailService.cs)
manager.EmailService = new MyEmailService();
return manager;
} //Create
} //class
MyEmailService
public class MyEmailService : IIdentityMessageService
{
public Task SendAsync(IdentityMessage message)
{
MailMessage email = new MailMessage("me#sample.com", message.Destination);
email.Subject = message.Subject;
email.Body = message.Body;
email.IsBodyHtml = true;
var mailClient = new SmtpClient();
mailClient.Host = "EmailServer";
mailClient.Port = 25;
return mailClient.SendMailAsync(email);
} //SendAsync
} //class
Action to send email
public ActionResult ForgotPassword(string email)
{
if (ModelState.IsValid)
{
AppUser user = UserManager.FindByEmail(email);
if (user == null || !(UserManager.IsEmailConfirmed(user.Id)))
{
return View("../Home/Index");
} //if
string code = UserManager.GenerateEmailConfirmationToken(user.Id);
string callbackUrl = Url.Action("ResetPassword", "Admin", new { Id = user.Id, code = code }, protocol: Request.Url.Scheme);
string strMessage = getHTMLMessage(); //variable Html message here, with images references in it (ex. "<img src='cid:IMAGE_TITLE'>")
UserManager.SendEmail(user.Id, "Message Subject", strMessage);
return View("../Home/Index");
}
// If we got this far, something failed, redisplay form
return View();
} //ForgotPassword
My doubt is how to attach images in that message...
Thanks for any help..
Julio Schurt

I had to create another method in my service and call it directly
public Task SendAsync(IdentityMessage message, IEnumerable<KeyValuePair<string, Stream>> attach)
{
var myMessage = new SendGridMessage() { From = new MailAddress("my#email.com") };
myMessage.AddTo(message.Destination);
myMessage.Subject = message.Subject;
myMessage.Html = message.Body;
myMessage.Text = message.Body;
var credentials = new NetworkCredential("myuser", "mypassword");
var transportWeb = new Web(credentials);
foreach (var file in attach)
{
myMessage.AddAttachment(file.Value,file.Key);
}
return transportWeb.DeliverAsync(myMessage);
}
EDIT: (2015-08-14)
Final class:
public class EmailService : IIdentityMessageService
{
const string from = "mail#domain.com";
const string username = "username";//Environment.GetEnvironmentVariable("SENDGRID_USER");
const string pswd = "password";//Environment.GetEnvironmentVariable("SENDGRID_PASS");
private List<KeyValuePair<string, Stream>> _attachments;
private List<KeyValuePair<string, string>> _recipients;
public Task SendAsync(IdentityMessage message)
{
var myMessage = new SendGridMessage() { From = new MailAddress(from) };
var credentials = new NetworkCredential(username, pswd);
var transportWeb = new Web(credentials);
myMessage.AddTo(message.Destination);
if (_recipients != null)
{
_recipients.ForEach(r => myMessage.AddTo(string.Format("{0} {1}", r.Key, r.Value)));
}
myMessage.Subject = message.Subject;
myMessage.Html = message.Body;
myMessage.Text = message.Body;
if (_attachments != null)
{
foreach (var attachment in _attachments)
{
myMessage.AddAttachment(attachment.Value, attachment.Key);
}
}
return transportWeb.DeliverAsync(myMessage);
}
public Task SendAsync(IdentityMessage message, IEnumerable<KeyValuePair<string, Stream>> attachments)
{
var myMessage = new SendGridMessage() { From = new MailAddress(from) };
var credentials = new NetworkCredential(username, pswd);
var transportWeb = new Web(credentials);
myMessage.AddTo(message.Destination);
myMessage.Subject = message.Subject;
myMessage.Html = message.Body;
myMessage.Text = message.Body;
foreach (var attachment in attachments)
{
myMessage.AddAttachment(attachment.Value, attachment.Key);
}
return transportWeb.DeliverAsync(myMessage);
}
public Task SendAsync(IdentityMessage message, KeyValuePair<string, Stream> attachment)
{
var myMessage = new SendGridMessage() { From = new MailAddress(from) };
var credentials = new NetworkCredential(username, pswd);
var transportWeb = new Web(credentials);
myMessage.AddTo(message.Destination);
myMessage.Subject = message.Subject;
myMessage.Html = message.Body;
myMessage.Text = message.Body;
myMessage.AddAttachment(attachment.Value, attachment.Key);
return transportWeb.DeliverAsync(myMessage);
}
public void AddTo(string name,string mail)
{
_recipients = _recipients ?? new List<KeyValuePair<string, string>>();
_recipients.Add(new KeyValuePair<string, string>(name, string.Format("<{0}>", mail)));
}
public void AddAttachment(string name, Stream file)
{
_attachments = _attachments ?? new List<KeyValuePair<string, Stream>>();
_attachments.Add(new KeyValuePair<string, Stream>(name, file));
}
public void AddAttachment<T>(string name, T records)
{
var jsonSerialiser = new JavaScriptSerializer();
var json = jsonSerialiser.Serialize(records);
var bytes = Encoding.UTF8.GetBytes(json);
var ms = new MemoryStream(bytes);
ms.Flush();
ms.Seek(0, SeekOrigin.Begin);
_attachments = _attachments ?? new List<KeyValuePair<string, Stream>>();
_attachments.Add(new KeyValuePair<string, Stream>(name, ms));
}
}
Then can use it...
// use it directly
var emailService = new EmailService();
IdentityMessage msg = new IdentityMessage()
{
Destination = "test <test#domain.com>",
Subject = "Subject",
Body = "Body"
};
emailService.AddTo("Name1", "mail1#domain.com");
emailService.AddTo("Name2", "mail2#domain.com");
emailService.AddTo("Name3", "mail3#domaincom");
emailService.AddAttachment(filename", stream);
await emailService.SendAsync(msg);
// Or use it from UserManager
(UserManager.EmailService as EmailService).AddAttachment("name", yourStream);
await UserManager.SendEmailAsync("userid", "subject", "body");

Related

Errors in my generic class when trying to pass an interface in the constructor

Here I have 2 methods out of 15+ that calls a SOAP web service
public async void CallWebServiceA() {
var client = new WebServiceWRGClient();
var binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
binding.MaxReceivedMessageSize = Int32.MaxValue;
binding.MaxBufferSize = Int32.MaxValue;
var endpoint = new EndpointAddress("https://trustonline.delawarecpf.com/tows/webservicewrg.svc");
var channelFactory = new ChannelFactory<WebServiceWRG>(binding, endpoint);
var webService = channelFactory.CreateChannel();
var user = new GRACE_GRACES.User();
user.UserName = await webService.EncryptValueAsync("username");
user.Password = await webService.EncryptValueAsync("password12345");
var response = await client.ClaimSearchAsync(user, "", "", 12345, GRACE_GRACES.statuscode.NotSet, "");
}
public async void CallWebServiceB() {
var client = new WebServiceAWIClient();
var binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
binding.MaxReceivedMessageSize = Int32.MaxValue;
binding.MaxBufferSize = Int32.MaxValue;
var endpoint = new EndpointAddress("https://trustonline.delawarecpf.com/tows/webserviceawi.svc");
var channelFactory = new ChannelFactory<WebServiceAWI>(binding, endpoint);
var webService = channelFactory.CreateChannel();
var user = new ARM_ARMS.User();
user.UserName = await webService.EncryptValueAsync("username");
user.Password = await webService.EncryptValueAsync("password12345");
var response = await client.ClaimSearchAsync(user, "", "", 12345, ARM_ARMS.statuscode.NotSet, "");
}
I have this generic class below that I created so I don't have to make multiple redudant methods for each of my 15+ urls/web services all from the same provider, but I have some errors with passing in the 2nd parameter, which is an interface (from the web service reference).
public class WebService<T1, T2, T3> {
private T1 _client;
private T2 _interface;
private T3 _user;
public WebService(T1 wsClient, T2 wsInterface, T3 wsUser) {
_client = wsClient;
_interface = wsInterface;
_user = wsUser;
}
public void CallWebService(string url, string userName, string password) {
var client = _client;
var binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
var endpoint = new EndpointAddress(url);
var channelFactory = new ChannelFactory<_interface>(binding, endpoint);
var webService = channelFactory.CreateChannel();
var user = _user;
user.UserName = webService.EncryptValue(userName);
user.Password = webService.EncryptValue(password);
var response = client.ClaimSearch(user, "", "", 12345, GraceStatuscode.NotSet, "");
}
}
I'm calling it like this below but the interfaces WebServiceWRG and WebServiceAWI aren't recognized in the parameters when I create the instance or in the method when I create a new instance of ChannelFactory. I get a red line saying "the type of namespace name _interface could not be found" and in the line where I create the new class instance I get a red line for each interface parameter saying "'WebServiceWRG' is a type, which is not valid in the given context"
WebService someServiceA = new WebService(new WebServiceWRGClient(), WebServiceWRG, new GRACE_GRACES.User());
WebService someServiceB = new WebService(new WebServiceAWIClient(), WebServiceAWI, new ARM_ARMS.User());
UPDATED
Here is what I've tried below but still have errors:
user.UserName = webService.EncryptValue(userName); //TTwo does not contain a def for 'UserName', T does not contain a def for EncryptValue
public class Test {
public void TestWebService() {
var ws = new WebService<WebServiceWRG>();
ws.SearchClaim(new WebServiceWRGClient(), new GraceUser(), "url", "userName", "password");
}
}
public class WebService<T> {
public void SearchClaim<TOne, TTwo>(TOne entity1, TTwo entity2, string url, string userName, string password)
where TOne : class
where TTwo : class
{
var client = entity1;
var binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
var endpoint = new EndpointAddress(url);
var channelFactory = new ChannelFactory<T>(binding, endpoint);
var webService = channelFactory.CreateChannel();
var user = entity2;
user.UserName = webService.EncryptValue(userName); // username not found, encryptvalue not found
user.Password = webService.EncryptValue(password); // passwordnot found, encryptvalue not found
var response = client.ClaimSearch(user, "", "", 12345, GraceStatuscode.NotSet, ""); // claimsearch not found
}
}
Let's first acknowledge that the changes you're making are the right kind of changes. Working to avoid code duplication is important and can improve code quality significantly. Well done!
This could be a step in the right direction:
public class WebService<T1, T2, T3> {
private T1 _client;
private T3 _user;
//No T2
public WebService(T1 wsClient, T3 wsUser) {
_client = wsClient;
_user = wsUser;
//No interface
}
public void CallWebService(string url, string userName, string password) {
(...)
var channelFactory = new ChannelFactory<T2>(binding, endpoint); <---- use the T2 type
(...)
}
}
and then
var someServiceA = new WebService<WebServiceWRGClient, WebServiceWRG, Whatever_GRACE_GRACES_User()_returns_here>(new WebServiceWRGClient(), new GRACE_GRACES.User());
(Please note the var because the type is not WebService but WebService<X,Y,Z>)
Next step
I don't think you need generics for the client and the user, and the next step could be:
public class WebService<T> {
private Baseclassofallclients _client;
private UserTypeHere _user;
public WebService( Baseclassofallclients wsClient, UserTypeHere wsUser) {
_client = wsClient;
_user = wsUser;
//No interface
}
public void CallWebService(string url, string userName, string password) {
(...)
var channelFactory = new ChannelFactory<T>(binding, endpoint); <---- use the T type here
(...)
}
and then
var someServiceA = new WebService<WebServiceWRG>(new WebServiceWRGClient(), new GRACE_GRACES.User());
var someServiceB = new WebService<WebServiceAWI>(new WebServiceAWIClient(), new ARM_ARMS.User());

Not able to integrate account confirmation mail in asp.net mvc using sendgrid

I am following this article: https://learn.microsoft.com/en-us/aspnet/identity/overview/features-api/account-confirmation-and-password-recovery-with-aspnet-identity
But still not able to enable account confirmation email sending using asp.net MVC. It's showing
type or namespace web could not be found
public class EmailService : IIdentityMessageService
{
public Task SendAsync(IdentityMessage message)
{
return configSendGridasync(message);
}
private Task configSendGridasync(IdentityMessage message)
{
var myMessage = new SendGridMessage();
myMessage.AddTo(message.Destination);
myMessage.From = new MailAddress("xxxx#.com", "ABC");
myMessage.Subject = message.Subject;
myMessage.PlainTextContent = message.Body;
myMessage.HtmlContent = message.Body;
var credentials = new NetworkCredential(
ConfigurationManager.AppSettings["mailAccount"],
ConfigurationManager.AppSettings["mailPassword"]
);
// Create a Web transport for sending email.
var transportWeb = new System.Web(credentials);
// Send the email.
if (transportWeb != null)
{
return transportWeb.DeliverAsync(myMessage);
}
else
{
return Task.FromResult(0);
}
}
}

Identity EmailService not firing when attempting to send confirmation email

I've been modifying my implementation of ASP.Net Identity in my WebForms application. My modifications have caused my EmailService's SendAsync function to not fire and i'm not sure why. The only thing i can think of is how i am instantiating the UserManager on the register page. Before i was doing var manager = Context.GetOwinContext().GetUserManager(); and now i'm doing var manager = new DecisionLogicIdentity.ApplicationUserManager(userStore);. I am setting the EmailService in the Create function of the UserManager (manager.EmailService = new EmailService();). The SendGrid implementation was working prior to my change to how i call the UserManager. Does anyone have any idea what i am missing here?
Register.aspx.cs:
protected void CreateUser_Click(object sender, EventArgs e)
{
var context = HttpContext.Current.GetOwinContext().Get<DecisionLogicIdentity.ApplicationDbContext>();
var userStore = new DecisionLogicIdentity.UserStore<DecisionLogicIdentity.ApplicationUser>(context)
{
CompanyId = Int32.Parse(CompanyId.Text)
};
var manager = new DecisionLogicIdentity.ApplicationUserManager(userStore);
var signinManager = new DecisionLogicIdentity.ApplicationSignInManager(manager, HttpContext.Current.GetOwinContext().Authentication);
var provider = new DpapiDataProtectionProvider("SampleAppName");
manager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser, int>(provider.Create("SampleTokenName"));
var user = new DecisionLogicIdentity.ApplicationUser()
{
CompanyId = Int32.Parse(CompanyId.Text),
UserName = Email.Text,
Email = Email.Text,
IsExpired = false,
IsDeleted = false
};
IdentityResult result = manager.Create(user, Password.Text);
if (result.Succeeded)
{
user = userStore.FindByEmailAsync(user.Email).GetAwaiter().GetResult();
string code = manager.GenerateEmailConfirmationToken(user.Id);
string callbackUrl = IdentityHelper.GetUserConfirmationRedirectUrl(code, user.Id, Request);
manager.SendEmail(user.Id, "Confirm your account", "Please confirm your account by clicking here.");
//signinManager.SignIn(user, isPersistent: false, rememberBrowser: false);
//signinManager.PasswordSignIn(Email.Text, Password.Text, true, shouldLockout: true);
IdentityHelper.RedirectToReturnUrl(Request.QueryString["ReturnUrl"], Response);
}
else
{
ErrorMessage.Text = result.Errors.FirstOrDefault();
}
}
EmailService:
public class EmailService : IIdentityMessageService
{
public async Task SendAsync(IdentityMessage message)
{
await configSendGridasync(message);
}
// Use NuGet to install SendGrid (Basic C# client lib)
private async Task configSendGridasync(IdentityMessage message)
{
SendGridClient client = new SendGridClient(ConfigurationManager.AppSettings["SendGrid--APIKey"].ToString());
var msg = MailHelper.CreateSingleEmail(new SendGrid.Helpers.Mail.EmailAddress("someemail#somedomain.com"),
new SendGrid.Helpers.Mail.EmailAddress(message.Destination),
message.Subject,
message.Body,
message.Body);
msg.Attachments = null;
await client.SendEmailAsync(msg);
}
}
ApplicationUserManager:
public class ApplicationUserManager : UserManager<ApplicationUser, int>
{
public ApplicationUserManager(IUserStore<ApplicationUser, int> store)//, IIdentityMessageService emailService)
: base(store)
{
}
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var manager = new ApplicationUserManager(
new DecisionLogicIdentity.UserStore<ApplicationUser>(
context.Get<ApplicationDbContext>() as DatabaseContext));
// Configure validation logic for usernames
manager.UserValidator = new UserValidator<ApplicationUser, int>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
// Configure validation logic for passwords
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = true,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
};
// Configure user lockout defaults
manager.UserLockoutEnabledByDefault = true;
manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromSeconds(Int32.Parse(ConfigurationManager.AppSettings["UserLockoutMinutes"].ToString()));
manager.MaxFailedAccessAttemptsBeforeLockout = Int32.Parse(ConfigurationManager.AppSettings["UserMaxLoginAttempts"].ToString());
// Register two factor authentication providers. This application uses Phone and Emails as a step of receiving a code for verifying the user
// You can write your own provider and plug it in here.
manager.RegisterTwoFactorProvider("Phone Code", new PhoneNumberTokenProvider<ApplicationUser, int>
{
MessageFormat = "Your security code is {0}"
});
manager.RegisterTwoFactorProvider("Email Code", new EmailTokenProvider<ApplicationUser, int>
{
Subject = "Security Code",
BodyFormat = "Your security code is {0}"
});
manager.EmailService = new EmailService();
manager.SmsService = new SmsService();
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
var provider = new DpapiDataProtectionProvider("SampleAppName");
manager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser, int>(provider.Create("SampleTokenName"));
}
return manager;
}
}
In case anyone needs the answer to this, I was able to get this functioning by modifying my UserManager like so:
public class ApplicationUserManager : UserManager<ApplicationUser, int>
{
public ApplicationUserManager(IUserStore<ApplicationUser, int> store, IIdentityMessageService emailService)
: base(store)
{
this.EmailService = emailService;
}
...
And when instantiating the UserManager:
var manager = new ApplicationUserManager(userStore, new EmailService());

ASP.NET Identity UserManager IIdentityMessageService pass extra parameters to SendAsync

I've successfully created custom email service provider for UserManager by extending IIdentityMessageService:
public class ExchangeEmailService : IIdentityMessageService
{
public async Task SendAsync(IdentityMessage message)
{
using (var client = new SmtpClient())
{
client.Host = "mail.example.com";
client.DeliveryMethod = SmtpDeliveryMethod.Network;
client.UseDefaultCredentials = false;
client.Credentials = new NetworkCredential(#"noreply", "P#ssw0rd");
var from = new MailAddress("no-reply#example.com");
var to = new MailAddress(message.Destination);
var mailMessage = new MailMessage(from, to)
{
Subject = message.Subject,
Body = message.Body,
IsBodyHtml = true
};
await client.SendMailAsync(mailMessage);
}
}
}
Inside UserManager's Create I'm creating new instance of my service and assigning it to EmailService:
userManager.EmailService = new ExchangeEmailService();
This all works fine, but I have requirement to send emails from different email addresses, based on place this is called from, unfortunately UserManager.SendEmailAsync isn't allowing me to pass anything except userId, subject and body.
Ideally I'd like to be able to specify email address when calling SendEmailAcync or enum value.
I've searched in UserManager source and I thought about creating custom SendEmailAsync implementation, but I'd have to change multiple places - IIdentityMessageService, UserManager.
I know I can create new interface and build my email sending class on it, but I'd like to keep changes to minimum.
What I'd like to get:
I'd like to have enum containing names of mailboxes:
public enum Mailboxes
{
Noreply = 0,
Service = 1,
Contact = 2
}
and be able to call SendEmailAsync with that additional parameter:
await UserManager.SendEmailAsync(user.Id, "Account created", "Nice email content.", Mailboxes.Noreply );
I'm aware that I can set different title and use same email address, but this is requirement I got.
How can I do that?
A hack would be to create a model to store what you want sent to the service
public class MessageBody {
public string Body { get; set; }
public MailBoxes Source { get; set; }
}
and then serialize it (JSON maybe) as the body parameter of the SendEmailAsync method call.
var message = new MessageBody {
Body = "Nice email content.",
Source = Mailboxes.Noreply
};
var body = JsonConvert.SerializeObject(message);
await UserManager.SendEmailAsync(user.Id, "Account created", body);
The service would then deserialize the model, construct the email with the custom info and then send the email.
public class ExchangeEmailService : IIdentityMessageService {
readonly IMailBoxProvider provider;
public ExchangeEmailService(IMailBoxProvider provider) {
this.provider = provider;
}
public async Task SendAsync(IdentityMessage message) {
using (var client = new SmtpClient()) {
client.Host = "mail.example.com";
client.DeliveryMethod = SmtpDeliveryMethod.Network;
client.UseDefaultCredentials = false;
client.Credentials = new NetworkCredential(#"noreply", "P#ssw0rd");
//Get the body and from address
var fromEmailAddress = "default-email#example.com";
var body = message.Body;
try {
var msg = JsonConvert.DeserializeObject<MessageBody>(body);
if(msg != null) {
body = msg.Body;
fromEmailAddress = provider.GetMailbox(msg.Source);
}
} catch { }
var from = new MailAddress(fromEmailAddress);
var to = new MailAddress(message.Destination);
var mailMessage = new MailMessage(from, to)
{
Subject = message.Subject,
Body = body,
IsBodyHtml = true
};
await client.SendMailAsync(mailMessage);
}
}
}
And just make sure to assigning it to UserManager.EmailService:
userManager.EmailService = new ExchangeEmailService(new MailBoxProvider());

Send mail from my SMTP Client in xamarin

I try send mail with my smtp client but i dont have exception and mail doesn't recieved.
public void SendSMTPMail(string from, string to, string subject, string body)
{
var smtp_client = new SmtpClient("mail.mydomain.gr",25);
smtp_client.UseDefaultCredentials = false;
smtp_client.EnableSsl = false;
smtp_client.Credentials = new NetworkCredential("noreply#mydomain.gr", "mypass");
ServicePointManager.ServerCertificateValidationCallback = (s, certificate, chain, sslPolicyErrors) => true;
var msg = new MailMessage(from, to );
msg.Subject = subject;
msg.Body = body;
smtp_client.SendAsync(msg , string.Empty);
}
i use breakpoint and i find some info
smtp_client.ServicePoint System.NotImplementException: The request feature is not implemented
but i use this code with another smtp and works fine. Any help ?
As an alternative, you could use my MailKit library to send mail using Xamarin.iOS/Android/Mac.
public void SendSMTPMail(string from, string to, string subject, string body)
{
var message = new MimeMessage ();
var builder = new BodyBuilder ();
message.From.Add (InternetAddress.Parse (from));
message.To.Add (InternetAddress.Parse (to));
message.Subject = subject;
builder.TextBody = body;
message.Body = builder.ToMessageBody ();
using (var client = new SmtpClient ()) {
client.ServerCertificateValidationCallback = (s, certificate, chain, sslPolicyErrors) => true;
client.Connect ("mail.mydomain.gr", 25, false);
client.Authenticate ("noreply#mydomain.gr", "mypass");
client.Send (message);
client.Disconnect (true);
}
}
It seems like you can't use the System.Net.Mail.SmtpClient in Xamarin.
Instead you should use the the mail service with native implementation. Tiny example here. The Forms code:
public abstract class EmailService
{
public static readonly Lazy<EmailService> Instance = new Lazy<EmailService>(() => DependencyService.Get<EmailService>());
public abstract bool CanSend { get; }
public abstract void ShowDraft(string subject, string body, bool html, string to, byte[] screenshot = null);
public abstract void ShowDraft(string subject, string body, bool html, string[] to, string[] cc, string[] bcc, byte[] screenshot = null);
}
The native iOS code:
public class EmailServiceIos : EmailService
{
public override bool CanSend
{
get
{
return MFMailComposeViewController.CanSendMail;
}
}
public override void ShowDraft(
string subject,
string body,
bool html,
string[] to,
string[] cc,
string[] bcc,
byte[] screenshot = null)
{
var mailer = new MFMailComposeViewController();
mailer.SetMessageBody(body ?? string.Empty, html);
mailer.SetSubject(subject ?? string.Empty);
mailer.SetCcRecipients(cc);
mailer.SetToRecipients(to);
mailer.Finished += (s, e) => ((MFMailComposeViewController)s).DismissViewController(true, () => { });
if (screenshot != null)
{
mailer.AddAttachmentData(NSData.FromArray(screenshot), "image/png", "screenshot.png");
}
UIViewController vc = UIApplication.SharedApplication.KeyWindow.RootViewController;
while (vc.PresentedViewController != null)
{
vc = vc.PresentedViewController;
}
vc.PresentViewController(mailer, true, null);
}
public override void ShowDraft(string subject, string body, bool html, string to, byte[] screenshot = null)
{
this.ShowDraft(subject, body, html, new[] { to }, new string[] { }, new string[] { }, screenshot);
}
}
And invoke the whole thing from the Forms code like:
var emailService = DependencyService.Get<EmailService>();
if (emailService.CanSend)
{
emailService.ShowDraft(
"Your caption",
"Your text",
true,
"your#ddress.com");
}

Categories