xamarin forms: Calling phone and sending email (IOS, Android and UWP) - email

Currently using following code for calling and email features, but it is only working in Android and not working in IOS. Also, I need these features in UWP.
For call:
string phoneno = "1234567890";
Device.OpenUri(new Uri("tel:" + phoneno));
For mail:
string email = "sreejithsree139#gmail.com";
Device.OpenUri(new Uri("mailto:" + email ));
Any package available for this?

Xamarin.Essentials (Nuget) is available as a preview package and contains functionality to both open the default mail app and attach information such as the recipients, subject and the body as well as open the phone dialer with a certain number.
There is also a blog post about Xamarin.Essentials available on blog.xamarin.com.
Edit:
As for your mail issue, Xamarin.Essentials expects an array of strings as recipients so you are able to send mail to multiple people at once. Just pass a string array with one single value.
var recipients = new string[1] {"me#watercod.es"};
If you're using the overload that expects an EmailMessage instance, you are supposed to pass a List of string objects.
In that case, the following should work:
var recipients = new List<string> {"me#watercod.es"};

Updating the complete code for calling and mailing features using Xamarin.Essentials, this might help others.
For call:
try
{
PhoneDialer.Open(number);
}
catch (ArgumentNullException anEx)
{
// Number was null or white space
}
catch (FeatureNotSupportedException ex)
{
// Phone Dialer is not supported on this device.
}
catch (Exception ex)
{
// Other error has occurred.
}
For Mail:
List<string> recipients = new List<string>();
string useremail = email.Text;
recipients.Add(useremail);
try
{
var message = new EmailMessage
{
//Subject = subject,
//Body = body,
To = recipients
//Cc = ccRecipients,
//Bcc = bccRecipients
};
await Email.ComposeAsync(message);
}
catch (Exception ex)
{
Debug.WriteLine("Exception:>>"+ex);
}

Hello to make a call in UWP:
if (Windows.Foundation.Metadata.ApiInformation.IsTypePresent("Windows.ApplicationModel.Calls.PhoneCallManager"))
{
Windows.ApplicationModel.Calls.PhoneCallManager.ShowPhoneCallUI("123", "name to call");
}
To send a Text:
private async void ComposeSms(Windows.ApplicationModel.Contacts.Contact recipient,
string messageBody,
StorageFile attachmentFile,
string mimeType)
{
var chatMessage = new Windows.ApplicationModel.Chat.ChatMessage();
chatMessage.Body = messageBody;
if (attachmentFile != null)
{
var stream = Windows.Storage.Streams.RandomAccessStreamReference.CreateFromFile(attachmentFile);
var attachment = new Windows.ApplicationModel.Chat.ChatMessageAttachment(
mimeType,
stream);
chatMessage.Attachments.Add(attachment);
}
var phone = recipient.Phones.FirstOrDefault<Windows.ApplicationModel.Contacts.ContactPhone>();
if (phone != null)
{
chatMessage.Recipients.Add(phone.Number);
}
await Windows.ApplicationModel.Chat.ChatMessageManager.ShowComposeSmsMessageAsync(chatMessage);
}
as found in Microsoft documentation here: Compose SMS documentation
==> So you can make (If not already done) a shared service interface in your Xamarin app, then the implementation with these codes in your UWP app...
To send an email:
To send an email in UWP, you can refer to the Microsoft documentation too:
Send Email documentation (UWP)
Using a plugin
Else you can use a Xamarin plugin:
documentation: Xamarin cross messaging plugin
Nuget: Nuget plugin package

In our app, we are doing the phone calling with a DependencyService.
Therefore in our PCL, we have
public interface IPhoneCall
{
void Call(string number);
}
On the iOS side, the following method does the calling:
public void Call(string number)
{
if (string.IsNullOrEmpty(number))
return;
var url = new NSUrl("tel:" + number);
if (!UIApplication.SharedApplication.OpenUrl(url))
{
var av = new UIAlertView("Error",
"Your device does not support calls",
null,
Keys.Messages.BUTTON_OK,
null);
av.Show();
}
}

