Bot builder : Adaptive cards - call a method when submitting - azure

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;
}
}

Related

Send Push Notifications with Image Payload using Firebase Admin SDK

I am trying to send push notifications through the firebase admin sdk, but the image somehow is not displayed in the push notification.
What's weird is that when I use an invalid key in the notifications object (like image) I get an error. So I assume I got the right keys specified. Documentation for the Notification can be found here.
The following code successfully sends a push notification but there is no image displayed on the users phone:
const admin = require('firebase-admin');
const app = admin.initializeApp({...}); // authenticated with credentials json file
await app.messaging().sendMulticast({
notification: {
title: "hello User",
body: "This is a push notification with an image",
imageUrl: "https://example.com/myPublicImage.png",
},
tokens: ["device_token_1", "device_token_2","..."]
});
Change imageUrl key to image in notification being sent by Firebase Admin SDK. I checked with imageUrl key, it does not work, rather,
it gives null to remoteMessage.getNotification().getImageUrl() in app.
In node.js server, you can create post request to send the Firebase message using Firebase Admin SDK:
Request.post({
"headers": {"Authorization":auth_key_string, "content-type": "application/json" },
"url": "https://fcm.googleapis.com/fcm/send",
"body": JSON.stringify({
"registration_ids" :receiver_token ,
"notification" : {
"title": title,
"body" : message,
"image":imageUrlVal
}
})
}, (error, response, body) => {
if(error) {
return console.dir(error);
}
console.dir(JSON.parse(body));
});
Now, handle this message from FirebaseActivity in Android App code.
In onMessageReceived method add this lines.
if (remoteMessage.getNotification() != null) {
// Since the notification is received directly from
// FCM, the title and the body can be fetched
// directly as below.
Log.d(TAG, "Message Received: " + "YES");
Bitmap bitmap = null;
try {
bitmap = getBitmapfromUrl(remoteMessage.getNotification().getImageUrl().toString());
} catch (Exception e) {
e.printStackTrace(); }
try {
showNotification(
remoteMessage.getNotification().getTitle(),
remoteMessage.getNotification().getBody(),bitmap );
} catch (IOException e) {
e.printStackTrace();
}
}
Define getBitmapfromUrl() as below:
public Bitmap getBitmapfromUrl(String imageUrl) {
try {
URL url = new URL(imageUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.connect();
InputStream input = connection.getInputStream();
return BitmapFactory.decodeStream(input);
} catch (Exception e) {
Log.e("awesome", "Error in getting notification image: " + e.getLocalizedMessage());
return null;
}
}
showNotification() can be defined as:
public void showNotification(String title,
String message, Bitmap bitmap) throws IOException {
Intent intent
= new Intent(this, NextPageActivity.class);
// Assign channel ID
String channel_id = "notification_channel";
// Here FLAG_ACTIVITY_CLEAR_TOP flag is set to clear
// the activities present in the activity stack,
// on the top of the Activity that is to be launched
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
// Pass the intent to PendingIntent to start the
// next Activity
PendingIntent pendingIntent
= PendingIntent.getActivity(
this, 0, intent,
PendingIntent.FLAG_ONE_SHOT);
NotificationCompat.Builder builder
= new NotificationCompat
.Builder(getApplicationContext(), channel_id)
.setSmallIcon(R.drawable.app_icon)
.setAutoCancel(true)
.setVibrate(new long[]{1000, 1000, 1000, 1000, 1000})
.setOnlyAlertOnce(true)
.setContentIntent(pendingIntent);
// A customized design for the notification can be
// set only for Android versions 4.1 and above. Thus
// condition for the same is checked here.
if (Build.VERSION.SDK_INT
>= Build.VERSION_CODES.JELLY_BEAN) {
Log.d(TAG, "Higher Version: ");
builder = builder.setContent(
getCustomDesign(title, message));
if (bitmap != null) {
builder.setLargeIcon(bitmap)
.setStyle(
new NotificationCompat.BigPictureStyle()
.bigPicture(bitmap)
.bigLargeIcon(null)
.setBigContentTitle(title)
.setSummaryText(message)
);
}
} // If Android Version is lower than Jelly Beans,
// customized layout cannot be used and thus the
// layout is set as follows
else {
Log.d(TAG, "Lower Version: ");
builder = builder.setContentTitle(title)
.setContentText(message)
.setSmallIcon(R.drawable.app_icon);
}
// Create an object of NotificationManager class to
// notify the
// user of events that happen in the background.
NotificationManager notificationManager
= (NotificationManager) getSystemService(
Context.NOTIFICATION_SERVICE);
// Check if the Android Version is greater than Oreo
if (Build.VERSION.SDK_INT
>= Build.VERSION_CODES.O) {
NotificationChannel notificationChannel
= new NotificationChannel(
channel_id, "web_app",
NotificationManager.IMPORTANCE_HIGH);
notificationManager.createNotificationChannel(
notificationChannel);
}
notificationManager.notify(0, builder.build());
}
getCustomDesign() can be defined as:
private RemoteViews getCustomDesign(String title,
String message) {
#SuppressLint("RemoteViewLayout") RemoteViews remoteViews =
new RemoteViews(getApplicationContext().getPackageName(),
R.layout.notification);
remoteViews.setTextViewText(R.id.title, title);
remoteViews.setTextViewText(R.id.message, message);
remoteViews.setTextViewText(R.id.note_button, "Reply");
remoteViews.setImageViewResource(R.id.icon, R.drawable.app_icon);
//remoteViews.setImageViewResource(R.id.message_image, R.drawable.app_icon);
return remoteViews;
}

Support for Multilingual feature using Adaptive Cards in Microsoft Bot framework

I am testing the Multilingual Bot downloaded from Microsoft bot framework. While doing so, some of my content are not getting translated.
Image link
You can see the following code where I have inserted few lines that asks the user if there is anything I can help ? This gets translated in to tthe language selected by the User. But, the content in CardAction() object Title 'Yes' and 'No' are not getting translated.
How to handle such translations in the middleware ?
bool translate = userLanguage != TranslationSettings.DefaultLanguage;
if (IsLanguageChangeRequested(turnContext.Activity.Text))
{
await _accessors.LanguagePreference.SetAsync(turnContext, turnContext.Activity.Text);
var reply = turnContext.Activity.CreateReply($"Your current language code is: {turnContext.Activity.Text}");
await turnContext.SendActivityAsync(reply, cancellationToken);
await _accessors.UserState.SaveChangesAsync(turnContext, false, cancellationToken);
// This content is getting partially translated.
var newRply = turnContext.Activity.CreateReply("Is there anything else I can help you with?");
newRply.SuggestedActions = new SuggestedActions()
{
Actions = new List<CardAction>()
{
// The title is not getting translated
new CardAction() { Title = "Yes", Type = ActionTypes.PostBack, Value = Spanish },
// The title is not getting translated
new CardAction() { Title = "No", Type = ActionTypes.PostBack, Value = English },
},
};
await turnContext.SendActivityAsync(newRply);
}
else
{
var reply = turnContext.Activity.CreateReply("Choose your language:");
reply.SuggestedActions = new SuggestedActions()
{
Actions = new List<CardAction>()
{
new CardAction() { Title = "EspaƱol", Type = ActionTypes.PostBack, Value = Spanish },
new CardAction() { Title = "English", Type = ActionTypes.PostBack, Value = English },
},
};
await turnContext.SendActivityAsync(reply);
}
}
Expecting that string in the CardAction() should also be translated into the language chosen by the user.
I assume you are using Microsoft Translator class that comes with the Sample. From the same sample, I implemented a new class (MultilingualCardAction) by inheriting CardAction class.
This works for me but there may be better ways as well.
public class MultilingualCardAction : CardAction
{
private readonly MicrosoftTranslator _translator;
private string _language;
public MultilingualCardAction(string language)
{
_language = language;
_translator = new MicrosoftTranslator(<<YOUR TRANSLATION KEY>>);
}
public string cardTitle
{
get
{
return this.Title;
}
set
{
this.Title = getTranslatedText(value).Result;
}
}
async Task<string> getTranslatedText(string title)
{
return await _translator.TranslateAsync(title, _language);
}
}
Then I created the CardAction object this way.
var newRply = turnContext.Activity.CreateReply("Is there anything else I can help you with?");
newRply.SuggestedActions = new SuggestedActions()
{
Actions = new List<CardAction>()
{
new MultilingualCardAction('es') { cardTitle = "Yes", Type = ActionTypes.PostBack, Value = "Yes" },
new MultilingualCardAction('es') { cardTitle = "No, thanks!", Type = ActionTypes.PostBack, Value = "No" },
},
};
await turnContext.SendActivityAsync(newRply);
Refer the image below.

