BotFramework-WebChat
BotFramework-WebChat copied to clipboard
webchat.js open links in same tab
Is it an issue related to Adaptive Cards?
No
Is this an accessibility issue?
No
What version of Web Chat are you using?
Latest production
Which distribution are you using Web Chat from?
Bundle (webchat.js)
Which hosting environment does this issue primarily affect?
SharePoint
Which browsers and platforms do the issue happened?
Browser: Edge (latest), Browser: Chrome (latest), Browser: Firefox (latest), Browser: Safari (latest), Platform: iOS/iPadOS, Platform: Android
Which area does this issue affect?
Suggested action
What is the public URL for the website?
https://yourroomuat.citc.health.nsw.gov.au/
Please describe the bug
We are using webchat.js with directline in our html file, the bot works however all the hyperlinks in the bot are coming up with target set to as "_blank" even internal url's as well as external. We wanted to check how we can change this behavior. We did try to fix it with the markdown.js file as described here however it doesn't fix the complete problem as we have suggested actions with link and those still have hyperlinks set as target blank.
Is there a way to update these so that any internal link within the site comes as _self target and external link as _blank.
Version we are currently using '\n\n\n\n'
Do you see any errors in console log?
no
How to reproduce the issue?
open chat bot and type quit and click send
What do you expect?
Links to show however the internal links to open in the same tab and external links to open in a new tab. The site also has pdf and they should open in new tab
What actually happened?
All links open in new tab including internal, external and pdf as well. We need internal links to open in the same tab
Do you have any screenshots or recordings to repro the issue?
No response
Adaptive Card JSON
No response
Additional context
No response
We are following the below link for the bot development
@jagjotkhera the link provided doesn't work to me. I'm getting:
504 Gateway Time-out
So I have to ask: is the issue appears with Suggeted action links or Adaptive card links? More details here: https://github.com/microsoft/BotFramework-WebChat/issues/3833#issuecomment-815145159
The progress on implementing a centralized way of handling links is tracked in #2892
@OEvgeny thanks for responding, I believe the site is only accessible from Australia which is why you are getting 504.
To answer your question we are using List style hero card on a multichoice, please see below screenshot from composer
Also we had a look at issue 2892 and implemented the markdown functionality and it works for the plain text (see below)
However when the link is part of the List style hero card on a multichoice the markdown doesn't work (see below)
Please let me know if you need any more information.
@jagjotkhera got it, I'll look into it tomorrow. I think, it falls into the Adaptive Cards bucket and if they support adjusting action link targets, you should be able to create a different card with actions.
Alternatively you could try using cardActionMiddleware
to adjust actions.
Thanks @OEvgeny , I'm not sure what you mean by cardActionMiddleware as in our webchat.js we are rendering the bot like this
window.WebChat.renderWebChat( { directLine: directLine, username: 'Web Chat User', locale: 'en-AU', renderMarkdown: text => MarkdownIt.render(text), // Passing 'styleSet' when rendering Web Chat styleSet, styleOptions: { hideUploadButton: true, autoScrollSnapOnPage: messSnap, autoScrollSnapOnPageOffset: -75, suggestedActionLayout: 'stacked', }, overrideLocalizedStrings: (strings, language) => ({ ...strings, TEXT_INPUT_PLACEHOLDER: 'Ask a Question ...' }), }, Can you please let me know where do i need to update
You're still able to use the middleware to adjust the actions. See this example.
More about hero card here.
Unfortunatelly adaptive cards OpenUrl
action doesn't provide a way to configure the link target. So it looks like the middleware is the only way to implement the functionality at the moment.
Thanks @OEvgeny , I did try to implement the middleware option however I don't think the code is being called. See below my render method function loadBotResources(bearerToken) { /* ChatBot not to be loaded on pages in the games and tools site, if url contains 'games-and-tools', skip chatbot injection. */
if(_spPageContextInfo.webServerRelativeUrl.indexOf('games-and-tools') <0)
{
/* ChatBot Jquery page injection code Start */
jQuery("document").ready(function(){
jQuery("head").append('<style type="text/css" href="' + _spPageContextInfo.siteAbsoluteUrl + '/Style%20Library/ChatBot/YourRoomChatBot.css"></style>')
jQuery("head").append('<link id="ChatBotCSSLink" rel="stylesheet" type="text/css" href="' + _spPageContextInfo.siteAbsoluteUrl + '/Style%20Library/ChatBot/YourRoomChatBot.css">')
jQuery("head").append('<link rel="preconnect" href="https://fonts.googleapis.com">')
jQuery("head").append('<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>')
jQuery("head").append('<link href="https://fonts.googleapis.com/css2?family=Inter&display=swap" rel="stylesheet">')
jQuery('#yourroomchatbot').html('<!-- Icon Expanded View Code --><div id="ChatBotIconBanner" role="banner"><div id="ChatBotBannerContent" role="group">' +
'<div id="ChatBotIcon" role="banner" onclick="ShowChatBot()"><img src="' + _spPageContextInfo.siteAbsoluteUrl + '/Style%20Library/ChatBot/Chatbot.png" ' +
'title="YasBot Icon" alt="YasBot Icon"></img></div><h3 id="ChatBotBannerText" onclick="ShowChatBot()">Ask me questions ...</h3><img src="' +
_spPageContextInfo.siteAbsoluteUrl + '/Style%20Library/ChatBot/questions.png" title="Questions Icon" alt="Questions Icon" onclick="ShowChatBot()"></im' +
'g></div></div><!-- Icon Minimised View Code --><div id="ChatBotIconMin" role="banner"><img src="' + _spPageContextInfo.siteAbsoluteUrl + '/Style%20Lib' +
'rary/ChatBot/Chatbot.png" title="YasBot Icon Minimised" alt="YasBot Icon Minimised" onclick="ShowChatBot()"></img></div><!-- BotView Code --><div id="' +
'chatBotIframe" role="main" onfocusout="hideChatbot"><div id="chatBotHeader" role="banner"><div id="ChatBotHeaderLogo" role="img"><img src="' +
_spPageContextInfo.siteAbsoluteUrl + '/Style%20Library/ChatBot/Chatbot.png" title="YASLogo" alt="Yaz ChatBot Logo"></img></div><div id="ChatBotHeaderTe' +
'xt" role="heading"><h3>Talk to Yas</h3></div><div id=MinimiseChatBot role="button"><img src="' + _spPageContextInfo.siteAbsoluteUrl + '/Style%20Library/C' +
'hatBot/minimise.png" title="Minimise ChatBot" alt="Minimise ChatBot" onclick="ShowIconMin()"></img></div></div><div id="chatBotBody" role="main"></div><div id=' +
'"chatBotFooter" role="contentinfo"><div id="FooterLinks" role="link"><p><a href="#" onclick="clickPrivacy()">Privacy information</a> | <a href="#" onclick="clickEmergency()">Emergency information</a></p' +
'></div><div class="FooterImg" role="img"><img src="' + _spPageContextInfo.siteAbsoluteUrl + '/Style%20Library/ChatBot/FontSizeIcon.png" title="Font si' +
'ze icon" onclick="fontToggle()" alt="Toggle font size"></img></div><div class="FooterImg" role="img"><img src="' + _spPageContextInfo.siteAbsoluteUrl +
'/Style%20Library/ChatBot/Phone.webp" onclick="clickPhone()" title="Phone Icon" alt="Support contacts"></img></div></div>')
jQuery('#ChatBotIconM').hide();
jQuery('#ChatBotIconBanner').hide();
jQuery('#ChatBotIconMin').hide();
});
/* ChatBot jQuery page injection code Start */
//Init fontSize Variable
fontSize = false
/* ChatBot Bot Framework config and render code Start */
import('' + _spPageContextInfo.siteAbsoluteUrl + '/Style%20Library/ChatBot/webchat.js').then(WebChat => {
(async function () {
//create session to hold conversation token
let { token, conversation_Id, timeStamp } = sessionStorage;
if(!token || tokenExpired()) {
console.log("Attempting to retrieve a new token from 'https://directline.botframework.com//v3/directline/tokens/generate'");
const res = await fetch('https://directline.botframework.com//v3/directline/tokens/generate', { method: 'POST', headers: { Authorization :bearerToken}});
const { token: directLineToken, conversationId, expires_in } = await res.json();
//Save token to session
if(res.status == 200)
{
sessionStorage[ 'token' ] = directLineToken;
sessionStorage[ 'conversation_Id' ] = conversationId;
sessionStorage[ 'timeStamp' ] = Date.now() + expires_in * 1000;
token = directLineToken;
}
}
const directLine = createDirectLine( {
token
});
// "styleSet" is a set of CSS rules which are generated from "styleOptions"
const styleSet = window.WebChat.createStyleSet({
// BotWindow customisatons
rootHeight: '100%',
rootWidth: '100%',
backgroundColor: 'white',
showSpokenText: true,
videoHeight: 270, // based on bubbleMaxWidth, 480 / 16 * 9 = 270
paddingRegular: 5,
paddingWide: 10,
// YASbot bubble customisations
botAvatarImage: '/Style%20Library/ChatBot/Chatbot.png',
botAvatarInitials: 'YAS',
bubbleBackground: '#D9D9D9',
bubbleBorder: 'solid 1px #444',
bubbleBorderRadius: 10,
bubblePadding: 5,
bubbleTextColor: 'black',
// User bubble custominsations
userAvatarImage: '/Style%20Library/ChatBot/questions.png',
userAvatarInitials: 'YU',
bubbleFromUserBackground: '#62CDC7',
bubbleFromUserBorder: 'solid 1px #444',
bubbleFromUserBorderRadius: 10,
bubbleFromUserTextColor: 'black',
// Sendbox customisations
hideSendBox: false,
hideUploadButton: 1,
microphoneButtonColorOnDictate: '#F33',
sendBoxBackground: 'white',
sendBoxButtonColor: '#62CDC7', // defaults to subtle
sendBoxButtonColorOnDisabled: '#CCC',
sendBoxButtonColorOnFocus: '#333',
sendBoxButtonColorOnHover: '#333',
sendBoxDisabledTextColor: '#CCC', // defaults to subtle
sendBoxHeight: 40,
sendBoxMaxHeight: 200,
sendBoxTextColor: '#818080',
sendBoxBorderBottom: '',
sendBoxBorderLeft: '',
sendBoxBorderRight: '',
sendBoxBorderTop: 'solid 1px #E6E6E6',
sendBoxPlaceholderColor: '#818080', // defaults to subtle
sendBoxTextWrap: true,
suggestedActionBorderRadius: 4,
});
// After generated, you can modify the CSS rules
styleSet.textContent = {
...styleSet.textContent,
fontFamily: "'Inter', 'Arial', sans-serif",
fontWeight: 'regular',
lineHeight: 1.25,
};
window.WebChat.renderWebChat(
{
directLine: directLine,
username: 'Web Chat User',
locale: 'en-AU',
// We are adding a new middleware to handle card action
cardActionMiddleware: () => next => async ({ cardAction, getSignInUrl }) => {
console.log(cardAction)
const { type, value } = cardAction;
switch (type) {
case 'signin':
console.log("signin")
// For OAuth or sign-in popups, we will open the auth dialog directly.
const popup = window.open();
const url = await getSignInUrl();
popup.location.href = url;
break;
case 'openUrl':
console.log("openurl")
if (confirm(`Do you want to open this URL?\n\n${value}`)) {
window.open(value, '_blank');
}
break;
default:
console.log("default")
return next({ cardAction, getSignInUrl });
}
},
// Passing 'styleSet' when rendering Web Chat
styleSet,
styleOptions: {
hideUploadButton: true,
autoScrollSnapOnPage: messSnap,
autoScrollSnapOnPageOffset: -75,
suggestedActionLayout: 'stacked',
},
overrideLocalizedStrings: (strings, language) => ({
...strings,
TEXT_INPUT_PLACEHOLDER: 'Ask a Question ...'
}),
},
document.getElementById('chatBotBody')
);
document.querySelector('#chatBotBody> *').focus();
/* ChatBot Icon animation control, if panel is smaller than 600px
Flash the ChatBotIconMin, else run the full expand animation */
if (jQuery(window).width() > 767) {
expandBotIcon()
} else {
flashBotIconMin()
}
})().catch(err => console.error(err));
}).catch(err => console.error(err));
/* ChatBot Bot Framework config and render code Finished */
}
}
I have added console.log in the switch case but the code is not getting triggered. Can you please validate if this is what you were suggesting.
On further checking the code is getting called when the user clicks on any of the cards (multichoice suggested action) within the response and the card type is coming as "imBack", see below
I was able to verify the middleware is running and is able to recieve the correct action along with values passed:
cardActionMiddleware: () => next => async ({ cardAction, getSignInUrl }) => {
const { type, value } = cardAction;
switch (type) {
default:
console.log(type, value)
return next({ cardAction, getSignInUrl });
}
},
Attachment sent:
"attachments": [
{
"contentType": "application/vnd.microsoft.card.hero",
"content": {
"title": "Hero Card Actions",
"buttons": [
{
"type": "imBack",
"title": "imBack",
"value": "string"
},
{
"type": "postBack",
"title": "postBack (string)",
"value": "string"
},
{
"type": "postBack",
"title": "postBack (JSON)",
"value": {
"value": "value"
}
},
{
"type": "messageBack",
"title": "messageBack (displayText + text + value)",
"text": "\"text\"",
"displayText": "displayText",
"value": {
"value": "value"
}
},
{
"type": "messageBack",
"title": "messageBack (displayText + text)",
"text": "\"text\"",
"displayText": "displayText"
},
{
"type": "messageBack",
"title": "messageBack (value)",
"value": {
"value": "value"
}
},
{
"type": "postBack",
"title": "postBack (empty)"
},
{
"type": "messageBack",
"title": "messageBack (empty)"
}
]
}
}
],
@jagjotkhera you should ignore actions you don't want to take control over, returning next(...)
. This is basically how middleware work
@OEvgeny , the attachment which you have sent to the webchat.js is not how we are sending data, we are sending it via the composer so dont have control over it snapshot of composer code
Also we want to capture this entire event as the links are in the text
what do you recommend we change
As far as I can tell, if you're using hero cards, you'll see actions with similary looking attachments comming your way through the directline connection.
Then you should be able to adjust the middleware on your end to respond to actions on your need. Logging every action as shown above, and inspecting the network, should help to figure out the rest of implementation details
@OEvgeny i was able to get the attachment via postman and below is the response
Any thoughts how we can get this "Text" as part of the Middleware as this is where the hyperlinks are and we need to update the target.
Complete postman response PostManResponse.json
@jagjotkhera from the payload, it looks like the hero card is not configured properly (see the action type for all actions is set to imBack
instead of openUrl
). And also the URL is missing.
Suggesting to look into Composer tutorials for an example of cards usage.
This is not a Composer support forum, the place is dedicated for WebChat issues only. Please use either StackOverflow or Composer Discussions if you have any further question regarding Composer
Closing due to inactivity