If don't want to wait for the Xamarin essentials that is still in pre-release as of today, you can use this open source plugin. It works on iOS, Android and UWP. There is a sample from the github documentation :
// Make Phone Call
var phoneDialer = CrossMessaging.Current.PhoneDialer;
if (phoneDialer.CanMakePhoneCall)
phoneDialer.MakePhoneCall("+27219333000");
// Send Sms
var smsMessenger = CrossMessaging.Current.SmsMessenger;
if (smsMessenger.CanSendSms)
smsMessenger.SendSms("+27213894839493", "Well hello there from Xam.Messaging.Plugin");
var emailMessenger = CrossMessaging.Current.EmailMessenger;
if (emailMessenger.CanSendEmail)
{
// Send simple e-mail to single receiver without attachments, bcc, cc etc.
emailMessenger.SendEmail("to.plugins#xamarin.com", "Xamarin Messaging Plugin", "Well hello there from Xam.Messaging.Plugin");
// Alternatively use EmailBuilder fluent interface to construct more complex e-mail with multiple recipients, bcc, attachments etc.
var email = new EmailMessageBuilder()
.To("to.plugins#xamarin.com")
.Cc("cc.plugins#xamarin.com")
.Bcc(new[] { "bcc1.plugins#xamarin.com", "bcc2.plugins#xamarin.com" })
.Subject("Xamarin Messaging Plugin")
.Body("Well hello there from Xam.Messaging.Plugin")
.Build();
emailMessenger.SendEmail(email);
}

Related

How to implement Microsoft Graph deferred sending

I am trying to implement a deferred sending function to my site which currently sends email via Microsoft Graph. I have found some articles about SingleValueLegacyExtendedProperty being used to defer sending, but so far has been unsuccessful with it.
My current code just ignores the deferred sending time and sends the email immediately.
var message = new Message
{
Subject = Subject,
Body = new ItemBody
{
ContentType = BodyType.Html,
Content = bodyText
},
ToRecipients = new List<Recipient>()
{
new Recipient
{
EmailAddress = new EmailAddress
{
Address = recipient
}
}
},
};
message.SingleValueExtendedProperties = new MessageSingleValueExtendedPropertiesCollectionPage
{
new SingleValueLegacyExtendedProperty()
{
Id = "SystemTime 0x3FEF",
Value = DateTimeToSend.ToString("o")
}
};
var saveToSentItems = true;
await graphServiceClient.Me
.SendMail(message, saveToSentItems)
.Request()
.PostAsync();
In this article they suggest that the ID should be String {8ECCC264-6880-4EBE-992F-8888D2EEAA1D} Name pidTagDeferredSendTime when passing as JSON but it looks like that was not successful for other. I checked and can confirm that it did not work for me either.
Its important that the DateTime that you want the message to be sent is in UTC eg
"value": "2022-08-01T23:39:00Z"
Using local time won't work as Exchange does everything in UTC

Leverage Groups for Workflow Approvals