How to return top previous URL after Edit in ASP.NET Core

I am using asp,net core and have used the tutorial to create sorted, paged and search page (Index). Once I edit an item from this page the controller always dumps me back to the default index page. How do I return to the previous URL. Many thanks.
Here is a section of my controller file.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, Bind("id,UserPassword,user")] UserProfiles userProfiles)
{
var users = from u in _context.UserProfiles
select u;
if (id != userProfiles.id)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(userProfiles);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!UserProfilesExists(userProfiles.id))
{
return NotFound();
}
else
{
throw;
}
}
// ***************
// Redirect to the previous URL,i.e. the Index
return Redirect(TempData["PreviousURL"].ToString()) ;
}
return View(userProfiles);
}
public async Task<IActionResult> Index(string sortOrder, string currentFilter, string searchString, int? page)
{
ViewData["CurrentSort"] = sortOrder;
ViewData["NameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
// paging
if (searchString != null)
{
page = 1;
}
else
{
searchString = currentFilter;
}
// search
ViewData["CurrentFilter"] = searchString;
var users = from u in _context.UserProfiles
select u;
if (!String.IsNullOrEmpty(searchString))
{
users = users.Where(u => u.user.Contains(searchString)
);
}
//sort
switch (sortOrder)
{
case "name_desc":
users = users.OrderByDescending(u => u.user);
break;
default:
users = users.OrderBy(s => s.user);
break;
}
// ***************
// store the current path and query string in TempData["PreviousURL" session variable
TempData["PreviousURL"] = HttpContext.Request.Path.ToString() + HttpContext.Request.QueryString.ToString();
return View(await PaginatedList<UserProfiles>.CreateAsync(users.AsNoTracking(), page ?? 1, pageSize));
}
This is my first MVC project.
It depends on your logic where controller takes you after saving data.
You need to pass search, sort and paging related data to controller when saving data. You can send them as part of extra post data, as query string parameters or as part of the model itself which is being posted.
After saving data retrieve data based on those parameters and populater your view with that paged, filtred and sorted data.
I solved my problem with the use of session variables: ViewData, ViewBag and TempData. The following two pages were very useful:
https://www.codeproject.com/Articles/476967/What-is-ViewData-ViewBag-and-TempData-MVC-Option
http://andrewlock.net/an-introduction-to-session-storage-in-asp-net-core/
Please see edited question above for the solution.

How to intercept hardware Bar Back Button Clicked in Android in Xamarin Forms?

I want to intercept hardware back buttion in my Xamarin Forms Portable app, At First I have login page,You have to login and it will navigate to my Homepage2.. after going to my homepage2, when i click on my hardware backbutton...It goes to my login page or the pages i opened previously.. I want to Prevent it. Can anyone please resolve me this issue..
here is my login page
public LoginPage()
{
InitializeComponent();
}
public async void LoginBtn_Clicked(object sender, EventArgs e)
{
if (UserName.Text == null || password1.Text == null)
{
DisplayAlert("Alert ! ", "Please Enter UserName Or/Password!", "OK");
}
else
{
string uname = UserName.Text;
string pswd = password1.Text;
LoginService objservice = new LoginService();
LoginTokenModel result = await objservice.GetLogin(uname, pswd);
LoginTokenModel logintokenmodel = new LoginTokenModel();
logintokenmodel.User_Id = result.User_Id;
var Login_Token = result.Login_Token;
int user_Id = result.User_Id;
if (uname == result.User_Nmae)
{
// HomePage2 HOMEPge = new HomePage2();
await Navigation.PushModalAsync(new HomePage2(user_Id));
}
else
{
DisplayAlert("Alert ! ", "Invalid Credentials!", "OK");
}
}
}
Replace the App's MainPage with your Homepage2.
as you are not coming back on Login page you don'n want it in the NavigationStack.
This is what I do on successful Login:
if (uname == result.User_Nmae)
{
App.Current.MainPage = new HomePage2(user_Id);
}
In addition you can also keep this user_Id stored for the second time when the user enters again in application properties and check if the user_Id exists or not, if it does just navigate to Homepage2
Application.Current.Properties["id"] = user_Id;
in App.cs onStart
if (Application.Current.Properties.ContainsKey("id"))
{
var user_Id = Application.Current.Properties["id"] as string;
MainPage.Navigation.PushModalAsync(new Views.Homepage2(user_Id ));
}
else
{
MainPage.Navigation.PushModalAsync(new Views.Login());
}

Need if-else advice in actionscript3

function clickButtonHandler(event:MouseEvent):void
{
var message:Object = new Object();
message.text = txtMessage.text;
message.userName = txtUser.text;
//Posts to this swf
showMessage(message);
//Posts to ALL OTHER swf files..
group.post(message);
}
function showMessage(message:Object):void
{
output_txt.appendText(message.userName+": "+message.text + "\n");
}
function jsalertwindow(event:MouseEvent):void
{
var alert:URLRequest = new URLRequest("javascript:alert('Please enter your User name')");
navigateToURL(alert, "_self");
}
As you can see there are two function which are contain mouseevent. I want to send those function with an if-else statement. If user write something in text input component which name is txtUser and,
send_btn.addEventListener(MouseEvent.CLICK, clickButtonHandler);
will work, else(if the user forget writing anything)
send_btn.addEventListener(MouseEvent.CLICK, jsalertwindow);
will work.
And one more question should i use MouseEvent.CLICK or MouseEvent.MOUSE_DOWN? Thanks for your advice.
Assign a single handler to the button click (MouseEvent.CLICK is the right event to use) and check the field is populated in the handler:
function clickButtonHandler(event:MouseEvent):void
{
var message:Object = new Object();
// Check the field is populated
if (txtUser.text != "")
{
message.text = txtMessage.text;
message.userName = txtUser.text;
showMessage(message);
//Posts to ALL OTHER swf files..
group.post(message);
}
else
{
// Nothing in the input field, show the alert
showAlert();
}
}
function showMessage(message:Object):void
{
output_txt.appendText(message.userName+": "+message.text + "\n");
}
function showAlert():void
{
var alert:URLRequest = new URLRequest("javascript:alert('Please enter your User name')");
navigateToURL(alert, "_self");
}

Resources