We're implementing a new workflow (combined with staging task sync) on an existing website where we would like to notify all members that "own" that particular section/content to approve changes.
One of the options is to have multiple roles and their corresponding workflows configured for their role and scope, but this seems like overkill - at least for us, as currently one single role is set for approvals (and another for editors)
However I've recently come across this new page property:
And have a couple of questions:
Can regular CMS users (without membership) be part of a group?
Would we be able to leverage this group for the workflow's email notifications instead of the roles? E.g. email to everyone in the owner group when a page was sent for approval.
Is this option by default inherited from the parent page when a new one is created or does it need to be set individually for each page?
We have a Kentico 11 EMS license and working on an advanced workflow, therefore custom code is possible.
Can regular CMS users (without membership) be part of a group?
- why don't you use roles here?
Would we be able to leverage this group for the workflow's email
notifications instead of the roles? E.g. email to everyone in the
owner group when a page was sent for approval.
- you'll need to customize workflow manager class, but in general yes, it is possible. You could find an inspiration in this post
Is this option by default inherited from the parent page when a new
one is created or does it need to be set individually for each page?
- Use a macro to default the field. If you populate it with anything else then the new values will be saved.
Sample code snippet for Custom Global Event Handler for Workflow steps i.e., Reject and Approve steps.
using CMS;
using CMS.Base;
using CMS.DataEngine;
using CMS.DocumentEngine;
using CMS.EmailEngine;
using CMS.EventLog;
using CMS.Helpers;
using CMS.MacroEngine;
using CMS.SiteProvider;
using CMS.WorkflowEngine;
using System;
// Registers the custom module into the system
[assembly: RegisterModule(typeof(CustomWorkflowEvent))]
public class CustomWorkflowEvent : CMSModuleLoader
{
// Module class constructor, the system registers the module under the name "CustomInit"
public CustomWorkflowEvent()
: base("CustomInit")
{
}
// Contains initialization code that is executed when the application starts
protected override void OnInit()
{
base.OnInit();
// Assigns custom handlers to events
// WorkflowEvents.Approve.After += WorkFlow_Event_After();
WorkflowEvents.Reject.After += WorkFlow_Event_After;
WorkflowEvents.Approve.After += Approve_After;
// WorkflowEvents.Action.After += WorkFlowAction_Event_After;
}
private void Approve_After(object sender, WorkflowEventArgs e)
{
try
{
WorkflowStepInfo wsi = e.PreviousStep;
if (wsi != null)
{
CMS.WorkflowEngine.Definitions.SourcePoint s = wsi.GetSourcePoint(Guid.NewGuid());
//Make sure it was an approval (standard) step
var approvers = WorkflowStepInfoProvider.GetUsersWhoCanApprove(wsi, null, SiteContext.CurrentSiteID, "UserID = " + CMSActionContext.CurrentUser.UserID, "UserID", 0, "Email, FullName, Username");
EventLogProvider.LogInformation("Approvers Data", "Approvers Data", approvers.ToString());
if (approvers != null)
{
//Loop through the approvers
string siteName = null;
SiteInfo si = SiteInfoProvider.GetSiteInfo(SiteContext.CurrentSiteID);
if (si != null)
{
siteName = si.SiteName;
}
EmailTemplateInfo eti = EmailTemplateProvider.GetEmailTemplate("Workflow.Rejected", SiteContext.CurrentSiteName);
MacroResolver mcr = MacroResolver.GetInstance();
EmailMessage message = new EmailMessage();
// Get sender from settings
message.EmailFormat = EmailFormatEnum.Both;
message.From = eti.TemplateFrom;
// Do not send the e-mail if there is no sender specified
if (message.From != "")
{
// Initialize message
// message.Recipients = strRecipientEmail;
message.Subject = eti.TemplateSubject;
// Send email via Email engine API
// EmailSender.SendEmailWithTemplateText(SiteContext.CurrentSiteName, message, eti, mcr, true);
}
}
}
}
catch (Exception ex)
{
throw;
}
}
private void WorkFlow_Event_After(object sender, WorkflowEventArgs e)
{
try
{
WorkflowStepInfo wsi = e.PreviousStep;
if (wsi != null)
{
CMS.WorkflowEngine.Definitions.SourcePoint s = wsi.GetSourcePoint(Guid.NewGuid());
//Make sure it was an approval (standard) step
var approvers = WorkflowStepInfoProvider.GetUsersWhoCanApprove(wsi, null, SiteContext.CurrentSiteID, "UserID = " + CMSActionContext.CurrentUser.UserID, "UserID", 0, "Email, FullName, Username");
EventLogProvider.LogInformation("Approvers Data", "Approvers Data", approvers.ToString());
if (approvers != null)
{
//Loop through the approvers
string siteName = null;
SiteInfo si = SiteInfoProvider.GetSiteInfo(SiteContext.CurrentSiteID);
if (si != null)
{
siteName = si.SiteName;
}
EmailTemplateInfo eti = EmailTemplateProvider.GetEmailTemplate("Workflow.Rejected", SiteContext.CurrentSiteName);
MacroResolver mcr = MacroResolver.GetInstance();
EmailMessage message = new EmailMessage();
// Get sender from settings
message.EmailFormat = EmailFormatEnum.Both;
message.From = eti.TemplateFrom;
// Do not send the e-mail if there is no sender specified
if (message.From != "")
{
// Initialize message
// message.Recipients = strRecipientEmail;
message.Subject = eti.TemplateSubject;
// Send email via Email engine API
// EmailSender.SendEmailWithTemplateText(SiteContext.CurrentSiteName, message, eti, mcr, true);
}
}
}
}
catch (Exception ex)
{
throw;
}
}
}
Hope Helps you.
Can regular CMS users (without membership) be part of a group?
It is not part of CMS users. Groups are coming from Groups Application.
GROUP: Allows you to manage user groups. Groups are a social networking
feature enabling users to find information and communicate according
to shared interests.
Would we be able to leverage this group for the workflow's email notifications instead of the roles? E.g. email to everyone in the owner group when a page was sent for approval.
No
Is this option by default inherited from the parent page when a new one is created or does it need to be set individually for each page?
No

I want to trigger mail when the bot says that it has no answer

I want to trigger mail when the bot says that it has no answer.
I'm using MS bot framework SDk4, and using LUIS and QnA maker also, when the bot reached the to a point where it says that it has no answer , we want a mail to be triggered or add a new item in the sharepoint
If you want to add a no answer to a SharePoint List, I managed to get it working using the csom-node package and Bot Framework v4 / NodeJS. Granted, it's not the most elegant solution, but it works.
Bot.JS
const csomapi = require('../node_modules/csom-node');
settings = require('../settings').settings;
// Set CSOM settings
csomapi.setLoaderOptions({url: settings.siteurl});
Bit further down the page...
// If no answers were returned from QnA Maker, reply with help.
} else {
await context.sendActivity("Er sorry, I don't seem to have an answer.");
console.log(context.activity.text);
var response = context.activity.text;
var authCtx = new AuthenticationContext(settings.siteurl);
authCtx.acquireTokenForApp(settings.clientId, settings.clientSecret, function (err, data) {
var ctx = new SP.ClientContext("/sites/yoursite"); //set root web
authCtx.setAuthenticationCookie(ctx); //authenticate
var web = ctx.get_web();
var list = web.get_lists().getByTitle('YourList');
var creationInfo = new SP.ListItemCreationInformation();
var listItem = list.addItem(creationInfo);
listItem.set_item('Title', response);
listItem.update();
ctx.load(listItem);
ctx.executeQueryAsync();
});
}
Proactive Messaging doesn't really work for email (to prevent spam), so you're better off not using the Bot Framework SDK for the email portion. #Baruch's link, How to send email in ASP.NET C# is good if you're using the C# SDK. Here's one for sending emails in Node.
All you have to do is send the email when QnA Maker doesn't return any results. In this sample, you would do so here:
if (response != null && response.Length > 0)
{
await turnContext.SendActivityAsync(MessageFactory.Text(response[0].Answer), cancellationToken);
}
else
{
await turnContext.SendActivityAsync(MessageFactory.Text("No QnA Maker answers were found."), cancellationToken);
// Add code that sends Notification Email
}
That being said, if you'd like to try a semi-proactive route, you can enable the Email Channel in your bot, then use this:
if (response != null && response.Length > 0)
{
await turnContext.SendActivityAsync(MessageFactory.Text(response[0].Answer), cancellationToken);
}
else
{
await turnContext.SendActivityAsync(MessageFactory.Text("No QnA Maker answers were found."), cancellationToken);
MicrosoftAppCredentials.TrustServiceUrl(#"https://email.botframework.com/", DateTime.MaxValue);
var user = new ChannelAccount(name: "MyUser", id: "<notified Email Address>");
var parameters = new ConversationParameters()
{
Members = new ChannelAccount[] { user },
Bot = turnContext.Activity.Recipient
};
var connector = new ConnectorClient(new Uri("https://email.botframework.com"), "<appId>", "<appPassword>");
var conversation = await connector.Conversations.CreateConversationAsync(parameters);
var activity = MessageFactory.Text("This is a notification email");
activity.From = parameters.Bot;
activity.Recipient = user;
await connector.Conversations.SendToConversationAsync(conversation.Id, activity);
}
The catch is that <notified Email Address> has to send a message to the bot before any notifications will work. If it doesn't, it will return a 401: Unauthorized error. Again, I don't recommend this route.
Note: If you're using the Dispatch sample, you'd place the code here:
private async Task ProcessSampleQnAAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
_logger.LogInformation("ProcessSampleQnAAsync");
var results = await _botServices.SampleQnA.GetAnswersAsync(turnContext);
if (results.Any())
{
await turnContext.SendActivityAsync(MessageFactory.Text(results.First().Answer), cancellationToken);
}
else
{
// PLACE IT HERE
await turnContext.SendActivityAsync(MessageFactory.Text("Sorry, could not find an answer in the Q and A system."), cancellationToken);
}
}

Email Sending .NET Core

I just want to have a function, which will be sending mail to different people.
I have written a Email Service, just like this:
public void SendEMail(EMail mail)
{
var body = JObject.FromObject(settings);
body.Merge(JObject.FromObject(mail));
GlobalTelemetry.TrackDependency("E-Mail Service", "Send", () =>
{
var result = AsyncHelper.RunSync(() => httpClient.PostAsync(azureFunctionUrl,
new StringContent(body.ToString(), Encoding.UTF8, "application/json")));
if (!result.IsSuccessStatusCode)
{
throw new InvalidOperationException(
$"E-Mail Service failed to send mails. Http Status {result.StatusCode}. Please check if the Url '{azureFunctionUrl}' is correct. Env: {Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}");
}
});
}
and by every other functions, which want to use this mail service, do just like this:
SendMail = mail =>
{
var fireAndForgetTask = Task.Run(() =>
{
try
{
eMailService.SendEMail(mail);
}
catch (Exception e)
{
Log.Fatal(e, $"Fail to send E-Mail to: '{mail.To}'");
}
});
};
it does work, but fireAndForgetTask is not used, I have no idea how to do it better.
Is it necessary to write a Email Job???
Some better suggestion is welcome :-)
I had a similar issue in my app for sending a password reset email, I did not want the delay of waiting for the email to send because if a hacker is trying to find out if an account exists for an email address the delay could indicate that the account does exist. So I wanted to fire and forget the email with no delay.
So I implemented a simple extension method named forget like this in a separate static class based on this so question:
public static void Forget(this Task task)
{
}
and then I send my email async and use that .Forget extension to not wait for it like this:
emailSender.SendPasswordResetEmailAsync(
Site,
model.Email,
sr["Reset Password"],
resetUrl).Forget();
note that I am not using "await" when I call this async method and the .Forget gets rid of the warning that would otherwise appear in Visual Studio when calling an async method without the await keyword

How to retrieve Someone's Avatar/Photo with agsXmpp

this is what I have so far:
void xmppConnection_OnReadXml(object sender, string xml)
{
if (xml.Contains(XmlTags.PhotoOpen))
{
int startIndex = xml.IndexOf(XmlTags.PhotoOpen) + XmlTags.PhotoOpen.Length;
int length = xml.IndexOf(XmlTags.PhotoClose) - startIndex;
string photoHash = xml.Substring(startIndex, length);
}
}
I guess I can't undo the hash, but I want to the get a person's avatar/photo. How do I achieve this?
You need to handle the VCard events and responses from XMPP connection:
private void vcardToolStripMenuItem_Click(object sender, EventArgs e)
{
RosterNode node = rosterControl.SelectedItem();
if (node != null)
{
frmVcard f = new frmVcard(node.RosterItem.Jid, XmppCon);
f.Show();
}
}
The above is from the miniclient solution example from the AGSXMPP download. Note, it happens when a user request a VCARD for a user. You can initiate that request whenever you want, however.
private void VcardResult(object sender, IQ iq, object data)
{
if (InvokeRequired)
{
// Windows Forms are not Thread Safe, we need to invoke this :(
// We're not in the UI thread, so we need to call BeginInvoke
BeginInvoke(new IqCB(VcardResult), new object[] { sender, iq, data });
return;
}
if (iq.Type == IqType.result)
{
Vcard vcard = iq.Vcard;
if (vcard!=null)
{
txtFullname.Text = vcard.Fullname;
txtNickname.Text = vcard.Nickname;
txtBirthday.Text = vcard.Birthday.ToString();
txtDescription.Text = vcard.Description;
Photo photo = vcard.Photo;
if (photo != null)
picPhoto.Image = vcard.Photo.Image;
}
}
}
That is what happens when someone requests the VCARD information from XMPP and the IQ type matches the proper data. You can thenpull the photo from vcard.Photo.
You trigger the pull with:
VcardIq viq = new VcardIq(IqType.get, new Jid(jid.Bare));
con.IqGrabber.SendIq(viq, new IqCB(VcardResult), null);
The first line there is the request to the XMPP server, that the VCARD form uses to request user information.
The second line there, sets up another grabber (callback of sorts), that the form uses to wait for the information to arrive, and then parse out the necessary information. IN this case, the grabber is in a new form, so that the main application doesn't have to worry about parsing that information.
You can look at the entire source by extracting the AGSXMPP zip file to your local drive, and looking in the Samples\VS2008\miniclient folder.
You can click link:http://forum.ag-software.de/thread/192-How-to-save-vcard-data